Skip to content

Udith-creates/Phoenix-QNX-

Repository files navigation

πŸ«€ Real-Time Vital Sign Monitor

QNX Neutrino RTOS 8.0 Β· Raspberry Pi 4B Β· Arduino Uno R4 WiFi

A deterministic, hard real-time medical monitoring system that streams live ECG + EMG biosignals, triggers priority-scheduled alarms within 100 ms, and logs clinical data β€” all running on a microkernel RTOS.


πŸ“Έ Demo

================================================
         VITAL SIGN MONITOR (QNX 8.0)
================================================

ECG |β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘| 487 BPM
EMG |══════════════░░░░░░░░░░░░░░░░░░░░░░░░░| 342 mV

------------------------------------------------
 STATUS: βœ… SYSTEM NOMINAL
------------------------------------------------

🧠 What Makes This Interesting

This isn't a hobbyist Arduino sketch β€” it's a full RTOS architecture project. The Raspberry Pi runs QNX Neutrino, a hard real-time microkernel OS used in medical devices, cars (BlackBerry QNX), and aerospace. The Arduino is just the ADC front-end.

Key RTOS concepts demonstrated:

  • Preemptive priority scheduling (SCHED_FIFO) with 4 threads at distinct priorities
  • Microkernel fault isolation β€” a USB driver crash cannot affect the alarm engine
  • procmgr_ability() β€” non-root process acquiring real-time scheduling rights
  • Deterministic alarm response β€” worst-case latency bounded to 100 ms by the scheduler
  • Priority inheritance via QNX IPC to prevent priority inversion

πŸ—‚ Repository Structure

.
β”œβ”€β”€ arduino/
β”‚   └── vital_signs.ino          # EMG + ECG acquisition firmware (500 Hz)
β”œβ”€β”€ qnx/
β”‚   └── medical_monitor.cpp      # Multi-threaded QNX C++ application
β”œβ”€β”€ scripts/
β”‚   └── setup_arduino.sh         # QNX target: USB driver + serial setup
└── README.md

πŸ”§ Hardware

Component Model Role
SBC Raspberry Pi 4B (8 GB) Runs QNX Neutrino 8.0
MCU Arduino Uno R4 WiFi 14-bit ADC; EMG filter + ECG sampling
EMG sensor Muscle BioAmp Shield Surface electrode + instrumentation amp
ECG sensor BioAmp EXG Pill 3-lead wet electrode (RA, LA, RL)
Link USB Serial (CDC-ACM) 115,200 baud β†’ /dev/serusb1 on QNX

Why Arduino R4? The classic Uno has a 10-bit ADC. The R4 gives 14-bit (16,384 levels), significantly reducing quantisation noise in the EMG signal.


πŸ— System Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              QNX Neutrino RTOS 8.0               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”             β”‚
β”‚  β”‚ devc-serusb  β”‚  β”‚  med_monitor β”‚             β”‚
β”‚  β”‚  (USB driver β”‚  β”‚   process    β”‚             β”‚
β”‚  β”‚  resource    β”‚  β”‚              β”‚             β”‚
β”‚  β”‚  manager)    β”‚  β”‚  Pri 20 ───► Alarm thread  β”‚
β”‚  β”‚  /dev/serusb1β”‚  β”‚  Pri 15 ───► Data thread   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  Pri 10 ───► UI thread     β”‚
β”‚         β”‚ read()   β”‚  Pri  5 ───► Log thread    β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚          QNX Microkernel                β”‚    β”‚
β”‚  β”‚  Scheduling Β· IPC Β· Memory Β· Timers     β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚ USB CDC-ACM 115200 baud
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚    Arduino Uno R4 WiFi  β”‚
         β”‚  EMG: A0 β†’ filter β†’     β”‚
         β”‚        envelope β†’ CSV   β”‚
         β”‚  ECG: A2 β†’ raw β†’ CSV    β”‚
         β”‚  Output: "342,487\n"    β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

⚑ Thread Priority Model

Thread Priority Policy Responsibility
Alarm engine 20 SCHED_FIFO ECG spike detection β†’ alarm in ≀100 ms
Data acquisition 15 SCHED_FIFO Drain USB buffer; parse CSV into globals
UI / HMI 10 SCHED_RR Terminal dashboard at 20 fps
Logger 5 SCHED_RR Background CSV β†’ /tmp/patient_summary.csv

SCHED_FIFO = no time-slicing; thread runs until it blocks or is preempted by higher priority. Used for the two critical threads so timer jitter never delays them.


πŸ”¬ Arduino Firmware

Sampling loop β€” non-blocking timer accumulator

// Accumulator pattern: carries forward overshoot deficit
// maintains exact 500 Hz long-term even with loop jitter
timer += 1000000 / SAMPLE_RATE;  // reload 2000 Β΅s

EMG signal processing pipeline

