final

Dependencies:   TextLCD mbed-rtos mbed

Fork of 541-pacemaker by Terry Fang

main.cpp

Committer:
ems316
Date:
2016-12-12
Revision:
5:b506b5bdeee7
Parent:
4:a9c37c60425c

File content as of revision 5:b506b5bdeee7:

/* @Author: Grayson Honan, Phil Perilstein, Terry Fang, Eric Stahl
   @Course: CIS 541
   @Due Date: 12 Dec 2016
   @Assignment: Pacemaker Project - Pacemaker Component

   @Description: This code is representative of the functionality
   of a DDD pacemaker. The code was generated from an UPAAL model
   that describes the basic timing functionality of a pacemaker and
   the operation of system peripherals (lcd screen, alarm, keyboard).

   Main timing threads:

   PaceSense - handles triggering pace output events via timeouts

   PaceSignal - filters and accepts heart signal input events by
   synchronizing with interupt ins.

   Main Peripheral Threads:

   displayThread - displays current bpm and then resets the bpm
   and determines if abnormal bpm

   alarmThread - synchronizes with displayThread if abnormal bpm
   and displays warning message to lcd screen

   ledThread - synchronizes with PaceSense or PaceSignal on a Vsense,
   Asense, Vpace, Apace

   Rx_interrupt - interrupt that accepts keyboard input and filters
   for valid keyboard input. Synchrnoizes with the thread that the
   keyboard input is destined for via a Queue.

   Pace Mode Threads:

   Main - initializes to a Normal mode Pacemaker with an observation
   interval of 10 seconds. Synchronizes with the Rx_interrupt when
   valid keyboard input is determined. The main thread acts as the
   Choose_PaceMode automata.

   manualmode - disables pace events from the pacemaker & allows for
   user input to manually pace an atrial or ventrical event.

   Reference: https://developer.mbed.org/handbook/CMSIS-RTOS

   Run Instructions: Whenever I ran the program, I found I needed to first
   run screen to open up the fd pointing to the usb. I would then kill the
   screen process and run the ./start_game script after uncommenting the
   port selection in the script.

*/

#include "mbed.h"
#include "rtos.h"
#include "TextLCD.h"
#include <stdio.h>

// Input/Output declarations

InterruptIn vsignal(p7);
InterruptIn asignal(p8);
DigitalOut Vpace(p5);
DigitalOut Apace(p6);

DigitalOut asense_led(LED1);
DigitalOut vsense_led(LED2);
DigitalOut apace_led(LED3);
DigitalOut vpace_led(LED4);

// thread definitions
osThreadId signalTid;
osThreadId senseTid;
osThreadId displayTid;
osThreadId pacemodeTid;
osThreadId alarmTid;
osThreadId ledTid;

//peripheral API declarations
TextLCD lcd(p15, p16, p17, p18, p19, p20, TextLCD::LCD16x2);
RawSerial pc(USBTX, USBRX);

//timing declarations
Timer vClock;
Timer aClock;
Timer arpClock;

//normal mode timing constants
double LRI = 1000;
double URI = 700;
double VRP = 200;
double ARP = 50;
double AVI = 150;
double PVARP = 300;
double ratio;
int wait_period = 10;

double observation_interval = 10000; // In miliseconds
int upperBound; //for mode changes
int lowerBound; //for mode changes
double heart_beats = 0; // Heart-Beats (sensed or paced) since the last observation interval
Mutex hr_mutex; //hr_mutex.lock()/unlock()

//message passing queues
Queue<char,256> mode_q;
Queue<char,256> signal_q;
Queue<char,256> obsint_q;

//keyboard interrupt variables
volatile char c;
volatile int mm = 0;
volatile int om = 0;

//flag to disable automatic pacing
int mm_flag = 0;

//init the ventrical timing values
void initialize_intervals()
{
    LRI = 1000;
    URI = 700;
}

//interrupt for keyboard input. Filters valid input.
void Rx_interrupt()
{
    while(pc.readable()) {
        c = pc.getc();

        //synchronize mode switch thread for manual mode
        if(c == 'm' && om != 1) {
            mode_q.put((char*)c);
            mm = 1;
            //synchronize mode switch thread for normal, excercise or sleep mode
        } else if(c == 'n' || c == 'e' || c == 's' && om != 1) {
            mode_q.put((char*)c);
            mm = 0;
            //if manual mode, synchronize with manual mode thread for a or v signal
        } else if((c == 'a' || c == 'v') && mm) {
            signal_q.put((char*)c);
            //start accepting input to update the observation interval. Disable mode switching.
        } else if(c == 'o' && om != 1) {
            mode_q.put((char*)c);
            om = 1;
            //end receiving input to update observation interval. Enable mode switching.
        } else if (c == '\r' && om) {
            obsint_q.put((char*)c);
            om = 0;
            //updating observation interval, filter valid input.
        } else if ((int)c > 47 && (int)c < 58 && om) {
            obsint_q.put((char*)c);
        }
    }
}

