diff --git a/examples/zolertia/zoul/Makefile b/examples/zolertia/zoul/Makefile index 7ead94a69..3cfa30021 100644 --- a/examples/zolertia/zoul/Makefile +++ b/examples/zolertia/zoul/Makefile @@ -3,9 +3,10 @@ DEFINES+=PROJECT_CONF_H=\"project-conf.h\" CONTIKI_PROJECT = zoul-demo test-tsl2563 test-sht25 test-pwm test-power-mgmt CONTIKI_PROJECT += test-bmp085 test-motion test-rotation-sensor CONTIKI_PROJECT += test-grove-light-sensor test-grove-loudness-sensor +CONTIKI_PROJECT += test-weather-meter CONTIKI_TARGET_SOURCEFILES += tsl2563.c sht25.c bmp085.c motion-sensor.c -CONTIKI_TARGET_SOURCEFILES += adc-sensors.c +CONTIKI_TARGET_SOURCEFILES += adc-sensors.c weather-meter.c all: $(CONTIKI_PROJECT) diff --git a/examples/zolertia/zoul/test-weather-meter.c b/examples/zolertia/zoul/test-weather-meter.c new file mode 100644 index 000000000..2e2bcba2f --- /dev/null +++ b/examples/zolertia/zoul/test-weather-meter.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2016, Zolertia - http://www.zolertia.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup zoul-examples + * @{ + * + * \defgroup zoul-weather-meter-test Test the Sparkfun's weather meter + * + * The example application shows how to read data from the anemometer, wind vane + * and rain gauge, on board the Sparkfun's weater meter + * + * @{ + * + * \file + * Test application for the Sparkfun's weather meter + * + * \author + * Antonio Lignan + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "cpu.h" +#include "sys/etimer.h" +#include "dev/leds.h" +#include "dev/weather-meter.h" + +#include +#include +/*---------------------------------------------------------------------------*/ +#define READ_SENSOR_PERIOD CLOCK_SECOND +#define ANEMOMETER_THRESHOLD_TICK 13 /**< 16 Km/h */ +#define RAIN_GAUGE_THRESHOLD_TICK 15 /**< each increment of 4.19 mm */ +/*---------------------------------------------------------------------------*/ +static struct etimer et; +/*---------------------------------------------------------------------------*/ +PROCESS(test_weather_meter_sensors, "Test Weather meter sensors"); +AUTOSTART_PROCESSES(&test_weather_meter_sensors); +/*---------------------------------------------------------------------------*/ +static void +rain_callback(uint16_t value) +{ + /* To calculate ticks from mm of rain, divide by 0.2794 mm */ + printf("*** Rain gauge over threshold (%u ticks)\n", value); + weather_meter.configure(WEATHER_METER_RAIN_GAUGE_INT_OVER, + (value + RAIN_GAUGE_THRESHOLD_TICK)); +} +/*---------------------------------------------------------------------------*/ +static void +wind_speed_callback(uint16_t value) +{ + /* This checks for instant wind speed values (over a second), the minimum + * value is 1.2 Km/h (one tick), as the reference is 2.4KM/h per rotation, and + * the anemometer makes 2 ticks per rotation. Instant speed is calculated as + * multiples of this, so if you want to check for 16Km/h, then it would be 13 + * ticks + */ + printf("*** Wind speed over threshold (%u ticks)\n", value); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(test_weather_meter_sensors, ev, data) +{ + PROCESS_BEGIN(); + + static uint32_t rain; + static uint16_t wind_speed; + static uint16_t wind_dir; + static uint16_t wind_dir_avg_2m; + static uint16_t wind_speed_avg; + static uint16_t wind_speed_avg_2m; + static uint16_t wind_speed_max; + + printf("Weather meter test example, integration period %u\n", + WEATHER_METER_AVG_PERIOD); + + /* Register the callback handler when thresholds are met */ + WEATHER_METER_REGISTER_ANEMOMETER_INT(wind_speed_callback); + WEATHER_METER_REGISTER_RAIN_GAUGE_INT(rain_callback); + + /* Enable the sensors, this has to be called before any of the interrupt calls + * like the ones below + */ + SENSORS_ACTIVATE(weather_meter); + + /* And the upper threshold value to compare and generate an interrupt */ + weather_meter.configure(WEATHER_METER_ANEMOMETER_INT_OVER, + ANEMOMETER_THRESHOLD_TICK); + weather_meter.configure(WEATHER_METER_RAIN_GAUGE_INT_OVER, + RAIN_GAUGE_THRESHOLD_TICK); + + etimer_set(&et, READ_SENSOR_PERIOD); + + while(1) { + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + + rain = weather_meter.value(WEATHER_METER_RAIN_GAUGE); + wind_speed = weather_meter.value(WEATHER_METER_ANEMOMETER); + wind_dir = weather_meter.value(WEATHER_METER_WIND_VANE); + wind_dir_avg_2m = weather_meter.value(WEATHER_METER_WIND_VANE_AVG_X); + wind_speed_avg = weather_meter.value(WEATHER_METER_ANEMOMETER_AVG); + wind_speed_avg_2m = weather_meter.value(WEATHER_METER_ANEMOMETER_AVG_X); + wind_speed_max = weather_meter.value(WEATHER_METER_ANEMOMETER_MAX); + +#if WEATHER_METER_RAIN_RETURN_TICKS + rain *= WEATHER_METER_AUX_RAIN_MM; + if(rain > (WEATHER_METER_AUX_RAIN_MM * 3)) { + printf("Rain: %lu.%lu mm, ", (rain / 10000), (rain % 10000)); +#else + if(rain >= 10) { + printf("Rain: %u.%u mm, ", (rain / 10), (rain % 10)); +#endif + } else { + printf("Rain: 0.%lu mm, ", rain); + } + + printf("Wind dir: %u.%01u deg, ", (wind_dir / 10), (wind_dir % 10)); + printf("(%u.%01u deg avg)\n", (wind_dir_avg_2m / 10), (wind_dir_avg_2m % 10)); + printf("Wind speed: %u m/h ", wind_speed); + printf("(%u m/h avg, %u m/h 2m avg, %u m/h max)\n\n", wind_speed_avg, + wind_speed_avg_2m, + wind_speed_max); + etimer_reset(&et); + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + * @} + */ diff --git a/examples/zolertia/zoul/zoul-demo.c b/examples/zolertia/zoul/zoul-demo.c index 127c18d50..a88e7c48c 100644 --- a/examples/zolertia/zoul/zoul-demo.c +++ b/examples/zolertia/zoul/zoul-demo.c @@ -108,7 +108,7 @@ broadcast_recv(struct broadcast_conn *c, const linkaddr_t *from) static const struct broadcast_callbacks bc_rx = { broadcast_recv }; static struct broadcast_conn bc; /*---------------------------------------------------------------------------*/ -void +static void rt_callback(struct rtimer *t, void *ptr) { leds_off(LEDS_PERIODIC); diff --git a/platform/zoul/dev/weather-meter.c b/platform/zoul/dev/weather-meter.c new file mode 100644 index 000000000..8d3ee07f1 --- /dev/null +++ b/platform/zoul/dev/weather-meter.c @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2016, Zolertia + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup zoul-weather-meter-sensor + * @{ + * + * The Sparkfun's weather meter comprises an anemometer, wind vane and rain + * gauge, see https://www.sparkfun.com/products/8942 + * + * \file + * Weather meter sensor driver + * \author + * Antonio Lignan + */ +/*---------------------------------------------------------------------------*/ +#include +#include "contiki.h" +#include "dev/adc-sensors.h" +#include "dev/weather-meter.h" +#include "dev/zoul-sensors.h" +#include "lib/sensors.h" +#include "dev/sys-ctrl.h" +#include "dev/gpio.h" +#include "dev/ioc.h" +#include "sys/timer.h" +#include "sys/rtimer.h" +/*---------------------------------------------------------------------------*/ +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif +/*---------------------------------------------------------------------------*/ +#define DEBOUNCE_DURATION (CLOCK_SECOND >> 6) +/*---------------------------------------------------------------------------*/ +#define ANEMOMETER_SENSOR_PORT_BASE GPIO_PORT_TO_BASE(ANEMOMETER_SENSOR_PORT) +#define ANEMOMETER_SENSOR_PIN_MASK GPIO_PIN_MASK(ANEMOMETER_SENSOR_PIN) +#define RAIN_GAUGE_SENSOR_PORT_BASE GPIO_PORT_TO_BASE(RAIN_GAUGE_SENSOR_PORT) +#define RAIN_GAUGE_SENSOR_PIN_MASK GPIO_PIN_MASK(RAIN_GAUGE_SENSOR_PIN) +/*---------------------------------------------------------------------------*/ +void (*rain_gauge_int_callback)(uint16_t value); +void (*anemometer_int_callback)(uint16_t value); +/*---------------------------------------------------------------------------*/ +static uint8_t enabled; +/*---------------------------------------------------------------------------*/ +process_event_t anemometer_int_event; +process_event_t rain_gauge_int_event; +/*---------------------------------------------------------------------------*/ +static struct rtimer rt; +static struct timer debouncetimer; +/*---------------------------------------------------------------------------*/ +typedef struct { + uint16_t ticks; + uint16_t value; + uint8_t int_en; + uint16_t int_thres; +} weather_meter_sensors_t; + +typedef struct { + uint16_t value_max; + uint64_t ticks_avg; + uint64_t value_avg; + uint32_t value_buf_xm; + uint16_t value_avg_xm; +} weather_meter_ext_t; + +typedef struct { + uint16_t wind_vane; + weather_meter_sensors_t rain_gauge; + weather_meter_sensors_t anemometer; +} weather_meter_sensors; + +typedef struct { + uint32_t value_buf_xm; + uint16_t value_prev; + uint16_t value_avg_xm; +} weather_meter_wind_vane_ext_t; + +static weather_meter_sensors weather_sensors; +static weather_meter_ext_t anemometer; +static weather_meter_wind_vane_ext_t wind_vane; +/*---------------------------------------------------------------------------*/ +typedef struct { + uint16_t mid_point; + uint16_t degree; +} wind_vane_mid_point_t; + +/* From the datasheet we adjusted the values for a 3V divider, using a 10K + * resistor, the check values are the following: + * --------------------+------------------+------------------------------- + * Direction (Degrees) Resistance (Ohms) Voltage (mV) + * 0 33k 2532.55 * + * 22.5 6.57k 1308.44 * + * 45 8.2k 1486.81 * + * 67.5 891 269.97 * + * 90 1k 300.00 * + * 112.5 688 212.42 * + * 135 2.2k 595.08 * + * 157.5 1.41k 407.80 * + * 180 3.9k 925.89 * + * 202.5 3.14k 788.58 * + * 225 16k 2030.76 * + * 247.5 14.12k 1930.84 * + * 270 120k 3046.15 * + * 292.5 42.12k 2666.84 * + * 315 64.9k 2859.41 * + * 337.5 21.88k 2264.86 * + * --------------------+------------------+------------------------------- + */ +static const wind_vane_mid_point_t wind_vane_table[16] = { + { 2124, 1125 }, + { 2699, 675 }, + { 3000, 900 }, + { 4078, 1575 }, + { 5950, 1350 }, + { 7885, 2025 }, + { 9258, 1800 }, + { 13084, 225 }, + { 14868, 450 }, + { 19308, 2475 }, + { 20307, 2250 }, + { 22648, 3375 }, + { 25325, 0 }, + { 26668, 2925 }, + { 28594, 3150 }, + { 30461, 2700 }, +}; +/*---------------------------------------------------------------------------*/ +static int +weather_meter_wind_vane_degrees(uint16_t value) +{ + uint8_t i; + for(i = 0; i < 16; i++) { + if(value <= wind_vane_table[i].mid_point) { + return (int)wind_vane_table[i].degree; + } else { + if(i == 15) { + return (int)wind_vane_table[i].degree; + } + } + } + + PRINTF("Weather: invalid wind vane value\n"); + return WEATHER_METER_ERROR; +} +/*---------------------------------------------------------------------------*/ +static int +weather_meter_get_wind_dir(void) +{ + weather_sensors.wind_vane = adc_sensors.value(WIND_VANE_ADC); + if((int16_t)weather_sensors.wind_vane < 0) { + weather_sensors.wind_vane = 0; + } + return weather_meter_wind_vane_degrees(weather_sensors.wind_vane); +} +/*---------------------------------------------------------------------------*/ +static void +rt_callback(struct rtimer *t, void *ptr) +{ + uint32_t wind_speed; + int16_t wind_dir; + int16_t wind_dir_delta; + + /* Disable to make the calculations in an interrupt-safe context */ + GPIO_DISABLE_INTERRUPT(ANEMOMETER_SENSOR_PORT_BASE, + ANEMOMETER_SENSOR_PIN_MASK); + wind_speed = weather_sensors.anemometer.ticks; + wind_speed *= WEATHER_METER_ANEMOMETER_SPEED_1S; + weather_sensors.anemometer.value = (uint16_t)wind_speed; + anemometer.ticks_avg++; + anemometer.value_avg += weather_sensors.anemometer.value; + anemometer.value_buf_xm += weather_sensors.anemometer.value; + + /* Take maximum value */ + if(weather_sensors.anemometer.value > anemometer.value_max) { + anemometer.value_max = weather_sensors.anemometer.value; + } + + /* Mitsuta method to get the wind direction average */ + wind_dir = weather_meter_get_wind_dir(); + wind_dir_delta = wind_dir - wind_vane.value_prev; + + if(wind_dir_delta < -1800) { + wind_vane.value_prev += wind_dir_delta + 3600; + } else if(wind_dir_delta > 1800) { + wind_vane.value_prev += wind_dir_delta - 3600; + } else { + wind_vane.value_prev += wind_dir_delta; + } + + wind_vane.value_buf_xm += wind_vane.value_prev; + + /* Calculate the 2 minute average */ + if(!(anemometer.ticks_avg % WEATHER_METER_AVG_PERIOD)) { + PRINTF("\nWeather: calculate the %u averages ***\n", WEATHER_METER_AVG_PERIOD); + + if(anemometer.value_buf_xm) { + anemometer.value_avg_xm = anemometer.value_buf_xm / WEATHER_METER_AVG_PERIOD; + anemometer.value_buf_xm = 0; + } else { + anemometer.value_avg_xm = 0; + } + + wind_vane.value_buf_xm = wind_vane.value_buf_xm / WEATHER_METER_AVG_PERIOD; + wind_vane.value_avg_xm = (uint16_t)wind_vane.value_buf_xm; + if(wind_vane.value_avg_xm >= 3600) { + wind_vane.value_avg_xm -= 3600; + } + + if(wind_vane.value_avg_xm < 0) { + wind_vane.value_avg_xm += 3600; + } + + wind_vane.value_buf_xm = 0; + wind_vane.value_prev = wind_dir; + } + + /* Check for roll-over */ + if(!anemometer.ticks_avg) { + anemometer.value_avg = 0; + } + + weather_sensors.anemometer.ticks = 0; + + /* Enable the interrupt again */ + GPIO_ENABLE_INTERRUPT(ANEMOMETER_SENSOR_PORT_BASE, + ANEMOMETER_SENSOR_PIN_MASK); + rtimer_set(&rt, RTIMER_NOW() + RTIMER_SECOND, 1, rt_callback, NULL); +} +/*---------------------------------------------------------------------------*/ +PROCESS(weather_meter_int_process, "Weather meter interrupt process handler"); +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(weather_meter_int_process, ev, data) +{ + PROCESS_EXITHANDLER(); + PROCESS_BEGIN(); + + while(1) { + PROCESS_YIELD(); + + if((ev == anemometer_int_event) && (weather_sensors.anemometer.int_en)) { + if(weather_sensors.anemometer.ticks >= + weather_sensors.anemometer.int_thres) { + anemometer_int_callback(weather_sensors.anemometer.ticks); + } + } + + if((ev == rain_gauge_int_event) && (weather_sensors.rain_gauge.int_en)) { + if(weather_sensors.rain_gauge.ticks >= + weather_sensors.rain_gauge.int_thres) { + rain_gauge_int_callback(weather_sensors.rain_gauge.ticks); + } + } + } + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +static void +weather_meter_interrupt_handler(uint8_t port, uint8_t pin) +{ + uint32_t aux; + + /* Prevent bounce events */ + if(!timer_expired(&debouncetimer)) { + return; + } + + timer_set(&debouncetimer, DEBOUNCE_DURATION); + + /* We make a process_post() to check in the pollhandler any specific threshold + * value + */ + + if((port == ANEMOMETER_SENSOR_PORT) && (pin == ANEMOMETER_SENSOR_PIN)) { + weather_sensors.anemometer.ticks++; + process_post(&weather_meter_int_process, anemometer_int_event, NULL); + } else if((port == RAIN_GAUGE_SENSOR_PORT) && (pin == RAIN_GAUGE_SENSOR_PIN)) { + weather_sensors.rain_gauge.ticks++; + aux = weather_sensors.rain_gauge.ticks * WEATHER_METER_AUX_RAIN_MM; + aux /= 1000; + weather_sensors.rain_gauge.value = (uint16_t)aux; + process_post(&weather_meter_int_process, rain_gauge_int_event, NULL); + } +} +/*---------------------------------------------------------------------------*/ +static int +value(int type) +{ + uint64_t aux; + + if((type != WEATHER_METER_ANEMOMETER) && + (type != WEATHER_METER_RAIN_GAUGE) && + (type != WEATHER_METER_WIND_VANE) && + (type != WEATHER_METER_WIND_VANE_AVG_X) && + (type != WEATHER_METER_ANEMOMETER_AVG) && + (type != WEATHER_METER_ANEMOMETER_AVG_X) && + (type != WEATHER_METER_ANEMOMETER_MAX)) { + PRINTF("Weather: requested an invalid sensor value\n"); + return WEATHER_METER_ERROR; + } + + if(!enabled) { + PRINTF("Weather: module is not configured\n"); + return WEATHER_METER_ERROR; + } + + switch(type) { + case WEATHER_METER_WIND_VANE: + return weather_meter_get_wind_dir(); + + case WEATHER_METER_WIND_VANE_AVG_X: + return wind_vane.value_avg_xm; + + case WEATHER_METER_ANEMOMETER: + return weather_sensors.anemometer.value; + + case WEATHER_METER_ANEMOMETER_AVG: + if(anemometer.value_avg <= 0) { + return (uint16_t)anemometer.value_avg; + } + aux = anemometer.value_avg / anemometer.ticks_avg; + return (uint16_t)aux; + + case WEATHER_METER_ANEMOMETER_AVG_X: + return anemometer.value_avg_xm; + + case WEATHER_METER_ANEMOMETER_MAX: + return anemometer.value_max; + + /* as the default return type is int, we have a lower resolution if returning + * the calculated value as it is truncated, an alternative is returning the + * ticks and calculating on your own with WEATHER_METER_AUX_RAIN_MM + */ + case WEATHER_METER_RAIN_GAUGE: +#if WEATHER_METER_RAIN_RETURN_TICKS + return weather_sensors.rain_gauge.ticks; +#else + return weather_sensors.rain_gauge.value; +#endif + + default: + return WEATHER_METER_ERROR; + } +} +/*---------------------------------------------------------------------------*/ +static int +configure(int type, int value) +{ + if((type != WEATHER_METER_ACTIVE) && + (type != WEATHER_METER_ANEMOMETER_INT_OVER) && + (type != WEATHER_METER_RAIN_GAUGE_INT_OVER) && + (type != WEATHER_METER_ANEMOMETER_INT_DIS) && + (type != WEATHER_METER_RAIN_GAUGE_INT_DIS)) { + PRINTF("Weather: invalid configuration option\n"); + return WEATHER_METER_ERROR; + } + + if(type == WEATHER_METER_ACTIVE) { + + anemometer.value_avg = 0; + anemometer.ticks_avg = 0; + + weather_sensors.anemometer.int_en = 0; + weather_sensors.rain_gauge.int_en = 0; + weather_sensors.anemometer.ticks = 0; + weather_sensors.rain_gauge.ticks = 0; + weather_sensors.anemometer.value = 0; + weather_sensors.rain_gauge.value = 0; + + if(!value) { + anemometer_int_callback = NULL; + rain_gauge_int_callback = NULL; + GPIO_DISABLE_INTERRUPT(ANEMOMETER_SENSOR_PORT_BASE, + ANEMOMETER_SENSOR_PIN_MASK); + GPIO_DISABLE_INTERRUPT(RAIN_GAUGE_SENSOR_PORT_BASE, + RAIN_GAUGE_SENSOR_PIN_MASK); + process_exit(&weather_meter_int_process); + enabled = 0; + PRINTF("Weather: disabled\n"); + return WEATHER_METER_SUCCESS; + } + + /* Configure the wind vane */ + adc_sensors.configure(SENSORS_HW_INIT, WIND_VANE_ADC); + + /* Configure anemometer interruption */ + GPIO_SOFTWARE_CONTROL(ANEMOMETER_SENSOR_PORT_BASE, ANEMOMETER_SENSOR_PIN_MASK); + GPIO_SET_INPUT(ANEMOMETER_SENSOR_PORT_BASE, ANEMOMETER_SENSOR_PIN_MASK); + GPIO_DETECT_RISING(ANEMOMETER_SENSOR_PORT_BASE, ANEMOMETER_SENSOR_PIN_MASK); + GPIO_TRIGGER_SINGLE_EDGE(ANEMOMETER_SENSOR_PORT_BASE, + ANEMOMETER_SENSOR_PIN_MASK); + ioc_set_over(ANEMOMETER_SENSOR_PORT, ANEMOMETER_SENSOR_PIN, IOC_OVERRIDE_DIS); + gpio_register_callback(weather_meter_interrupt_handler, ANEMOMETER_SENSOR_PORT, + ANEMOMETER_SENSOR_PIN); + + /* Configure rain gauge interruption */ + GPIO_SOFTWARE_CONTROL(RAIN_GAUGE_SENSOR_PORT_BASE, RAIN_GAUGE_SENSOR_PIN_MASK); + GPIO_SET_INPUT(RAIN_GAUGE_SENSOR_PORT_BASE, RAIN_GAUGE_SENSOR_PIN_MASK); + GPIO_DETECT_RISING(RAIN_GAUGE_SENSOR_PORT_BASE, RAIN_GAUGE_SENSOR_PIN_MASK); + GPIO_TRIGGER_SINGLE_EDGE(RAIN_GAUGE_SENSOR_PORT_BASE, + RAIN_GAUGE_SENSOR_PIN_MASK); + ioc_set_over(RAIN_GAUGE_SENSOR_PORT, RAIN_GAUGE_SENSOR_PIN, IOC_OVERRIDE_DIS); + gpio_register_callback(weather_meter_interrupt_handler, RAIN_GAUGE_SENSOR_PORT, + RAIN_GAUGE_SENSOR_PIN); + + process_start(&weather_meter_int_process, NULL); + + /* Initialize here prior the first second tick */ + wind_vane.value_prev = weather_meter_get_wind_dir(); + + rtimer_set(&rt, RTIMER_NOW() + RTIMER_SECOND, 1, rt_callback, NULL); + + GPIO_ENABLE_INTERRUPT(ANEMOMETER_SENSOR_PORT_BASE, ANEMOMETER_SENSOR_PIN_MASK); + GPIO_ENABLE_INTERRUPT(RAIN_GAUGE_SENSOR_PORT_BASE, RAIN_GAUGE_SENSOR_PIN_MASK); + nvic_interrupt_enable(ANEMOMETER_SENSOR_VECTOR); + nvic_interrupt_enable(RAIN_GAUGE_SENSOR_VECTOR); + + enabled = 1; + PRINTF("Weather: started\n"); + return WEATHER_METER_SUCCESS; + } + + switch(type) { + case WEATHER_METER_ANEMOMETER_INT_OVER: + weather_sensors.anemometer.int_en = 1; + weather_sensors.anemometer.int_thres = value; + PRINTF("Weather: anemometer threshold %u\n", value); + break; + case WEATHER_METER_RAIN_GAUGE_INT_OVER: + weather_sensors.rain_gauge.int_en = 1; + weather_sensors.rain_gauge.int_thres = value; + PRINTF("Weather: rain gauge threshold %u\n", value); + break; + case WEATHER_METER_ANEMOMETER_INT_DIS: + PRINTF("Weather: anemometer int disabled\n"); + weather_sensors.anemometer.int_en = 0; + break; + case WEATHER_METER_RAIN_GAUGE_INT_DIS: + PRINTF("Weather: rain gauge int disabled\n"); + weather_sensors.rain_gauge.int_en = 0; + break; + default: + return WEATHER_METER_ERROR; + } + + return WEATHER_METER_SUCCESS; +} +/*---------------------------------------------------------------------------*/ +SENSORS_SENSOR(weather_meter, WEATHER_METER_SENSOR, value, configure, NULL); +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/zoul/dev/weather-meter.h b/platform/zoul/dev/weather-meter.h new file mode 100644 index 000000000..9f6d1fc7f --- /dev/null +++ b/platform/zoul/dev/weather-meter.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2016, Zolertia + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup zoul-sensors + * @{ + * + * \defgroup zoul-weather-meter-sensor Sparkfun's weather meter + * @{ + * + * \file + * Weather meter header file + * \author + * Antonio Lignan + */ +/*---------------------------------------------------------------------------*/ +#include "lib/sensors.h" +#include "dev/zoul-sensors.h" +/* -------------------------------------------------------------------------- */ +#ifndef WEATHER_METER_H_ +#define WEATHER_METER_H_ +/* -------------------------------------------------------------------------- */ +/** + * \name Weather meter sensor return and operation values + * @{ + */ +#define WEATHER_METER_RAIN_GAUGE 0x01 +#define WEATHER_METER_WIND_VANE 0x02 +#define WEATHER_METER_WIND_VANE_AVG_X 0x03 +#define WEATHER_METER_ANEMOMETER 0x04 +#define WEATHER_METER_ANEMOMETER_AVG 0x05 +#define WEATHER_METER_ANEMOMETER_AVG_X 0x06 +#define WEATHER_METER_ANEMOMETER_MAX 0x07 + +/* Period (seconds) to calculate an average */ +#ifdef WEATHER_METER_CONF_AVG_PERIOD +#define WEATHER_METER_AVG_PERIOD WEATHER_METER_CONF_AVG_PERIOD +#else +#define WEATHER_METER_AVG_PERIOD 120 +#endif + +#define WEATHER_METER_ACTIVE SENSORS_ACTIVE +#define WEATHER_METER_ANEMOMETER_INT_OVER HW_INT_OVER_THRS +#define WEATHER_METER_ANEMOMETER_INT_DIS HW_INT_DISABLE +#define WEATHER_METER_RAIN_GAUGE_INT_OVER (HW_INT_OVER_THRS << 1) +#define WEATHER_METER_RAIN_GAUGE_INT_DIS (HW_INT_DISABLE << 1) + +#define WEATHER_METER_SUCCESS 0 +#define WEATHER_METER_ERROR (-1) + +/* 2.4Km/h per tick, 2 per rotation */ +#define WEATHER_METER_ANEMOMETER_SPEED_1S (1200) + +/* 0.2794mm per tick */ +#define WEATHER_METER_AUX_RAIN_MM 2794 + +/* Allows to select the return type: ticks or converted value (truncated) */ +#ifdef WEATHER_METER_RAIN_CONF_RETURN +#define WEATHER_METER_RAIN_RETURN_TICKS WEATHER_METER_RAIN_CONF_RETURN +#else +#define WEATHER_METER_RAIN_RETURN_TICKS 1 +#endif +/** @} */ +/* -------------------------------------------------------------------------- */ +/** + * \name Anemometer and rain gauge sensor interrupt callback macro + * @{ + */ +#define WEATHER_METER_REGISTER_ANEMOMETER_INT(ptr) anemometer_int_callback = ptr; +#define WEATHER_METER_REGISTER_RAIN_GAUGE_INT(ptr) rain_gauge_int_callback = ptr; +extern void (*anemometer_int_callback)(uint16_t value); +extern void (*rain_gauge_int_callback)(uint16_t value); +/** @} */ +/* -------------------------------------------------------------------------- */ +/** + * \name Weather meter's anemometer default pin, port and interrupt vector + * @{ + */ +#ifdef WEATHER_METER_CONF_ANEMOMETER_PIN +#define ANEMOMETER_SENSOR_PIN WEATHER_METER_CONF_ANEMOMETER_PIN +#else +#define ANEMOMETER_SENSOR_PIN 1 +#endif +#ifdef WEATHER_METER_CONF_ANEMOMETER_PORT +#define ANEMOMETER_SENSOR_PORT WEATHER_METER_CONF_ANEMOMETER_PORT +#else +#define ANEMOMETER_SENSOR_PORT GPIO_D_NUM +#endif +#ifdef WEATHER_METER_CONF_ANEMOMETER_VECTOR +#define ANEMOMETER_SENSOR_VECTOR WEATHER_METER_CONF_ANEMOMETER_VECTOR +#else +#define ANEMOMETER_SENSOR_VECTOR NVIC_INT_GPIO_PORT_D +#endif +/** @} */ +/* -------------------------------------------------------------------------- */ +/** + * \name Weather meter's rain gauge default pin, port and interrupt vector + * @{ + */ +#ifdef WEATHER_METER_CONF_RAIN_GAUGE_PIN +#define RAIN_GAUGE_SENSOR_PIN WEATHER_METER_CONF_RAIN_GAUGE_PIN +#else +#define RAIN_GAUGE_SENSOR_PIN 2 +#endif +#ifdef WEATHER_METER_CONF_RAIN_GAUGE_PORT +#define RAIN_GAUGE_SENSOR_PORT WEATHER_METER_CONF_RAIN_GAUGE_PORT +#else +#define RAIN_GAUGE_SENSOR_PORT GPIO_D_NUM +#endif +#ifdef WEATHER_METER_CONF_RAIN_GAUGE_VECTOR +#define RAIN_GAUGE_SENSOR_VECTOR WEATHER_METER_CONF_RAIN_GAUGE_VECTOR +#else +#define RAIN_GAUGE_SENSOR_VECTOR NVIC_INT_GPIO_PORT_D +#endif +/** @} */ +/* -------------------------------------------------------------------------- */ +/** + * \name Weather meter's wind vane default ADCx pin (see board.h) + * @{ + */ +#ifdef WEATHER_METER_CONF_RAIN_WIND_VANE_ADC +#define WIND_VANE_ADC WEATHER_METER_CONF_RAIN_WIND_VANE_ADC +#else +#define WIND_VANE_ADC ZOUL_SENSORS_ADC3 +#endif +/** @} */ +/* -------------------------------------------------------------------------- */ +#define WEATHER_METER_SENSOR "Sparkfun weather meter" +/* -------------------------------------------------------------------------- */ +extern const struct sensors_sensor weather_meter; +/* -------------------------------------------------------------------------- */ +#endif /* ifndef WEATHER_METER_H_ */ +/** + * @} + * @} + */