From 8eea54771b5d0007dd95e97dcb0c56d7e54cfdb9 Mon Sep 17 00:00:00 2001 From: Nigreon Date: Tue, 12 Mar 2024 23:26:00 +0100 Subject: [PATCH] Refactor in files. Add BMP180 and SHT4x sensor, monitor VDD voltage, scheduling with timer --- boards/nrf52832_outdoor.overlay | 8 - platformio.ini | 31 ++ src/battery-vdd.c | 154 ++++++++ src/battery.c | 4 +- src/battery.h | 5 + src/bmp180.c | 237 +++++++++++ src/bmp180.h | 3 + src/main.c | 496 ++++++++++++++---------- src/sds011.c | 227 +++++++++++ src/sds011.h | 5 + src/sht4x.c | 56 +++ src/sht4x.h | 4 + CMakeLists.txt => zephyr/CMakeLists.txt | 4 +- zephyr/boards/nrf52dk_nrf52832.overlay | 76 ++++ prj.conf => zephyr/prj.conf | 10 +- 15 files changed, 1108 insertions(+), 212 deletions(-) delete mode 100644 boards/nrf52832_outdoor.overlay create mode 100644 platformio.ini create mode 100644 src/battery-vdd.c create mode 100644 src/bmp180.c create mode 100644 src/bmp180.h create mode 100644 src/sds011.c create mode 100644 src/sds011.h create mode 100644 src/sht4x.c create mode 100644 src/sht4x.h rename CMakeLists.txt => zephyr/CMakeLists.txt (71%) create mode 100644 zephyr/boards/nrf52dk_nrf52832.overlay rename prj.conf => zephyr/prj.conf (84%) diff --git a/boards/nrf52832_outdoor.overlay b/boards/nrf52832_outdoor.overlay deleted file mode 100644 index 7fa4ed5..0000000 --- a/boards/nrf52832_outdoor.overlay +++ /dev/null @@ -1,8 +0,0 @@ -/ { - vbatt { - compatible = "voltage-divider"; - io-channels = <&adc 4>; - output-ohms = <4600>; - full-ohms = <(4600 + 3230)>; - }; -}; diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..9eaa5b5 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,31 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + + +[platformio] +default_envs = nrf52beacon + +[env:nrf52beacon] +platform = nordicnrf52 +framework = zephyr +platform_packages = + platformio/framework-zephyr@^2.30400.230705 + platformio/toolchain-gccarmnoneeabi@^1.120201.221222 +board = nrf52_dk +#build_flags = -DNRF51_S130 +#board_build.variant = CJMCU_LIS3DH +monitor_speed = 115200 +extra_scripts = pre:extra_script.py +upload_protocol = custom +upload_command = echo "rtt stop; halt; program $PROJECT_DIR/$SOURCE verify reset; rtt start" | ncat localhost 4444 +monitor_port = socket://localhost:9090 +monitor_filters = + direct +; time ; Add timestamp with milliseconds for each new line diff --git a/src/battery-vdd.c b/src/battery-vdd.c new file mode 100644 index 0000000..61e2a83 --- /dev/null +++ b/src/battery-vdd.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2018-2019 Peter Bigot Consulting, LLC + * Copyright (c) 2019-2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "battery.h" + +LOG_MODULE_REGISTER(BATTERYVDD, CONFIG_ADC_LOG_LEVEL); + +#define ZEPHYR_USER DT_PATH(zephyr_user) + +/* Other boards may use dividers that only reduce battery voltage to + * the maximum supported by the hardware (3.6 V) + */ +#define BATTERY_ADC_GAIN ADC_GAIN_1_6 + +struct io_channel_config { + uint8_t channel; +}; + +struct divider_config { + struct io_channel_config io_channel; + struct gpio_dt_spec power_gpios; + /* output_ohm is used as a flag value: if it is nonzero then + * the battery is measured through a voltage divider; + * otherwise it is assumed to be directly connected to Vdd. + */ + uint32_t output_ohm; + uint32_t full_ohm; +}; + +static const struct divider_config dividervdd_config = { + .io_channel = { + DT_IO_CHANNELS_INPUT(ZEPHYR_USER), + }, +}; + +struct divider_data { + const struct device *adc; + struct adc_channel_cfg adc_cfg; + struct adc_sequence adc_seq; + int16_t raw; +}; +static struct divider_data dividervdd_data = { + .adc = DEVICE_DT_GET(DT_IO_CHANNELS_CTLR(ZEPHYR_USER)), +}; + +static int dividervdd_setup(void) +{ + const struct divider_config *cfg = ÷rvdd_config; + const struct io_channel_config *iocp = &cfg->io_channel; + const struct gpio_dt_spec *gcp = &cfg->power_gpios; + struct divider_data *ddp = ÷rvdd_data; + struct adc_sequence *asp = &ddp->adc_seq; + struct adc_channel_cfg *accp = &ddp->adc_cfg; + int rc; + + if (!device_is_ready(ddp->adc)) { + LOG_ERR("ADC device is not ready %s", ddp->adc->name); + return -ENOENT; + } + + if (gcp->port) { + if (!device_is_ready(gcp->port)) { + LOG_ERR("%s: device not ready", gcp->port->name); + return -ENOENT; + } + rc = gpio_pin_configure_dt(gcp, GPIO_OUTPUT_INACTIVE); + if (rc != 0) { + LOG_ERR("Failed to control feed %s.%u: %d", + gcp->port->name, gcp->pin, rc); + return rc; + } + } + + *asp = (struct adc_sequence){ + .channels = BIT(0), + .buffer = &ddp->raw, + .buffer_size = sizeof(ddp->raw), + .oversampling = 4, + .calibrate = true, + }; + +#ifdef CONFIG_ADC_NRFX_SAADC + *accp = (struct adc_channel_cfg){ + .gain = BATTERY_ADC_GAIN, + .reference = ADC_REF_INTERNAL, + .acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40), + }; + + accp->input_positive = SAADC_CH_PSELP_PSELP_VDD; + + asp->resolution = 14; +#else /* CONFIG_ADC_var */ +#error Unsupported ADC +#endif /* CONFIG_ADC_var */ + + rc = adc_channel_setup(ddp->adc, accp); + LOG_INF("Setup VDD AIN%u got %d", iocp->channel, rc); + + return rc; +} + +static bool battery_ok; + +int batteryvdd_setup() +{ + int rc = dividervdd_setup(); + + battery_ok = (rc == 0); + LOG_INF("Battery VDD setup: %d %d", rc, battery_ok); + return rc; +} + +//SYS_INIT(batteryvdd_setup, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); + +int16_t batteryvdd_sample(void) +{ + int16_t rc = -ENOENT; + + if (battery_ok) { + struct divider_data *ddp = ÷rvdd_data; + //const struct divider_config *dcp = ÷rvdd_config; + struct adc_sequence *sp = &ddp->adc_seq; + + rc = adc_read(ddp->adc, sp); + sp->calibrate = false; + if (rc == 0) { + int32_t val = ddp->raw; + + adc_raw_to_millivolts(adc_ref_internal(ddp->adc), + ddp->adc_cfg.gain, + sp->resolution, + &val); + rc = val; + LOG_INF("raw %u ~ %u mV\n", ddp->raw, val); + } + } + + return rc; +} diff --git a/src/battery.c b/src/battery.c index e2c87c8..66bf6e0 100644 --- a/src/battery.c +++ b/src/battery.c @@ -142,7 +142,7 @@ static int divider_setup(void) static bool battery_ok; -static int battery_setup(const struct device *arg) +int battery_setup() { int rc = divider_setup(); @@ -151,7 +151,7 @@ static int battery_setup(const struct device *arg) return rc; } -SYS_INIT(battery_setup, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); +//SYS_INIT(battery_setup, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); int battery_measure_enable(bool enable) { diff --git a/src/battery.h b/src/battery.h index 2da50ab..3eb9cb2 100644 --- a/src/battery.h +++ b/src/battery.h @@ -15,12 +15,17 @@ */ int battery_measure_enable(bool enable); + +int battery_setup(); +int batteryvdd_setup(); + /** Measure the battery voltage. * * @return the battery voltage in millivolts, or a negative error * code. */ int16_t battery_sample(void); +int16_t batteryvdd_sample(void); /** A point in a battery discharge curve sequence. * diff --git a/src/bmp180.c b/src/bmp180.c new file mode 100644 index 0000000..f49f03f --- /dev/null +++ b/src/bmp180.c @@ -0,0 +1,237 @@ +#include +LOG_MODULE_REGISTER(bmp180); + +#include +#include +#include +#include + +#include + +#include "bmp180.h" +#include "sds011.h" + +#define BMP180_I2CADDR 0x77 //!< BMP180 I2C address + +#define BMP180_ULTRALOWPOWER 0 //!< Ultra low power mode +#define BMP180_STANDARD 1 //!< Standard mode +#define BMP180_HIGHRES 2 //!< High-res mode +#define BMP180_ULTRAHIGHRES 3 //!< Ultra high-res mode + +#define OVERSAMPLING BMP180_ULTRALOWPOWER + +#define BMP180_CAL_AC1 0xAA //!< R Calibration data (16 bits) +#define BMP180_CAL_AC2 0xAC //!< R Calibration data (16 bits) +#define BMP180_CAL_AC3 0xAE //!< R Calibration data (16 bits) +#define BMP180_CAL_AC4 0xB0 //!< R Calibration data (16 bits) +#define BMP180_CAL_AC5 0xB2 //!< R Calibration data (16 bits) +#define BMP180_CAL_AC6 0xB4 //!< R Calibration data (16 bits) +#define BMP180_CAL_B1 0xB6 //!< R Calibration data (16 bits) +#define BMP180_CAL_B2 0xB8 //!< R Calibration data (16 bits) +#define BMP180_CAL_MB 0xBA //!< R Calibration data (16 bits) +#define BMP180_CAL_MC 0xBC //!< R Calibration data (16 bits) +#define BMP180_CAL_MD 0xBE //!< R Calibration data (16 bits) + +#define BMP180_CONTROL 0xF4 //!< Control register +#define BMP180_TEMPDATA 0xF6 //!< Temperature data register +#define BMP180_PRESSUREDATA 0xF6 //!< Pressure data register +#define BMP180_READTEMPCMD 0x2E //!< Read temperature control register value +#define BMP180_READPRESSURECMD 0x34 //!< Read pressure control register value + +#define I2C_PORT DT_NODELABEL(i2c0) + +int16_t ac1, ac2, ac3, b1, b2, mb, mc, md; +uint16_t ac4, ac5, ac6; + +const struct device *devi2c; + +uint8_t read_reg_8(const struct device *devi2c, uint8_t reg) +{ + uint8_t bufin[1]; + uint8_t bufout[1]; + int ret; + bufin[0]=reg; + ret = i2c_write_read(devi2c, 0x77, bufin, 1, bufout, 1); + if (ret < 0) { + printk("Failed to read from devi2cice\n"); + return ret; + } + return bufout[0]; +} + +uint16_t read_reg_16(const struct device *devi2c, uint8_t reg) +{ + uint16_t bufout; + int ret; + ret = i2c_write_read(devi2c, BMP180_I2CADDR, ®, 1, &bufout, 2); +// ret = i2c_burst_read(devi2c, BMP180_I2CADDR, reg, + if (ret < 0) { + printk("Failed to read from devi2cice\n"); + return ret; + //} else { + // printk("Reg %d Value %d\n", reg, sys_be16_to_cpu(bufout)); +} + return sys_be16_to_cpu(bufout); +} + +int write_reg_8(const struct device *devi2c, uint8_t reg, uint8_t data) +{ + int ret; + ret = i2c_reg_write_byte(devi2c, BMP180_I2CADDR, reg, data); + if (ret < 0) { + printk("Failed to write to device\n"); + return ret; + } + return ret; +} + +int32_t computeB5(int32_t UT) { + int32_t X1 = (UT - (int32_t)ac6) * ((int32_t)ac5) >> 15; + int32_t X2 = ((int32_t)mc << 11) / (X1 + (int32_t)md); + return X1 + X2; +} + +uint16_t readRawTemperature(const struct device *devi2c) +{ + write_reg_8(devi2c, BMP180_CONTROL, BMP180_READTEMPCMD); + k_msleep(10); + return read_reg_16(devi2c, BMP180_TEMPDATA); +} + +uint16_t readRawPressure(const struct device *devi2c) +{ + uint32_t raw; + /*uint8_t raw1; + uint8_t raw2; + uint8_t raw3;*/ + + write_reg_8(devi2c, BMP180_CONTROL, BMP180_READPRESSURECMD + (OVERSAMPLING << 6)); + + if (OVERSAMPLING == BMP180_ULTRALOWPOWER) { + k_msleep(5); + } else if (OVERSAMPLING == BMP180_STANDARD) { + k_msleep(8); + } else if (OVERSAMPLING == BMP180_HIGHRES) { + k_msleep(14); + } else { + k_msleep(26); + } + raw = read_reg_16(devi2c, BMP180_PRESSUREDATA); + + raw <<= 8; + raw |= read_reg_8(devi2c, BMP180_PRESSUREDATA + 2); + + +/* raw1 = read_reg_8(devi2c, BMP180_PRESSUREDATA); + raw2 = read_reg_8(devi2c, BMP180_PRESSUREDATA + 1); + raw3 = read_reg_8(devi2c, BMP180_PRESSUREDATA + 2); + + raw = raw1 << 16 | raw2 << 8 | raw3; + + */ + raw >>= (8 - OVERSAMPLING); + + return raw; +} + +int32_t readPressure(const struct device *devi2c) { + int32_t UT, UP, B3, B5, B6, X1, X2, X3, p; + uint32_t B4, B7; + + UT = readRawTemperature(devi2c); + UP = readRawPressure(devi2c); + + B5 = computeB5(UT); +/* + // do pressure calcs + B6 = B5 - 4000; + X1 = ((int32_t)b2 * ((B6 * B6) >> 12)) >> 11; + X2 = ((int32_t)ac2 * B6) >> 11; + X3 = X1 + X2; + B3 = ((((int32_t)ac1 * 4 + X3) << OVERSAMPLING) + 2) / 4; + + X1 = ((int32_t)ac3 * B6) >> 13; + X2 = ((int32_t)b1 * ((B6 * B6) >> 12)) >> 16; + X3 = ((X1 + X2) + 2) >> 2; + B4 = ((uint32_t)ac4 * (uint32_t)(X3 + 32768)) >> 15; + B7 = ((uint32_t)UP - B3) * (uint32_t)(50000UL >> OVERSAMPLING); + + if (B7 < 0x80000000) { + p = (B7 * 2) / B4; + } else { + p = (B7 / B4) * 2; + } + X1 = (p >> 8) * (p >> 8); + X1 = (X1 * 3038) >> 16; + X2 = (-7357 * p) >> 16; + + p = p + ((X1 + X2 + (int32_t)3791) >> 4); + */ + B6= B5 - 4000; + X1 = (b2 * ((B6 * B6) >> 12)) >> 11; + X2 = (ac2 * B6) >> 11; + X3 = X1 + X2; + B3 = (((ac1 * 4 + X3) << OVERSAMPLING) + 2) >> 2; + + X1 = (ac3 * B6) >> 13; + X2 = (b1 * ((B6 * B6) >> 12)) >> 16; + X3 = ((X1 + X2) + 2) >> 2; + B4 = (ac4 * (unsigned long)(X3 + 32768UL)) >> 15; + B7 = ((unsigned long)UP - B3) * (50000UL >> OVERSAMPLING); + + if(B7 < 0x80000000) + p = (B7 * 2) / B4; + else + p = (B7 / B4) * 2; + + X1 = (p >> 8) * (p >> 8); + X1 = (X1 * 3038) >> 16; + X2 = (-7357 * p) >> 16; + + p = p + ((X1 + X2 + 3791) >> 4); + + return p; +} + +uint32_t readSealevelPressure(const struct device *devi2c, float altitude_meters) { + float pressure = readPressure(devi2c); + return (uint32_t)(pressure / pow(1.0 - altitude_meters / 44330, 5.255)); +}; + +uint32_t bmp180_readPressure() { + return readSealevelPressure(devi2c, 146.0); +}; + +int bmp180_setup() +{ + int ret; + devi2c = DEVICE_DT_GET(I2C_PORT); + + if (!devi2c) { + printk("I2C: device driver not found.\n"); + return -1; + } + + ret = write_reg_8(devi2c, 0xe0, 0xb6); + if(ret < 0) { + return ret; + } + k_msleep(500); + + ac1 = read_reg_16(devi2c, BMP180_CAL_AC1); + ac2 = read_reg_16(devi2c, BMP180_CAL_AC2); + ac3 = read_reg_16(devi2c, BMP180_CAL_AC3); + ac4 = read_reg_16(devi2c, BMP180_CAL_AC4); + ac5 = read_reg_16(devi2c, BMP180_CAL_AC5); + ac6 = read_reg_16(devi2c, BMP180_CAL_AC6); + + b1 = read_reg_16(devi2c, BMP180_CAL_B1); + b2 = read_reg_16(devi2c, BMP180_CAL_B2); + + mb = read_reg_16(devi2c, BMP180_CAL_MB); + mc = read_reg_16(devi2c, BMP180_CAL_MC); + md = read_reg_16(devi2c, BMP180_CAL_MD); + + return 0; +}; + diff --git a/src/bmp180.h b/src/bmp180.h new file mode 100644 index 0000000..01a361b --- /dev/null +++ b/src/bmp180.h @@ -0,0 +1,3 @@ +#include +int bmp180_setup(); +uint32_t bmp180_readPressure(); diff --git a/src/main.c b/src/main.c index 1ca7252..4507fd8 100644 --- a/src/main.c +++ b/src/main.c @@ -27,39 +27,45 @@ #include #include #include -#include #include +#include +LOG_MODULE_REGISTER(main); + #include "battery.h" +#include "sht4x.h" +#include "bmp180.h" +#include "sds011.h" #include "hal/nrf_gpiote.h" -static K_SEM_DEFINE(ble_init_ok, 0, 1); static K_FIFO_DEFINE(fifo_adv_data); + +static K_SEM_DEFINE(ble_init_ok, 0, 1); +static K_SEM_DEFINE(adv_send_ok, 0, 1); + +#define BTHOME_MAXELT 9 +enum captor_type { BATTERY, BATTERYVDD, TEMPERATURE, PRESSURE, VOLTAGE, VOLTAGEVDD, PM25, PM10, HUMIDITY }; +uint8_t eltid[BTHOME_MAXELT] = { 0x01, 0x01, 0x02, 0x04, 0x0c, 0x0c, 0x0d, 0x0e, 0x2e }; +uint8_t eltsize[BTHOME_MAXELT] = { 1, 1, 2, 3, 2, 2, 2, 2, 1}; +uint8_t eltenabled[BTHOME_MAXELT] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + struct bthome_adv_t { void *fifo_reserved; uint8_t type; int16_t data_s16; uint16_t data_u16; uint8_t data_u8; + uint32_t data_u32; }; + static uint8_t mfg_bthome_data[28]; + uint8_t mfg_bthome_sizetosend = 0; + uint8_t mfg_bthome_numelt = 0; + uint8_t bthome_datapos[BTHOME_MAXELT]; + //uint8_t bthome_datapos[] = { 4, 6, 9, 12, 14, 18, 21 }; -#define UPDATE_MIN(MIN, SAMPLE) if (SAMPLE < MIN) { MIN = SAMPLE; } -#define UPDATE_MAX(MAX, SAMPLE) if (SAMPLE > MAX) { MAX = SAMPLE; } -#define UPDATE_MIN_MAX(MIN, MAX, SAMPLE) { UPDATE_MIN(MIN, SAMPLE); UPDATE_MAX(MAX, SAMPLE); } - - /* -0x01: Battery -0x02: Temperature -0x0c: Voltage -0x0d: PM2.5 -0x0e: PM10 -*/ - static uint8_t mfg_bthome_data[] = { 0xd2, 0xfc, 0x40, 0x01, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0e, 0x00, 0x00 }; - uint8_t bthome_datapos[] = { 4, 6, 9, 12, 15 }; - - enum captor_type { BATTERY, TEMPERATURE, VOLTAGE, PM25, PM10 }; + struct captor_wait { enum captor_type captor; @@ -69,16 +75,22 @@ struct bthome_adv_t { struct captor_wait params[] = { {BATTERY, 120 }, {TEMPERATURE, 60}, {PM25, 60} }; - static const struct bt_data ad[] = { + /*static const struct bt_data ad[] = { BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), - BT_DATA(BT_DATA_SVC_DATA16, mfg_bthome_data, 17), - }; + BT_DATA(BT_DATA_SVC_DATA16, mfg_bthome_data, 23), + };*/ #define GPIO_PORT DT_NODELABEL(gpio0) #define STACKSIZE 2048 #define PRIORITY 7 +K_THREAD_STACK_DEFINE(stack_wq_pms, STACKSIZE); +K_THREAD_STACK_DEFINE(stack_wq_i2c, STACKSIZE); + +struct k_work_q workqueue_pms; +struct k_work_q workqueue_i2c; + static const struct battery_level_point battery_levels[] = { /* "Curve" here eyeballed from captured data for the [Adafruit * 3.7v 2000 mAh](https://www.adafruit.com/product/2011) LIPO @@ -96,24 +108,27 @@ struct bthome_adv_t { { 625, 3550 }, { 0, 3100 }, }; + static const struct battery_level_point battery_nimh_levels[] = { + /* "Curve" here eyeballed from captured data for the [Adafruit + * 3.7v 2000 mAh](https://www.adafruit.com/product/2011) LIPO + * under full load that started with a charge of 3.96 V and + * dropped about linearly to 3.58 V over 15 hours. It then + * dropped rapidly to 3.10 V over one hour, at which point it + * stopped transmitting. + * + * Based on eyeball comparisons we'll say that 15/16 of life + * goes between 3.95 and 3.55 V, and 1/16 goes between 3.55 V + * and 3.1 V. + */ -#define UART_PORT DT_NODELABEL(uart0) - static const struct device *const uart_dev = DEVICE_DT_GET(UART_PORT); - - uint8_t SDS_SLEEPCMD[19] = { - 0xAA, 0xB4, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x05, 0xAB - }; - uint8_t SDS_WAKEUPCMD[19] = { - 0xAA, 0xB4, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x06, 0xAB + { 10000, 2800 }, + { 90, 2600 }, + { 50, 2440 }, + { 0, 2300 }, }; - static K_FIFO_DEFINE(fifo_pms); - struct pms_data_t { - void *fifo_reserved; - uint32_t ts; - char data[10]; - }; + //#define UART_DEVICE_NODE DT_CHOSEN(zephyr_shell_uart) @@ -131,14 +146,14 @@ struct bthome_adv_t { printk("Disconnected (reason 0x%02x)\n", reason); }*/ - uint32_t getTs() - { + /* uint32_t getTs() + {e uint32_t ts = (k_uptime_get_32()/1000); // LOG_INF("getTs: %d", ts); return ts; } - uint16_t findsmallestwait() + uint16_t findsmallestwait() { uint16_t minval = UINT16_MAX; uint32_t ts = getTs(); @@ -166,7 +181,7 @@ struct bthome_adv_t { return UINT16_MAX; } - +*/ static void bt_ready(void) { @@ -179,7 +194,11 @@ struct bthome_adv_t { }*/ - err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0); + struct bt_data ad[] = { + //BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA(BT_DATA_SVC_DATA16, mfg_bthome_data, mfg_bthome_sizetosend), + }; + err = bt_le_adv_start(BT_LE_ADV_PARAM(BT_LE_ADV_OPT_USE_IDENTITY | BT_LE_ADV_OPT_USE_NAME, BT_GAP_ADV_SLOW_INT_MIN,BT_GAP_ADV_SLOW_INT_MAX, NULL), ad, ARRAY_SIZE(ad), NULL, 0); if (err) { printk("Advertising failed to start (err %d)\n", err); return; @@ -212,7 +231,7 @@ struct bthome_adv_t { .cancel = auth_cancel, };*/ - bool test_bthome_data_set() + /*bool test_bthome_data_set() { for(int i = 0; i < sizeof(bthome_datapos); i++) { @@ -223,59 +242,154 @@ struct bthome_adv_t { } } return true; + }*/ + +/*void shift_bthome_datapos(uint8_t value) +{ + for(int i = 0; i < BTHOME_MAXELT; i++) + { + if(bthome_datapos[i] == 255) + { + bthome_datapos[i] = bthome_datapos[i] + value; } + } + +} +uint8_t mfg_bthome_addelt(uint8_t eltid, uint8_t eltsize) +{ + if(mfg_bthome_numelt == 0) + { + mfg_bthome_numelt++; + mfg_bthome_data[3] = eltid; + mfg_bthome_sizetosend = mfg_bthome_sizetosend + eltsize +1; + return 3; + } else { + if(eltid < mfg_bthome_data[3]) + { + mfg_bthome_sizetosend = mfg_bthome_sizetosend + eltsize +1; + for(int i = 0; i < eltsize+1; i++) + { + for(int j = mfg_bthome_sizetosend; j > 2; j--) + { + mfg_bthome_data[j] = mfg_bthome_data[j-1]; + } + } + shift_bthome_datapos(eltsize+1); + mfg_bthome_data[3] = eltid; + return 3; + } else { + mfg_bthome_data[mfg_bthome_sizetosend] = eltid; + mfg_bthome_sizetosend = mfg_bthome_sizetosend + eltsize +1; + return mfg_bthome_sizetosend - eltsize - 1; + } + } +} +*/ + +void mfg_bthome_build() +{ + for(int i = 0; i < BTHOME_MAXELT; i++) + { + if(eltenabled[i] == 1) + { + mfg_bthome_numelt++; + mfg_bthome_data[mfg_bthome_sizetosend] = eltid[i]; + bthome_datapos[i] = mfg_bthome_sizetosend; + mfg_bthome_sizetosend = mfg_bthome_sizetosend + eltsize[i] + 1; + } + } +} void publish_adv_thread() { bool adv_activate = false; + uint8_t numeltreceived = 0; +uint8_t eltreceived[BTHOME_MAXELT] = { 0, 0, 0, 0, 0, 0, 0 }; + //static uint8_t mfg_bthome_data[] = { 0xd2, 0xfc, 0x40, 0x01, 0x00, 0x0c, 0x00, 0x00, 0x02, 0x00, 0x00, 0x2e, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0e, 0x00, 0x00 }; + //uint8_t bthome_datapos[] = { 4, 6, 9, 12, 14, 18, 21 }; k_sem_take(&ble_init_ok, K_FOREVER); for (;;) { - struct bthome_adv_t *request = k_fifo_get(&fifo_adv_data, K_FOREVER); + k_sem_take(&adv_send_ok, K_FOREVER); + while(k_fifo_is_empty(&fifo_adv_data) == 0) + { + struct bthome_adv_t *request = k_fifo_get(&fifo_adv_data, K_NO_WAIT); +/* if(bthome_datapos[request->type] == 255) + { + bthome_datapos[request->type] = mfg_bthome_addelt(eltid[request->type], eltsize[request->type]); + }*/ + + if(eltreceived[request->type] == 0) { + numeltreceived++; + eltreceived[request->type] = 1; + } if(request->type == VOLTAGE) { - mfg_bthome_data[10] = request->data_u16 >> 8; - mfg_bthome_data[9] = request->data_u16; + mfg_bthome_data[bthome_datapos[VOLTAGE]+2] = request->data_u16 >> 8; + mfg_bthome_data[bthome_datapos[VOLTAGE]+1] = request->data_u16; } else if(request->type == BATTERY) { - mfg_bthome_data[4] = request->data_u8; + mfg_bthome_data[bthome_datapos[BATTERY]+1] = request->data_u8; + } else if(request->type == VOLTAGEVDD) + { + mfg_bthome_data[bthome_datapos[VOLTAGEVDD]+2] = request->data_u16 >> 8; + mfg_bthome_data[bthome_datapos[VOLTAGEVDD]+1] = request->data_u16; + } else if(request->type == BATTERYVDD) + { + mfg_bthome_data[bthome_datapos[BATTERYVDD]+1] = request->data_u8; } else if(request->type == TEMPERATURE) { - mfg_bthome_data[7] = request->data_s16 >> 8; - mfg_bthome_data[6] = request->data_s16; + mfg_bthome_data[bthome_datapos[TEMPERATURE]+2] = request->data_s16 >> 8; + mfg_bthome_data[bthome_datapos[TEMPERATURE]+1] = request->data_s16; + } else if(request->type == HUMIDITY) + { + mfg_bthome_data[bthome_datapos[HUMIDITY]+1] = request->data_u8; + } else if(request->type == PRESSURE) + { + mfg_bthome_data[bthome_datapos[PRESSURE]+3] = request->data_u32 >> 16; + mfg_bthome_data[bthome_datapos[PRESSURE]+2] = request->data_u32 >> 8; + mfg_bthome_data[bthome_datapos[PRESSURE]+1] = request->data_u32; } else if(request->type == PM25) { - mfg_bthome_data[13] = request->data_u16 >> 8; - mfg_bthome_data[12] = request->data_u16; + mfg_bthome_data[bthome_datapos[PM25]+2] = request->data_u16 >> 8; + mfg_bthome_data[bthome_datapos[PM25]+1] = request->data_u16; } else if(request->type == PM10) { - mfg_bthome_data[16] = request->data_u16 >> 8; - mfg_bthome_data[15] = request->data_u16; + mfg_bthome_data[bthome_datapos[PM10]+2] = request->data_u16 >> 8; + mfg_bthome_data[bthome_datapos[PM10]+1] = request->data_u16; } - if(test_bthome_data_set() == true) - { + + k_free(request); + } + if(numeltreceived == mfg_bthome_numelt) { + //LOG_HEXDUMP_INF(mfg_bthome_data, mfg_bthome_sizetosend, "mfg_bthome_data"); + //printk("Send ADV Size %d\n", mfg_bthome_sizetosend); if(adv_activate == false) { bt_ready(); adv_activate = true; } else { + struct bt_data ad[] = { + //BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA(BT_DATA_SVC_DATA16, mfg_bthome_data, mfg_bthome_sizetosend), + }; bt_le_adv_update_data(ad, ARRAY_SIZE(ad), NULL, 0); printk("Update ADV\n"); //k_msleep(50); } } - k_free(request); - } + } } K_THREAD_DEFINE(publish_adv_thread_id, STACKSIZE, publish_adv_thread, NULL, NULL, NULL, PRIORITY, 0, 0); static void work_battery_handler(struct k_work *work) { struct bthome_adv_t bthome_adv; - bthome_adv.type = VOLTAGE; - bthome_adv.data_u16 = (uint16_t) battery_sample(); size_t size = sizeof(struct bthome_adv_t); + bthome_adv.type = VOLTAGE; + battery_setup(); + bthome_adv.data_u16 = (uint16_t) battery_sample(); char *mem_ptr = k_malloc(size); memcpy(mem_ptr, &bthome_adv, size); k_fifo_put(&fifo_adv_data, mem_ptr); @@ -285,167 +399,123 @@ struct bthome_adv_t { char *mem_ptr2 = k_malloc(size); memcpy(mem_ptr2, &bthome_adv, size); k_fifo_put(&fifo_adv_data, mem_ptr2); + k_sem_give(&adv_send_ok); printk("Battery: %d mV (%d%%)\n",bthome_adv.data_u16, bthome_adv.data_u8); + + bthome_adv.type = VOLTAGEVDD; + batteryvdd_setup(); + bthome_adv.data_u16 = (uint16_t) batteryvdd_sample(); + char *mem_ptr3 = k_malloc(size); + memcpy(mem_ptr3, &bthome_adv, size); + k_fifo_put(&fifo_adv_data, mem_ptr3); + + bthome_adv.type = BATTERYVDD; + bthome_adv.data_u8 = round(battery_level_pptt(bthome_adv.data_u16, battery_nimh_levels)/100); + char *mem_ptr4 = k_malloc(size); + memcpy(mem_ptr4, &bthome_adv, size); + k_fifo_put(&fifo_adv_data, mem_ptr4); + k_sem_give(&adv_send_ok); + printk("Battery VDD: %d mV (%d%%)\n",bthome_adv.data_u16, bthome_adv.data_u8); + } K_WORK_DEFINE(work_battery, work_battery_handler); +void timer_battery_handler(struct k_timer *stm) +{ + k_work_submit(&work_battery); +} + K_TIMER_DEFINE(timer_battery, timer_battery_handler, NULL); static void work_temperature_handler(struct k_work *work) { struct bthome_adv_t bthome_adv; + sht4x_probe(); bthome_adv.type = TEMPERATURE; - bthome_adv.data_s16 = 2512; + bthome_adv.data_s16 = (int16_t)(sht4x_ReadTempeature()*100); size_t size = sizeof(struct bthome_adv_t); char *mem_ptr = k_malloc(size); memcpy(mem_ptr, &bthome_adv, size); k_fifo_put(&fifo_adv_data, mem_ptr); printk("Temperature: %d°\n",bthome_adv.data_s16); + bthome_adv.type = HUMIDITY; + bthome_adv.data_u8 = (uint8_t)(sht4x_ReadHumidity()); + char *mem_ptr2 = k_malloc(size); + memcpy(mem_ptr2, &bthome_adv, size); + k_fifo_put(&fifo_adv_data, mem_ptr2); + k_sem_give(&adv_send_ok); + printk("Humidity: %d pc \n",bthome_adv.data_u8); } K_WORK_DEFINE(work_temperature, work_temperature_handler); - //char pm_measure_data[10]; - struct pms_data_t pms_data; - int pm_measure_position = 0; - bool pms_enable = false; - bool pms_warming = false; -#define WARMUPTIME_SDS_MS 15000 -#define READINGTIME_SDS_MS 5000 +void timer_temperature_handler(struct k_timer *stm) +{ + k_work_submit_to_queue(&workqueue_i2c, &work_temperature); +} + K_TIMER_DEFINE(timer_temperature, timer_temperature_handler, NULL); - - void send_uart_cmd(uint8_t *buf) +static void work_pressure_handler(struct k_work *work) { - //int msg_len = strlen(buf); - printk("Send to uart\n"); - - for (int i = 0; i < 19; i++) { - uart_poll_out(uart_dev, buf[i]); - } + struct bthome_adv_t bthome_adv; + bthome_adv.type = PRESSURE; + bthome_adv.data_u32 = bmp180_readPressure(); + size_t size = sizeof(struct bthome_adv_t); + char *mem_ptr = k_malloc(size); + memcpy(mem_ptr, &bthome_adv, size); + k_fifo_put(&fifo_adv_data, mem_ptr); + k_sem_give(&adv_send_ok); + printk("Pressure: %d Pa \n",bthome_adv.data_u32); } + K_WORK_DEFINE(work_pressure, work_pressure_handler); - static void work_pms_handler(struct k_work *work) + +void timer_pressure_handler(struct k_timer *stm) +{ + k_work_submit_to_queue(&workqueue_i2c, &work_pressure); +} + K_TIMER_DEFINE(timer_pressure, timer_pressure_handler, NULL); + +static void work_pms_handler(struct k_work *work) { - uint32_t ts_start_measure; - send_uart_cmd(SDS_WAKEUPCMD); - pms_warming = true; - pms_enable = true; - k_msleep(WARMUPTIME_SDS_MS); - pms_warming = false; - ts_start_measure = getTs(); - - struct pms_data_t *pms_data_read; - - uint32_t sds_pm10_sum = 0; - uint32_t sds_pm25_sum = 0; - uint32_t sds_val_count = 0; - uint32_t sds_pm10_max = 0; - uint32_t sds_pm10_min = 20000; - uint32_t sds_pm25_max = 0; - uint32_t sds_pm25_min = 20000; - - for(;;) - { - pms_data_read = k_fifo_get(&fifo_pms, K_MSEC(READINGTIME_SDS_MS)); - if(pms_data_read != NULL) - { - if(getTs() < (ts_start_measure + (READINGTIME_SDS_MS/1000))) - { - //printk("Parse PMS %d %d\n", ts_start_measure, getTs()); - uint32_t pm10_serial = 0; - uint32_t pm25_serial = 0; - int checksum_is = pms_data_read->data[2] + pms_data_read->data[3] + pms_data_read->data[4] + pms_data_read->data[5] + pms_data_read->data[6] + pms_data_read->data[7]; - if(pms_data_read->data[8] == (checksum_is % 256) && pms_data_read->data[0] == 170 && pms_data_read->data[1] == 192 && pms_data_read->data[9] == 171) - { - - pm25_serial = pms_data_read->data[2] | pms_data_read->data[3] << 8; - pm10_serial = pms_data_read->data[4] | pms_data_read->data[5] << 8; - sds_pm10_sum += pm10_serial; - sds_pm25_sum += pm25_serial; - UPDATE_MIN_MAX(sds_pm10_min, sds_pm10_max, pm10_serial) - UPDATE_MIN_MAX(sds_pm25_min, sds_pm25_max, pm25_serial) - - sds_val_count += 1; - - - printk("p10: %d p25: %d\n", pm10_serial, pm25_serial); - } - k_free(pms_data_read); - } else { - send_uart_cmd(SDS_SLEEPCMD); - pms_enable = false; - } - - } else { - break; - } - } - - - if (sds_val_count > 2) { - sds_pm10_sum = sds_pm10_sum - sds_pm10_min - sds_pm10_max; - sds_pm25_sum = sds_pm25_sum - sds_pm25_min - sds_pm25_max; - sds_val_count = sds_val_count - 2; - } - - if (sds_val_count > 0) { - - struct bthome_adv_t bthome_adv; + sds011_probe(); + struct bthome_adv_t bthome_adv; bthome_adv.type = PM25; - bthome_adv.data_u16 = sds_pm25_sum / (sds_val_count * 10); - size_t size = sizeof(struct bthome_adv_t); + bthome_adv.data_u16 = sds011_ReadPM25(); + size_t size = sizeof(struct bthome_adv_t); char *mem_ptr = k_malloc(size); memcpy(mem_ptr, &bthome_adv, size); k_fifo_put(&fifo_adv_data, mem_ptr); printk("PM25: %d\n",bthome_adv.data_u16); bthome_adv.type = PM10; - bthome_adv.data_u16 = sds_pm10_sum / (sds_val_count * 10); - char *mem_ptr2 = k_malloc(size); + bthome_adv.data_u16 = sds011_ReadPM10(); + char *mem_ptr2 = k_malloc(size); memcpy(mem_ptr2, &bthome_adv, size); k_fifo_put(&fifo_adv_data, mem_ptr2); printk("PM10: %d\n",bthome_adv.data_u16); - } - } - + k_sem_give(&adv_send_ok); +} K_WORK_DEFINE(work_pms, work_pms_handler); - void serial_cb(const struct device *dev, void *user_data) - { - uint8_t c; - - if (!uart_irq_update(uart_dev)) { - return; - } - - if (!uart_irq_rx_ready(uart_dev)) { - return; - } - - /* read until FIFO empty */ - while (uart_fifo_read(uart_dev, &c, 1) == 1) { - if(pms_enable == true && pms_warming == false) - { - //printk("UART char received: 0x%x\n", c); - if(c == 0xaa) { pm_measure_position = 0; pms_data.ts = getTs(); } - if(pm_measure_position >= 0) { - pms_data.data[pm_measure_position] = c; - pm_measure_position = pm_measure_position + 1; - } - if(pm_measure_position == 10) { - size_t size = sizeof(struct pms_data_t); - char *mem_ptr = k_malloc(size); - memcpy(mem_ptr, &pms_data, size); - k_fifo_put(&fifo_pms, mem_ptr); - } - //} else if(pms_enable == false) { - // send_uart_cmd(SDS_SLEEPCMD); - } - } - } +void timer_pms_handler(struct k_timer *stm) +{ + k_work_submit_to_queue(&workqueue_pms, &work_pms); +} + K_TIMER_DEFINE(timer_pms, timer_pms_handler, NULL); + int main(void) { printk("Hello, It's TuxOutdooor\n"); int err; // bt_passkey_set(192847); + mfg_bthome_data[0]=0xd2; + mfg_bthome_data[1]=0xfc; + mfg_bthome_data[2]=0x40; + mfg_bthome_sizetosend=3; + + for(int i = 0; i < BTHOME_MAXELT; i++) + { + bthome_datapos[i] = 255; + } err = bt_enable(NULL); if (err) { @@ -453,7 +523,6 @@ struct bthome_adv_t { return 0; } - k_sem_give(&ble_init_ok); const struct device *dev = DEVICE_DT_GET(GPIO_PORT); @@ -461,27 +530,60 @@ struct bthome_adv_t { gpio_pin_set(dev, 20, 0); //bt_conn_auth_cb_register(&auth_cb); //static const struct device *const uart_dev = DEVICE_DT_GET(UART_DEVICE_NODE); + + +k_work_queue_init(&workqueue_i2c); +k_work_queue_start(&workqueue_i2c, stack_wq_i2c, K_THREAD_STACK_SIZEOF(stack_wq_i2c), PRIORITY, NULL); +k_work_queue_init(&workqueue_pms); +k_work_queue_start(&workqueue_pms, stack_wq_pms, K_THREAD_STACK_SIZEOF(stack_wq_pms), PRIORITY, NULL); - if (!device_is_ready(uart_dev)) { - printk("UART device not found!"); - return 0; - } - int ret = uart_irq_callback_user_data_set(uart_dev, serial_cb, NULL); + if(sht4x_setup() == 0) { + printk("SHT4x enabled\n"); + k_work_submit_to_queue(&workqueue_i2c, &work_temperature); + k_timer_start(&timer_temperature, K_SECONDS(300), K_SECONDS(300)); + eltenabled[TEMPERATURE] = 1; + eltenabled[HUMIDITY] = 1; + } else { + printk("SHT4x disabled\n"); + } - if (ret < 0) { - if (ret == -ENOTSUP) { - printk("Interrupt-driven UART API support not enabled\n"); - } else if (ret == -ENOSYS) { - printk("UART device does not support interrupt-driven API\n"); - } else { - printk("Error setting UART callback: %d\n", ret); - } - return 0; - } - uart_irq_rx_enable(uart_dev); + k_msleep(50); - for(;;) + if(bmp180_setup() == 0) { + printk("BMP180 enabled\n"); + k_work_submit_to_queue(&workqueue_i2c, &work_pressure); + k_timer_start(&timer_pressure, K_SECONDS(1800), K_SECONDS(1800)); + eltenabled[PRESSURE] = 1; + } else { + printk("BMP180 disabled\n"); + } + + k_msleep(50); + if(sds011_setup() == 0) { + printk("SDS011 enabled\n"); + k_work_submit_to_queue(&workqueue_pms, &work_pms); + eltenabled[PM10] = 1; + eltenabled[PM25] = 1; + k_timer_start(&timer_pms, K_SECONDS(1800), K_SECONDS(1800)); + } else { + printk("SDS011 disabled\n"); + } + + + + k_msleep(50); + k_work_submit(&work_battery); + k_timer_start(&timer_battery, K_SECONDS(3600), K_SECONDS(3600)); + eltenabled[BATTERY] = 1; + eltenabled[VOLTAGE] = 1; + eltenabled[BATTERYVDD] = 1; + eltenabled[VOLTAGEVDD] = 1; + + mfg_bthome_build(); + + k_sem_give(&ble_init_ok); +/* for(;;) { if(findiftoprobe(BATTERY, true) < 5) { @@ -497,7 +599,7 @@ struct bthome_adv_t { } k_msleep(1000*findsmallestwait()); - } + } */ /*int len = 0; int pm10_serial = 0; diff --git a/src/sds011.c b/src/sds011.c new file mode 100644 index 0000000..7500116 --- /dev/null +++ b/src/sds011.c @@ -0,0 +1,227 @@ +#include +#include +#include + +#include "sds011.h" + +extern struct k_fifo fifo_adv_data; + +#define UPDATE_MIN(MIN, SAMPLE) if (SAMPLE < MIN) { MIN = SAMPLE; } +#define UPDATE_MAX(MAX, SAMPLE) if (SAMPLE > MAX) { MAX = SAMPLE; } +#define UPDATE_MIN_MAX(MIN, MAX, SAMPLE) { UPDATE_MIN(MIN, SAMPLE); UPDATE_MAX(MAX, SAMPLE); } + +#define UART_PORT DT_NODELABEL(uart0) + static const struct device *const uart_dev = DEVICE_DT_GET(UART_PORT); + + uint8_t SDS_SLEEPCMD[19] = { + 0xAA, 0xB4, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x05, 0xAB + }; + uint8_t SDS_WAKEUPCMD[19] = { + 0xAA, 0xB4, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x06, 0xAB + }; +static K_FIFO_DEFINE(fifo_pms); + struct pms_data_t { + void *fifo_reserved; + uint32_t ts; + char data[10]; + }; + //char pm_measure_data[10]; + struct pms_data_t pms_data; + int pm_measure_position = 0; + bool pms_enable = false; + bool pms_warming = false; +#define WARMUPTIME_SDS_MS 15000 +#define READINGTIME_SDS_MS 5000 + +uint16_t sds_pm10; +uint16_t sds_pm25; + +bool uart_data_received = false; + + +uint32_t getTsSDS() + { + uint32_t ts = (uint32_t) (k_uptime_get()/1000); + // LOG_INF("getTs: %d", ts); + return ts; + } + + void send_uart_cmd(uint8_t *buf) + { + //int msg_len = strlen(buf); + printk("Send to uart\n"); + + for (int i = 0; i < 19; i++) { + uart_poll_out(uart_dev, buf[i]); + } + } +void serial_cb(const struct device *dev, void *user_data) + { + uint8_t c; + + if (!uart_irq_update(uart_dev)) { + return; + } + + if (!uart_irq_rx_ready(uart_dev)) { + return; + } + + /* read until FIFO empty */ + while (uart_fifo_read(uart_dev, &c, 1) == 1) { + if(c == 0xaa) { uart_data_received = true; } + if(pms_enable == true && pms_warming == false) + { + //printk("UART char received: 0x%x\n", c); + if(c == 0xaa) { pm_measure_position = 0; pms_data.ts = getTsSDS(); } + if(pm_measure_position >= 0) { + pms_data.data[pm_measure_position] = c; + pm_measure_position = pm_measure_position + 1; + } + if(pm_measure_position == 10) { + size_t size = sizeof(struct pms_data_t); + char *mem_ptr = k_malloc(size); + memcpy(mem_ptr, &pms_data, size); + k_fifo_put(&fifo_pms, mem_ptr); + } + //} else if(pms_enable == false) { + // send_uart_cmd(SDS_SLEEPCMD); + } + } + } + +int sds011_setup() +{ + if (!device_is_ready(uart_dev)) { + printk("UART device not found!"); + return -1; + } + + const struct uart_config uartcfg = { 9600, UART_CFG_PARITY_NONE, UART_CFG_STOP_BITS_1, UART_CFG_DATA_BITS_8, UART_CFG_FLOW_CTRL_NONE }; + + int ret = uart_configure(uart_dev, &uartcfg); + if (ret < 0) { + printk("Error in comfiguring UART\n"); + } + + ret = uart_irq_callback_user_data_set(uart_dev, serial_cb, NULL); + + if (ret < 0) { + if (ret == -ENOTSUP) { + printk("Interrupt-driven UART API support not enabled\n"); + } else if (ret == -ENOSYS) { + printk("UART device does not support interrupt-driven API\n"); + } else { + printk("Error setting UART callback: %d\n", ret); + } + return -1; + } + uart_irq_rx_enable(uart_dev); + + send_uart_cmd(SDS_WAKEUPCMD); + k_msleep(2000); + if(uart_data_received == true) { + return 0; + } else { + return -1; + } +} + + + +int sds011_probe() + { + uint32_t ts_start_measure; + send_uart_cmd(SDS_WAKEUPCMD); + pms_warming = true; + pms_enable = true; + k_msleep(WARMUPTIME_SDS_MS); + pms_warming = false; + ts_start_measure = getTsSDS(); + + struct pms_data_t *pms_data_read; + + uint32_t sds_pm10_sum = 0; + uint32_t sds_pm25_sum = 0; + uint32_t sds_val_count = 0; + uint32_t sds_pm10_max = 0; + uint32_t sds_pm10_min = 20000; + uint32_t sds_pm25_max = 0; + uint32_t sds_pm25_min = 20000; + + for(;;) + { + pms_data_read = k_fifo_get(&fifo_pms, K_MSEC(READINGTIME_SDS_MS)); + if(pms_data_read != NULL) + { + if(getTsSDS() < (ts_start_measure + (READINGTIME_SDS_MS/1000))) + { + //printk("Parse PMS %d %d\n", ts_start_measure, getTsSDS()); + uint32_t pm10_serial = 0; + uint32_t pm25_serial = 0; + int checksum_is = pms_data_read->data[2] + pms_data_read->data[3] + pms_data_read->data[4] + pms_data_read->data[5] + pms_data_read->data[6] + pms_data_read->data[7]; + if(pms_data_read->data[8] == (checksum_is % 256) && pms_data_read->data[0] == 170 && pms_data_read->data[1] == 192 && pms_data_read->data[9] == 171) + { + + pm25_serial = pms_data_read->data[2] | pms_data_read->data[3] << 8; + pm10_serial = pms_data_read->data[4] | pms_data_read->data[5] << 8; + sds_pm10_sum += pm10_serial; + sds_pm25_sum += pm25_serial; + UPDATE_MIN_MAX(sds_pm10_min, sds_pm10_max, pm10_serial) + UPDATE_MIN_MAX(sds_pm25_min, sds_pm25_max, pm25_serial) + + sds_val_count += 1; + + + printk("p10: %d p25: %d\n", pm10_serial, pm25_serial); + } + k_free(pms_data_read); + } else { + send_uart_cmd(SDS_SLEEPCMD); + pms_enable = false; + } + + } else { + break; + } + } + + + if (sds_val_count > 2) { + sds_pm10_sum = sds_pm10_sum - sds_pm10_min - sds_pm10_max; + sds_pm25_sum = sds_pm25_sum - sds_pm25_min - sds_pm25_max; + sds_val_count = sds_val_count - 2; + } + + if (sds_val_count > 0) { + sds_pm25 = sds_pm25_sum / (sds_val_count * 10); + sds_pm10 = sds_pm10_sum / (sds_val_count * 10); + + /* struct bthome_adv_t bthome_adv; + bthome_adv.type = PM25; + bthome_adv.data_u16 = sds_pm25_sum / (sds_val_count * 10); + size_t size = sizeof(struct bthome_adv_t); + char *mem_ptr = k_malloc(size); + memcpy(mem_ptr, &bthome_adv, size); + k_fifo_put(&fifo_adv_data, mem_ptr); + printk("PM25: %d\n",bthome_adv.data_u16); + bthome_adv.type = PM10; + bthome_adv.data_u16 = sds_pm10_sum / (sds_val_count * 10); + char *mem_ptr2 = k_malloc(size); + memcpy(mem_ptr2, &bthome_adv, size); + k_fifo_put(&fifo_adv_data, mem_ptr2); + printk("PM10: %d\n",bthome_adv.data_u16); + */ + } + return 0; + } + +uint16_t sds011_ReadPM25() +{ + return sds_pm25; +} + +uint16_t sds011_ReadPM10() +{ + return sds_pm10; +} diff --git a/src/sds011.h b/src/sds011.h new file mode 100644 index 0000000..6eb327a --- /dev/null +++ b/src/sds011.h @@ -0,0 +1,5 @@ +#include +int sds011_setup(); +int sds011_probe(); +uint16_t sds011_ReadPM25(); +uint16_t sds011_ReadPM10(); diff --git a/src/sht4x.c b/src/sht4x.c new file mode 100644 index 0000000..a38e939 --- /dev/null +++ b/src/sht4x.c @@ -0,0 +1,56 @@ +#include +LOG_MODULE_REGISTER(sht4x); + +#include +#include +#include +#include + +#include + +#include + +#include "sht4x.h" + +#if !DT_HAS_COMPAT_STATUS_OKAY(sensirion_sht4x) +#error "No sensirion,sht4x compatible node found in the device tree" +#endif + +const struct device *const sht = DEVICE_DT_GET_ANY(sensirion_sht4x); + +int sht4x_setup() +{ + + if (!device_is_ready(sht)) { + printk("Device %s is not ready.\n", sht->name); + return -1; + } + return 0; +}; + +int sht4x_probe() +{ + if (sensor_sample_fetch(sht)) { + printk("Failed to fetch sample from SHT4X device\n"); + return -1; + } + return 0; +} + +double sht4x_ReadTempeature() +{ + struct sensor_value temp; + sensor_channel_get(sht, SENSOR_CHAN_AMBIENT_TEMP, &temp); + + + return sensor_value_to_double(&temp); +} + +double sht4x_ReadHumidity() +{ + struct sensor_value hum; + sensor_channel_get(sht, SENSOR_CHAN_HUMIDITY, &hum); + + + return sensor_value_to_double(&hum); +} diff --git a/src/sht4x.h b/src/sht4x.h new file mode 100644 index 0000000..f0008db --- /dev/null +++ b/src/sht4x.h @@ -0,0 +1,4 @@ +int sht4x_setup(); +int sht4x_probe(); +double sht4x_ReadTempeature(); +double sht4x_ReadHumidity(); diff --git a/CMakeLists.txt b/zephyr/CMakeLists.txt similarity index 71% rename from CMakeLists.txt rename to zephyr/CMakeLists.txt index 74707e5..449ea78 100644 --- a/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.20.0) -set(BOARD nrf52832_outdoor) +set(BOARD nrf52dk_nrf52832) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(tuxoutdoor) -FILE(GLOB app_sources src/*.c*) +FILE(GLOB app_sources ../src/*.c*) target_sources(app PRIVATE ${app_sources}) diff --git a/zephyr/boards/nrf52dk_nrf52832.overlay b/zephyr/boards/nrf52dk_nrf52832.overlay new file mode 100644 index 0000000..e0f15cb --- /dev/null +++ b/zephyr/boards/nrf52dk_nrf52832.overlay @@ -0,0 +1,76 @@ +/ { + vbatt { + compatible = "voltage-divider"; + io-channels = <&adc 4>; + output-ohms = <4600>; + full-ohms = <(4600 + 3230)>; + }; +}; + + +/ { + zephyr,user { + io-channels = <&adc 2>; + }; +}; + +&spi0 { + status = "disabled"; +}; + +&spi1 { + status = "disabled"; +}; + + +&i2c0 { + status = "okay"; + clock-frequency = ; + sht4x@44 { + status = "okay"; + compatible = "sensirion,sht4x"; + reg = <0x44>; + repeatability = <2>; + }; +}; + +&i2c1 { + status = "disabled"; +}; + + +&pinctrl { + uart0_default: uart0_default { + group1 { + psels = , + , + , + ; + }; + }; + + uart0_sleep: uart0_sleep { + group1 { + psels = , + , + , + ; + low-power-enable; + }; + }; + + i2c0_default: i2c0_default { + group1 { + psels = , + ; + }; + }; + + i2c0_sleep: i2c0_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; +}; diff --git a/prj.conf b/zephyr/prj.conf similarity index 84% rename from prj.conf rename to zephyr/prj.conf index fd6e606..da4a0e1 100644 --- a/prj.conf +++ b/zephyr/prj.conf @@ -1,7 +1,7 @@ CONFIG_BT=y #CONFIG_BT_SMP=y #CONFIG_BT_SIGNING=y -CONFIG_BT_PERIPHERAL=y +#CONFIG_BT_PERIPHERAL=y #CONFIG_BT_PRIVACY=n # Random Address #CONFIG_BT_FIXED_PASSKEY=y CONFIG_BT_DEVICE_NAME="TuxOutdoor" @@ -16,9 +16,13 @@ CONFIG_BT_DEVICE_NAME="TuxOutdoor" #CONFIG_NVS=y #CONFIG_SETTINGS=y +CONFIG_SENSOR=y +CONFIG_SHT4X=y + CONFIG_LOG=y -CONFIG_LOG_DEFAULT_LEVEL=3 -CONFIG_LOG_MAX_LEVEL=3 +CONFIG_LOG_DEFAULT_LEVEL=2 +CONFIG_LOG_MAX_LEVEL=2 +CONFIG_LOG_PRINTK=y CONFIG_USE_SEGGER_RTT=y CONFIG_LOG_BACKEND_RTT=y CONFIG_UART_CONSOLE=n