// Function to toggle the LEDs 1,2,3,4
void ledThread(void const *args)

{
    while (1) {
        //wait for synchronization for Apace, Vpace, Asense, Vsense
        osEvent ext_signal = osSignalWait(0, osWaitForever);
        int evt = ext_signal.value.signals;

        if (evt == 0xA) { //asense
            asense_led = 1;
            Thread::wait(wait_period);
            asense_led = 0;
        } else if (evt == 0xB) { //vsense
            vsense_led = 1;
            Thread::wait(wait_period);
            vsense_led = 0;
        } else if (evt == 0xC) { //apace
            apace_led = 1;
            Thread::wait(wait_period);
            apace_led = 0;
        } else if (evt == 0xD) { //vpace
            vpace_led = 1;
            Thread::wait(wait_period);
            vpace_led = 0;
        }
    }
}

//Synchronized with displayThread if alarmThread should alarm
void alarmThread(void const *args)
{
    while (1) {
        osEvent ext_signal = osSignalWait(0, osWaitForever);
        int evt = ext_signal.value.signals;

        if (evt == 0xb) { //upper bound violated
            lcd.printf("%s", "\nALARM HIGH");
        } else if (evt == 0xc) { //lower bound violated
            lcd.printf("%s", "\nALARM LOW");
        }
    }
}
//Thread for displaying bpm & alerts
void displayThread(void const *args)
{
    while (1) {
        Thread::wait(observation_interval);
        lcd.cls();

        hr_mutex.lock(); //acquire lock because changing bpm
        double hr = (heart_beats*60) / (observation_interval / 1000); //calculate bpm
        heart_beats = 0; //reset bpm
        hr_mutex.unlock();

        lcd.printf("%s%d%s","HR: ", (int)hr, " bpm"); //display bpm

        if (hr > upperBound) { //synchronize if upperBound violated
            osSignalSet(alarmTid, 0xb);
        } else if (hr < lowerBound) { //synchronize if lowerBound violated
            osSignalSet(alarmTid, 0xc);
        }
    }
}



// Incoming atrial signal from the heart
void asignal_irq()
{
    osSignalSet(signalTid, 0x1); //synchronize with pacesignal thread
}

// Incoming ventrical signal from the heart
void vsignal_irq()
{
    osSignalSet(signalTid, 0x2); //synchronize with pacesignal thread
}

//Main timing thread for filtering Asignal and Vsignal from heart and
//generating Asense and Vsense to synchronize with the PaceSense thread.
void PaceSignal(void const *args)
{

    int pFlag1 = 0; //flag that allows us to move to the second UPPAAL state in PaceSignal
    int pFlag2 = 0; //flag that allows us to move to the first UPPAAL state in PaceSignal

    vClock.start();
    aClock.start();
    arpClock.start();

    while(1) {
        while (!pFlag1) {
            osEvent ext_signal = osSignalWait(0, osWaitForever);
            int evt = ext_signal.value.signals;

            if (evt == 0x1 && vClock.read_ms() >= PVARP) { //aSense
                osSignalSet(senseTid, 0x1);
                aClock.reset();
                arpClock.reset();
                pFlag1 = 1;
            } else if(evt == 0x2 && vClock.read_ms() >= VRP) { //vSense
                hr_mutex.lock();
                osSignalSet(senseTid, 0x2); //syncrhonize with PaceSense thread
                heart_beats++; //increment bpm
                vClock.reset();
                aClock.reset();
                arpClock.reset();
                hr_mutex.unlock();
                pFlag1 = 1; //progress to state 2

            } else if (evt == 0x3) { //aPace
                pFlag1 = 1; //progress to state 2
            }
        }
        pFlag1 = 0;
        while(!pFlag2) {

            osEvent ext_signal = osSignalWait(0, osWaitForever);
            int evt = ext_signal.value.signals;

            if (evt == 0x1 && arpClock.read_ms() >= ARP) { //aSense
                osSignalSet(senseTid, 0x1);
                arpClock.reset(); //determine valid consecutive a event

            } else if(evt == 0x2) { //vSense
                hr_mutex.lock();
                osSignalSet(senseTid, 0x2);
                heart_beats++; //increment bpm
                vClock.reset();
                aClock.reset();
                arpClock.reset();
                hr_mutex.unlock();
                pFlag2 = 1; //progress to state 1
            } else if (evt == 0x4) { //vPace
                pFlag2 = 1; //progress to state 1
            }
        }
        pFlag2 = 0;
    }
}

