sl-firmware e21526327b feat: Auto-detect magnetometer + barometer (#24)
Shared I2C1 bus (i2c1.c/h, PB8=SCL PB9=SDA 100kHz):
- i2c1_init() called once in main() before sensor probes.
- hi2c1 exported globally; baro and mag drivers use it directly.

Barometer (bmp280.c):
- Probes I2C1 at 0x76 then 0x77 (covers both SDO options).
- bmp280_init() returns chip_id (0x58/0x60) on success, neg if absent.
- Added bmp280_pressure_to_alt_cm() — ISA barometric formula.
- Added bmp280.h (was missing).

Magnetometer (mag.c / mag.h):
- Auto-detects QMC5883L (0x0D, id=0xFF), HMC5883L (0x1E, id='H43'),
  IST8310 (0x0E, id=0x10) in that order.
- mag_read_heading() returns degrees×10 (0–3599) or -1 if not ready.
- HMC5883L: correct XZY byte order applied.
- IST8310: single-measurement trigger mode.

main.c:
- i2c1_init() + bmp280_init() + mag_init() after all other inits.
- Both skip gracefully (baro_ok=0, mag_type=MAG_NONE) if not present.
- Telemetry JSON: incremental builder appends ",\"hd\":<n>" when mag
  found and ",\"alt\":<n>" when baro found. No extra bytes when absent.

UI (index.html):
- HEADING and ALT rows hidden until first packet with that field.
- Heading shown in degrees, alt in metres (firmware sends cm).

Closes #24.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 17:48:53 -05:00

104 lines
3.6 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* bmp280.c — BMP280/BME280 barometer driver (I2C1, shared bus)
*
* Probes 0x76 first, then 0x77. Requires i2c1_init() before bmp280_init().
* Returns chip_id on success (0x58=BMP280, 0x60=BME280), negative if absent.
*/
#include "bmp280.h"
#include "i2c1.h"
#include <math.h>
/* Shift I2C address for HAL (7-bit left-shifted) */
static uint16_t s_addr;
/* Calibration data */
static uint16_t dig_T1;
static int16_t dig_T2, dig_T3;
static uint16_t dig_P1;
static int16_t dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9;
static int32_t t_fine;
static uint8_t i2c_read(uint8_t reg) {
uint8_t val = 0;
HAL_I2C_Mem_Read(&hi2c1, s_addr, reg, 1, &val, 1, 100);
return val;
}
static void i2c_read_burst(uint8_t reg, uint8_t *buf, uint8_t len) {
HAL_I2C_Mem_Read(&hi2c1, s_addr, reg, 1, buf, len, 100);
}
static void i2c_write(uint8_t reg, uint8_t val) {
HAL_I2C_Mem_Write(&hi2c1, s_addr, reg, 1, &val, 1, 100);
}
static int try_init(uint16_t addr) {
s_addr = addr;
uint8_t id = i2c_read(0xD0);
if (id != 0x58 && id != 0x60) return -(int)id;
/* Read calibration */
uint8_t cal[26];
i2c_read_burst(0x88, cal, 26);
dig_T1 = (uint16_t)(cal[1] << 8 | cal[0]);
dig_T2 = (int16_t) (cal[3] << 8 | cal[2]);
dig_T3 = (int16_t) (cal[5] << 8 | cal[4]);
dig_P1 = (uint16_t)(cal[7] << 8 | cal[6]);
dig_P2 = (int16_t) (cal[9] << 8 | cal[8]);
dig_P3 = (int16_t) (cal[11] << 8 | cal[10]);
dig_P4 = (int16_t) (cal[13] << 8 | cal[12]);
dig_P5 = (int16_t) (cal[15] << 8 | cal[14]);
dig_P6 = (int16_t) (cal[17] << 8 | cal[16]);
dig_P7 = (int16_t) (cal[19] << 8 | cal[18]);
dig_P8 = (int16_t) (cal[21] << 8 | cal[20]);
dig_P9 = (int16_t) (cal[23] << 8 | cal[22]);
/* Normal mode, ×16 oversampling temp+press, 0.5 ms standby */
i2c_write(0xF5, 0x00); /* config: standby=0.5ms, filter=off */
i2c_write(0xF4, 0xB7); /* ctrl_meas: osrs_t=×16, osrs_p=×16, normal mode */
return (int)id;
}
int bmp280_init(void) {
int ret = try_init(0x76 << 1);
if (ret > 0) return ret;
return try_init(0x77 << 1);
}
void bmp280_read(int32_t *pressure_pa, int16_t *temp_x10) {
uint8_t buf[6];
i2c_read_burst(0xF7, buf, 6);
int32_t adc_P = (int32_t)((buf[0] << 12) | (buf[1] << 4) | (buf[2] >> 4));
int32_t adc_T = (int32_t)((buf[3] << 12) | (buf[4] << 4) | (buf[5] >> 4));
/* Temperature compensation (BMP280 datasheet Section 4.2.3) */
int32_t v1 = ((((adc_T >> 3) - ((int32_t)dig_T1 << 1))) * ((int32_t)dig_T2)) >> 11;
int32_t v2 = (((((adc_T >> 4) - (int32_t)dig_T1) *
((adc_T >> 4) - (int32_t)dig_T1)) >> 12) * (int32_t)dig_T3) >> 14;
t_fine = v1 + v2;
*temp_x10 = (int16_t)((t_fine * 5 + 128) >> 8); /* 0.1 °C */
/* Pressure compensation */
int64_t p1 = ((int64_t)t_fine) - 128000;
int64_t p2 = p1 * p1 * (int64_t)dig_P6;
p2 += (p1 * (int64_t)dig_P5) << 17;
p2 += ((int64_t)dig_P4) << 35;
p1 = ((p1 * p1 * (int64_t)dig_P3) >> 8) + ((p1 * (int64_t)dig_P2) << 12);
p1 = ((((int64_t)1 << 47) + p1)) * ((int64_t)dig_P1) >> 33;
if (p1 == 0) { *pressure_pa = 0; return; }
int64_t p = 1048576 - adc_P;
p = (((p << 31) - p2) * 3125) / p1;
p1 = ((int64_t)dig_P9 * (p >> 13) * (p >> 13)) >> 25;
p2 = ((int64_t)dig_P8 * p) >> 19;
*pressure_pa = (int32_t)(((p + p1 + p2) >> 8) + ((int64_t)dig_P7 << 4)) / 256;
}
int32_t bmp280_pressure_to_alt_cm(int32_t pressure_pa) {
/* Barometric formula: h = 44330 * (1 - (p/p0)^(1/5.255)) metres */
float ratio = (float)pressure_pa / 101325.0f;
float alt_m = 44330.0f * (1.0f - powf(ratio, 0.1902949f));
return (int32_t)(alt_m * 100.0f); /* cm */
}