# Barometric pressure test

## Goals

1. Read barometric pressure from two sensors. One connected to the MCU with SPI and the other connected with I2C bus.
2. The reading must be done in real time and data should be checked for validity.
3. The output should be sent to a serial port.
4. In case of reading error the LED should be turned on for 1 second. Each sensor has its own LED.

## Hardware
- MCU - STM32F042K6T6 (Nucleo-32, board MB1180) - 32 KB flash, 6 KB RAM
- Barometric pressure sensor - 2x GY-63 (MS5611)

## Wiring

Pin assignments follow ST UM1956 Table 10 and the MB1180 C.2 schematic, and match
the STM32duino defaults for this variant, so `Wire.begin()` and `SPI.begin()` need
no explicit pin overrides.

![Wiring diagram](docs/wiring.png)

Vector source: [`docs/wiring.svg`](docs/wiring.svg). Pins there are grouped by
function, not by physical header order.

The same wiring drawn physically, with the real header order from UM1956
Table 10, so it is clear which hole each jumper goes into:

![Physical wiring diagram](docs/wiring-physical.png)

Vector source: [`docs/wiring-physical.svg`](docs/wiring-physical.svg). Note that
SPI is split across both connectors: MOSI, MISO and CS are on CN3, while SCK is
CN4 pin 15.

### Baro0 - I2C (GY-63 <=> STM32F042)
| GY-63 | Nucleo | STM32 | Note |
|-------|--------|-------|------|
| VCC | 3V3 | - | |
| GND | GND | - | |
| PS | 3.3V | - | high selects I2C |
| SDA | A4 | PB7 | `PIN_WIRE_SDA`, reaches the A4 pad through SB18 |
| SCL | A5 | PB6 | `PIN_WIRE_SCL`, reaches the A5 pad through SB16 |
| CSB | GND | - | sets address `0x77`; tie to VCC for `0x76` |

The MS5611 address is `1110 11Cx`, where C is the **complement** of CSB
(datasheet p.12). `CSB` must not be left floating. If you strap it high, change
`BARO_I2C_ADDRESS` in [`include/config.h`](include/config.h).

### Baro1 - SPI (GY-63 <=> STM32F042)
| GY-63 | Nucleo | STM32 | Note |
|-------|--------|-------|------|
| VCC | 3V3 | - | |
| GND | GND | - | |
| PS | GND | - | low selects SPI |
| SCLK | D13 | PB3 | SPI1_SCK |
| SDI | D11 | PB5 | SPI1_MOSI |
| SDO | D12 | PB4 | SPI1_MISO |
| CSB | D10 | PA11 | chip select, `BARO_SPI_CS_PIN` |

SPI1 has to stay on this pin group. The alternative group (PA5/PA6/PA7) is
unusable while I2C is active: with the factory-default solder bridges SB16 and
SB18 closed, PA6 shares a net with PB6 (SCL) and PA5 shares a net with PB7 (SDA).

### Error LEDs

Two external LEDs, one per sensor, each in series with roughly 510 R to GND.

| Signal | Nucleo | STM32 |
|--------|--------|-------|
| I2C sensor error | D3 | PB0 |
| SPI sensor error | D6 | PB1 |

Both pins are set in [`include/config.h`](include/config.h).

## Build and run

```sh
pio run              # build
pio run -t upload    # flash over ST-LINK
pio device monitor   # 115200 baud, ST-LINK Virtual COM Port
```

`Serial` is USART2 on PA2/PA15, wired to the ST-LINK VCP, so no USB-serial
adapter is needed.

The image is close to the flash ceiling (about 89% of 32 KB). `-flto` and
`-fsingle-precision-constant` in [`platformio.ini`](platformio.ini) are required,
not cosmetic: both MS5611 drivers write their compensation maths with unsuffixed
double literals, which otherwise links roughly 5 KB of double soft-float helpers
and overflows flash.

## Output

CSV at 10 Hz on the serial port, with a header line printed once at startup:

```
ms,i2c_status,i2c_p_mbar,i2c_t_c,spi_status,spi_p_mbar,spi_t_c
1043,OK,1013.24,24.31,OK,1013.19,24.44
1143,OK,1013.25,24.31,ERR_READ,1013.19,24.44
```

Pressure is in mbar, temperature in degrees C, both to two decimals. The status
column is authoritative: when it is not `OK`, the two values next to it are the
last ones successfully computed, not fresh readings.

| Status | Meaning |
|--------|---------|
| `INIT` | not initialised yet |
| `OK` | reading passed every check |
| `ERR_INIT` | reset / PROM handshake did not complete |
| `ERR_CRC` | factory calibration failed CRC-4, or is all-zero / all-ones |
| `ERR_READ` | driver reported a bus or ADC failure |
| `ERR_RANGE` | value outside the MS5611 operating envelope |
| `ERR_STALE` | sensor still answers but stopped producing new samples |

Sensors are polled continuously, as fast as the ADC conversions allow (roughly
90 Hz per sensor at OSR 1024); the serial report is throttled to 10 Hz.

## Validation

Neither driver library detects every failure on its own, so the checks live in
[`BaroChannel`](src/baro_channel.cpp):

- **PROM CRC-4** at init, per datasheet p.13 / AN520. Neither library verifies it.
- **All-zero / all-ones PROM screening**, because an absent part reads back as
  one of those, `0xFFFF` is accepted by both libraries' `reset()`, and an
  all-zero PROM satisfies the CRC.
- **Driver return code** on every read.
- **Range check** against 10..1200 mbar and -40..+85 C. This is the one that
  catches the failure the I2C driver misses entirely: an ADC read taken before
  the conversion completes returns 0 (datasheet p.11), and `MS5611::read()`
  still reports `MS5611_READ_OK` for it.
- **Stale detection** - bit-identical pressure *and* temperature for 32
  consecutive polls means the part stopped converting while still acknowledging.

Any failure lights that sensor's LED for 1 second (non-blocking, extended if
further errors follow). After 10 consecutive failures the channel is taken back
through the full reset and PROM handshake, so a sensor that was unplugged and
reconnected recovers on its own.

The CRC-4 implementation was verified differentially on the host against the
independent NuttX reference over 200000 randomised PROM images.

## References
- [Barometric Pressure Sensor MS5611](docs/datasheets/ENG_DS_MS5611-01BA03_B3.pdf)
- [STM32F042K6T6 Nucleo](docs/datasheets/DS_stm32f042k6.pdf)
- [STM32 Nucleo-32 boards (MB1180)](https://www.st.com/resource/en/user_manual/um1956-stm32-nucleo32-boards-mb1180-stmicroelectronics.pdf) - UM1956, LED and connector tables
