Refactor in files. Add BMP180 and SHT4x sensor, monitor VDD voltage, scheduling with timer

main
Nigreon 2024-03-12 23:26:00 +01:00
parent 82c0bcc836
commit 8eea54771b
15 changed files with 1108 additions and 212 deletions

View File

@ -1,8 +0,0 @@
/ {
vbatt {
compatible = "voltage-divider";
io-channels = <&adc 4>;
output-ohms = <4600>;
full-ohms = <(4600 + 3230)>;
};
};

31
platformio.ini Normal file
View File

@ -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

154
src/battery-vdd.c Normal file
View File

@ -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 <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <zephyr/kernel.h>
#include <zephyr/init.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/logging/log.h>
#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 = &dividervdd_config;
const struct io_channel_config *iocp = &cfg->io_channel;
const struct gpio_dt_spec *gcp = &cfg->power_gpios;
struct divider_data *ddp = &dividervdd_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 = &dividervdd_data;
//const struct divider_config *dcp = &dividervdd_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;
}

View File

@ -142,7 +142,7 @@ static int divider_setup(void)
static bool battery_ok; static bool battery_ok;
static int battery_setup(const struct device *arg) int battery_setup()
{ {
int rc = divider_setup(); int rc = divider_setup();
@ -151,7 +151,7 @@ static int battery_setup(const struct device *arg)
return rc; 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) int battery_measure_enable(bool enable)
{ {

View File

@ -15,12 +15,17 @@
*/ */
int battery_measure_enable(bool enable); int battery_measure_enable(bool enable);
int battery_setup();
int batteryvdd_setup();
/** Measure the battery voltage. /** Measure the battery voltage.
* *
* @return the battery voltage in millivolts, or a negative error * @return the battery voltage in millivolts, or a negative error
* code. * code.
*/ */
int16_t battery_sample(void); int16_t battery_sample(void);
int16_t batteryvdd_sample(void);
/** A point in a battery discharge curve sequence. /** A point in a battery discharge curve sequence.
* *

237
src/bmp180.c Normal file
View File

@ -0,0 +1,237 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bmp180);
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/i2c.h>
#include <stdbool.h>
#include <math.h>
#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, &reg, 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;
};

3
src/bmp180.h Normal file
View File

@ -0,0 +1,3 @@
#include <zephyr/sys/byteorder.h>
int bmp180_setup();
uint32_t bmp180_readPressure();

View File

@ -27,39 +27,45 @@
#include <zephyr/devicetree.h> #include <zephyr/devicetree.h>
#include <zephyr/drivers/sensor.h> #include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/gpio.h> #include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/uart.h>
#include <math.h> #include <math.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main);
#include "battery.h" #include "battery.h"
#include "sht4x.h"
#include "bmp180.h"
#include "sds011.h"
#include "hal/nrf_gpiote.h" #include "hal/nrf_gpiote.h"
static K_SEM_DEFINE(ble_init_ok, 0, 1);
static K_FIFO_DEFINE(fifo_adv_data); 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 { struct bthome_adv_t {
void *fifo_reserved; void *fifo_reserved;
uint8_t type; uint8_t type;
int16_t data_s16; int16_t data_s16;
uint16_t data_u16; uint16_t data_u16;
uint8_t data_u8; 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 { struct captor_wait {
enum captor_type captor; enum captor_type captor;
@ -69,16 +75,22 @@ struct bthome_adv_t {
struct captor_wait params[] = { {BATTERY, 120 }, {TEMPERATURE, 60}, {PM25, 60} }; 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_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 GPIO_PORT DT_NODELABEL(gpio0)
#define STACKSIZE 2048 #define STACKSIZE 2048
#define PRIORITY 7 #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[] = { static const struct battery_level_point battery_levels[] = {
/* "Curve" here eyeballed from captured data for the [Adafruit /* "Curve" here eyeballed from captured data for the [Adafruit
* 3.7v 2000 mAh](https://www.adafruit.com/product/2011) LIPO * 3.7v 2000 mAh](https://www.adafruit.com/product/2011) LIPO
@ -96,23 +108,26 @@ struct bthome_adv_t {
{ 625, 3550 }, { 625, 3550 },
{ 0, 3100 }, { 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) { 10000, 2800 },
static const struct device *const uart_dev = DEVICE_DT_GET(UART_PORT); { 90, 2600 },
{ 50, 2440 },
uint8_t SDS_SLEEPCMD[19] = { { 0, 2300 },
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];
};
//#define UART_DEVICE_NODE DT_CHOSEN(zephyr_shell_uart) //#define UART_DEVICE_NODE DT_CHOSEN(zephyr_shell_uart)
@ -131,8 +146,8 @@ struct bthome_adv_t {
printk("Disconnected (reason 0x%02x)\n", reason); printk("Disconnected (reason 0x%02x)\n", reason);
}*/ }*/
uint32_t getTs() /* uint32_t getTs()
{ {e
uint32_t ts = (k_uptime_get_32()/1000); uint32_t ts = (k_uptime_get_32()/1000);
// LOG_INF("getTs: %d", ts); // LOG_INF("getTs: %d", ts);
return ts; return ts;
@ -166,7 +181,7 @@ struct bthome_adv_t {
return UINT16_MAX; return UINT16_MAX;
} }
*/
static void bt_ready(void) 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) { if (err) {
printk("Advertising failed to start (err %d)\n", err); printk("Advertising failed to start (err %d)\n", err);
return; return;
@ -212,7 +231,7 @@ struct bthome_adv_t {
.cancel = auth_cancel, .cancel = auth_cancel,
};*/ };*/
bool test_bthome_data_set() /*bool test_bthome_data_set()
{ {
for(int i = 0; i < sizeof(bthome_datapos); i++) for(int i = 0; i < sizeof(bthome_datapos); i++)
{ {
@ -223,49 +242,143 @@ struct bthome_adv_t {
} }
} }
return true; 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() void publish_adv_thread()
{ {
bool adv_activate = false; 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); k_sem_take(&ble_init_ok, K_FOREVER);
for (;;) { 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) if(request->type == VOLTAGE)
{ {
mfg_bthome_data[10] = request->data_u16 >> 8; mfg_bthome_data[bthome_datapos[VOLTAGE]+2] = request->data_u16 >> 8;
mfg_bthome_data[9] = request->data_u16; mfg_bthome_data[bthome_datapos[VOLTAGE]+1] = request->data_u16;
} else if(request->type == BATTERY) } 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) } else if(request->type == TEMPERATURE)
{ {
mfg_bthome_data[7] = request->data_s16 >> 8; mfg_bthome_data[bthome_datapos[TEMPERATURE]+2] = request->data_s16 >> 8;
mfg_bthome_data[6] = request->data_s16; 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) } else if(request->type == PM25)
{ {
mfg_bthome_data[13] = request->data_u16 >> 8; mfg_bthome_data[bthome_datapos[PM25]+2] = request->data_u16 >> 8;
mfg_bthome_data[12] = request->data_u16; mfg_bthome_data[bthome_datapos[PM25]+1] = request->data_u16;
} else if(request->type == PM10) } else if(request->type == PM10)
{ {
mfg_bthome_data[16] = request->data_u16 >> 8; mfg_bthome_data[bthome_datapos[PM10]+2] = request->data_u16 >> 8;
mfg_bthome_data[15] = request->data_u16; 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) if(adv_activate == false)
{ {
bt_ready(); bt_ready();
adv_activate = true; adv_activate = true;
} else { } 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); bt_le_adv_update_data(ad, ARRAY_SIZE(ad), NULL, 0);
printk("Update ADV\n"); printk("Update ADV\n");
//k_msleep(50); //k_msleep(50);
} }
} }
k_free(request);
} }
} }
K_THREAD_DEFINE(publish_adv_thread_id, STACKSIZE, publish_adv_thread, NULL, NULL, NULL, PRIORITY, 0, 0); K_THREAD_DEFINE(publish_adv_thread_id, STACKSIZE, publish_adv_thread, NULL, NULL, NULL, PRIORITY, 0, 0);
@ -273,9 +386,10 @@ struct bthome_adv_t {
static void work_battery_handler(struct k_work *work) static void work_battery_handler(struct k_work *work)
{ {
struct bthome_adv_t bthome_adv; 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); 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); char *mem_ptr = k_malloc(size);
memcpy(mem_ptr, &bthome_adv, size); memcpy(mem_ptr, &bthome_adv, size);
k_fifo_put(&fifo_adv_data, mem_ptr); k_fifo_put(&fifo_adv_data, mem_ptr);
@ -285,160 +399,107 @@ struct bthome_adv_t {
char *mem_ptr2 = k_malloc(size); char *mem_ptr2 = k_malloc(size);
memcpy(mem_ptr2, &bthome_adv, size); memcpy(mem_ptr2, &bthome_adv, size);
k_fifo_put(&fifo_adv_data, mem_ptr2); 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); 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); 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) static void work_temperature_handler(struct k_work *work)
{ {
struct bthome_adv_t bthome_adv; struct bthome_adv_t bthome_adv;
sht4x_probe();
bthome_adv.type = TEMPERATURE; 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); size_t size = sizeof(struct bthome_adv_t);
char *mem_ptr = k_malloc(size); char *mem_ptr = k_malloc(size);
memcpy(mem_ptr, &bthome_adv, size); memcpy(mem_ptr, &bthome_adv, size);
k_fifo_put(&fifo_adv_data, mem_ptr); k_fifo_put(&fifo_adv_data, mem_ptr);
printk("Temperature: %d°\n",bthome_adv.data_s16); 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); K_WORK_DEFINE(work_temperature, work_temperature_handler);
//char pm_measure_data[10]; void timer_temperature_handler(struct k_timer *stm)
struct pms_data_t pms_data; {
int pm_measure_position = 0; k_work_submit_to_queue(&workqueue_i2c, &work_temperature);
bool pms_enable = false; }
bool pms_warming = false; K_TIMER_DEFINE(timer_temperature, timer_temperature_handler, NULL);
#define WARMUPTIME_SDS_MS 15000
#define READINGTIME_SDS_MS 5000
static void work_pressure_handler(struct k_work *work)
void send_uart_cmd(uint8_t *buf)
{ {
//int msg_len = strlen(buf); struct bthome_adv_t bthome_adv;
printk("Send to uart\n"); bthome_adv.type = PRESSURE;
bthome_adv.data_u32 = bmp180_readPressure();
for (int i = 0; i < 19; i++) { size_t size = sizeof(struct bthome_adv_t);
uart_poll_out(uart_dev, buf[i]); 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; sds011_probe();
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; struct bthome_adv_t bthome_adv;
bthome_adv.type = PM25; bthome_adv.type = PM25;
bthome_adv.data_u16 = sds_pm25_sum / (sds_val_count * 10); bthome_adv.data_u16 = sds011_ReadPM25();
size_t size = sizeof(struct bthome_adv_t); size_t size = sizeof(struct bthome_adv_t);
char *mem_ptr = k_malloc(size); char *mem_ptr = k_malloc(size);
memcpy(mem_ptr, &bthome_adv, size); memcpy(mem_ptr, &bthome_adv, size);
k_fifo_put(&fifo_adv_data, mem_ptr); k_fifo_put(&fifo_adv_data, mem_ptr);
printk("PM25: %d\n",bthome_adv.data_u16); printk("PM25: %d\n",bthome_adv.data_u16);
bthome_adv.type = PM10; bthome_adv.type = PM10;
bthome_adv.data_u16 = sds_pm10_sum / (sds_val_count * 10); bthome_adv.data_u16 = sds011_ReadPM10();
char *mem_ptr2 = k_malloc(size); char *mem_ptr2 = k_malloc(size);
memcpy(mem_ptr2, &bthome_adv, size); memcpy(mem_ptr2, &bthome_adv, size);
k_fifo_put(&fifo_adv_data, mem_ptr2); k_fifo_put(&fifo_adv_data, mem_ptr2);
printk("PM10: %d\n",bthome_adv.data_u16); printk("PM10: %d\n",bthome_adv.data_u16);
} k_sem_give(&adv_send_ok);
} }
K_WORK_DEFINE(work_pms, work_pms_handler); 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)) { void timer_pms_handler(struct k_timer *stm)
return; {
} k_work_submit_to_queue(&workqueue_pms, &work_pms);
}
if (!uart_irq_rx_ready(uart_dev)) { K_TIMER_DEFINE(timer_pms, timer_pms_handler, NULL);
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);
}
}
}
int main(void) int main(void)
{ {
@ -446,6 +507,15 @@ struct bthome_adv_t {
int err; int err;
// bt_passkey_set(192847); // 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); err = bt_enable(NULL);
if (err) { if (err) {
@ -453,7 +523,6 @@ struct bthome_adv_t {
return 0; return 0;
} }
k_sem_give(&ble_init_ok);
const struct device *dev = DEVICE_DT_GET(GPIO_PORT); const struct device *dev = DEVICE_DT_GET(GPIO_PORT);
@ -462,26 +531,59 @@ struct bthome_adv_t {
//bt_conn_auth_cb_register(&auth_cb); //bt_conn_auth_cb_register(&auth_cb);
//static const struct device *const uart_dev = DEVICE_DT_GET(UART_DEVICE_NODE); //static const struct device *const uart_dev = DEVICE_DT_GET(UART_DEVICE_NODE);
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); 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 (ret < 0) {
if (ret == -ENOTSUP) { if(sht4x_setup() == 0) {
printk("Interrupt-driven UART API support not enabled\n"); printk("SHT4x enabled\n");
} else if (ret == -ENOSYS) { k_work_submit_to_queue(&workqueue_i2c, &work_temperature);
printk("UART device does not support interrupt-driven API\n"); k_timer_start(&timer_temperature, K_SECONDS(300), K_SECONDS(300));
eltenabled[TEMPERATURE] = 1;
eltenabled[HUMIDITY] = 1;
} else { } else {
printk("Error setting UART callback: %d\n", ret); printk("SHT4x disabled\n");
} }
return 0;
}
uart_irq_rx_enable(uart_dev);
for(;;) k_msleep(50);
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) if(findiftoprobe(BATTERY, true) < 5)
{ {
@ -497,7 +599,7 @@ struct bthome_adv_t {
} }
k_msleep(1000*findsmallestwait()); k_msleep(1000*findsmallestwait());
} } */
/*int len = 0; /*int len = 0;
int pm10_serial = 0; int pm10_serial = 0;

227
src/sds011.c Normal file
View File

@ -0,0 +1,227 @@
#include <sys/_stdint.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/uart.h>
#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;
}

5
src/sds011.h Normal file
View File

@ -0,0 +1,5 @@
#include <zephyr/types.h>
int sds011_setup();
int sds011_probe();
uint16_t sds011_ReadPM25();
uint16_t sds011_ReadPM10();

56
src/sht4x.c Normal file
View File

@ -0,0 +1,56 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(sht4x);
#include <zephyr/kernel.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/device.h>
#include <stdbool.h>
#include <zephyr/drivers/sensor/sht4x.h>
#include <math.h>
#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);
}

4
src/sht4x.h Normal file
View File

@ -0,0 +1,4 @@
int sht4x_setup();
int sht4x_probe();
double sht4x_ReadTempeature();
double sht4x_ReadHumidity();

View File

@ -1,8 +1,8 @@
cmake_minimum_required(VERSION 3.20.0) cmake_minimum_required(VERSION 3.20.0)
set(BOARD nrf52832_outdoor) set(BOARD nrf52dk_nrf52832)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(tuxoutdoor) project(tuxoutdoor)
FILE(GLOB app_sources src/*.c*) FILE(GLOB app_sources ../src/*.c*)
target_sources(app PRIVATE ${app_sources}) target_sources(app PRIVATE ${app_sources})

View File

@ -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 = <I2C_BITRATE_FAST>;
sht4x@44 {
status = "okay";
compatible = "sensirion,sht4x";
reg = <0x44>;
repeatability = <2>;
};
};
&i2c1 {
status = "disabled";
};
&pinctrl {
uart0_default: uart0_default {
group1 {
psels = <NRF_PSEL(UART_TX, 0, 19)>,
<NRF_PSEL(UART_RX, 0, 18)>,
<NRF_PSEL(UART_RTS, 0, 5)>,
<NRF_PSEL(UART_CTS, 0, 7)>;
};
};
uart0_sleep: uart0_sleep {
group1 {
psels = <NRF_PSEL(UART_TX, 0, 19)>,
<NRF_PSEL(UART_RX, 0, 18)>,
<NRF_PSEL(UART_RTS, 0, 5)>,
<NRF_PSEL(UART_CTS, 0, 7)>;
low-power-enable;
};
};
i2c0_default: i2c0_default {
group1 {
psels = <NRF_PSEL(TWIM_SDA, 0, 17)>,
<NRF_PSEL(TWIM_SCL, 0, 16)>;
};
};
i2c0_sleep: i2c0_sleep {
group1 {
psels = <NRF_PSEL(TWIM_SDA, 0, 17)>,
<NRF_PSEL(TWIM_SCL, 0, 16)>;
low-power-enable;
};
};
};

View File

@ -1,7 +1,7 @@
CONFIG_BT=y CONFIG_BT=y
#CONFIG_BT_SMP=y #CONFIG_BT_SMP=y
#CONFIG_BT_SIGNING=y #CONFIG_BT_SIGNING=y
CONFIG_BT_PERIPHERAL=y #CONFIG_BT_PERIPHERAL=y
#CONFIG_BT_PRIVACY=n # Random Address #CONFIG_BT_PRIVACY=n # Random Address
#CONFIG_BT_FIXED_PASSKEY=y #CONFIG_BT_FIXED_PASSKEY=y
CONFIG_BT_DEVICE_NAME="TuxOutdoor" CONFIG_BT_DEVICE_NAME="TuxOutdoor"
@ -16,9 +16,13 @@ CONFIG_BT_DEVICE_NAME="TuxOutdoor"
#CONFIG_NVS=y #CONFIG_NVS=y
#CONFIG_SETTINGS=y #CONFIG_SETTINGS=y
CONFIG_SENSOR=y
CONFIG_SHT4X=y
CONFIG_LOG=y CONFIG_LOG=y
CONFIG_LOG_DEFAULT_LEVEL=3 CONFIG_LOG_DEFAULT_LEVEL=2
CONFIG_LOG_MAX_LEVEL=3 CONFIG_LOG_MAX_LEVEL=2
CONFIG_LOG_PRINTK=y
CONFIG_USE_SEGGER_RTT=y CONFIG_USE_SEGGER_RTT=y
CONFIG_LOG_BACKEND_RTT=y CONFIG_LOG_BACKEND_RTT=y
CONFIG_UART_CONSOLE=n CONFIG_UART_CONSOLE=n