void PaceSense(void const *args)
{
    int pFlag1 = 0; //flag that allows us to move to the second UPPAAL state in PaceSense
    int pFlag2 = 0; //flag that allows us to move to the first UPPAAL state in PaceSense
    int time_sub = 0; //used to determine timeout for when to pace (this is our invariant)
    int evt = 0;
    while(1) {
        while (!pFlag1) {

            time_sub = LRI-AVI - vClock.read_ms(); //aPace at LRI-AVI default

            if (time_sub > 0  && !mm_flag) {
                osEvent ext_signal = osSignalWait(0, time_sub); //allow pacing
                evt = ext_signal.value.signals;
            } else if(mm_flag) {
                osEvent ext_signal = osSignalWait(0, osWaitForever); //disable pacing
                evt = ext_signal.value.signals;
            } else {
                evt = 0x0; //time_sub is less than 0
            }

            if (evt == 0x0) { //aPace
                aClock.reset();
                arpClock.reset();
                Apace = 1;
                Thread::wait(1);
                Apace = 0;
                osSignalSet(signalTid, 0x3);
                osSignalSet(ledTid, 0xC);
                pFlag1 = 1;
            } else if (evt == 0x1) { //aSense
                osSignalSet(ledTid, 0xA);
                pFlag1 = 1;
            } else if(evt == 0x2) { //vSense
                osSignalSet(ledTid, 0xB);
            } else if(evt == 0x3) { //manual apace
                pFlag1 = 1;
            }
        }
        pFlag1 = 0;

        while(!pFlag2) {
            //vpace occurs at either URI or vclock + AVI
            time_sub = (vClock.read_ms() + AVI >= URI) ? AVI - aClock.read_ms() : URI - vClock.read_ms();

            if (time_sub > 0 && !mm_flag) { //allow pacing
                osEvent ext_signal = osSignalWait(0, time_sub);
                evt = ext_signal.value.signals;
            } else if(mm_flag) { //disable pacing
                osEvent ext_signal = osSignalWait(0, osWaitForever);
                evt = ext_signal.value.signals;
            } else {
                evt = 0x0; //time_sub is negative
            }

            if (evt == 0x0) { //vPace

                hr_mutex.lock();
                heart_beats++;
                vClock.reset();
                aClock.reset();
                arpClock.reset();
                Vpace = 1;
                Thread::wait(1);
                Vpace = 0;
                osSignalSet(signalTid, 0x4);
                hr_mutex.unlock();

                osSignalSet(ledTid, 0xD);
                pFlag2 = 1;

            } else if (evt == 0x1) { //aSense
                osSignalSet(ledTid, 0xA);
            } else if(evt == 0x2) { //vSense
                osSignalSet(ledTid, 0xB);
                pFlag2 = 1;
            } else if (evt == 0x4) { //manual vpace
                pFlag2 = 1;
            }
        }
        pFlag2 = 0;
    }
}
//update timing constraints for mode and reset bpm
void normalmode(void const *args)
{
    initialize_intervals();
    upperBound = 100; //beats per msecond
    lowerBound = 40; //beats per msecond
    //hr_mutex.lock();
    //reset bpm
    heart_beats = 0;
    hr_mutex.unlock();

    vClock.reset();
    aClock.reset();
}
//update timing constraints for mode and reset bpm
void exercisemode(void const *args)
{
    initialize_intervals();
    upperBound = 175; //beats per msecond
    lowerBound = 100; //beats per msecond
    ratio = (175.00/100.00 + 100.00/40.00) / 2.00;
    LRI /= ratio;
    URI /= ratio;
    //reset bpm
    hr_mutex.lock();
    heart_beats = 0;
    hr_mutex.unlock();

    vClock.reset();
    aClock.reset();
}
//update timing constraints for mode and reset bpm
void sleepmode(void const *args)
{
    initialize_intervals();
    upperBound = 60; //beats per msecond
    lowerBound = 30; //beats per msecond v-v 0.5s
    ratio = (60.00/100.00 + 30.00/40.00) / 2.00;
    LRI /= ratio;
    URI /= ratio;
    hr_mutex.lock();
    //reset bpm
    heart_beats = 0;
    hr_mutex.unlock();

    vClock.reset();
    aClock.reset();
}
//handle manual pacing events
void m_vpace()
{
    vClock.reset();
    aClock.reset();
    arpClock.reset();
    Vpace = 1;
    Thread::wait(1);
    Vpace = 0;
    osSignalSet(signalTid, 0x4);
    osSignalSet(senseTid, 0x4);
    hr_mutex.lock();
    heart_beats++;
    hr_mutex.unlock();

    osSignalSet(ledTid, 0xD);
}
//handle manual pacing events
void m_apace()
{
    aClock.reset();
    arpClock.reset();
    Apace = 1;
    Thread::wait(1);
    Apace = 0;
    osSignalSet(senseTid, 0x3);
    osSignalSet(signalTid, 0x3);
    osSignalSet(ledTid, 0xC);
}
//update timing constraints for mode and handle manual pace events
void manualmode(void const *args)
{
    upperBound = 175; //beats per msecond
    lowerBound = 30; //beats per msecond
    LRI = 2125; // max V-V (LRI) based on exercise mode
    URI = 675; // min V-V (URI) based on sleep mode

    while(1) {
        osEvent evt = signal_q.get();
        if(evt.status == osEventMessage) {
            if((char)evt.value.p == 'v') {
                m_vpace();
            } else if((char)evt.value.p == 'a') {
                m_apace();
            }
        }
    }
}
//manage user input for updating the observation interval
void obsinterval()
{
    char newObsInt[8];
    int isChangingObsInt = 1;
    int i = 0;
    char key = 'n';
    while(isChangingObsInt) {
        osEvent evt = obsint_q.get();
        if(evt.status == osEventMessage) {
            key = (char)evt.value.p;
            if(key != '\r' && i < 7 ) { //bound number of integers for overflow
                newObsInt[i] = key;
                i++;
            } else if((key == '\r') && (i > 0)) { //conver input to observatio interval
                heart_beats = 0;
                int obsint;
                newObsInt[i] = '\0';
                sscanf(newObsInt, "%d", &obsint);

                //check bounds
                if(obsint < 300) {
                    observation_interval = 300.0;
                } else if (obsint > 10000) {
                    observation_interval = 10000.0;
                } else {
                    observation_interval = (double)obsint;
                }
                isChangingObsInt = 0;

            }
        }
    }
}


