Eric Stahl / Mbed 2 deprecated 541-pacemaker-final

Dependencies:   TextLCD mbed-rtos mbed

Fork of 541-pacemaker by Terry Fang

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /* @Author: Grayson Honan, Phil Perilstein, Terry Fang, Eric Stahl
00002    @Course: CIS 541
00003    @Due Date: 12 Dec 2016
00004    @Assignment: Pacemaker Project - Pacemaker Component
00005 
00006    @Description: This code is representative of the functionality
00007    of a DDD pacemaker. The code was generated from an UPAAL model
00008    that describes the basic timing functionality of a pacemaker and
00009    the operation of system peripherals (lcd screen, alarm, keyboard).
00010 
00011    Main timing threads:
00012 
00013    PaceSense - handles triggering pace output events via timeouts
00014 
00015    PaceSignal - filters and accepts heart signal input events by
00016    synchronizing with interupt ins.
00017 
00018    Main Peripheral Threads:
00019 
00020    displayThread - displays current bpm and then resets the bpm
00021    and determines if abnormal bpm
00022 
00023    alarmThread - synchronizes with displayThread if abnormal bpm
00024    and displays warning message to lcd screen
00025 
00026    ledThread - synchronizes with PaceSense or PaceSignal on a Vsense,
00027    Asense, Vpace, Apace
00028 
00029    Rx_interrupt - interrupt that accepts keyboard input and filters
00030    for valid keyboard input. Synchrnoizes with the thread that the
00031    keyboard input is destined for via a Queue.
00032 
00033    Pace Mode Threads:
00034 
00035    Main - initializes to a Normal mode Pacemaker with an observation
00036    interval of 10 seconds. Synchronizes with the Rx_interrupt when
00037    valid keyboard input is determined. The main thread acts as the
00038    Choose_PaceMode automata.
00039 
00040    manualmode - disables pace events from the pacemaker & allows for
00041    user input to manually pace an atrial or ventrical event.
00042 
00043    Reference: https://developer.mbed.org/handbook/CMSIS-RTOS
00044 
00045    Run Instructions: Whenever I ran the program, I found I needed to first
00046    run screen to open up the fd pointing to the usb. I would then kill the
00047    screen process and run the ./start_game script after uncommenting the
00048    port selection in the script.
00049 
00050 */
00051 
00052 #include "mbed.h"
00053 #include "rtos.h"
00054 #include "TextLCD.h"
00055 #include <stdio.h>
00056 
00057 // Input/Output declarations
00058 
00059 InterruptIn vsignal(p7);
00060 InterruptIn asignal(p8);
00061 DigitalOut Vpace(p5);
00062 DigitalOut Apace(p6);
00063 
00064 DigitalOut asense_led(LED1);
00065 DigitalOut vsense_led(LED2);
00066 DigitalOut apace_led(LED3);
00067 DigitalOut vpace_led(LED4);
00068 
00069 // thread definitions
00070 osThreadId signalTid;
00071 osThreadId senseTid;
00072 osThreadId displayTid;
00073 osThreadId pacemodeTid;
00074 osThreadId alarmTid;
00075 osThreadId ledTid;
00076 
00077 //peripheral API declarations
00078 TextLCD lcd(p15, p16, p17, p18, p19, p20, TextLCD::LCD16x2);
00079 RawSerial pc(USBTX, USBRX);
00080 
00081 //timing declarations
00082 Timer vClock;
00083 Timer aClock;
00084 Timer arpClock;
00085 
00086 //normal mode timing constants
00087 double LRI = 1000;
00088 double URI = 700;
00089 double VRP = 200;
00090 double ARP = 50;
00091 double AVI = 150;
00092 double PVARP = 300;
00093 double ratio;
00094 int wait_period = 10;
00095 
00096 double observation_interval = 10000; // In miliseconds
00097 int upperBound; //for mode changes
00098 int lowerBound; //for mode changes
00099 double heart_beats = 0; // Heart-Beats (sensed or paced) since the last observation interval
00100 Mutex hr_mutex; //hr_mutex.lock()/unlock()
00101 
00102 //message passing queues
00103 Queue<char,256> mode_q;
00104 Queue<char,256> signal_q;
00105 Queue<char,256> obsint_q;
00106 
00107 //keyboard interrupt variables
00108 volatile char c;
00109 volatile int mm = 0;
00110 volatile int om = 0;
00111 
00112 //flag to disable automatic pacing
00113 int mm_flag = 0;
00114 
00115 //init the ventrical timing values
00116 void initialize_intervals()
00117 {
00118     LRI = 1000;
00119     URI = 700;
00120 }
00121 
00122 //interrupt for keyboard input. Filters valid input.
00123 void Rx_interrupt()
00124 {
00125     while(pc.readable()) {
00126         c = pc.getc();
00127 
00128         //synchronize mode switch thread for manual mode
00129         if(c == 'm' && om != 1) {
00130             mode_q.put((char*)c);
00131             mm = 1;
00132             //synchronize mode switch thread for normal, excercise or sleep mode
00133         } else if(c == 'n' || c == 'e' || c == 's' && om != 1) {
00134             mode_q.put((char*)c);
00135             mm = 0;
00136             //if manual mode, synchronize with manual mode thread for a or v signal
00137         } else if((c == 'a' || c == 'v') && mm) {
00138             signal_q.put((char*)c);
00139             //start accepting input to update the observation interval. Disable mode switching.
00140         } else if(c == 'o' && om != 1) {
00141             mode_q.put((char*)c);
00142             om = 1;
00143             //end receiving input to update observation interval. Enable mode switching.
00144         } else if (c == '\r' && om) {
00145             obsint_q.put((char*)c);
00146             om = 0;
00147             //updating observation interval, filter valid input.
00148         } else if ((int)c > 47 && (int)c < 58 && om) {
00149             obsint_q.put((char*)c);
00150         }
00151     }
00152 }
00153 
00154 // Function to toggle the LEDs 1,2,3,4
00155 void ledThread(void const *args)
00156 
00157 {
00158     while (1) {
00159         //wait for synchronization for Apace, Vpace, Asense, Vsense
00160         osEvent ext_signal = osSignalWait(0, osWaitForever);
00161         int evt = ext_signal.value.signals;
00162 
00163         if (evt == 0xA) { //asense
00164             asense_led = 1;
00165             Thread::wait(wait_period);
00166             asense_led = 0;
00167         } else if (evt == 0xB) { //vsense
00168             vsense_led = 1;
00169             Thread::wait(wait_period);
00170             vsense_led = 0;
00171         } else if (evt == 0xC) { //apace
00172             apace_led = 1;
00173             Thread::wait(wait_period);
00174             apace_led = 0;
00175         } else if (evt == 0xD) { //vpace
00176             vpace_led = 1;
00177             Thread::wait(wait_period);
00178             vpace_led = 0;
00179         }
00180     }
00181 }
00182 
00183 //Synchronized with displayThread if alarmThread should alarm
00184 void alarmThread(void const *args)
00185 {
00186     while (1) {
00187         osEvent ext_signal = osSignalWait(0, osWaitForever);
00188         int evt = ext_signal.value.signals;
00189 
00190         if (evt == 0xb) { //upper bound violated
00191             lcd.printf("%s", "\nALARM HIGH");
00192         } else if (evt == 0xc) { //lower bound violated
00193             lcd.printf("%s", "\nALARM LOW");
00194         }
00195     }
00196 }
00197 //Thread for displaying bpm & alerts
00198 void displayThread(void const *args)
00199 {
00200     while (1) {
00201         Thread::wait(observation_interval);
00202         lcd.cls();
00203 
00204         hr_mutex.lock(); //acquire lock because changing bpm
00205         double hr = (heart_beats*60) / (observation_interval / 1000); //calculate bpm
00206         heart_beats = 0; //reset bpm
00207         hr_mutex.unlock();
00208 
00209         lcd.printf("%s%d%s","HR: ", (int)hr, " bpm"); //display bpm
00210 
00211         if (hr > upperBound) { //synchronize if upperBound violated
00212             osSignalSet(alarmTid, 0xb);
00213         } else if (hr < lowerBound) { //synchronize if lowerBound violated
00214             osSignalSet(alarmTid, 0xc);
00215         }
00216     }
00217 }
00218 
00219 
00220 
00221 // Incoming atrial signal from the heart
00222 void asignal_irq()
00223 {
00224     osSignalSet(signalTid, 0x1); //synchronize with pacesignal thread
00225 }
00226 
00227 // Incoming ventrical signal from the heart
00228 void vsignal_irq()
00229 {
00230     osSignalSet(signalTid, 0x2); //synchronize with pacesignal thread
00231 }
00232 
00233 //Main timing thread for filtering Asignal and Vsignal from heart and
00234 //generating Asense and Vsense to synchronize with the PaceSense thread.
00235 void PaceSignal(void const *args)
00236 {
00237 
00238     int pFlag1 = 0; //flag that allows us to move to the second UPPAAL state in PaceSignal
00239     int pFlag2 = 0; //flag that allows us to move to the first UPPAAL state in PaceSignal
00240 
00241     vClock.start();
00242     aClock.start();
00243     arpClock.start();
00244 
00245     while(1) {
00246         while (!pFlag1) {
00247             osEvent ext_signal = osSignalWait(0, osWaitForever);
00248             int evt = ext_signal.value.signals;
00249 
00250             if (evt == 0x1 && vClock.read_ms() >= PVARP) { //aSense
00251                 osSignalSet(senseTid, 0x1);
00252                 aClock.reset();
00253                 arpClock.reset();
00254                 pFlag1 = 1;
00255             } else if(evt == 0x2 && vClock.read_ms() >= VRP) { //vSense
00256                 hr_mutex.lock();
00257                 osSignalSet(senseTid, 0x2); //syncrhonize with PaceSense thread
00258                 heart_beats++; //increment bpm
00259                 vClock.reset();
00260                 aClock.reset();
00261                 arpClock.reset();
00262                 hr_mutex.unlock();
00263                 pFlag1 = 1; //progress to state 2
00264 
00265             } else if (evt == 0x3) { //aPace
00266                 pFlag1 = 1; //progress to state 2
00267             }
00268         }
00269         pFlag1 = 0;
00270         while(!pFlag2) {
00271 
00272             osEvent ext_signal = osSignalWait(0, osWaitForever);
00273             int evt = ext_signal.value.signals;
00274 
00275             if (evt == 0x1 && arpClock.read_ms() >= ARP) { //aSense
00276                 osSignalSet(senseTid, 0x1);
00277                 arpClock.reset(); //determine valid consecutive a event
00278 
00279             } else if(evt == 0x2) { //vSense
00280                 hr_mutex.lock();
00281                 osSignalSet(senseTid, 0x2);
00282                 heart_beats++; //increment bpm
00283                 vClock.reset();
00284                 aClock.reset();
00285                 arpClock.reset();
00286                 hr_mutex.unlock();
00287                 pFlag2 = 1; //progress to state 1
00288             } else if (evt == 0x4) { //vPace
00289                 pFlag2 = 1; //progress to state 1
00290             }
00291         }
00292         pFlag2 = 0;
00293     }
00294 }
00295 
00296 void PaceSense(void const *args)
00297 {
00298     int pFlag1 = 0; //flag that allows us to move to the second UPPAAL state in PaceSense
00299     int pFlag2 = 0; //flag that allows us to move to the first UPPAAL state in PaceSense
00300     int time_sub = 0; //used to determine timeout for when to pace (this is our invariant)
00301     int evt = 0;
00302     while(1) {
00303         while (!pFlag1) {
00304 
00305             time_sub = LRI-AVI - vClock.read_ms(); //aPace at LRI-AVI default
00306 
00307             if (time_sub > 0  && !mm_flag) {
00308                 osEvent ext_signal = osSignalWait(0, time_sub); //allow pacing
00309                 evt = ext_signal.value.signals;
00310             } else if(mm_flag) {
00311                 osEvent ext_signal = osSignalWait(0, osWaitForever); //disable pacing
00312                 evt = ext_signal.value.signals;
00313             } else {
00314                 evt = 0x0; //time_sub is less than 0
00315             }
00316 
00317             if (evt == 0x0) { //aPace
00318                 aClock.reset();
00319                 arpClock.reset();
00320                 Apace = 1;
00321                 Thread::wait(1);
00322                 Apace = 0;
00323                 osSignalSet(signalTid, 0x3);
00324                 osSignalSet(ledTid, 0xC);
00325                 pFlag1 = 1;
00326             } else if (evt == 0x1) { //aSense
00327                 osSignalSet(ledTid, 0xA);
00328                 pFlag1 = 1;
00329             } else if(evt == 0x2) { //vSense
00330                 osSignalSet(ledTid, 0xB);
00331             } else if(evt == 0x3) { //manual apace
00332                 pFlag1 = 1;
00333             }
00334         }
00335         pFlag1 = 0;
00336 
00337         while(!pFlag2) {
00338             //vpace occurs at either URI or vclock + AVI
00339             time_sub = (vClock.read_ms() + AVI >= URI) ? AVI - aClock.read_ms() : URI - vClock.read_ms();
00340 
00341             if (time_sub > 0 && !mm_flag) { //allow pacing
00342                 osEvent ext_signal = osSignalWait(0, time_sub);
00343                 evt = ext_signal.value.signals;
00344             } else if(mm_flag) { //disable pacing
00345                 osEvent ext_signal = osSignalWait(0, osWaitForever);
00346                 evt = ext_signal.value.signals;
00347             } else {
00348                 evt = 0x0; //time_sub is negative
00349             }
00350 
00351             if (evt == 0x0) { //vPace
00352 
00353                 hr_mutex.lock();
00354                 heart_beats++;
00355                 vClock.reset();
00356                 aClock.reset();
00357                 arpClock.reset();
00358                 Vpace = 1;
00359                 Thread::wait(1);
00360                 Vpace = 0;
00361                 osSignalSet(signalTid, 0x4);
00362                 hr_mutex.unlock();
00363 
00364                 osSignalSet(ledTid, 0xD);
00365                 pFlag2 = 1;
00366 
00367             } else if (evt == 0x1) { //aSense
00368                 osSignalSet(ledTid, 0xA);
00369             } else if(evt == 0x2) { //vSense
00370                 osSignalSet(ledTid, 0xB);
00371                 pFlag2 = 1;
00372             } else if (evt == 0x4) { //manual vpace
00373                 pFlag2 = 1;
00374             }
00375         }
00376         pFlag2 = 0;
00377     }
00378 }
00379 //update timing constraints for mode and reset bpm
00380 void normalmode(void const *args)
00381 {
00382     initialize_intervals();
00383     upperBound = 100; //beats per msecond
00384     lowerBound = 40; //beats per msecond
00385     //hr_mutex.lock();
00386     //reset bpm
00387     heart_beats = 0;
00388     hr_mutex.unlock();
00389 
00390     vClock.reset();
00391     aClock.reset();
00392 }
00393 //update timing constraints for mode and reset bpm
00394 void exercisemode(void const *args)
00395 {
00396     initialize_intervals();
00397     upperBound = 175; //beats per msecond
00398     lowerBound = 100; //beats per msecond
00399     ratio = (175.00/100.00 + 100.00/40.00) / 2.00;
00400     LRI /= ratio;
00401     URI /= ratio;
00402     //reset bpm
00403     hr_mutex.lock();
00404     heart_beats = 0;
00405     hr_mutex.unlock();
00406 
00407     vClock.reset();
00408     aClock.reset();
00409 }
00410 //update timing constraints for mode and reset bpm
00411 void sleepmode(void const *args)
00412 {
00413     initialize_intervals();
00414     upperBound = 60; //beats per msecond
00415     lowerBound = 30; //beats per msecond v-v 0.5s
00416     ratio = (60.00/100.00 + 30.00/40.00) / 2.00;
00417     LRI /= ratio;
00418     URI /= ratio;
00419     hr_mutex.lock();
00420     //reset bpm
00421     heart_beats = 0;
00422     hr_mutex.unlock();
00423 
00424     vClock.reset();
00425     aClock.reset();
00426 }
00427 //handle manual pacing events
00428 void m_vpace()
00429 {
00430     vClock.reset();
00431     aClock.reset();
00432     arpClock.reset();
00433     Vpace = 1;
00434     Thread::wait(1);
00435     Vpace = 0;
00436     osSignalSet(signalTid, 0x4);
00437     osSignalSet(senseTid, 0x4);
00438     hr_mutex.lock();
00439     heart_beats++;
00440     hr_mutex.unlock();
00441 
00442     osSignalSet(ledTid, 0xD);
00443 }
00444 //handle manual pacing events
00445 void m_apace()
00446 {
00447     aClock.reset();
00448     arpClock.reset();
00449     Apace = 1;
00450     Thread::wait(1);
00451     Apace = 0;
00452     osSignalSet(senseTid, 0x3);
00453     osSignalSet(signalTid, 0x3);
00454     osSignalSet(ledTid, 0xC);
00455 }
00456 //update timing constraints for mode and handle manual pace events
00457 void manualmode(void const *args)
00458 {
00459     upperBound = 175; //beats per msecond
00460     lowerBound = 30; //beats per msecond
00461     LRI = 2125; // max V-V (LRI) based on exercise mode
00462     URI = 675; // min V-V (URI) based on sleep mode
00463 
00464     while(1) {
00465         osEvent evt = signal_q.get();
00466         if(evt.status == osEventMessage) {
00467             if((char)evt.value.p == 'v') {
00468                 m_vpace();
00469             } else if((char)evt.value.p == 'a') {
00470                 m_apace();
00471             }
00472         }
00473     }
00474 }
00475 //manage user input for updating the observation interval
00476 void obsinterval()
00477 {
00478     char newObsInt[8];
00479     int isChangingObsInt = 1;
00480     int i = 0;
00481     char key = 'n';
00482     while(isChangingObsInt) {
00483         osEvent evt = obsint_q.get();
00484         if(evt.status == osEventMessage) {
00485             key = (char)evt.value.p;
00486             if(key != '\r' && i < 7 ) { //bound number of integers for overflow
00487                 newObsInt[i] = key;
00488                 i++;
00489             } else if((key == '\r') && (i > 0)) { //conver input to observatio interval
00490                 heart_beats = 0;
00491                 int obsint;
00492                 newObsInt[i] = '\0';
00493                 sscanf(newObsInt, "%d", &obsint);
00494 
00495                 //check bounds
00496                 if(obsint < 300) {
00497                     observation_interval = 300.0;
00498                 } else if (obsint > 10000) {
00499                     observation_interval = 10000.0;
00500                 } else {
00501                     observation_interval = (double)obsint;
00502                 }
00503                 isChangingObsInt = 0;
00504 
00505             }
00506         }
00507     }
00508 }
00509 
00510 
00511 //create thread definitions
00512 osThreadDef(PaceSignal, osPriorityNormal, DEFAULT_STACK_SIZE);
00513 osThreadDef(PaceSense, osPriorityNormal, DEFAULT_STACK_SIZE);
00514 osThreadDef(alarmThread, osPriorityBelowNormal, DEFAULT_STACK_SIZE);
00515 osThreadDef(ledThread, osPriorityBelowNormal, DEFAULT_STACK_SIZE);
00516 
00517 osThreadDef(displayThread, osPriorityLow, DEFAULT_STACK_SIZE);
00518 osThreadDef(manualmode, osPriorityNormal, DEFAULT_STACK_SIZE);
00519 
00520 int main()
00521 {
00522     //create thread ids
00523     alarmTid = osThreadCreate(osThread(alarmThread), NULL);
00524     senseTid = osThreadCreate(osThread(PaceSense), NULL);
00525     signalTid = osThreadCreate(osThread(PaceSignal), NULL);
00526     displayTid = osThreadCreate(osThread(displayThread), NULL);
00527     ledTid = osThreadCreate(osThread(ledThread), NULL);
00528 
00529     //start pacemaker in normal mode
00530     normalmode(NULL);
00531 
00532     //set interrupt ins on signaling inputs
00533     vsignal.rise(&vsignal_irq);
00534     asignal.rise(&asignal_irq);
00535 
00536     //clear lcd
00537     lcd.cls();
00538 
00539     //set interrupt in for serial input
00540     pc.attach(&Rx_interrupt, RawSerial::RxIrq);
00541     while(true) { //handle mode switching synchronization
00542         osEvent evt = mode_q.get();
00543         if(evt.status == osEventMessage) {
00544             switch((char)evt.value.p) {
00545                 case('n'): //normal mode
00546                     mm_flag = 0;
00547                     osSignalSet(senseTid, 0x5);
00548                     osThreadTerminate (pacemodeTid);
00549                     osThreadTerminate (displayTid);
00550                     normalmode(NULL);
00551                     displayTid = osThreadCreate(osThread(displayThread), NULL);
00552                     break;
00553                 case('s'): //sleep mode
00554                     mm_flag = 0;
00555                     osSignalSet(senseTid, 0x5);
00556                     osThreadTerminate (pacemodeTid);
00557                     osThreadTerminate (displayTid);
00558                     sleepmode(NULL);
00559                     displayTid = osThreadCreate(osThread(displayThread), NULL);
00560                     break;
00561                 case('e'): //excercise mode
00562                     mm_flag = 0;
00563                     osSignalSet(senseTid, 0x5);
00564                     osThreadTerminate (pacemodeTid);
00565                     osThreadTerminate (displayTid);
00566                     exercisemode(NULL);
00567                     displayTid = osThreadCreate(osThread(displayThread), NULL);
00568                     break;
00569                 case('m'): //manual mode
00570                     mm_flag = 1;
00571                     osSignalSet(senseTid, 0x5);
00572                     osThreadTerminate (pacemodeTid);
00573                     pacemodeTid = osThreadCreate(osThread(manualmode), NULL);
00574                     break;
00575                 case('o'): //observation interval
00576                     obsinterval();
00577                     osThreadTerminate (displayTid);
00578                     displayTid = osThreadCreate(osThread(displayThread), NULL);
00579                     break;
00580             }
00581         }
00582     }
00583 }