analogRead(A0)
    └─► EMGFilter()       4-stage IIR biquad bandpass (20–450 Hz)
            └─► abs()     full-wave rectification
                    └─► getEnvelop()   128-sample running average
                                └─► Serial.print()   CSV output

Bandpass filter β€” 4 cascaded biquad stages

Stage Type Purpose
1 Lowpass Attenuate noise above ~450 Hz
2 Highpass Remove DC + motion artifact < 20 Hz
3 Highpass Steepen roll-off skirt
4 Bandstop Notch 50/60 Hz power line interference

Each stage is Direct Form II Transposed β€” most numerically stable IIR form for single-precision float. State vars are static, preserving filter memory between loop() calls.

Serial output format

EMG_ENVELOPE,ECG_RAW\n

342,487     ← nominal
0,512       ← no muscle activity  
891,803     ← ECG > 800 β†’ QNX alarm fires

πŸ–₯ QNX Application

Building (on host β€” Windows/Linux with QNX SDP 8.0)

# Cross-compile for AArch64 QNX target
qcc -Vgcc_ntoaarch64le_cxx -o med_monitor medical_monitor.cpp

Deploying to RPi4

# Copy binary
scp -c aes128-ctr -o MACs=hmac-sha2-256 med_monitor qnxuser@<TARGET_IP>:/tmp/

# SSH to target
ssh -c aes128-ctr -o MACs=hmac-sha2-256 qnxuser@<TARGET_IP>

Running on target

# 1. Run Arduino setup script first
chmod +x setup_arduino.sh && sudo ./setup_arduino.sh

# 2. Launch monitor
sudo ./med_monitor

Alarm acknowledgment

Key Action
A Acknowledge alarm β€” silences buzzer, keeps visual warning until ECG normalises
Ctrl+C Exit monitor (restores terminal settings)

πŸ“‘ QNX Driver Setup (manual)

# Find Arduino USB IDs
usb -vv

# Kill any existing driver
sudo slay devc-serusb devc-sercdc

# Start USB serial resource manager
sudo /usr/sbin/devc-serusb -v -d vid=0x2341,did=0x1002 &

# Verify device node
ls -l /dev/ser*          # expect /dev/serusb1

# Configure baud rate
stty baud=115200 < /dev/serusb1

# Run
sudo ./med_monitor

πŸ”¬ QNX Concepts Used

Microkernel architecture

QNX's kernel is < 80 KB and handles only scheduling, IPC, timers, and memory. Every driver (including devc-serusb) runs as a user-space resource manager process. If the USB driver crashes, the kernel detects it β€” the alarm and UI threads keep running unaffected.

procmgr_ability() β€” capability-based privileges
procmgr_ability(0,
    PROCMGR_ADN_ROOT | PROCMGR_AOP_ALLOW | PROCMGR_AID_PRIORITY,
    PROCMGR_AID_EOL);

Grants this process the ability to set SCHED_FIFO priorities above the default ceiling β€” without running as root. Principle of least privilege.

Priority inversion prevention

QNX's microkernel IPC automatically applies priority inheritance: when a high-priority thread blocks on MsgSend() to a lower-priority server, the server inherits the caller's priority. This is guaranteed by the kernel β€” the fix that would have prevented the 1997 Mars Pathfinder reset.

volatile shared globals
volatile int g_ecg = 0;
volatile int g_emg = 0;

volatile prevents the compiler from caching these in registers across loop iterations β€” without it, an optimising compiler might never see cross-thread updates. Note: volatile is not atomic; production systems should use pthread_mutex_t or QNX's atomic_* functions.

Non-canonical terminal input
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);

Switches terminal to raw mode so 'A' is detected without pressing Enter. check_ack() uses select() with zero timeout β€” non-blocking poll that doesn't stall the 20 fps render loop.


πŸ“Š Alarm State Machine

              ECG > 800
NOMINAL ─────────────────► CRITICAL (red, beeping)
   β–²                              β”‚
   β”‚ ECG ≀ 800                    β”‚ press 'A'
   β”‚ (auto-reset)                 β–Ό
   └──────────────── ACKNOWLEDGED (yellow, silent)

πŸ“ Output Files

File Content
/tmp/patient_summary.csv timestamp, EMG, ECG, status β€” logged every 5 s by background thread

πŸ›  Requirements

Host (development)

  • QNX SDP 8.0 (includes qcc cross-compiler)
  • VS Code + QNX SDP Extension (optional, for profiling)
  • Windows 11 or Linux

Target

  • Raspberry Pi 4B with QNX Neutrino 8.0 Quick Start Image
  • Network access (for SCP/SSH deployment)

Hardware

  • Arduino Uno R4 WiFi
  • Muscle BioAmp Shield (EMG)
  • BioAmp EXG Pill (ECG)
  • Electrodes + leads

πŸ“„ License

MIT β€” see LICENSE

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors