MVM-IT: Difference between revisions

From DaqWiki
Jump to navigation Jump to search
Line 588: Line 588:
| "version" | all | _FIRMWARE_VERSION_ | string | |
| "version" | all | _FIRMWARE_VERSION_ | string | |
| "alarm" | all | ALARM_FLAG, see alarm function | uint32_t | |
| "alarm" | all | ALARM_FLAG, see alarm function | uint32_t | |
| "warning" | all | 0; V5: WARNING_FLAG, always 0 | not implemented | | |
| "warning" | all | 0; V5: WARNING_FLAG, always 0. not implemented | - | |
| "run" | all | "1" if core_config.run, else "0" | bool | |
| "run" | all | "1" if core_config.run, else "0" | bool | |
| "mode" | all | "1" if BreathMode == M_BREATH_ASSISTED, else "0" | bool | |
| "mode" | all | "1" if BreathMode == M_BREATH_ASSISTED, else "0" | bool | |
Line 606: Line 606:
| "calib" | all | see "get calib" | list of values | |
| "calib" | all | see "get calib" | list of values | |
| "calibv" | hw_rev3, hw_rev4 | see "get calibv" | |
| "calibv" | hw_rev3, hw_rev4 | see "get calibv" | |
| "calibv" | V5 | not implemented | | |
| "calibv" | V5 | not implemented | - | |
| "calib_o2" | hw_v4 | see "get calib_o2" | |
| "calib_o2" | hw_v4 | see "get calib_o2" | |
| "calib_o2" | V5 | "OK", calls CalibrateOxygenSensor() | | |
| "calib_o2" | V5 | "OK", calls CalibrateOxygenSensor() | - | |
| "stats" | hw_rev3, hw_rev4 | see "get stats" | | |
| "stats" | hw_rev3, hw_rev4 | see "get stats" | | |
| "stats" | V5 | not implemented | | |
| "stats" | V5 | not implemented | - | |
| "ads" | hw_v4 | see "get ads" | | |
| "ads" | hw_v4 | see "get ads" | | |
| "pcv_trigger_enable" | hw_v4 | 1 if core_config.pcv_trigger_enable, else 0 | bool | |
| "pcv_trigger_enable" | hw_v4 | 1 if core_config.pcv_trigger_enable, else 0 | bool | |
| "pcv_trigger" | hw_v4 | core_config.pcv_trigger | ? | |
| "pcv_trigger" | hw_v4 | core_config.pcv_trigger | ? | |
| "get_fp" | V5 | FlowIn, VenturiP | ? | |
| "get_fp" | V5 | FlowIn, VenturiP | ? | |
| "venturi_scan" | V5 | "OK", calls DOVenturiMeterScan() | | |
| "venturi_scan" | V5 | "OK", calls DOVenturiMeterScan() | - | |
</pre>
</pre>



Revision as of 13:06, 30 April 2020

Links

  • MVM-IT MVM with Italian control board
  • MVM-TR MVM with TRIUMF control board
  • ESP32 - ESP32 information: adafruit HUZZA32, ESP-WROOM32 modules, arduino and IDF cross-compilers
  • RPI3 - RPi information (SD cards, boot modes, etc)

Software Quality Assurance

Guiding documents:

Organized by Masa and Dave T.:

Additional repositories:

Documents under development:

MVM Hardware tests:

MVM box

hw_rev3

Control board

Per board.pdf dated "4/13/20"

  • external power supply:
  • Adafruit HUZZA32 module (ESP-WROOM32 ESP32 microcontroller ESP32)
  • 6 pressure sensors (5525DSO-DB001DS)
  • 2 GPIO pins (J5, CH3, CH4, pull up to 3.3V)
  • 2 push button inputs (J6, J7 BTN_1, BTN_2, pull up to 3.3V)
  • 2 LED drivers (J8, J9 LED, BUZ, 5V)
  • 2 "modulation control" (J10, J11)
  • 2 "24V solid state relay" (J12, J13, OMRON_G3VM-61FR1)
  • 2 I2C 3.3V (J14, J15)
  • 2 I2C 5V (J16, J17)
  • 1 ESP32 serial console (J18, 3.3V, RX, TX)
  • 1 O2 sensor input (J19, OXY_IN)
  • 1 SPI 3.3V (J20A)

System configurations

  • "Italian model": [ MVM-IT + ESP32 ] ---- usb-serial ---- [ RPi + GUI ] (remote control) ---- wifi or cat5 or SD/USB flash for data collection
  • "2.5 Pi": [ MVM-IT + ESP32 --- usb-serial --- RPi + GUI (local control) ] ---- cat5 ---- [ RPi + GUI ] (remote control)

Notable hardware limitations:

  • ESP32: yes wifi, no cat5 ethernet, no usb, no sd flash (sd flash maybe via adafruit LCD "feather")
  • RPi: cat5/rj45 ethernet, wifi, HDMI/DP, USB, SD flash slot.

Abbreviations: MVM-IT - Italian PCB, ESP32 - adafruit HUZZA32/WROOM32/ESP32 icrocontroller, RPi - RaspberryPi, GUI - medium size LCD running the MVM python GUI

ESP32 connections

!!! ALL GPIO PINS are 3.3V, NOT 5V safe !!!

ESP32 - link to the ESP32 page

ESP32 pin and name - WROOM32 - HUZZA32 - header - schematic name - function

"JP1 16 pin"

