MVM-IT: Difference between revisions

From DaqWiki
Jump to navigation Jump to search
Line 469: Line 469:


* install arduino: https://daq.triumf.ca/DaqWiki/index.php/ESP32#Arduino_IDE
* install arduino: https://daq.triumf.ca/DaqWiki/index.php/ESP32#Arduino_IDE
* install arduino library SimpleCLI (1.0.9)


== Build and Flash ==
== Build and Flash ==

Revision as of 08:01, 4 May 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
  • git clone git@github.com:dd1dd1/MVMFirmwareCpp.git ### Konstantin's github via ssh
  • git clone https://github.com/dd1dd1/MVMFirmwareCpp.git ### Konstantin's github via https
  • cd MVMFirmwareCpp
  • git checkout ko1 ### get the "V5" firmware
  • git checkout v0.1_ko2 ### git the "v0.1" firmware
  • ~/arduino-1.8.12/arduino MVMFirmwareCore.ino
  • install library SimpleCLI (1.0.9)
  • build sketch
  • firmware tag "V5" and "v0.1" will fail with error about missing arduino.h. Please use tag "V5_ko1a" or "v0.1_ko2a" instead from Konstantin's github.
  • 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 | source |
|---|---|---|---|---|
| "pinput" | hw_v4 | Pinput | ? | |
| "pressure" | all | pressure[0].last-pressure; V5: Ploop | ? | PS_0 |
| "ppressure" | V5 | PPatient | ? | PS_1 |
| "flow" | all | gasflux[0].last_flux; V5: FlowIn | ? | FLOW1 |
| "o2" | hw_rev3 | last_O2=21.7 | %pct | hardwired 21.7 |
| "o2" | hw_rev4, V5 | last_O2 | ? | i2c adc_channel 0 |
| "bpm" | all | last_bpm | ? | |
| "backup" | all | 0, not implemented | - |  |
| "tidal" | all | tidal_volume_c.TidalVolume*VOL_COMP; V5: TidalVolume | ? | |
| "peep" | all | last_peep | ? | averaged_PPatient |
| "temperature" | all | temperature; V5: GasTemperature | ? | FLOW1 temperature |
| "power_mode" | all | "1" if batteryPowered, else "0" |  bool | ReadSupervisor(0x52) |
| "battery" | all | currentBatteryCharge | ? | ReadSupervisor(0x51) |
| "version" | all | _FIRMWARE_VERSION_ | string | |
| "version" | hw_rev3 | "HW_V3_2020_04_15_00" | string | |
| "version" | hw_rev4 | "HW_V4_2020_04_20_00" | string | |
| "version" | V5 | "NI_MVM-HW_V3_2020_04_17_00" | string | FwVersion.h |
| "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 core_config.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 | ? |  FLOW1, PS_2 |
| "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 is controlled by __CONSOLE_MODE, enabled by command "set console 1", disabled by common "set console 0"

When console mode is enabled, the watchdog function is turned off.

When console mode is enabled, command are processed normally, but periodiclally the following information is sent on the serial link:

hw_rev3:

String ts = __ADDTimeStamp ? String(millis()) + "," : "";
        DBG_print(1, ts + String(gasflux[0].last_flux) + "," + String(pressure[0].last_pressure) + "," + String(pressure[1].last_pressure) + "," + String(PIDMonitor * 100 / 4096) + "," + String(PIDMonitor2) + "," + String(valve2_status) + "," + String(VenturiFlux) + "," + String(tidal_volume_c.FLUX) + "," + String(tidal_volume_c.TidalVolume * 0.02) + "," + String(dgb_delta));

hw_rev4:

String ts = __ADDTimeStamp ? String(millis()) + "," : "";
        DBG_print(1, ts + String(gasflux[0].last_flux) + "," + String(pressure[0].last_pressure) + "," + String(pressure[1].last_pressure) + "," + String(PIDMonitor * 100 / 4096) + "," + String(PIDMonitor2) + "," + String(valve2_status) + "," + String(VenturiFlux) + "," + String(tidal_volume_c.FLUX) + "," + String(tidal_volume_c.TidalVolume * 0.02) + "," + String(dgb_delta));

V5:

	float pid_slow, pid_fast;
	float InputValveSetPoint;
	float OutputValveSetPoint;
	MVM_HAL.GetInputValvePID(&pid_slow, &pid_fast);
	InputValveSetPoint = MVM_HAL.GetInputValve();
	OutputValveSetPoint = MVM_HAL.GetOutputValve() * 100;

	String ts = CMC.core_config.__ADDTimeStamp ? String((uint32_t)MVM_HAL.GetMillis()) + "," : "";
	/*
	MVM_HAL.WriteUART0(ts+ 
		String(sys_s.FlowIn) + "," +
		String(sys_s.pLoop) + "," +
		String(sys_s.pPatient) + "," +
		String(pid_fast) + "," +
		String(pid_slow) + "," +
		String(OutputValveSetPoint) + "," +
		String(sys_s.VenturiFlux) + "," +
		String(sys_s.Flux) + "," +
		String(sys_s.TidalVolume) + "," +
		String(sys_s.PPatient_delta2 * 10)
		);
	*/

	MVM_HAL.WriteUART0(
		String(sys_s.pLoop) + "," +
		String(sys_s.pPatient) + "," +
		String(pid_fast) + "," +
		String(pid_slow) 
	);

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