diff --git a/.cproject b/.cproject
index 16623ca..1698297 100644
--- a/.cproject
+++ b/.cproject
@@ -24,13 +24,17 @@
+
-
-
+
+
@@ -51,6 +55,7 @@
+
@@ -59,7 +64,7 @@
-
+
@@ -102,27 +107,30 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
+
-
-
-
+
+
-
-
-
+
+
-
+
@@ -183,4 +192,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Core/Inc/main.h b/Core/Inc/main.h
index baf8ada..046e5ce 100644
--- a/Core/Inc/main.h
+++ b/Core/Inc/main.h
@@ -61,7 +61,7 @@ void Error_Handler(void);
#define SPI2_CS_GPIO_Port GPIOB
/* USER CODE BEGIN Private defines */
-
+#define APP_NAME "hothouse"
/* USER CODE END Private defines */
#ifdef __cplusplus
diff --git a/Core/Inc/terminal.h b/Core/Inc/terminal.h
new file mode 100644
index 0000000..f42172f
--- /dev/null
+++ b/Core/Inc/terminal.h
@@ -0,0 +1,24 @@
+/*
+ * terminal.h
+ *
+ * Created on: Feb 26, 2023
+ * Author: Daniel Peter Chokola
+ */
+
+#ifndef TERMINAL_H_
+#define TERMINAL_H_
+
+/* Includes */
+#include
+#include "usbd_cdc_if.h"
+
+/* Definitions */
+#define TERM_RXBUF_LEN (APP_RX_DATA_SIZE)
+
+/* Function Prototypes */
+void term_prompt();
+void term_poll();
+void term_rx(char *buf, uint32_t len);
+void term_printf(const char *fmt, ...);
+
+#endif /* TERMINAL_H_ */
diff --git a/Core/Inc/util.h b/Core/Inc/util.h
new file mode 100755
index 0000000..2b0b6be
--- /dev/null
+++ b/Core/Inc/util.h
@@ -0,0 +1,46 @@
+/*
+ * util.h
+ *
+ * Created on: Feb 26, 2023
+ * Author: Daniel Peter Chokola
+ */
+
+#ifndef UTIL_H_
+#define UTIL_H_
+
+/* Definitions */
+#define return_if_fail(cond) \
+ if(!(cond)) { return; }
+#define return_val_if_fail(cond, val) \
+ if(!(cond)) { return (val); }
+#define return_null_if_fail(cond) return_val_if_fail((cond), NULL)
+/* MIN() and MAX() */
+#ifdef __GNUC__
+# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9))
+/* prevent double evaluation */
+# define MIN(a,b) \
+ __extension__({ __auto_type _a = (a); \
+ __auto_type _b = (b); \
+ _a < _b ? _a : _b; })
+# define MAX(a,b) \
+ __extension__({ __auto_type _a = (a); \
+ __auto_type _b = (b); \
+ _a > _b ? _a : _b; })
+# else
+/* typesafety but double evaluation is possible */
+# define MIN(a,b) \
+ __extension__({ __typeof__ (a) _a = (a); \
+ __typeof__ (b) _b = (b); \
+ _a < _b ? _a : _b; })
+# define MAX(a,b) \
+ __extension__({ __typeof__ (a) _a = (a); \
+ __typeof__ (b) _b = (b); \
+ _a > _b ? _a : _b; })
+# endif
+#else
+/* no typesafety and double evaluation is possible */
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+# define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#endif /* UTIL_H_ */
diff --git a/Core/Src/gpio.c b/Core/Src/gpio.c
index dfda8c3..8527541 100644
--- a/Core/Src/gpio.c
+++ b/Core/Src/gpio.c
@@ -51,7 +51,7 @@ void MX_GPIO_Init(void)
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
- HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, GPIO_PIN_RESET);
+ HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, GPIO_PIN_SET);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = SPI2_CS_Pin;
diff --git a/Core/Src/main.c b/Core/Src/main.c
index 56a1310..49f388c 100644
--- a/Core/Src/main.c
+++ b/Core/Src/main.c
@@ -24,7 +24,9 @@
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
-
+#include "usbd_cdc_if.h"
+#include "bme280.h"
+#include "terminal.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
@@ -65,7 +67,7 @@ void SystemClock_Config(void);
int main(void)
{
/* USER CODE BEGIN 1 */
-
+ bme280_t bme280;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
@@ -89,17 +91,22 @@ int main(void)
MX_SPI2_Init();
MX_USB_DEVICE_Init();
/* USER CODE BEGIN 2 */
-
+ bme280_init(&bme280, &hspi2, SPI2_CS_GPIO_Port, SPI2_CS_Pin);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
- while (1)
- {
+ while(1)
+ {
+ term_printf("T: %f DegC, P: %f hPa, H: %f %%RH\r\n",
+ (double) bme280_temp_get(&bme280) / 100.0,
+ (double) bme280_press_get(&bme280) / 25600.0,
+ (double) bme280_hum_get(&bme280) / 1024.0);
+ HAL_Delay(1000);
+ }
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
- }
/* USER CODE END 3 */
}
diff --git a/Core/Src/spi.c b/Core/Src/spi.c
index 47a370a..23a72eb 100644
--- a/Core/Src/spi.c
+++ b/Core/Src/spi.c
@@ -44,7 +44,7 @@ void MX_SPI2_Init(void)
hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT;
- hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
+ hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
diff --git a/Core/Src/terminal.c b/Core/Src/terminal.c
new file mode 100644
index 0000000..add8d85
--- /dev/null
+++ b/Core/Src/terminal.c
@@ -0,0 +1,136 @@
+/*
+ * terminal.c
+ *
+ * Created on: Aug 2, 2022
+ * Author: daniel.chokola
+ */
+
+/* Includes */
+#include
+#include
+/*
+#include "config.h"
+*/
+#include "terminal.h"
+
+/* Private Variables */
+static char wbuf[TERM_RXBUF_LEN]; /* working buffer */
+static char rxbuf[TERM_RXBUF_LEN]; /* receive buffer */
+static uint32_t rxlen;
+static int8_t rxline;
+
+/* Function Prototypes */
+__attribute__((always_inline)) inline int8_t term_is_whitespace(char c)
+{
+ if(c == '\n' ||
+ c == '\r' ||
+ c == '\t' ||
+ c == '\v' ||
+ c == '\f' ||
+ c == ' ')
+ {
+ return !0;
+ }
+
+ return 0;
+}
+static int32_t term_stripn(char *buf, int32_t n);
+
+/* Function Definitions */
+void term_prompt()
+{
+ /*
+ term_printf("\r%s-g%s $ ", APP_NAME, GIT_REV);
+ */
+ term_printf("\r%s $ ", APP_NAME);
+}
+
+void term_rx(char *buf, uint32_t len)
+{
+ uint32_t i;
+ char c;
+
+ for(i = 0; i < len; i++)
+ {
+ c = buf[i];
+
+ /* ignore null bytes */
+ if(c == '\0')
+ {
+ continue;
+ }
+ /* handle backspace */
+ if(c == '\b')
+ {
+ if(rxlen)
+ {
+ rxlen--;
+ }
+ rxbuf[rxlen] = '\0';
+ if(rxlen)
+ {
+ /* term_printf("\b\x1b[0K"); */
+ }
+
+ continue;
+ }
+ /* handle escape sequences */
+ if(c == '\x1b')
+ {
+ c = '^';
+ }
+
+ rxbuf[rxlen++] = (char) c;
+ /* local echo */
+ /* term_printf("%c", c); */
+ if(c == '\n' || c == '\r')
+ {
+ /* EOL */
+ rxbuf[rxlen - 1] = '\0';
+ rxline = 1;
+ }
+ }
+}
+
+void term_printf(const char *fmt, ...)
+{
+ va_list args;
+ int32_t len;
+ uint8_t res;
+
+ va_start(args, fmt);
+ len = vsprintf(wbuf, fmt, args);
+ va_end(args);
+
+ /* FIXME: this will lock up if there's too much Tx/Rx traffic */
+ do
+ {
+ res = CDC_Transmit_FS((uint8_t *) wbuf, (uint16_t) len);
+ }
+ while(res == USBD_BUSY);
+}
+
+/* strip trailing whitespace from a string of length n */
+static int32_t term_stripn(char *buf, int32_t n)
+{
+ int32_t i = n - 1;
+
+ while(i >= 0)
+ {
+ if(term_is_whitespace(buf[i]))
+ {
+ buf[i--] = '\0';
+ }
+ else if(buf[i] == '\0')
+ {
+ i--;
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return i + 1;
+}
diff --git a/Drivers/BME280/Inc/bme280.h b/Drivers/BME280/Inc/bme280.h
new file mode 100755
index 0000000..b8797cd
--- /dev/null
+++ b/Drivers/BME280/Inc/bme280.h
@@ -0,0 +1,57 @@
+/*
+ * bme280.h
+ *
+ * Created on: Feb 26, 2023
+ * Author: Daniel Peter Chokola
+ * License: Beerware - Use this code however you'd like. If you find it
+ * useful you can buy me a beer some time.
+ */
+
+#ifndef BME280_H_
+#define BME280_H_
+
+/* Includes */
+#include
+#include "spi.h"
+
+/* Definitions */
+#define BME280_CAL_LEN (32u)
+
+/* Data Structures */
+typedef struct bme280_s {
+ SPI_HandleTypeDef *hspi;
+ GPIO_TypeDef *port;
+ uint16_t pin;
+ union __attribute__((packed, aligned(1))) bme280_calib_u {
+ uint8_t byte[BME280_CAL_LEN];
+ struct __attribute__((packed, aligned(1))) bme280_calib_s {
+ uint16_t cal_t1;
+ int16_t cal_t2;
+ int16_t cal_t3;
+ uint16_t cal_p1;
+ int16_t cal_p2;
+ int16_t cal_p3;
+ int16_t cal_p4;
+ int16_t cal_p5;
+ int16_t cal_p6;
+ int16_t cal_p7;
+ int16_t cal_p8;
+ int16_t cal_p9;
+ uint8_t cal_h1;
+ int16_t cal_h2;
+ uint8_t cal_h3;
+ int16_t cal_h4;
+ int16_t cal_h5;
+ int8_t cal_h6;
+ } field;
+ } cal;
+ int32_t t_fine;
+} bme280_t;
+
+/* Function Prototypes */
+int32_t bme280_init(bme280_t *bme280, SPI_HandleTypeDef *hspi, GPIO_TypeDef *port, uint16_t pin);
+int32_t bme280_temp_get(bme280_t *bme280);
+int32_t bme280_press_get(bme280_t *bme280);
+int32_t bme280_hum_get(bme280_t *bme280);
+
+#endif /* BME280_H_ */
diff --git a/Drivers/BME280/Src/bme280.c b/Drivers/BME280/Src/bme280.c
new file mode 100755
index 0000000..f8e6366
--- /dev/null
+++ b/Drivers/BME280/Src/bme280.c
@@ -0,0 +1,477 @@
+/*
+ * bme280.c
+ *
+ * Created on: Feb 26, 2023
+ * Author: Daniel Peter Chokola
+ * License: Beerware - Use this code however you'd like. If you find it
+ * useful you can buy me a beer some time.
+ */
+
+/* Includes */
+#include "bme280.h"
+#include "spi.h"
+#include "util.h"
+
+/* Definitions */
+/* BME280 registers */
+#define BME280_REG_HUM_LSB (0xfe)
+#define BME280_REG_HUM_MSB (0xfd)
+#define BME280_REG_TEMP_XLSB (0xfc)
+#define BME280_REG_TEMP_LSB (0xfb)
+#define BME280_REG_TEMP_MSB (0xfa)
+#define BME280_REG_PRESS_XLSB (0xf9)
+#define BME280_REG_PRESS_LSB (0xf8)
+#define BME280_REG_PRESS_MSB (0xf7)
+#define BME280_REG_CONFIG (0xf5)
+#define BME280_REG_CTRL_MEAS (0xf4)
+#define BME280_REG_STATUS (0xf3)
+#define BME280_REG_CTRL_HUM (0xf2)
+#define BME280_REG_RESET (0xe0)
+#define BME280_REG_ID (0xd0) /* should return 0x60 when read (BME280) */
+#define BME280_REG_CALIB26 (0xe1)
+#define BME280_REG_CALIB25 (0xa1)
+#define BME280_REG_CALIB00 (0x88)
+/* BME280 SPI Register Masks */
+#define BME280_READ_MASK (0x80)
+#define BME280_WRITE_MASK (0x7f)
+/* BME280 Magic Register Values */
+#define BME280_RESET (0xb6)
+#define BME280_ID (0x60)
+#define BMP280_ID_PROD (0x58)
+#define BMP280_ID_SAMP1 (0x56)
+#define BMP280_ID_SAMP2 (0x57)
+
+enum H_OSR {
+ H_OSR_00 = 0, /* skip */
+ H_OSR_01,
+ H_OSR_02,
+ H_OSR_04,
+ H_OSR_08,
+ H_OSR_16
+};
+enum P_OSR {
+ P_OSR_00 = 0, /* skip */
+ P_OSR_01,
+ P_OSR_02,
+ P_OSR_04,
+ P_OSR_08,
+ P_OSR_16
+};
+enum T_OSR {
+ T_OSR_00 = 0, /* skip */
+ T_OSR_01,
+ T_OSR_02,
+ T_OSR_04,
+ T_OSR_08,
+ T_OSR_16
+};
+enum IIR_COEFF {
+ IIR_OFF = 0, /* no filtering */
+ IIR_02,
+ IIR_04,
+ IIR_08,
+ IIR_16,
+};
+enum MODE {
+ MODE_SLEEP = 0,
+ MODE_FORCED1,
+ MODE_FORCED2,
+ MODE_NORMAL
+};
+enum STBY_TIME {
+ STBY_00_5ms = 0,
+ STBY_62_5ms,
+ STBY_125ms,
+ STBY_250ms,
+ STBY_500ms,
+ STBY_1000ms,
+ STBY_10ms, /* 2000 ms on BMP280 */
+ STBY_20ms, /* 4000 ms on BMP280 */
+};
+
+/* Data Structures */
+
+/* Function Prototypes */
+static int32_t bme280_cal_get(bme280_t *bme280);
+static int32_t bme280_writebyte(bme280_t *bme280, uint8_t reg, uint8_t byte);
+static int32_t bme280_readbyte(bme280_t *bme280, uint8_t reg, uint8_t *byte);
+static int32_t bme280_compensate_t(bme280_t *bme280, int32_t t);
+static uint32_t bme280_compensate_p(bme280_t *bme280, int32_t p_comp);
+static uint32_t bme280_compensate_h(bme280_t *bme280, int32_t h);
+
+/* Function Definitions */
+int32_t bme280_init(bme280_t *bme280, SPI_HandleTypeDef *hspi, GPIO_TypeDef *port, uint16_t pin)
+{
+ uint8_t id = 0;
+
+ return_val_if_fail(bme280, -1);
+ return_val_if_fail(hspi, -1);
+
+ bme280->hspi = hspi;
+ bme280->port = port;
+ bme280->pin = pin;
+
+ /*
+ * toggle CS line and send a dummy byte to to put the chip in SPI mode and
+ * let the SPI lines settle */
+ return_val_if_fail(bme280_writebyte(bme280, BME280_REG_RESET, 0x00) == 0, -1);
+
+ /* reset */
+ return_val_if_fail(bme280_writebyte(bme280, BME280_REG_RESET, BME280_RESET) == 0, -1);
+ HAL_Delay(1); /* give the chip a millisecond to come out of reset */
+
+ /* query chip ID */
+ return_val_if_fail(bme280_readbyte(bme280, BME280_REG_ID, &id) == 0, -1);
+ switch(id)
+ {
+ case BME280_ID:
+ /* BME280 */
+ break;
+ case BMP280_ID_PROD:
+ case BMP280_ID_SAMP1:
+ case BMP280_ID_SAMP2:
+ /* maybe support these one day */
+ default:
+ return -1; /* not a BME280 */
+ }
+
+ /* fetch calibration constants */
+ return_val_if_fail(bme280_cal_get(bme280) == 0, -1);
+
+ /*
+ * FIXME: don't hardcode these
+ * Maybe leave the chip in sleep mode here and create new function(s) to
+ * adjust config?
+ */
+ /* configure humidity acquisition options */
+ return_val_if_fail(bme280_writebyte(bme280, BME280_REG_CTRL_HUM, H_OSR_01) == 0, -1);
+ /* configure temperature and pressure acquisition options and acquisition mode */
+ return_val_if_fail(bme280_writebyte(bme280,
+ BME280_REG_CTRL_MEAS,
+ (T_OSR_01 << 5) | (P_OSR_16 << 2) | MODE_NORMAL) == 0,
+ -1);
+ /* configure acquisition rate, IIR filtering, and SPI options */
+ return_val_if_fail(bme280_writebyte(bme280,
+ BME280_REG_CONFIG,
+ (STBY_00_5ms << 5) | (IIR_16 << 2) | 0) == 0,
+ -1);
+
+ return 0;
+}
+
+int32_t bme280_temp_get(bme280_t *bme280)
+{
+ uint8_t reg;
+ uint8_t rxdata[3];
+ int32_t t;
+ HAL_StatusTypeDef res;
+
+ /* read temperature; data must be read in one transaction to guarantee consistency */
+ reg = BME280_REG_TEMP_MSB;
+ bme280->port->BSRR = bme280->pin << 16;
+ res = HAL_SPI_Transmit(bme280->hspi, ®, 1, 10);
+ res |= HAL_SPI_Receive(bme280->hspi, &(rxdata[0]), 3, 10);
+ bme280->port->BSRR = bme280->pin;
+ return_val_if_fail(res == HAL_OK, -1);
+
+ /* assemble raw temperature */
+ t = ((int32_t) rxdata[0] << 12) | ((uint32_t) rxdata[1] << 4) | ((uint32_t) rxdata[2] >> 4);
+
+ return bme280_compensate_t(bme280, t);
+}
+
+int32_t bme280_press_get(bme280_t *bme280)
+{
+ uint8_t reg;
+ uint8_t rxdata[3];
+ int32_t p;
+ HAL_StatusTypeDef res;
+
+ /* read pressure; data must be read in one transaction to guarantee consistency */
+ reg = BME280_REG_PRESS_MSB;
+ bme280->port->BSRR = bme280->pin << 16;
+ res = HAL_SPI_Transmit(bme280->hspi, ®, 1, 10);
+ res |= HAL_SPI_Receive(bme280->hspi, &(rxdata[0]), 3, 10);
+ bme280->port->BSRR = bme280->pin;
+ return_val_if_fail(res == HAL_OK, -1);
+
+ /* assemble raw temperature */
+ p = ((int32_t) rxdata[0] << 12) | ((uint32_t) rxdata[1] << 4) | ((uint32_t) rxdata[2] >> 4);
+
+ return bme280_compensate_p(bme280, p);
+}
+
+int32_t bme280_hum_get(bme280_t *bme280)
+{
+ uint8_t reg;
+ uint8_t rxdata[2];
+ int32_t h;
+ HAL_StatusTypeDef res;
+
+ /* read pressure; data must be read in one transaction to guarantee consistency */
+ reg = BME280_REG_HUM_MSB;
+ bme280->port->BSRR = bme280->pin << 16;
+ res = HAL_SPI_Transmit(bme280->hspi, ®, 1, 10);
+ res |= HAL_SPI_Receive(bme280->hspi, &(rxdata[0]), 2, 10);
+ bme280->port->BSRR = bme280->pin;
+ return_val_if_fail(res == HAL_OK, -1);
+
+ /* assemble raw temperature */
+ h = ((int32_t) rxdata[0] << 8) | rxdata[1];
+
+ return bme280_compensate_h(bme280, h);
+}
+
+static int32_t bme280_cal_get(bme280_t *bme280)
+{
+ uint8_t reg;
+ uint8_t calh[7]; /* humidity cal which needs some massaging */
+ int32_t i;
+ HAL_StatusTypeDef res;
+
+ for(i = 0; i < BME280_CAL_LEN; i++)
+ {
+ bme280->cal.byte[i] = 0;
+ }
+ reg = BME280_REG_CALIB00;
+ bme280->port->BSRR = bme280->pin << 16;
+ res = HAL_SPI_Transmit(bme280->hspi, ®, 1, 10);
+ res |= HAL_SPI_Receive(bme280->hspi, &(bme280->cal.byte[ 0]), 24, 10);
+ bme280->port->BSRR = bme280->pin;
+ return_val_if_fail(res == HAL_OK, -1);
+ reg = BME280_REG_CALIB25;
+ bme280->port->BSRR = bme280->pin << 16;
+ res = HAL_SPI_Transmit(bme280->hspi, ®, 1, 10);
+ res |= HAL_SPI_Receive(bme280->hspi, &(bme280->cal.byte[24]), 1, 10);
+ bme280->port->BSRR = bme280->pin;
+ return_val_if_fail(res == HAL_OK, -1);
+ reg = BME280_REG_CALIB26;
+ bme280->port->BSRR = bme280->pin << 16;
+ res = HAL_SPI_Transmit(bme280->hspi, ®, 1, 10);
+ res |= HAL_SPI_Receive(bme280->hspi, &(calh[0]), 7, 10);
+ bme280->port->BSRR = bme280->pin;
+ return_val_if_fail(res == HAL_OK, -1);
+ /* massage the humidity cal into its struct */
+ bme280->cal.field.cal_h2 = ((int16_t) calh[1] << 8) | calh[0];
+ bme280->cal.field.cal_h3 = calh[2];
+ bme280->cal.field.cal_h4 = ((int16_t) calh[3] << 4) | (calh[4] & 0x0f);
+ bme280->cal.field.cal_h5 = ((int16_t) calh[5] << 4) | ((calh[4] >> 4) & 0x0f);
+ bme280->cal.field.cal_h6 = (int8_t) calh[6];
+
+ return 0;
+}
+
+static int32_t bme280_writebyte(bme280_t *bme280, uint8_t reg, uint8_t byte)
+{
+ uint8_t txdata[2] = { reg & BME280_WRITE_MASK, byte };
+ HAL_StatusTypeDef res;
+
+ /* assert chip select */
+ bme280->port->BSRR = bme280->pin << 16;
+ res = HAL_SPI_Transmit(bme280->hspi, &(txdata[0]), 2, 10);
+ /* deassert chip select */
+ bme280->port->BSRR = bme280->pin;
+ return_val_if_fail(res == HAL_OK, -1);
+
+ return 0;
+}
+
+static int32_t bme280_readbyte(bme280_t *bme280, uint8_t reg, uint8_t *byte)
+{
+ uint8_t txdata[2] = { reg | BME280_READ_MASK, 0x00 };
+ uint8_t rxdata[2];
+ HAL_StatusTypeDef res;
+
+ /* assert chip select */
+ bme280->port->BSRR = bme280->pin << 16;
+ res = HAL_SPI_TransmitReceive(bme280->hspi, &(txdata[0]), &(rxdata[0]), 2, 10);
+ /* deassert chip select */
+ bme280->port->BSRR = bme280->pin;
+ return_val_if_fail(res == HAL_OK, -1);
+ *byte = rxdata[1];
+
+ return 0;
+}
+
+/*
+ * Returns temperature in DegC, resolution is 0.01 DegC. Output value of
+ * "5123" equals 51.23 DegC.
+ */
+static int32_t bme280_compensate_t(bme280_t *bme280, int32_t t)
+{
+ int32_t var1;
+ int32_t var2;
+ int32_t t_comp;
+
+ var1 = ((((t >> 3) - ((int32_t) bme280->cal.field.cal_t1 << 1))) * ((int32_t) bme280->cal.field.cal_t2)) >> 11;
+ var2 = (((((t >> 4) - ((int32_t) bme280->cal.field.cal_t1)) * ((t >> 4) - ((int32_t) bme280->cal.field.cal_t1))) >> 12) * ((int32_t) bme280->cal.field.cal_t3)) >> 14;
+ bme280->t_fine = var1 + var2;
+ t_comp = (bme280->t_fine * 5 + 128) >> 8;
+
+ return t_comp;
+}
+
+/*
+ * Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24
+ * integer bits and 8 fractional bits).
+ * Output value of "24674867" represents 24674867/256 = 96386.2 Pa =
+ * 963.862 hPa
+ */
+static uint32_t bme280_compensate_p(bme280_t *bme280, int32_t p)
+{
+ int64_t var1;
+ int64_t var2;
+ int64_t p_comp;
+
+ var1 = ((int64_t) bme280->t_fine) - 128000;
+ var2 = var1 * var1 * (int64_t) bme280->cal.field.cal_p6;
+ var2 = var2 + ((var1 * (int64_t) bme280->cal.field.cal_p5) << 17);
+ var2 = var2 + (((int64_t) bme280->cal.field.cal_p4) << 35);
+ var1 = ((var1 * var1 * (int64_t) bme280->cal.field.cal_p3) >> 8) + ((var1 * (int64_t) bme280->cal.field.cal_p2) << 12);
+ var1 = (((((int64_t) 1) << 47) + var1)) * ((int64_t) bme280->cal.field.cal_p1) >> 33;
+ if(var1 == 0)
+ {
+ /* division by zero */
+ return 0;
+ }
+ p_comp = 1048576 - p;
+ p_comp = (((p_comp << 31) - var2) * 3125) / var1;
+ var1 = (((int64_t) bme280->cal.field.cal_p9) * (p_comp >> 13) * (p_comp >> 13)) >> 25;
+ var2 = (((int64_t) bme280->cal.field.cal_p8) * p_comp) >> 19;
+ p_comp = ((p_comp + var1 + var2) >> 8) + (((int64_t) bme280->cal.field.cal_p7) << 4);
+
+ return (uint32_t) p_comp;
+}
+
+/*
+ * Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22
+ * integer and 10 fractional bits).
+ * Output value of “47445” represents 47445/1024 = 46.333 %RH
+ */
+static uint32_t bme280_compensate_h(bme280_t *bme280, int32_t h)
+{
+ int32_t h_comp;
+
+ h_comp = (bme280->t_fine - ((int32_t) 76800));
+ h_comp = (((((h << 14) - (((int32_t) bme280->cal.field.cal_h4) << 20) - (((int32_t) bme280->cal.field.cal_h5) *
+ h_comp)) + ((int32_t) 16384)) >> 15) * (((((((h_comp *
+ ((int32_t) bme280->cal.field.cal_h6)) >> 10) * (((h_comp * ((int32_t) bme280->cal.field.cal_h3)) >> 11) +
+ ((int32_t) 32768))) >> 10) + ((int32_t) 2097152)) * ((int32_t) bme280->cal.field.cal_h2) +
+ 8192) >> 14));
+ h_comp = (h_comp - (((((h_comp >> 15) * (h_comp >> 15)) >> 7) *
+ ((int32_t) bme280->cal.field.cal_h1)) >> 4));
+ h_comp = (h_comp < 0 ? 0 : h_comp);
+ h_comp = (h_comp > 419430400 ? 419430400 : h_comp);
+
+ return (uint32_t) (h_comp >> 12);
+}
+
+
+#if (0) /* crusty old code */
+/* Global Variables */
+/* Specify BME280 configuration */
+uint8_t Posr = P_OSR_16;
+uint8_t Tosr = T_OSR_02;
+uint8_t Mode = normal;
+uint8_t IIRFilter = BW0_042ODR;
+uint8_t SBy = t_62_5ms; // set pressure and temperature output data rate
+/* t_fine carries fine temperature as global value for BME280 */
+int32_t t_fine;
+/* BME280 compensation parameters */
+uint16_t dig_T1, dig_P1;
+int16_t dig_T2, dig_T3, dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9;
+double Temperature, Pressure; // stores BME280 pressures sensor pressure and temperature
+int32_t rawPress, rawTemp; // pressure and temperature raw count output for BME280
+
+/* Function Definitions */
+int32_t readBME280Temperature()
+{
+ uint8_t rawData[3]; // 20-bit pressure register data stored here
+
+ HAL_I2C_Mem_Read(&hi2c1, BME280_ADDRESS, BME280_TEMP_MSB, 3, &rawData[0], 3, 10);
+
+ return (int32_t) (((int32_t) rawData[0] << 16 | (int32_t) rawData[1] << 8 | rawData[2]) >> 4);
+}
+
+int32_t readBME280Pressure()
+{
+ uint8_t rawData[3]; // 20-bit pressure register data stored here
+
+ HAL_I2C_Mem_Read(&hi2c1, BME280_ADDRESS, BME280_PRESS_MSB, 3, &rawData[0], 3, 10);
+
+ return (int32_t) (((int32_t) rawData[0] << 16 | (int32_t) rawData[1] << 8 | rawData[2]) >> 4);
+}
+
+void BME280Init()
+{
+ uint8_t TPosrMode = Tosr << 5 | Posr << 2 | Mode;
+ uint8_t SByTimeIntervalModeAndBandwidth = SBy << 5 | IIRFilter << 2;
+ uint8_t calib[24];
+
+ /* Configure the BME280 */
+ /* Set T and P oversampling rates and sensor mode */
+ HAL_I2C_Mem_Write(&hi2c1, BME280_ADDRESS, BME280_CTRL_MEAS, 1, &TPosrMode, 1, 10);
+ /* Set standby time interval in normal mode and bandwidth */
+ HAL_I2C_Mem_Write(&hi2c1, BME280_ADDRESS, BME280_CONFIG, 1, &SByTimeIntervalModeAndBandwidth, 1, 10);
+ /* Read and store calibration data */
+ HAL_I2C_Mem_Read(&hi2c1, BME280_ADDRESS, BME280_CALIB00, 24, &calib[0], 24, 10);
+ dig_T1 = (uint16_t) (((uint16_t) calib[ 1] << 8) | calib[ 0]);
+ dig_T2 = ( int16_t) ((( int16_t) calib[ 3] << 8) | calib[ 2]);
+ dig_T3 = ( int16_t) ((( int16_t) calib[ 5] << 8) | calib[ 4]);
+ dig_P1 = (uint16_t) (((uint16_t) calib[ 7] << 8) | calib[ 6]);
+ dig_P2 = ( int16_t) ((( int16_t) calib[ 9] << 8) | calib[ 8]);
+ dig_P3 = ( int16_t) ((( int16_t) calib[11] << 8) | calib[10]);
+ dig_P4 = ( int16_t) ((( int16_t) calib[13] << 8) | calib[12]);
+ dig_P5 = ( int16_t) ((( int16_t) calib[15] << 8) | calib[14]);
+ dig_P6 = ( int16_t) ((( int16_t) calib[17] << 8) | calib[16]);
+ dig_P7 = ( int16_t) ((( int16_t) calib[19] << 8) | calib[18]);
+ dig_P8 = ( int16_t) ((( int16_t) calib[21] << 8) | calib[20]);
+ dig_P9 = ( int16_t) ((( int16_t) calib[23] << 8) | calib[22]);
+}
+
+/*
+ * Returns temperature in DegC, resolution is 0.01 DegC. Output value of
+ * "5123" equals 51.23 DegC.
+ */
+int32_t bmp280_compensate_T(int32_t adc_T)
+{
+ int32_t var1, var2, T;
+
+ var1 = ((((adc_T >> 3) - ((int32_t) dig_T1 << 1))) * ((int32_t) dig_T2)) >> 11;
+ var2 = (((((adc_T >> 4) - ((int32_t) dig_T1)) * ((adc_T >> 4) - ((int32_t) dig_T1))) >> 12) * ((int32_t) dig_T3)) >> 14;
+ t_fine = var1 + var2;
+ T = (t_fine * 5 + 128) >> 8;
+
+ return T;
+}
+
+/*
+ * Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24
+ * integer bits and 8 fractional bits).
+ * Output value of "24674867" represents 24674867/256 = 96386.2 Pa =
+ * 963.862 hPa
+ */
+uint32_t bmp280_compensate_P(int32_t adc_P)
+{
+ uint64_t var1, var2, p;
+
+ var1 = ((uint64_t) t_fine) - 128000;
+ var2 = var1 * var1 * (uint64_t) dig_P6;
+ var2 = var2 + ((var1 * (uint64_t) dig_P5) << 17);
+ var2 = var2 + (((uint64_t) dig_P4) << 35);
+ var1 = ((var1 * var1 * (uint64_t) dig_P3) >> 8) + ((var1 * (uint64_t) dig_P2) << 12);
+ var1 = (((((uint64_t) 1) << 47) + var1)) * ((uint64_t) dig_P1) >> 33;
+ if(var1 == 0)
+ {
+ /* division by zero */
+ return 0;
+ }
+ p = 1048576 - adc_P;
+ p = (((p << 31) - var2) * 3125) / var1;
+ var1 = (((uint64_t) dig_P9) * (p >> 13) * (p >> 13)) >> 25;
+ var2 = (((uint64_t) dig_P8) * p)>> 19;
+ p = ((p + var1 + var2) >> 8) + (((uint64_t) dig_P7) << 4);
+
+ return (uint32_t) p;
+}
+#endif /* crusty old code */
diff --git a/hothouse-mon.ioc b/hothouse-mon.ioc
index 23661df..4a4d5ae 100644
--- a/hothouse-mon.ioc
+++ b/hothouse-mon.ioc
@@ -55,9 +55,11 @@ PA13.Mode=Serial_Wire
PA13.Signal=SYS_JTMS-SWDIO
PA14.Mode=Serial_Wire
PA14.Signal=SYS_JTCK-SWCLK
-PB12.GPIOParameters=GPIO_Label
+PB12.GPIOParameters=GPIO_Speed,PinState,GPIO_Label
PB12.GPIO_Label=SPI2_CS
+PB12.GPIO_Speed=GPIO_SPEED_FREQ_LOW
PB12.Locked=true
+PB12.PinState=GPIO_PIN_SET
PB12.Signal=GPIO_Output
PB13.Locked=true
PB13.Mode=Full_Duplex_Master
@@ -102,7 +104,7 @@ ProjectManager.StackSize=0x400
ProjectManager.TargetToolchain=STM32CubeIDE
ProjectManager.ToolChainLocation=
ProjectManager.UnderRoot=true
-ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false
+ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_SPI2_Init-SPI2-false-HAL-true,4-MX_USB_DEVICE_Init-USB_DEVICE-false-HAL-false
RCC.48MHZClocksFreq_Value=48000000
RCC.AHBFreq_Value=72000000
RCC.APB1CLKDivider=RCC_HCLK_DIV2
@@ -131,9 +133,10 @@ RCC.VCOI2SOutputFreq_Value=192000000
RCC.VCOInputFreq_Value=1000000
RCC.VCOOutputFreq_Value=144000000
RCC.VcooutputI2S=96000000
-SPI2.CalculateBaudRate=18.0 MBits/s
+SPI2.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_4
+SPI2.CalculateBaudRate=9.0 MBits/s
SPI2.Direction=SPI_DIRECTION_2LINES
-SPI2.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate
+SPI2.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,BaudRatePrescaler
SPI2.Mode=SPI_MODE_MASTER
SPI2.VirtualType=VM_MASTER
USB_DEVICE.CLASS_NAME_FS=CDC