From 24152cbc0b10f31761fb222500c48e7b0d011f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Fri, 13 Jan 2023 00:39:59 +0100 Subject: [PATCH] correct wind calc & use dht22 --- Makefile | 2 +- components/circbuf/CMakeLists.txt | 5 + components/circbuf/circbuf.c | 260 ++++++++++++++++++++++++++++++ components/circbuf/circbuf.h | 151 +++++++++++++++++ components/circbuf/component.mk | 10 ++ main/meteo_task.c | 116 ++++++------- 6 files changed, 487 insertions(+), 57 deletions(-) create mode 100644 components/circbuf/CMakeLists.txt create mode 100644 components/circbuf/circbuf.c create mode 100644 components/circbuf/circbuf.h create mode 100644 components/circbuf/component.mk diff --git a/Makefile b/Makefile index 6dacfb4..03826de 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # project subdirectory. # -DUMMY_VAR = 2 +DUMMY_VAR = 1 PROJECT_NAME := meteo include $(IDF_PATH)/make/project.mk diff --git a/components/circbuf/CMakeLists.txt b/components/circbuf/CMakeLists.txt new file mode 100644 index 0000000..6e729e7 --- /dev/null +++ b/components/circbuf/CMakeLists.txt @@ -0,0 +1,5 @@ +set(COMPONENT_ADD_INCLUDEDIRS .) + +set(COMPONENT_SRCS "circbuf.c") + +register_component() diff --git a/components/circbuf/circbuf.c b/components/circbuf/circbuf.c new file mode 100644 index 0000000..b60c8fb --- /dev/null +++ b/components/circbuf/circbuf.c @@ -0,0 +1,260 @@ +#include +#include +#include + +#include "circbuf.h" + +// --- Circbuf data structure ---- + +/** Offset in void* buffer */ +#define PV_OFFS(pvBuf, elem_size, index) ((uint8_t*)(pvBuf) + ((elem_size)*(index))) + + +/** + * @brief Write data to a CircBuf slot + * @param cb : circbuf + * @param index : slot index + * @param source : data source + */ +static inline void write_buffer(CircBuf *cb, circbuf_size_t index, const void *source) +{ + memcpy(PV_OFFS(cb->buf, cb->elem_size, index), source, cb->elem_size); +} + + +/** + * @brief Copy data from a CircBuf slot to a buffer + * @param cb : circbuf + * @param index : slot index + * @param dest : destination buffer + */ +static inline void read_buffer(const CircBuf *cb, circbuf_size_t index, void *dest) +{ + memcpy(dest, PV_OFFS(cb->buf, cb->elem_size, index), cb->elem_size); +} + +/** Get index of the front position (for write) */ +static inline circbuf_size_t front_writepos(const CircBuf *cb) { + return (cb->back + cb->num_used) % cb->cap; +} + + +void cbuf_init(CircBuf *cb, void *buf, circbuf_size_t capacity, circbuf_size_t elem_size) +{ + // allocate the buffer + cb->buf = buf; + + // set capacity, clear state + cb->elem_size = elem_size; + cb->cap = capacity; + + cbuf_clear(cb); +} + + +/** Check if cbuf is full */ +bool cbuf_full(const CircBuf *cb) +{ + if (cb == NULL) { + return false; + } + + return cb->num_used == cb->cap; +} + + +/** Check if cbuf is empty */ +bool cbuf_empty(const CircBuf *cb) +{ + if (cb == NULL) { + return true; + } + + return cb->num_used == 0; +} + + +/** Get the max capacity of the buffer */ +circbuf_size_t cbuf_capacity(const CircBuf *cb) +{ + return cb->cap; +} + + +/** Get the current number of items in the buffer */ +circbuf_size_t cbuf_count(const CircBuf *cb) +{ + return cb->num_used; +} + + +/** Push one element to the front_writepos */ +bool cbuf_push(CircBuf *cb, const void *source) +{ + if (cb == NULL || source == NULL || cb->num_used == cb->cap) { + return false; + } + + write_buffer(cb, front_writepos(cb), source); + + // increment + cb->num_used++; + + return true; +} + + +/** Pop one element from the front_writepos */ +bool cbuf_pop(CircBuf *cb, void *dest) +{ + if (cb == NULL || cb->num_used == 0) { + return false; + } + + cb->num_used--; + + circbuf_size_t f = front_writepos(cb); + + if (dest != NULL) { + read_buffer(cb, f, dest); + } + +#ifdef CIRCBUF_ZERO_FREE_SLOTS + memset(PV_OFFS(cb->buf, cb->elem_size, f), 0, cb->elem_size); +#endif + + return true; +} + + +/** Peek at the front_writepos element */ +bool cbuf_peek(const CircBuf *cb, void *dest) +{ + if (cb == NULL || dest == NULL || cb->num_used == 0) { + return false; + } + + circbuf_size_t f = (cb->back + cb->num_used - 1) % cb->cap; + read_buffer(cb, f, dest); + return true; +} + + +void * cbuf_ptr(const CircBuf *cb) +{ + if (cb == NULL || cb->num_used == 0) { + return NULL; + } + + circbuf_size_t f = (cb->back + cb->num_used - 1) % cb->cap; + return PV_OFFS(cb->buf, cb->elem_size, f); +} + + +/** Peek at the nth element (counted from back) */ +bool cbuf_nth(const CircBuf *cb, circbuf_size_t num, void *dest) +{ + if (cb == NULL || dest == NULL || num > cb->num_used) { + return false; + } + + circbuf_size_t index = (cb->back + num) % cb->cap; + read_buffer(cb, index, dest); + return true; +} + + +void *cbuf_ptr_nth(const CircBuf *cb, circbuf_size_t num) +{ + if (cb == NULL || num > cb->num_used) { + return NULL; + } + + circbuf_size_t index = (cb->back + num) % cb->cap; + return PV_OFFS(cb->buf, cb->elem_size, index); +} + + +/** Push one element to the back */ +bool cbuf_push_back(CircBuf *cb, const void *source) +{ + if (cb == NULL || source == NULL || cb->num_used == cb->cap) { + return false; + } + + // move lr back + if (cb->back == 0) { + cb->back = cb->cap - 1; // wrap to the end + } else { + cb->back--; + } + cb->num_used++; + + write_buffer(cb, cb->back, source); + + return true; +} + + +/** Pop one element from the back */ +bool cbuf_pop_back(CircBuf *cb, void *dest) +{ + if (cb == NULL || cb->num_used == 0) { + return false; + } + + if (dest != NULL) { + read_buffer(cb, cb->back, dest); + } + +#ifdef CIRCBUF_ZERO_FREE_SLOTS + memset(PV_OFFS(cb->buf, cb->elem_size, cb->back), 0, cb->elem_size); +#endif + + // increment + cb->back++; + if (cb->back == cb->cap) { + cb->back = 0; + } + cb->num_used--; + + return true; +} + + +/** Pop one element from the back */ +bool cbuf_peek_back(const CircBuf *cb, void *dest) +{ + if (cb == NULL || dest == NULL || cb->num_used == 0) { + return false; + } + + read_buffer(cb, cb->back, dest); + return true; +} + + +void* cbuf_ptr_back(const CircBuf *cb) +{ + if (cb == NULL || cb->num_used == 0) { + return NULL; + } + + return PV_OFFS(cb->buf, cb->elem_size, cb->back); +} + + +/** Clear a cbuf */ +void cbuf_clear(CircBuf *cb) +{ + if (cb == NULL) { + return; + } + + cb->num_used = 0; + cb->back = 0; + +#ifdef CIRCBUF_ZERO_FREE_SLOTS + memset(PV_OFFS(cb->buf, cb->elem_size, 0), 0, cb->cap * cb->elem_size); +#endif +} diff --git a/components/circbuf/circbuf.h b/components/circbuf/circbuf.h new file mode 100644 index 0000000..6330c34 --- /dev/null +++ b/components/circbuf/circbuf.h @@ -0,0 +1,151 @@ +/** + * @file circbuf.h + * @author Ondřej Hruška, 2016,2023 + * + * Circular buffer / queue / stack. + * Slots are pre-allocated, values are copied into the buffer. + * + * The buffer may be used as a stack, event queue or a simple buffer. + * + * MIT license + */ + +#ifndef CIRCBUF_H +#define CIRCBUF_H + +#include +#include +#include + +// Enable to zero a freed slots after pop, useful for debugging +#define CIRCBUF_ZERO_FREE_SLOTS + +// size_t can be replaced by a more suitable type for circbuf, e.g. uint8_t for tiny buffers +typedef uint32_t circbuf_size_t; + +/** Instance structure - public to allow static allocation, but consider the structure internal matter */ +struct circbuf_struct { + void *buf; + circbuf_size_t num_used; + circbuf_size_t elem_size; + circbuf_size_t cap; + circbuf_size_t back; +// circbuf_size_t front; +}; + +typedef struct circbuf_struct CircBuf; + +/** + * @brief Initialize a circular buffer + * + * @param[in,out] cb - pointer to the buffer to init, can be statically or dynamically allocated + * @param buf : backing buffer, can be statically or dynamically allocated + * @param capacity : buffer capacity + * @param elem_size : size of one element + */ +void cbuf_init(CircBuf *cb, void *buf, circbuf_size_t capacity, circbuf_size_t elem_size); + + +/** Test for full buffer */ +bool cbuf_full(const CircBuf *cb); + + +/** Test for empty buffer */ +bool cbuf_empty(const CircBuf *cb); + + +/** Get the max capacity of the buffer */ +circbuf_size_t cbuf_capacity(const CircBuf *cb); + + +/** Get the current number of items in the buffer */ +circbuf_size_t cbuf_count(const CircBuf *cb); + + +/** Peek at the nth element (counted from back) */ +bool cbuf_nth(const CircBuf *cb, circbuf_size_t num, void *dest); + + +/** Get a mutable reference to nth element (counted from back) */ +void *cbuf_ptr_nth(const CircBuf *cb, circbuf_size_t num); + + +/** @brief Remove all data from buffer */ +void cbuf_clear(CircBuf *cb); + + +/** + * @brief Append a value to the buffer (FIFO) + * @param cb : buffer + * @param source : pointer to a value (will be copied) + * @return success + */ +bool cbuf_push(CircBuf *cb, const void *source); + + +/** + * @brief Read a value from the buffer, return susccess. + * + * @param cb : buffer + * @param dest : read destination. If NULL, value is discarded. + * @return success + */ +bool cbuf_pop(CircBuf *cb, void *dest); + + +/** + * @brief Copy the frontmost element without changing the buffer + * @param cb : buffer + * @param dest : read destination + * @return success + */ +bool cbuf_peek(const CircBuf *cb, void *dest); + + +/** + * @brief Get a mutable reference to the front element + * @param cb : buffer + * @param dest : read destination + * @return reference or NULL + */ +void * cbuf_ptr(const CircBuf *cb); + + +/** + * @brief Push a value into the circbuf (LIFO). + * + * @param cb : buffer + * @param source : pointer to a value (will be copied) + * @return success + */ +bool cbuf_push_back(CircBuf *cb, const void *source); + + +/** + * @brief Read a value from the buffer, return susccess. + * + * @param cb : buffer + * @param dest : read destination. If NULL, value is discarded. + * @return success + */ +bool cbuf_pop_back(CircBuf *cb, void *dest); + + +/** + * @brief Copy the backmost element without changing the buffer + * @param cb : buffer + * @param dest : read destination + * @return success + */ +bool cbuf_peek_back(const CircBuf *cb, void *dest); + + +/** + * @brief Get a mutable reference to the backmost element + * @param cb : buffer + * @param dest : read destination + * @return reference or NULL + */ +void* cbuf_ptr_back(const CircBuf *cb); + +#endif // CIRCBUF_H diff --git a/components/circbuf/component.mk b/components/circbuf/component.mk new file mode 100644 index 0000000..e0e9f4c --- /dev/null +++ b/components/circbuf/component.mk @@ -0,0 +1,10 @@ +# +# Component Makefile +# +# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the SDK documents if you need to do this. +# + +COMPONENT_ADD_INCLUDEDIRS := . diff --git a/main/meteo_task.c b/main/meteo_task.c index 41e052b..32c36f5 100644 --- a/main/meteo_task.c +++ b/main/meteo_task.c @@ -11,77 +11,80 @@ #include "ds18b20.h" #include "dht.h" #include "driver/gpio.h" +#include "circbuf.h" -static volatile uint32_t timestamp_ms = 0; -static volatile uint32_t last_revolution_ts = 0; +static volatile uint32_t timestamp = 0; -#define RPM_BUFFER_LEN 10 -static volatile uint16_t rpm_buffer[RPM_BUFFER_LEN] = {}; -static volatile int rpm_buffer_next = 0; -static volatile int num_valid_average = 0; +#define RPS_BUFFER_LEN (60*10) +static volatile uint16_t history[RPS_BUFFER_LEN] = {}; +static CircBuf rps_cb; + +static volatile float rpm_average = 0; +static volatile float rpm_gust = 0; + +static volatile uint16_t cycle_count = 0; + +void calculate_wind(); static void gpio_isr_handler(void *arg) { - uint32_t ts = timestamp_ms; - uint32_t cycle_ms = ts - last_revolution_ts; - last_revolution_ts = ts; - - if (cycle_ms > 0xFFFF) { - cycle_ms = 0xFFFF; - } - rpm_buffer[rpm_buffer_next++] = (uint16_t) cycle_ms; - if (rpm_buffer_next == RPM_BUFFER_LEN) { - rpm_buffer_next = 0; + if (cycle_count < 0xFFFF) { + cycle_count++; } - if (num_valid_average < RPM_BUFFER_LEN) { - num_valid_average++; +} + +void hw_timer_callback1s(void *arg) +{ + timestamp++; + + // FIXME use a freertos queue and pass this to a thread! + if (cbuf_full(&rps_cb)) { + cbuf_pop_back(&rps_cb, NULL); } + cbuf_push(&rps_cb, (void*) &cycle_count); + cycle_count = 0; + + calculate_wind(); } -float get_rpm() { - float res; - float current = (float)(timestamp_ms - last_revolution_ts); - - if (num_valid_average > 0) { - // we write num_valid_average only from here, so its safe to assume it stays nonzero - float average = 0; - int pos = rpm_buffer_next; - for (int i = 0; i < num_valid_average; i++) { - average += (float) rpm_buffer[pos]; - pos--; - if (pos < 0) { - pos = RPM_BUFFER_LEN - 1; - } +void calculate_wind() +{ + // Wind speed is average from 10 minutes + // Gust is max 3-second average anywhere within the 10 minutes + + float max_gust = 0; + + uint32_t tenmin_sum = 0; + uint32_t numsecs = cbuf_count(&rps_cb); + uint16_t threesec1 = 0, threesec2 = 0; + for(size_t i = 0; i < numsecs; i++) { + uint16_t *slot = cbuf_ptr_nth(&rps_cb, i); + if (!slot) { + continue; } - average /= (float) (num_valid_average); - // now we have ms per revolution + uint16_t slotval = *slot; + tenmin_sum += (uint32_t) slotval; - if (current > average * 10.0f) { - // if wind stopped, invalidate the averaging buffer and use the current time from the last hall event - res = current; - num_valid_average = 0; // invalidate average results - } else { - res = average; - } - } else { - res = current; - } + // gust is max avg from 3 seconds within the 10 minutes + uint32_t gust_sum = (uint32_t) threesec1 + (uint32_t) threesec2 + (uint32_t) slotval; + threesec1 = threesec2; + threesec2 = slotval; - float rpm = 60000.0f / res; - if (rpm < 1) { - rpm = 0; + float gust_avg = (float)gust_sum * (float)20.0f; + if (gust_avg > max_gust) { + max_gust = gust_avg; + } } - return rpm; // RPM + rpm_gust = max_gust; + rpm_average = ((float)tenmin_sum / (float)numsecs) * 60.0f; } -void hw_timer_callback1(void *arg) -{ - timestamp_ms++; -} void meteo_task(void* pvParameters) { + cbuf_init(&rps_cb, (void*)history, RPS_BUFFER_LEN, 2); // uint16 fields + // Try to unfuck GPIOs PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12); PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13); @@ -89,8 +92,8 @@ void meteo_task(void* pvParameters) PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15); // start timer used for timebase - hw_timer_init(hw_timer_callback1, NULL); - hw_timer_alarm_us(1000, true); // 1 ms timer + hw_timer_init(hw_timer_callback1s, NULL); + hw_timer_alarm_us(1000000, true); // 1s timer gpio_config_t io_conf; io_conf.intr_type = GPIO_INTR_POSEDGE; @@ -107,11 +110,12 @@ void meteo_task(void* pvParameters) // this works ... ds_temp = ds18b20_measure_and_read(0, DS18B20_ANY); - if (!dht_read_float_data(DHT_TYPE_DHT11, 12, &dht_hum, &dht_temp)) { + if (!dht_read_float_data(DHT_TYPE_DHT22, 12, &dht_hum, &dht_temp)) { dht_hum = dht_temp = NAN; } - printf("Dallas: %.2f °C, DHT %.2f °C, %.1f %%r.H, HALL %.1f RPM\n", ds_temp, dht_temp, dht_hum, get_rpm()); + printf("Dallas: %.2f °C, DHT %.2f °C, %.1f %%r.H, HALL avg %.1f RPM, gust %.1f RPM\n", + ds_temp, dht_temp, dht_hum, rpm_average, rpm_gust); vTaskDelay(pdMS_TO_TICKS(500)); }