42 - GPIO21 - IO21 - 21      - JP1-1  - CS - (output) SPI nCS - mux GPIO21 or VSPIHD
41 - U0TXD  - TXD0 - TX/17   - JP1-2  - TX  - ESP32 console - mux GPIO1 or U0TXD
40 - U0RXD  - RXD0 - RX/16   - JP1-3  - RX  - ESP32 console - mux GPIO3 or U0RXD
38 - GPIO19 - IO19 - MISO/19 - JP1-4  - MISO  - (input) SPI MISO - mux GPIO19 or U0CTS or VSPIQ
35 - GPIO18 - IO18 - MOSI/18 - JP1-5  - MOSI  - (output) SPI MOSI - mux GPIO18 or VSPICLK
34 - GPIO5  - IO5  - SCK/5   - JP1-6  - SCK  - (output) SPI CLK - mux GPIO5 or VSPICS0
24 - GPIO4  - IO4/A2_0 - ADC2/A5/4 - JP1-7  - N/C - mux GPIO4 or RTC_GPIO10
 5 - SENSOR_VP C16/270pF - IO36/SEN_VP/A1_0 - ADC1/A4/36 - JP1-8  - OXY_IN  - (input only)
 8 - SENSOR_VN C17/270pF - IO39/SEN_VN/A1_3 - ADC1/A3/39 - JP1-9  - BTN_2 - mux GPIO39 or RTC_GPIO3 (input only)
10 - VDET_1 - I34/A1_6       - ADC1/A2/34      - JP1-10 - BTN_1 - mux GPIO34 or RTC_GPIO4 (input only)
14 - GPIO25 - IO25/DAC1/A2_8 - ADC2/A1/DAC1/25 - JP1-11 - DAC1 - mux GPIO25 or RTC_GPIO6 or DAC1
15 - GPIO26 - IO26/DAC2/A2_9 - ADC2/A0/DAC2/26 - JP1-12 - DAC2 - mux GPIO26 or RTC_GPIO7 or DAC2
none - none - GND     - JP1-13 - AGND - GND
none - none - NC      - JP1-14 - N/C
none - none - "3V"    - JP1-15 - "3V"
none - none -  RST    - JP1-16 - "RST"

"JP2 12 pin"

none - none - BAT    - JP3-1  - N/C
none - none - EN     - JP3-2  - N/C
none - none - USB    - JP3-3  - "USB" - 5V USB input power
20 - MTCK   - IO13/A2_4 - "red LED" ADC2/13/A12 - JP3-4  - LED - (output) mux GPIO13 or RTC_GPIO14
18 - MTDI   - IO12/A2_5 - "internal pulldown, output only" ADC2/12/A11 - JP3-5  - BUZ - (output) mux GPIO12 or RTC_GPIO15 - "boot fail if pulled high"
16 - GPIO27 - IO27/A2_7      - ADC2/27/A10 - JP3-6  - CH4 - (output) mux GPIO27 or RTC_GPIO17
13 - 32K_XN - IO33/A1_5/X32N - ADC1/33/A9  - JP3-7  - CH3 - (output) mux GPIO33 or RTC_GPIO8
21 - MTDO   - IO15/A2_3      - ADC2/15/A8  - JP3-8  - CH2 - (output) mux GPIO15 or RTC_GPIO13
12 - 32K_XP - IO32/A1_4/X32P - ADC1/32/A7  - JP3-9  - CH1 - (output) mux GPIO32 or RTC_GPIO9
17 - MTMS   - IO14/A2_6      - ADC2/14/A6  - JP3-10 - "BATTERY" - pull up to 3.3V, mux GPIO14 or RTC_GPIO16 (note: ADC2 cannot be used if Wifi active)
39 - GPIO22 - IO22 - SCL/22 - JP3-11 - "SCL" - (I2C, pull up to 3.3V) - mux GPIO22 or U0RTS
36 - GPIO23 - IO23 - SDA/23 - JP3-12 - "SDA" - (I2C, pull up to 3.3V) - mux GPIO23

not on header, internally connected on HUZZA32:

11 - VDET_2 - I35/A1_7 - ADC1/35/A13 - N/C - VBAT voltmeter (through divider) - mux GPIO35 or ADC1_CH7 or RTC_GPIO5 (input only)

not clear what function:

23 - GPIO0   - IO0/A2_1 - GPIO0 - serial RTS - mux GPIO0 or RTC_GPIO11
22 - GPIO2   - IO2/A2_2 - GPIO2 - serial DTR (looks like mistake on schematic) - mux GPIO2 or RTC_GPIO12
25 - GPIO16  - IO16 - IO16 - not connected? - mux GPIO16 or U2RXD
27 - GPIO17  - IO17 - IO17 - not connected? - mux GPIO17 or U2TXD
 9 - CHIP_PU - EN - RESET - push button - no mux

WROOM32 flash memory connection ("U3"):

28 - SD_DATA_2 - SD2 - n/c - mux SD_DATA2 or GPIO9
29 - SD_DATA_3 - SD3 - n/c - mux SD_DATA3 or GPIO10
30 - SD_CMD - CMD - n/c - mux SD_CMD or GPIO11
31 - SD_CLK - CLK - n/c - mux SD_CLK or GPIO6
32 - SD_DATA_0 - SD0 - n/c - mux SD_DATA0 or GPIO7
33 - SD_DATA_1 - SD1 - n/c - mux SD_DATA1 or GPIO8

Not connected to ESP32:

CPU AUX - (maybe output) direct connection to AUX screw terminal

Note:
- I2C controller can use any/all GPIO pins
- SPI controller can use any/all GPIO pins

I2C bus configuration

  • ESP32 drives SCL_UC/SDA_UC, connected are:
    • U9 "nano dac", part unknown, maybe not installed
    • I2C mux TCA9548APWR (A0,A1,A2 tied to GND), output ports:
      • 0 - 3V I2C J14
      • 1 - 3V I2C J15
      • 2 - 5V I2C J16
      • 3 - 5V I2C J17
      • 4 - PS1 (CSB tied to 3.3V), PS2 (CSB tied to GND)
      • 5 - PS3, PS4 (same CSB)
      • 6 - PS5, PS6 (same CSB) (maybe absent)
      • 7 - N/C

