files: split sensors out into their own source files

This commit is contained in:
A.M. Rowsell 2026-06-27 21:28:41 -04:00
commit 657ee06eed
5 changed files with 167 additions and 32 deletions

2
aht.c
View file

@ -1,7 +1,7 @@
#include "aht.h" #include "aht.h"
#include "hardware/i2c.h" #include "hardware/i2c.h"
#include "pico/stdlib.h" #include "pico/stdlib.h" // IWYU pragma: keep
static const uint8_t AHT_MEASURE[3] = {0xAC, 0x33, 0x00}; static const uint8_t AHT_MEASURE[3] = {0xAC, 0x33, 0x00};

127
bmp.c
View file

@ -1,18 +1,121 @@
#include "bmp.h" // Copyright 2026 A.M. Rowsell <amr@frzn.dev>
// SPDX-License-Identifier: MIT
#include "./bmp.h"
uint8_t BMP_RESET[2] = {0x7E, 0xB6}; static const uint8_t BMP_RESET[2] = {0x7E, 0xB6};
uint8_t BMP_CAL_DATA[21]; static const uint8_t BMP_CAL_DATA[21];
uint8_t BMP_SETUP[6] = {0x1C, 0x0B, 0x1D, 0x08, 0x1F, 0x0A}; static const uint8_t BMP_SETUP[6] = {0x1C, 0x0B, 0x1D, 0x08, 0x1F, 0x0A};
uint8_t BMP_START[2] = {0x1B, 0x33}; static const uint8_t BMP_START[2] = {0x1B, 0x33};
double bmp_cal_data_fp[15]; float bmp_cal_data_fp[15];
int getBMPCalData(bmp_cal_data_t *cal) { int getBMPData(i2c_inst_t *i2c, float *temperature, float *pressure) {
// TODO: Implement calibration data retrieval uint8_t rawMeasurements[7];
return 0; uint32_t rawTemp;
uint32_t rawPress;
i2c_write_blocking(i2c, BMP_ADDR, &BMP_STATUS, 1, true);
i2c_read_blocking(i2c, BMP_ADDR, rawMeasurements, 7,
false); // read status & 6 data bytes
if ((rawMeasurements[0] & 0x60) != 0x60) {
// no measurements ready
return 1;
}
rawPress = (rawMeasurements[3] << 16) | (rawMeasurements[2] << 8) |
rawMeasurements[1];
rawTemp = (rawMeasurements[6] << 16) | (rawMeasurements[5] << 8) |
rawMeasurements[4];
// now for the crazy math
float partial_data1;
float partial_data2;
float t_lin;
partial_data1 = (float)(rawTemp - bmp_cal_data_fp[PAR_T1]);
partial_data2 = (float)(partial_data1 * bmp_cal_data_fp[PAR_T2]);
t_lin =
partial_data2 + (partial_data1 * partial_data1) * bmp_cal_data_fp[PAR_T3];
// t_lin is compensated temperature
float comp_press;
float partial_data3;
float partial_data4;
float partial_out1;
float partial_out2;
partial_data1 = bmp_cal_data_fp[PAR_P6] * t_lin;
partial_data2 = bmp_cal_data_fp[PAR_P7] * (t_lin * t_lin);
partial_data3 = bmp_cal_data_fp[PAR_P8] * (t_lin * t_lin * t_lin);
partial_out1 =
bmp_cal_data_fp[PAR_P5] + partial_data1 + partial_data2 + partial_data3;
partial_data1 = bmp_cal_data_fp[PAR_P2] * t_lin;
partial_data2 = bmp_cal_data_fp[PAR_P3] * (t_lin * t_lin);
partial_data3 = bmp_cal_data_fp[PAR_P4] * (t_lin * t_lin * t_lin);
partial_out2 = (float)rawPress * (bmp_cal_data_fp[PAR_P1] + partial_data1 +
partial_data2 + partial_data3);
partial_data1 = (float)rawPress * (float)rawPress;
partial_data2 = bmp_cal_data_fp[PAR_P9] + bmp_cal_data_fp[PAR_P10] * t_lin;
partial_data3 = partial_data1 * partial_data2;
partial_data4 =
partial_data3 + ((float)rawPress * (float)rawPress * (float)rawPress) *
bmp_cal_data_fp[PAR_P11];
comp_press = partial_out1 + partial_out2 + partial_data4;
*temperature = t_lin;
*pressure = comp_press;
return 0;
} }
int initBMP(void) { int getBMPCalData(i2c_inst_t *i2c, bmp_cal_data_t *calData) {
// TODO: Implement BMP initialization uint8_t rawValues[21];
return 0; i2c_write_blocking(i2c, BMP_ADDR, &BMP_CAL_ADDR, 1, true);
i2c_read_blocking(i2c, BMP_ADDR, rawValues, BMP_CAL_LEN, false);
calData->NVM_PAR_T1 = (uint16_t)((rawValues[1] << 8) | rawValues[0]);
calData->NVM_PAR_T2 = (uint16_t)((rawValues[3] << 8) | rawValues[2]);
calData->NVM_PAR_T3 = (int8_t)(rawValues[4]);
calData->NVM_PAR_P1 = (int16_t)((rawValues[6] << 8) | rawValues[5]);
calData->NVM_PAR_P2 = (int16_t)((rawValues[8] << 8) | rawValues[7]);
calData->NVM_PAR_P3 = (int8_t)(rawValues[9]);
calData->NVM_PAR_P4 = (int8_t)(rawValues[10]);
calData->NVM_PAR_P5 = (uint16_t)((rawValues[12] << 8) | rawValues[11]);
calData->NVM_PAR_P6 = (uint16_t)((rawValues[14] << 8) | rawValues[13]);
calData->NVM_PAR_P7 = (int8_t)(rawValues[15]);
calData->NVM_PAR_P8 = (int8_t)(rawValues[16]);
calData->NVM_PAR_P9 = (int16_t)((rawValues[18] << 8) | rawValues[17]);
calData->NVM_PAR_P10 = (int8_t)(rawValues[19]);
calData->NVM_PAR_P11 = (int8_t)(rawValues[20]);
// convert cal values to floating point, adjusted values
bmp_cal_data_fp[PAR_T1] = (float)calData->NVM_PAR_T1 / pow(2, -8);
bmp_cal_data_fp[PAR_T2] = (float)calData->NVM_PAR_T2 / pow(2, 30);
bmp_cal_data_fp[PAR_T3] = (float)calData->NVM_PAR_T3 / pow(2, 48);
bmp_cal_data_fp[PAR_P1] =
((float)calData->NVM_PAR_P1 - pow(2, 14)) / pow(2, 20);
bmp_cal_data_fp[PAR_P2] =
((float)calData->NVM_PAR_P2 - pow(2, 14)) / pow(2, 29);
bmp_cal_data_fp[PAR_P3] = (float)calData->NVM_PAR_P3 / pow(2, 32);
bmp_cal_data_fp[PAR_P4] = (float)calData->NVM_PAR_P4 / pow(2, 37);
bmp_cal_data_fp[PAR_P5] = (float)calData->NVM_PAR_P5 / pow(2, -3);
bmp_cal_data_fp[PAR_P6] = (float)calData->NVM_PAR_P6 / pow(2, 6);
bmp_cal_data_fp[PAR_P7] = (float)calData->NVM_PAR_P7 / pow(2, 8);
bmp_cal_data_fp[PAR_P8] = (float)calData->NVM_PAR_P8 / pow(2, 15);
bmp_cal_data_fp[PAR_P9] = (float)calData->NVM_PAR_P9 / pow(2, 48);
bmp_cal_data_fp[PAR_P10] = (float)calData->NVM_PAR_P10 / pow(2, 48);
bmp_cal_data_fp[PAR_P11] = (float)calData->NVM_PAR_P11 / pow(2, 65);
return 0;
}
int initBMP(i2c_inst_t *i2c) {
bmp_cal_data_t bmpRawCalValues, *bmpRawCalValues_t = &bmpRawCalValues;
i2c_write_blocking(i2c, BMP_ADDR, BMP_RESET, 2, false); // send reset
sleep_ms(2); // wait for reset to complete
getBMPCalData(i2c, bmpRawCalValues_t);
i2c_write_blocking(i2c, BMP_ADDR, BMP_SETUP, 6,
false); // set up sampling parameters
i2c_write_blocking(i2c, BMP_ADDR, BMP_START, 2,
false); // start automatic measurements
return 0;
} }

