The firmware runs on a LPC11U37 microcontroller.
- Gnu Arm Embedded Toolchain
- CMake
- JTAG/SWD programmer, such as the Black Magic Probe (BMP) , or any OpenOCD compatible device.
First we need to create a config.cmake
file in which we can configure our build
cp config.cmake.example config.cmake
In config.cmake
we can configure the BMP if we're using any. On MacOS that would look like:
set(BLACKMAGIC_DEV /dev/cu.usbmodemC1DDCDF81)
Next we need to create a build
directory in the project root and run CMake to fetch all dedpendencies and create the makefiles.
mkdir build
cd build
cmake ..
The previous part only needs to be done the first time.
Now we're ready to actually compile the project. Inside the build
directory run
make
And to flash
make flash
The microcontroller communicates with a Raspberry Pi over uart
. The PCB is designed as a Raspberry Pi Hat and uses the TXD
and RXD
pins in the Raspberry Pi header. Note that Rx
and Tx
are swapped on the microcontroller side.
A binary protocol is used where each command is a unique 32 bit integer (sent LSB first) followed by an optional payload.
The baudrate is 500000
.
Binary responses from the microcontroller for the specific command are also prefixed with the same 4 bytes. ASCII responses, for logging or debug puposes are prefixed with 0x23232323
('####') and end in a newline \n
character.
Command | Description | Prefix | Payload | Response |
---|---|---|---|---|
NewSettings | Send and apply new operation settings | 0x41424344 |
Settings | Applied Settings |
RequestSettings | Request current settings | 0x45464748 |
None | Settings |
RequestSensorValues | Request current samples for all sensors | 0x0D15EA5E |
None | Sensor Values |
InspiratoryHoldStart | Trigger inspiratory hold if breathing is started | 0x99998888 |
None | Log info (ascii) |
InspiratoryHoldStop | Stop inspiratory hold | 0x99999999 |
None | Log info (ascii) |
ExpiratoryHoldStart | Trigger expiratory hold if breathing is started | 0x77776666 |
None | Log info (ascii) |
ExpiratoryHoldStop | Stop expiratory hold | 0x77777777 |
None | Log info (ascii) |
StatusLedOn | Turn on status LED (Green) | 0x55551111 |
None | Log info (ascii) |
StatusLedOff | Turn off status LED (Green) | 0x55661111 |
None | Log info (ascii) |
ErrorLedOn | Turn on error LED (Red) | 0x55552222 |
None | Log info (ascii) |
ErrorLedOff | Turn off error LED (Red) | 0x55662222 |
None | Log info (ascii) |
SwitchExhaleOn | Turn on 24V switch for exhale | 0x66663333 |
None | Log info (ascii) |
SwitchExhaleOff | Turn off 24V switch for exhale | 0x66773333 |
None | Log info (ascii) |
SwitchInhaleOn | Turn on 24V switch for inhale | 0x66664444 |
None | Log info (ascii) |
SwitchInhaleOff | Turn off 24V switch for ihale | 0x66774444 |
None | Log info (ascii) |
SwitchExtraOn | Turn on 24V switch (extra) | 0x66665555 |
None | Log info (ascii) |
SwitchExtraOff | Turn off 24V switch (extra) | 0x66775555 |
None | Log info (ascii) |
MFCAirSet | Set 12 bit analog value for MFC (Air) | 0x77771111 |
uint16 (mV) | Log info (ascii) |
MFCAirGet | Get analog MFC (Air) feedback value | 0x77881111 |
None | MFC Feedback uint16 (mV) |
MFCO2Set | Set 12 bit analog value for MFC (O2) | 0x77772222 |
uint16 (mV) | Log info (ascii) |
MFCO2Get | Get analog MFC (O2) feedback value | 0x77882222 |
None | MFC Feedback uint16 (mV) |
UserSwitchGet | Get current user switch status (0 or 1) | 0x88883333 |
None | 0 or 1 (ascii) |
Settings is defined as a struct where each entry is a 16 bit unsigned integer.
typedef struct __attribute__((packed)) {
uint16_t start; // breathing is started
uint16_t peep; // exhale pressure
uint16_t frequency; // breathing cycles per minute
uint16_t ratio; // ratio between inhale exhale
uint16_t pressure; // inhale pressure
uint16_t oxygen; // oxygen percentage for mixer
uint16_t max_pressure_alarm;
uint16_t min_pressure_alarm;
uint16_t max_TV_alarm;
uint16_t min_TV_alarm;
uint16_t max_fiO2_alarm;
uint16_t min_fiO2_alarm;
uint16_t crc;
} OperationSettings;
The final entry is a crc16 usb
checksum with the following specs:
CRC16 USB
Poly: 0x8005
Init: 0xFFFF
RefIn: True
RefOut: True
XorOut: 0xFFFF
From microcontroller to host.
Sensor values are prefixed with the 32-bit little-endian value 0x0D15EA5E
and followed by a crc16 usb checksum. The sensor data is a struct of 32bit signed integers:
The total data packet is then 4 + (15 x 4) + 2 = 66 bytes
struct SensorsAllData {
int32_t flow_inhale; // Inhale flow [mL / minute] (approximation)
int32_t flow_exhale; // Exhale flow [mL / minute]
int32_t pressure_inhale; // Inhale pressure [Pa]
int32_t pressure_exhale; // Exhale pressure [Pa]
int32_t pressure_patient; // Pressure at patient [Pa] (TODO: Not Implemented Yet)
int32_t pressure_mfc; // Pressure at MFC pressure vessel [Pa]
int32_t oxygen; // Oxygen percentage [0-100]
int32_t tidal_volume_inhale; // Tidal volume [mL] (Based on inhale flow)
int32_t tidal_volume_exhale; // Tidal volume [mL] (Based on exhale flow)
int32_t minute_volume; // Average flow (exhale) [mL / minute] (average over last 10 sec interval)
int32_t cycle_state; // PeeP / Peak / None
int32_t power_status; // UPS status [mV OR-ed with UPSStatus bits]
int32_t inspiratory_hold_result;// Value for end of inspiratory hold mean sensors
int32_t expiratory_hold_result; // Value for end of expiratory hold mean sensors
uint32_t system_status; // enum SystemStatus value(s) OR-ed together
}
The power_status field shows the measured power voltage in mv, OR-combined with one of these status flags:
enum UPSStatus {
UPS_STATUS_UNKNOWN = (0),
UPS_STATUS_OK = (1 << 31),
UPS_STATUS_BATTERY_POWERED = (1 << 30),
UPS_STATUS_FAIL = (1 << 29)
};
The system_status field shows multiple OR-combined flags indicating the status
of the electronics, including errors that have occurred. In normal operation,
only the flag SYSTEM_STATUS_BOOT_POWER_ON
(or SYSTEM_STATUS_BOOT_RESET_BY_PI
after a firmware upgrade) is expected to be set. Flags are only cleared by a resetting the MCU: a flag being set means that the error has occurred at least once since the last reset.
enum SystemStatus {
SYSTEM_STATUS_UNKNOWN = 0,
// Boot info: one of these should be set at boot
SYSTEM_STATUS_BOOT_POWER_ON = (1 << 0), // Power-On-Reset
SYSTEM_STATUS_BOOT_RESET_BY_PI = (1 << 1), // External reset (triggered by RPI)
SYSTEM_STATUS_BOOT_RESET_BY_ERROR = (1 << 2), // Watchdog reset (firmware bug)
SYSTEM_STATUS_BOOT_RESET_BY_PWR_FAIL= (1 << 3), // BOD reset (3.3V unstable)
SYSTEM_STATUS_BOOT_RESET_BY_UNKNOWN = (1 << 4), // SYSRST or unknown
// Errors in sensors or actuators:
SYSTEM_STATUS_ERROR_SENSOR_P_INSP = (1 << 5), // Inspiration pressure sensor error
SYSTEM_STATUS_ERROR_SENSOR_P_EXP = (1 << 6), // Expiration pressure sensor error
SYSTEM_STATUS_ERROR_SENSOR_P_PATIENT= (1 << 7), // Patient pressure sensor error
SYSTEM_STATUS_ERROR_SENSOR_FLOW = (1 << 8), // Flow sensor error
SYSTEM_STATUS_ERROR_ACTUATOR_MFC_AIR= (1 << 16), // Air MFC (DAC) error
SYSTEM_STATUS_ERROR_ACTUATOR_MFC_O2 = (1 << 17), // O2 MFC (DAC) error
SYSTEM_STATUS_ERROR_MFC_OVERPRESSURE= (1 << 18), // MFC output pressure too high
// NOTE: new status flags may be added here
//
// Generic errors
SYSTEM_STATUS_ERROR_EEPROM = (1 << 29), // EEPROM error: settings may not be remembered after reset
SYSTEM_STATUS_ERROR_I2C_BUS = (1 << 30), // I2C bus is in invalid state
// Warning: the firmware is not a release build
SYSTEM_STATUS_DEBUG_ENABLED = (1 << 31), // Debug build should not be released
};
From microcontroller to host.
Settings are prefixed with the 32-bit little-endian value: 0x41424344
, followed by the Settings.
Set point for the mass flow controller. The MFC is controlled through a 12bit i2c DAC. The payload for the MFC set command accepts a unsigned 16 bit integer between 0 and 4095.
Feedback from the MFC are returned as uint16 value in millivolts (0-5000). Note: this command is binary and is not terminated with '\n'.