I2C bus addresses

TO BE UPDATED

* 0x70 - I2C switch - U1 - TI TCA9548APWR [[https://edev-group.triumf.ca/hw/ventilator-controller/rev0/-/blob/master/Altium/Datasheets/tca9548a%20-%20I2C%20switch%208-channel.pdf Datasheet]]
* 0x76 - pressure sensor - i2c switch port 7 mask 0x80 - "TE Connectivity Measurement Specialties" 5525DSO-DB001DS [[https://edev-group.triumf.ca/hw/ventilator-controller/rev0/-/blob/master/Altium/Datasheets/MS5525DSO_D20-1%20pressure%20sensor.pdf Datasheet]]
* 0x80 - flow meter - i2c switch port 0 mask 0x01 - SFM3000 - https://gitlab.triumf.ca/mvmdev/mvmcontroller/-/blob/dev-esp32/docs/datasheets/Sensirion-Mass-Flow-Meters-SFM3000-I2C-Functional-Description.pdf https://gitlab.triumf.ca/mvmdev/mvmcontroller/-/blob/dev-esp32/docs/datasheets/Sensirion_Mass_Flow_Meters_SFM3000_Datasheet.pdf https://gitlab.triumf.ca/mvmdev/mvmcontroller/-/blob/dev-esp32/docs/datasheets/SFM3xxx_Extended_I2C_Command_Set.pdf

To probe the pressure sensor:
<pre>
$ i2cset -y 1 0x70 0 0x80
$ i2cget -y 1 0x70 
0x80
$ i2cdetect -y 1 ### detects: 0x70 (switch) and 0x76 (pressure sensor)
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: 70 -- -- -- -- -- 76 --                         

hw_rev4

Control board

TBW, waiting for board schematics

System architecture

valves, sensors ---i2c--- ESP32 ---esp32-u0-serial---serial-usb-bridge-|---usb-cable---|---RPi4B---|---LCD screen

sensors,etc ---i2c--- "supervisor microcontroller" --- connected to TBW, waiting for board schematics

ESP32 connections

!!! ALL GPIO PINS are 3.3V, NOT 5V safe !!!

ESP32 - link to the ESP32 page

TBW, waiting for board schematics

I2C bus configuration

  • ESP32 drives SCL_UC/SDA_UC, connected are:

TBW, waiting for board schematics

I2C bus addresses

TBW, waiting for board schematics

Pressure sensor

Calibration constants (chip on KO's board):

read by i2cget (addr 0xa0 through 0xae)

pi@raspberrypi:~ $ i2cget -y 1 0x76 0xa0 w
0x0100

read by sensors.exe (branch dev-linux)

pressure sensor PROM 0x00: 0x0001
pressure sensor PROM 0x01: 0x392b
pressure sensor PROM 0x02: 0x1e52
pressure sensor PROM 0x03: 0x0e25
pressure sensor PROM 0x04: 0x071a
pressure sensor PROM 0x05: 0x943d
pressure sensor PROM 0x06: 0x1f6b
pressure sensor PROM 0x07: 0x000c
mvm_hal_init: PS1 CRC4 OK!
  • read ADC, no conversion
root@raspberrypi:~/i2c-tools-4.1/tools# ./i2cget3 -y 1 0x76 0
write returned 1
read returned 4, value 0x3f000000
  • read ADC, pressure
i2cset -y 1 0x76 0x48
root@raspberrypi:~/i2c-tools-4.1/tools# ./i2cget3 -y 1 0x76 0
write returned 1
read returned 4, value 0x3f900a44
  • read ADC, temperature
i2cset -y 1 0x76 0x58
root@raspberrypi:~/i2c-tools-4.1/tools# ./i2cget3 -y 1 0x76 0
write returned 1
read returned 4, value 0x3f9c424d

Flow sensor

TBW

Proportional Valve ArduinoCorePP_PWM_hw_v3 firmware

  • From Bryerton:
The Italian's use the PWM on DAC1 / A0 / IO25 pin. The API is the ledc API used to control an LED via PWM
  • see ArduinoCorePP_PWM_hw_v3.ino
    • function setup() attach pin "DAC1" to ledc
ledcSetup(0, 10000, 12); // KO: chan 0, freq in kHz, how many bits of PWM 12 bits = range 0..4095
ledcAttachPin(DAC1, 0); // KO: IO25/A0/DAC1
ledcWrite(0, 0);
    • function PressureControlLoop_PRESSIN() see ledcWrite()
if (Pset == 0) {
  ledcWrite(0, 0);
} else {
  pid_outb = ...; // KO: looks like a digital PID controller
  if (pid_outb<0) pid_outb=0; // KO: low limit
  pid_outb = pid_outb + 500; // KO: what is this?!?
  if (pid_outb>4090) pid_outb=4090; // KO: high limit
  ledcWrite(0, pid_outb); // KO: value 500..4090
}
    • also see valve_control() commented absent code for PWM.

MVM-GUI

Code review

Install

  • as root:
    • apt-get update
    • apt-get install python3-pyqt5 python3-numpy python3-pyqtgraph python3-serial
    • ### does not work, installs wrong pip: apt-get install python3-pip
    • curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
    • python3 get-pip.py --force-reinstall
    • ### does not work, installs too-old version of yaml 3.13: apt-get install python3-yaml
    • pip3 install PyYAML ### version 5.3.1

Run with python simulator

  • cd gui
  • ./mvm_gui.py fakeESP32

Run with ESP32 simulator

  • load the ardiuno sketch mock/mock.ino into ESP32 (see instructions below)
  • ./mvm_gui.py ### default connection is /dev/ttyUSB0 at 115200 bps, change it in default_settings.yaml

MVMFirmware monolithic hw_rev3, hw_rev4

Prepare

Build and Flash

Connect and Use manually

  • use Ardiuno tools -> serial console, set: serial 115200
  • or use minicom, set /dev/ttyUSB0, 115200 bps, no flow control
  • output on a base esp32 (no baseboard, no hardware, no sensors, etc)
ets Jun  8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:8896
load:0x40080400,len:5816
entry 0x400806ac
SCAN I2C BUS: 0
Scanning... I2C
No I2C devices found

SCAN I2C BUS: 1
Scanning... I2C
No I2C devices found

SCAN I2C BUS: 2
Scanning... I2C
No I2C devices found

SCAN I2C BUS: 3
Scanning... I2C
No I2C devices found

SCAN I2C BUS: 4
Scanning... I2C
No I2C devices found

SCAN I2C BUS: 5
Scanning... I2C
No I2C devices found

SCAN I2C BUS: 6
Scanning... I2C
No I2C devices found

SCAN I2C BUS: 7
Scanning... I2C
No I2C devices found

SENSOR:           0
SENS_T1:          65535
OFF_T1:           65535
TCS:              65535
TCO:              65535
TREF:             65535
TEMPSENS:         65535
OFFSET:            0.00
SENSOR:           1
SENS_T1:          65535
OFF_T1:           65535
TCS:              65535
TCO:              65535
TREF:             65535
TEMPSENS:         65535
OFFSET:            0.00
SENSOR:           2
SENS_T1:          65535
OFF_T1:           65535
TCS:              65535
TCO:              65535
TREF:             65535
TEMPSENS:         65535
OFFSET:            0.00
SENSOR:           3
SENS_T1:          65535
OFF_T1:           65535
TCS:              65535
TCO:              65535
TREF:             65535
TEMPSENS:         65535
OFFSET:            0.00
SFM driver version 0.1.0
SFM sensor probing failed
...
Measure Flow Sensor initialized!
  • send: "get all", reply: "valore=0.00,0.00,0,0,0,0,0,0,1"

Connect and read data using mvm-control

Follow the README file at: https://github.com/MechanicalVentilatorMilano/mvm-control

MVMFirmwareCpp

Prepare

Build and Flash

  • ssh pi@...
  • git clone git@github.com:NuclearInstruments/MVMFirmwareCpp.git
  • cd arduino; ./ardiuno
  • open .../MVMFirmwareCpp/MVMFirmwareCore/MVMFirmwareCore.ino
  • install library SimpleCLI (1.0.9)
  • build sketch
  • if there is error about missing arduino.h, create one with this contents in the same directory as the .ino file
// arduino.h
#include <Arduino.h>
  • upload sketch into esp32

Connect and Use manually

  • use Ardiuno tools -> serial console, set: serial 115200
  • or use minicom, set /dev/ttyUSB0, 115200 bps, no flow control
  • output on a base esp32 (no baseboard, no hardware, no sensors, etc)
...
  • send: "get all", reply: "valore=..."

Connect and read data using mvm-control

Follow the README file at: https://github.com/MechanicalVentilatorMilano/mvm-control

{"time":1.717,"p_patient":0.00,"f_total":0.00,"o2":100.00,"bpm":0.00,"v_total":0.00,"peep":0.00,"temp":0.00,"bat_pwr":1,"bat_charge":100.00,"p_peak":0.00,"v_total_insp":0.00,"v_total_exhl":0.00,"f_peak":0.00},
...

Connect with mvm_gui

See below for instructions to install mvm_gui

  • cd gui/gui
  • ./mvm_gui.py

Firmware to GUI interface

Physical link

USB serial, 115200 bps

Set commands

| set command | version | type | arguments |
|---|---|---|---|
| "run" | all | int | <1: core_config.run=false, else =true |
| "mode" | all | int | 0: BreathMode=M_BREATH_FORCED, else =M_BREATH_ASSISTED |
| "rate" | all | float | core_config.respiratory_rate |
| "ratio" | all | float | core_config.respiratory_ratio |
| "assist_ptrigger | all | float | core_config.assist_pressure_delta_trigger |
| "assist_flow_min" | all | float | flux_close |
| "ptarget | all | float | target_pressure_auto |
| "pressure_support" | all | float | target_ressure_assist |
| "peep" | hw_rev3, hw_rev4 | float | pressure_forced_exhale_min, REMOVED IN V5 |
| "pid_p" | all | float | .P |
| "pid_i" | all | float | .I |
| "pid_d" | all | float | .D |
| "pid_p2" | all | float | .P2 |
| "pid_i2" | all | float | .I2 |
| "pid_d2" | all | float | .D2 |
| "pause_inhale" | all | int | pause_inhale=arg, pause_timeout=500 |
| "pause_lg" | all | int | !=0, pause_lg=true, else =false |
| "pause_lg_time" | all | float->int | pause_lg_timer = arg*1000.0 |
| "pause_lg_p" | all | float->int | pause_lg_p |
| "pause_exhale" | all | int | pause_exhale=arg, pause_timeout=500 |
| "pid_limit" | all | float | pid.limit |
| "alarm_snooze" | all | int | see alarm snooze function |
| "alarm" | hw_rev3, hw_rev4 | int | arg not used, fires ALARM_GUI_ALARM |
| "alarm" | V5 | int | set and clear GUI alarm, see alarm function |
| "watchdog_reset" | all | int | arg not used, see watchdog function |
| "console" | all | int | !0, __CONSOLE_MODE=true, else =false |
| "timestamp" | all | int | !0, _ADDTimeStamp=true, else =false |
| "pcv_trigger_enable" | !hw_rev3, hw_v4, V5 | int | !0, pcv_trigger_enable=true, else =false |
| "pcv_trigger" | !hw_rev3, hw_v4, V5 | float | pcv_trigger |
| "wdenable" | all | int | !0, __WDENABLE=true, else =false; enables and disabled the watchdog, see watchdog function |
| "backup_enable" | all | int | !0, backup_enable=true, else =false |
| "backup_min_rate" | hw_rev3, hw_rev4 | float | <1, backup_min_rate=1, else =arg, REMOVED IN V5 |
| "backup_min_time" | only V5 | float | <1, backup_min_rate=1, else =arg |
| "stats_clear" | hw_rev3, hw_rev4 | none | ResetStatsBegin() |
| "stats_clear" | V5 | none | does nothing |
| "flush_pipe" | V5 | float | <1, FlushPipes(false,1), else FlushPipes(true,arg) |

Get commands

Get commands respond with "valore="+data, where data is shown in the following table.

hw_rev3, hw_rev4: unknown commands produce no response V5: unknown commands return "valore=ERROR:Invalid Command Argument"

| get command | version | returns | unit | description |
|---|---|---|---|---|
| "pinput" | hw_v4 | Pinput | ? | |
| "pressure" | all | pressure[0].last-pressure; V5: Ploop | ? | |
| "ppressure" | V5 | PPatient | ? | |
| "flow" | all | gasflux[0].last_flux; V5: FlowIn | ? | |
| "o2" | all | last_O2 | ? | |
| "bpm" | all | last_bpm | ? | |
| "backup" | all | 0, not implemented | - |  |
| "tidal" | all | tidal_volume_c.TidalVolume*VOL_COMP; V5: TidalVolume | ? | |
| "peep" | all | last_peep | ? | |
| "temperature" | all | temperature; V5: GasTemperature | ? | |
| "power_mode" | all | "1" if batteryPowered, else "0" |  bool | |
| "battery" | all | currentBatteryCharge | ? | |
| "version" | all | _FIRMWARE_VERSION_ | string | |
| "alarm" | all | ALARM_FLAG, see alarm function | uint32_t | |
| "warning" | all | 0; V5: WARNING_FLAG, always 0. not implemented | - | |
| "run" | all | "1" if core_config.run, else "0" | bool | |
| "mode" | all | "1" if BreathMode == M_BREATH_ASSISTED, else "0" | bool | |
| "rate" | all | core_config.respiratory_rate | ? | |
| "ratio" | all | core_config.respiratory_ratio | ? | |
| "assist_ptrigger" | all | core_config.assist_pressure_delta_trigger | ? | |
| "assist_flow_min" | all | core_config.flux_close | ? | |
| "ptarget" | all | core_config.target_pressure_auto | ? | |
| "pressure_support" | all | core_config.target_pressure_assist | ? | |
| "backup_enable" | all | 1 if core_config.backup_enable, else 0 | bool | |
| "backup_min_rate" | hw_rev3, V5 | core_config.backup_min_rate | ? | |
| "backup_min_time" | hw_rev4 | core_config.backup_min_time | ? | |
| "pause_lg" | all | core_config.pause_lg | ? | |
| "pause_lg_time" | all | core_config.pause_lg_timer/1000.0 | ? | |
| "pause_lg_p" | all | core_config.pause_lg_p | ? | |
| "all" | all | see "get all" | list of values | |
| "calib" | all | see "get calib" | list of values | |
| "calibv" | hw_rev3, hw_rev4 | see "get calibv" | |
| "calibv" | V5 | not implemented | - | |
| "calib_o2" | hw_v4 | see "get calib_o2" | |
| "calib_o2" | V5 | "OK", calls CalibrateOxygenSensor() | - | |
| "stats" | hw_rev3, hw_rev4 | see "get stats" | | |
| "stats" | V5 | not implemented | - | |
| "ads" | hw_v4 | see "get ads" | | |
| "pcv_trigger_enable" | hw_v4 | 1 if core_config.pcv_trigger_enable, else 0 | bool | |
| "pcv_trigger" | hw_v4 | core_config.pcv_trigger | ? | |
| "get_fp" | V5 | FlowIn, VenturiP | ? | |
| "venturi_scan" | V5 | "OK", calls DOVenturiMeterScan() | - | |

get all

  • commit Sun Apr 19 13:44:21 2020 +0200

hw_v3:

if (strPatam == "all") {
        Serial.println("valore=" + String(pressure[1].last_pressure) + "," + String(tidal_volume_c.FLUX) + "," + String(last_O2) + "," + String(last_bpm)
            + "," + String(tidal_volume_c.TidalVolume * VOL_COMP) + "," + String(last_peep)
            + "," + String(temperature) + "," + String(batteryPowered ? 1 : 0) + "," + String(currentBatteryCharge)
            + "," + String(currentP_Peak)
            + "," + String(currentTvIsnp * VOL_COMP)
            + "," + String(currentTvEsp * VOL_COMP)
            + "," + String(currentVM));
    }

hw_v4:

    if (strPatam == "all") {
        Serial.println("valore=" + String(pressure[1].last_pressure) + "," + String(tidal_volume_c.FLUX) + "," + String(last_O2) + "," + String(last_bpm)
            + "," + String(tidal_volume_c.TidalVolume * VOL_COMP) + "," + String(last_peep)
            + "," + String(temperature) + "," + String(batteryPowered ? 1 : 0) + "," + String(currentBatteryCharge)
            + "," + String(currentP_Peak)
            + "," + String(currentTvIsnp * VOL_COMP)
            + "," + String(currentTvEsp * VOL_COMP)
            + "," + String(currentTvIsnp * VOL_COMP * last_bpm/1000.0));
    }

"V5":

    if (strPatam == "all") {
       return "valore=" + String(sys_s->pPatient) 
            + "," + String(sys_s->Flux)
            + "," + String(sys_s->last_O2)
            + "," + String(sys_s->last_bpm)
            + "," + String(sys_s->TidalVolume) 
            + "," + String(sys_s->last_peep)
            + "," + String(sys_s->GasTemperature) 
            + "," + String(sys_s->batteryPowered ? 1 : 0) 
            + "," + String(sys_s->currentBatteryCharge)
            + "," + String(sys_s->currentP_Peak)
            + "," + String(sys_s->currentTvIsnp*1000.0)
            + "," + String(sys_s->currentTvEsp * 1000.0)
            + "," + String(sys_s->currentVM);

get calib

hw_rev3, hw_rev4:

Serial.print("Valore=");
        for (int j = 0; j < N_PRESSURE_SENSORS; j++) {
            i2c_MuxSelect(pressure_sensor_i2c_mux[j]);
            float mean = 0;
            PRES_SENS_CT[j].ZERO = 0;
            for (int q = 0; q < 100; q++) {
                read_pressure_sensor(j);
                mean += pressure[j].last_pressure;
            }
            PRES_SENS_CT[j].ZERO = mean / 100;

            Serial.print(String(PRES_SENS_CT[j].ZERO) + ",");
        }
        Serial.println(" ");

V5:

float zeros[4];
        int count=4;
        ((MVMCore*)core)->ZeroSensors(zeros,&count);
        String outval ="valore=";
        for (int i = 0; i < count; i++)
        {
            if (i != count - 1)
                outval += String(zeros[i]) + ",";
            else
                outval += String(zeros[i]);
        }
        return outval;

get calibv

hw_rev3, hw_rev4:

if (fabs(tidal_volume_c.ExpVolumeVenturi) > 0)
            tidal_volume_c.AutoZero = fabs(tidal_volume_c.InspVolumeVenturi) / fabs(tidal_volume_c.ExpVolumeVenturi);

        Serial.println("valore=" + String(tidal_volume_c.InspVolumeVenturi) + "," + String(tidal_volume_c.ExpVolumeVenturi) + "," + String(tidal_volume_c.AutoZero));

get calib_o2

hw_rev4:

 float o2_Temp = 0;
        o2_Temp=ADS_V[0];
        o2_Temp = o2_Temp* oxygen_q;
        oxygen_m = -(o2_Temp - 21);  
        
        Serial.println("valore=" + String(oxygen_m));

get ads

hw_rev4:

float V1, V2, V3, V4;
      uint8_t MUXBACKUP = LAST_MUX;


     /* V1 = (V1/V2)*2.5;
      V3 = (V1/V2)*2.5;
      V4 = (V1/V2)*2.5;
*/
      ADS_V[1] = ADS_V[1] < 1 ? 1:ADS_V[1];
      Serial.println("Oxygen: " + String(ADS_V[0]) + " REF: " + String(ADS_V[1]) + " 12v: " + String(ADS_V[2]/ADS_V[1]*2.5*5) + " 5v: " + String(ADS_V[3]/ADS_V[1]*2.5*2));

get stats

hw_rev3, hw_rev4:

if (__stat_param.mean_cnt > 0) {
            float overshoot_avg = __stat_param.overshoot_avg / __stat_param.mean_cnt;
            float overshoot_length_avg = __stat_param.overshoot_length_avg / __stat_param.mean_cnt;
            float final_error_avg = __stat_param.final_error_avg / __stat_param.mean_cnt;
            float t1050_avg = __stat_param.t1050_avg / __stat_param.mean_cnt;
            float t1090_avg = __stat_param.t1090_avg / __stat_param.mean_cnt;
            float tpeak_avg = __stat_param.tpeak_avg / __stat_param.mean_cnt;
            float t9010_avg = __stat_param.t9010_avg / __stat_param.mean_cnt;
            float t9050_avg = __stat_param.t9050_avg / __stat_param.mean_cnt;
            float peep_avg = __stat_param.peep_avg / __stat_param.mean_cnt;
            float t10_avg = __stat_param.t10_avg / __stat_param.mean_cnt;
            float time_to_peak_avg = __stat_param.time_to_peak_avg / __stat_param.mean_cnt;
            float flux_peak_avg = __stat_param.flux_peak_avg / __stat_param.mean_cnt;
            float flux_t1090_avg = __stat_param.flux_t1090_avg / __stat_param.mean_cnt;
            float flux_t9010_avg = __stat_param.flux_t9010_avg / __stat_param.mean_cnt;

            Serial.println("valore=overshoot_avg:" + String(overshoot_avg)
                + ",overshoot_length_avg:" + String(overshoot_length_avg)
                + ",final_error:" + String(final_error_avg)
                + ",t1050_avg:" + String(t1050_avg)
                + ",t1090_avg:" + String(t1090_avg)
                + ",tpeak_avg:" + String(tpeak_avg)
                + ",t9010_avg:" + String(t9010_avg)
                + ",t9050_avg:" + String(t9050_avg)
                + ",peep_avg:" + String(peep_avg)
                + ",t10_avg:" + String(t10_avg)
                + ",time_to_peak_avg:" + String(time_to_peak_avg)
                + ",flux_peak_avg:" + String(flux_peak_avg)
                + ",flux_t1090_avg:" + String(flux_t1090_avg)
                + ",flux_t9010_avg:" + String(flux_t9010_avg));
        }
        else {
            Serial.println("valore=no_data");
        }

console mode

__CONSOLE_MODE

get alarm

returns uint32_t ALARM_FLAG, see alarm bits in the next section: (hw_v3, hw_v4, V5)

alarms

"V5" ALARM_FLAG logic:

"get alarm" returns sys__s->ALARM_FLAG
sys_s->ALARM_FLAG bit ERROR_WDOG_PI cleared by command "watchdog_reset", also calls Alarms->ResetWatchdog()
AlarmClass::Tick() copies alarm->ALARM_FLAG_FILTERED to _sys_c->ALARM_FLAG
alarm->ALARM_FLAG_FILTERED is alarm->ALARM_FLAG without ALARM_FLAG_SNOOZE bits
alarm->ALARM_FLAG is set be TriggerAlarm()
alarm->ALARM_FLAG bit __ERROR_ALARM_PI is changed by SetAlarmGUI
AlarmClass::Tick() called by MVMCode.Tick() if alarm_enable.

"V5" logic of "alarm_enable" (MVMCore data member)

set to false: MVMCore::Init() - FIXME - cannot find who calls it
set to true: MVMCore::Tick() if last_alarm_CT > 5000
explanation: alarms are off startup, enabled after 5 seconds of running.

"V5" ALARM_FLAG_SNOOZE logic only in Alarms.cpp

in AlarmClass::Tick(): clear ALARM_FLAG_SNOOZE if ALARM_FLAG_SNOOZE_millis > 120000
in ResetAlarm(): ALARM_FLAG_SNOOZE = ALARM_FLAG, reset ALARM_FLAG_SNOOZE_millis
ResetAlarm() called by "set alarm_snooze !29"

Alarms bits are set from TriggerAlarm(enum t_ALARM):

List t_ALARM values:

PRESSURE_DROP_INHALE, // not used hw_v3, hw_v4, V5
UNABLE_TO_READ_SENSOR_PRESSURE, // loop() read_pressure_sensor(0,1) != 0
// "V5" HAL.cpp !drv_Ploop.asyncMeasure() || !drv_PPatient.asyncMeasure()
UNABLE_TO_READ_SENSOR_FLUX, // loop() MeasureFlux() != 0 || MeasureFlux_SFM3019() != 0
// "V5" HAL.cpp !drv_FlowIn.doMeasure()
UNABLE_TO_READ_SENSOR_VENTURI, // loop() read_pressure_sensor(2) != 0
// "V5" HAL.cpp !drv_PVenturi.asyncMeasure()
ALARM_COMPLETE_OCCLUSION, // CheckAlarmConditions() - commented out
// "V5" TransitionInhaleExhale_Event()
ALARM_PARTIAL_OCCLUSION, // CheckAlarmConditions() - commented out
// "V5" TransitionInhaleExhale_Event()
ALARM_PRESSURE_INSIDE_TOO_HIGH, // CheckAlarmCondition() pressure[0].last_pressure > 50
// "V5" CheckStaticAlarms() pPatient > 50 || pLoop > 65
ALARM_PRESSURE_INSIDE_TOO_LOW, // CheckAlarmCondition() pressure[0].last_pressure < 0.5 * core_config.target_pressure
// "V5" TransitionInhaleExhale_Event() pLoop < 0.5 current_pressure_setpoint
ALARM_LEAKAGE, // CheckAlarmConditions() pressure[1].last_pressure < 0.8 * core_config.target_pressure
// "V5" TransitionInhgaleExhale_Event() pPatient < 0.8 * current_pressure_setpoit
BATTERY_LOW, // CheckAlarmConditions() if currentBatteryCharge < 20
// "V5" (currentBatteryCharge < 20) && batteryPowered
ALARM_PRESSURE_INPUT_TOO_LOW, // not used
// "V5" HAL.cpp (InputValveValue > 0) && (Pin < MIN_PIN) (MIN_PIN=3000)
ALARM_PRESSURE_INPUT_TOO_HIGH, // not used
// "V5" HAL.cpp Pin > MAX_PIN (MAX_PIN=4500)
ALARM_GUI_ALARM, // command "set alarm"
// "V5" SetAlarmGUI(bool), cleared by "set alarm 0" and "alarm_snooze 29", set by "set alarm 1"
ALARM_GUI_WDOG, // loop() millis() > watchdog_time + 5000
// "V5" CheckStaticAlarms() wdog_timer > 6000
ALARM_OVER_UNDER_VOLTAGE, // "V5" HAL.cpp (adc_channel==2 VoltageProbe12V < 10 || > 15) || (adc_channel==3 VoltageProbe5V < 4.7 || > 5.3)
ALARM_SUPERVISOR, // "V5" HAL.cpp SuperVisorAlarms != 0
ALARM_OVERTEMPERATURE, // "V5" HAL.cpp BoardTemperature > 75
ALARM_APNEA, // "V5" state machine "dt / 1000.0 > core_config->backup_min_rate"
UNPREDICTABLE_CODE_EXECUTION // onTimerCoreTask()
// "V5" SMexecute()

Alarm bits:

#define __ERROR_INPUT_PRESSURE_LOW 0 // ALARM_PRESSURE_INPUT_TOO_LOW
#define __ERROR_INPUT_PRESSURE_HIGH 1 // ALARM_PRESSURE_INPUT_TOO_HIGH
#define __ERROR_INSIDE_PRESSURE_LOW 2 // ALARM_PRESSURE_INSIDE_TOO_LOW
#define __ERROR_INSIDE_PRESSURE_HIGH 3 // ALARM_PRESSURE_INSIDE_TOO_HIGH
#define __ERROR_BATTERY_LOW 4 // BATTERY_LOW
#define __ERROR_LEAKAGE 5 // PRESSURE_DROP_INHALE || ALARM_LEAKAGE
#define __ERROR_FULL_OCCLUSION 6 // ALARM_COMPLETE_OCCLUSION
#define __ERROR_PARTIAL_OCCLUSION 7 // ALARM_PARTIAL_OCCLUSION
#define __ERROR_APNEA 22 // "V5" ALARM_APNEA
#define __ERROR_ALARM_PI 29 // ALARM_GUI_ALARM
#define __ERROR_WDOG_PI 30 // ALARM_GUI_WDOG
#define __ERROR_SYSTEM_FALIURE 31 // UNABLE_TO_READ_SENSOR_PRESSURE || UNABLE_TO_READ_SENSOR_FLUX || UNABLE_TO_READ_SENSOR_VENTURI || UNPREDICTABLE_CODE_EXECUTION

bit ERROR_ALARM_PI:

  • set by ALARM_GUI_ALARM from command "set alarm"
  • cleared by ResetAlarm()
  • cleared by command "set wdenable"

bit ERROR_WDOG_PI:

  • hw_v3, hw_v4: set by ALARM_GUI_WDOG from loop() if millis() > watchdog_time + 5000
  • "V5": set by ALARM_GUI_WDOG from CheckStaticAlarms() if wdog_enable && wdog_timer > 6000
  • "V5": wdog_enable=false in Init(), =true in EnableWatchDog() called command "wdenable", 0=disable watchdog, !0=enable watchdog
  • cleared by command "set watchdog_reset"
  • watchdog_time is set in setup() and by command "set watchdog_reset"

alarm actions:

* if ALARM_FLAG contains any alarms:
* AlarmSound=true and Sound() drives the buzzer.
* isInAlarm=true and AlarmActions():
* blinks the alarm LED with period 250 ms, via HAL->SetAlarmLed()
* calls HAL->SetAlarmRele(true), this drives GPIO_RELEALLARM (hw_rev4 A12)

watchdog

esp32 firmware implements a watchdog timer to check that the GUI is running.

  • the watchdog is off at startup
  • command "set wdenable 1" enables the watchdog and starts the timer
  • command "set watchdog_reset" resets the watchdog timer
  • if timer is not reset within 5 sec (hw_rev3, hw_rev4) or 6 sec ("V5"), ALARM_WDOG_PI is fired and bit ERROR_WDOG_PI is set in ALARM_FLAG
  • if ALARM_FLAG is non-zero, alarm actions are initialed (see previous section)

Display Unit

SD image MvM_raspPi4_16gb_new.7z (14-Apr-2020)

-rw-r--r--@ 1 8ss  staff   5161636360 Apr 16 09:52 MvM_raspPi4_16gb_new.7z
-rwxr-xr-x  1 8ss  staff  15931539456 Apr 14 06:02 MvM_raspPi4_16gb_new.img

8s-macbook-pro:NOBACKUP 8ss$ shasum -a 256 MvM_raspPi4_16gb_new.*
91987742cbf2c810afa74a47455a21de5b75b85c4fa134c10f48a30afae64194  MvM_raspPi4_16gb_new.7z
bc5ed0efa279b027da4965132c91f494363ea98a297a649a062f346760d5de2e  MvM_raspPi4_16gb_new.img

On power up:

  • boots Raspbian 10 Buster
  • starts X11 (in the alt-shift-f7 console)
  • starts python3 gui (lightdm -> lxsession -> /home/pi/gui.sh -> /home/pi/work/MVMSoftware/gui/gui/mvm_gui.py)
  • if no ESP32 connected, displays an "abort, retry, ignore" dialog
  • if ESP32 connected, displays nothing (hung? wrong ESP32 firmware?)
  • starts TeamViewer (from systemd)
  • user pi is logged in the alt-shift-f1 console (no password) (via systemctl autologin agetty?)

Autologin:

  • lightdm: /etc/lightdm/lightdm.conf has this "autologin-user=pi"
  • lxsession: /home/pi/.config/lxsession/LXDE-pi/autostart has this: "@/home/pi/gui.sh"
  • /etc/systemd/system/autologin@.service has this: "ExecStart=-/sbin/agetty --autologin pi --noclear %I $TERM"

Partitions mounted:

  • / (mmcblk p7)
  • /boot (mmcblk p6)

Running processes:

  • ModemManager (this is a mistake, it will fight over access to /dev/USB0)
  • TeamViewer (is this secure, what's the access password?)
  • no sshd
  • no NetworkManager

Users:

  • root, password blank (login disabled)
  • pi, password raspberry (default password)

Network:

  • eth0 RJ45 - DHCP is enabled, gets an IP address
  • wlan0 (radio is on, per rfkill) - configured to connect to 3 wifi networks (2 of them maybe disabled)
  • bluetooth (radio is on, per rfkill) - looks to be on, not clear if paired with anything

SD Partition table:

Disk /dev/sda: 29.3 GiB, 31486640128 bytes, 61497344 sectors
Disk model: STORAGE DEVICE  
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x000d0750

Device     Boot   Start      End  Sectors  Size Id Type
/dev/sda1          8192  5015625  5007434  2.4G  c W95 FAT32 (LBA)
/dev/sda2       5015626 31116287 26100662 12.5G  5 Extended
/dev/sda5       5021696  5087229    65534   32M 83 Linux
/dev/sda6       5087232  5611517   524286  256M  c W95 FAT32 (LBA)
/dev/sda7       5611520 31116287 25504768 12.2G 83 Linux
root@raspberrypi:/mnt/tmp/home/pi# 

SD partition contents:

  • sda1: looks like RPi recovery partition (what is this?)
  • sda2: extended partition (why?)
  • sda5: (what is this?)
  • sda6: boot partition, cmdline.txt has "console=serial0,115200 console=tty1 root=/dev/mmcblk0p7 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles"
  • sda7: "/" including /home/pi.

End