23
bmp.h
View file

@ -1,17 +1,19 @@
#ifndef BMP_H #ifndef BMP_H
#define BMP_H #define BMP_H
#include "hardware/i2c.h"
#include <math.h>
#include <stdint.h> #include <stdint.h>
#define BMP_ADDR 0x77 #define BMP_ADDR 0x77
#define BMP_STATUS 0x03 static const uint8_t BMP_STATUS = 0x03;
#define BMP_DATA 0x04 static const uint8_t BMP_DATA = 0x04;
#define BMP_CAL_ADDR 0x31 static const uint8_t BMP_CAL_ADDR = 0x31;
#define BMP_CAL_LEN 21 #define BMP_CAL_LEN 21
extern uint8_t BMP_RESET[2]; static const uint8_t BMP_RESET[2];
extern uint8_t BMP_CAL_DATA[21]; static const uint8_t BMP_CAL_DATA[21];
extern uint8_t BMP_SETUP[6]; static const uint8_t BMP_SETUP[6];
extern uint8_t BMP_START[2]; static const uint8_t BMP_START[2];
// instead of a bunch of defines // instead of a bunch of defines
enum { enum {
@ -49,9 +51,10 @@ typedef struct {
} bmp_cal_data_t; } bmp_cal_data_t;
// floating point conversions // floating point conversions
extern double bmp_cal_data_fp[15]; extern float bmp_cal_data_fp[15];
int getBMPCalData(bmp_cal_data_t *cal); int getBMPData(i2c_inst_t *i2c, float *temperature, float *pressure);
int initBMP(void); int getBMPCalData(i2c_inst_t *i2c, bmp_cal_data_t *cal);
int initBMP(i2c_inst_t *i2c);
#endif // BMP_H #endif // BMP_H

2
ens.c
View file

@ -1,7 +1,7 @@
#include "ens.h" #include "ens.h"
#include "hardware/i2c.h" #include "hardware/i2c.h"
#include "pico/stdlib.h" #include "pico/stdlib.h" // IWYU pragma: keep
static const uint8_t ENS_ACTIVATE[2] = {0x10, 0x02}; static const uint8_t ENS_ACTIVATE[2] = {0x10, 0x02};
static const uint8_t ENS_IDLE[2] = {0x10, 0x01}; static const uint8_t ENS_IDLE[2] = {0x10, 0x01};

45
main.c
View file

@ -18,8 +18,9 @@
* the readings to Adafruit IO over MQTT using the Pico W. * the readings to Adafruit IO over MQTT using the Pico W.
******************************************************************************/ ******************************************************************************/
#include "aht.h" #include "./aht.h"
#include "ens.h" #include "./bmp.h"
#include "./ens.h"
#include "./mqtt.h" #include "./mqtt.h"
#include "hardware/gpio.h" #include "hardware/gpio.h"
#include "hardware/i2c.h" #include "hardware/i2c.h"
@ -32,8 +33,9 @@
#include <pico/error.h> #include <pico/error.h>
#include <stdio.h> #include <stdio.h>
#define AVERAGES 4 #define AVERAGES 8
#define I2C1_SCL_PIN 19
#define I2C1_SDA_PIN 18
// MQTT and WiFi defines // MQTT and WiFi defines
#define ADAFRUIT_IO_HOST "io.adafruit.com" #define ADAFRUIT_IO_HOST "io.adafruit.com"
#define ADAFRUIT_IO_USERNAME "ADAFRUIT_IO_USERNAME" #define ADAFRUIT_IO_USERNAME "ADAFRUIT_IO_USERNAME"
@ -58,6 +60,14 @@ static void mqtt_connection_cb(mqtt_client_t *client, void *arg,
printf("MQTT status: %d\n", status); printf("MQTT status: %d\n", status);
} }
static float average_float(const float *samples, uint8_t count) {
float sum = 0.0;
for (uint8_t i = 0; i < count; i++) {
sum += samples[i];
}
return sum / count;
}
static double average_double(const double *samples, uint8_t count) { static double average_double(const double *samples, uint8_t count) {
double sum = 0.0; double sum = 0.0;
for (uint8_t i = 0; i < count; i++) { for (uint8_t i = 0; i < count; i++) {
@ -74,14 +84,17 @@ static uint16_t average_uint16(const uint16_t *samples, uint8_t count) {
return (uint16_t)((sum + (uint32_t)count / 2) / (uint32_t)count); return (uint16_t)((sum + (uint32_t)count / 2) / (uint32_t)count);
} }
int main() { int main() {
uint16_t tvoc, eco2, etoh, avg_tvoc, avg_eco2, avg_etoh; uint16_t tvoc, eco2, etoh, avg_tvoc, avg_eco2, avg_etoh;
uint8_t aqi, validData; uint8_t aqi, validData;
double t, h, avg_t, avg_h; double t, h, avg_t, avg_h, avg_bmp_t, avg_bmp_p;
uint16_t tvoc_avg[AVERAGES], eco2_avg[AVERAGES], etoh_avg[AVERAGES]; uint16_t tvoc_avg[AVERAGES], eco2_avg[AVERAGES], etoh_avg[AVERAGES];
double t_avg[AVERAGES], h_avg[AVERAGES]; double t_avg[AVERAGES], h_avg[AVERAGES];
float bmpt_avg[AVERAGES];
float bmpp_avg[AVERAGES];
char payload[32]; char payload[32];
float bmpT;
float bmpP;
// MQTT setup // MQTT setup
ensaht_mqtt_t mqtt; ensaht_mqtt_t mqtt;
struct mqtt_connect_client_info_t mqtt_client_info = { struct mqtt_connect_client_info_t mqtt_client_info = {
@ -134,6 +147,15 @@ int main() {
bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN,
GPIO_FUNC_I2C)); GPIO_FUNC_I2C));
i2c_init(i2c1,
100 * 1000); // start i2c1 at 100kHz -- this is the stemma chain
gpio_set_function(I2C1_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(I2C1_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C1_SDA_PIN);
gpio_pull_up(I2C1_SCL_PIN);
bi_decl(bi_2pins_with_func(I2C1_SDA_PIN, I2C1_SCL_PIN, GPIO_FUNC_I2C));
initBMP(i2c1);
printf("\x1b[2J"); // clear screen printf("\x1b[2J"); // clear screen
uint8_t i = 0; uint8_t i = 0;
uint8_t sample_count = 0; uint8_t sample_count = 0;
@ -141,15 +163,22 @@ int main() {
while (1) { while (1) {
// cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); // cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
getAHTData(&t, &h); getAHTData(&t, &h);
getBMPData(i2c1, &bmpT, &bmpP);
t_avg[i] = t; t_avg[i] = t;
h_avg[i] = h; h_avg[i] = h;
bmpt_avg[i] = bmpT;
bmpp_avg[i] = bmpP;
if (sample_count < AVERAGES) { if (sample_count < AVERAGES) {
sample_count++; sample_count++;
} }
avg_t = average_double(t_avg, sample_count); avg_t = average_double(t_avg, sample_count);
avg_h = average_double(h_avg, sample_count); avg_h = average_double(h_avg, sample_count);
printf("\x1b[H\x1b[JTemperature %0.2f Humidity %0.2f\n", avg_t, avg_h); avg_bmp_t = average_float(bmpt_avg, sample_count);
validData = getENSData(&avg_t, &avg_h, &aqi, &tvoc, &eco2, &etoh); avg_bmp_p = average_float(bmpp_avg, sample_count);
printf("\x1b[H\x1b[JTemperature %0.2f C Humidity %0.2f%%\n", avg_t, avg_h);
printf("BMP Temperature %0.2f C Pressure %0.2f kPa\n", avg_bmp_t,
avg_bmp_p / 100.0);
validData = getENSData(&avg_bmp_t, &avg_h, &aqi, &tvoc, &eco2, &etoh);
tvoc_avg[i] = tvoc; tvoc_avg[i] = tvoc;
eco2_avg[i] = eco2; eco2_avg[i] = eco2;
etoh_avg[i] = etoh; etoh_avg[i] = etoh;