//create thread definitions
osThreadDef(PaceSignal, osPriorityNormal, DEFAULT_STACK_SIZE);
osThreadDef(PaceSense, osPriorityNormal, DEFAULT_STACK_SIZE);
osThreadDef(alarmThread, osPriorityBelowNormal, DEFAULT_STACK_SIZE);
osThreadDef(ledThread, osPriorityBelowNormal, DEFAULT_STACK_SIZE);

osThreadDef(displayThread, osPriorityLow, DEFAULT_STACK_SIZE);
osThreadDef(manualmode, osPriorityNormal, DEFAULT_STACK_SIZE);

int main()
{
    //create thread ids
    alarmTid = osThreadCreate(osThread(alarmThread), NULL);
    senseTid = osThreadCreate(osThread(PaceSense), NULL);
    signalTid = osThreadCreate(osThread(PaceSignal), NULL);
    displayTid = osThreadCreate(osThread(displayThread), NULL);
    ledTid = osThreadCreate(osThread(ledThread), NULL);

    //start pacemaker in normal mode
    normalmode(NULL);

    //set interrupt ins on signaling inputs
    vsignal.rise(&vsignal_irq);
    asignal.rise(&asignal_irq);

    //clear lcd
    lcd.cls();

    //set interrupt in for serial input
    pc.attach(&Rx_interrupt, RawSerial::RxIrq);
    while(true) { //handle mode switching synchronization
        osEvent evt = mode_q.get();
        if(evt.status == osEventMessage) {
            switch((char)evt.value.p) {
                case('n'): //normal mode
                    mm_flag = 0;
                    osSignalSet(senseTid, 0x5);
                    osThreadTerminate (pacemodeTid);
                    osThreadTerminate (displayTid);
                    normalmode(NULL);
                    displayTid = osThreadCreate(osThread(displayThread), NULL);
                    break;
                case('s'): //sleep mode
                    mm_flag = 0;
                    osSignalSet(senseTid, 0x5);
                    osThreadTerminate (pacemodeTid);
                    osThreadTerminate (displayTid);
                    sleepmode(NULL);
                    displayTid = osThreadCreate(osThread(displayThread), NULL);
                    break;
                case('e'): //excercise mode
                    mm_flag = 0;
                    osSignalSet(senseTid, 0x5);
                    osThreadTerminate (pacemodeTid);
                    osThreadTerminate (displayTid);
                    exercisemode(NULL);
                    displayTid = osThreadCreate(osThread(displayThread), NULL);
                    break;
                case('m'): //manual mode
                    mm_flag = 1;
                    osSignalSet(senseTid, 0x5);
                    osThreadTerminate (pacemodeTid);
                    pacemodeTid = osThreadCreate(osThread(manualmode), NULL);
                    break;
                case('o'): //observation interval
                    obsinterval();
                    osThreadTerminate (displayTid);
                    displayTid = osThreadCreate(osThread(displayThread), NULL);
                    break;
            }
        }
    }
}