// -------------------------------------------------------------------------- // // comm.cpp -- handles the serial communication with the devices // // -------------------------------------------------------------------------- #include #include #include #include "Serial.h" #include "comm.h" #include "main.h" // -------------------------------------------------------------------------- int GSR_INTERVAL = 1000; // size of the interval over which to accum data in msec int HR_INTERVAL = 1000; int EB_INTERVAL = 1000; int GSRportnum = 0; // int values for the port int EBportnum = 0; int HRportnum = 0; int err; int HRbuff[5]; int hr_buff_count = 0; // number of values in the current heart rate window int hr_avg_win_wid = 6; // size of the window over which to average heart rate char s[1000]; // blank string for writing out to the debug windows in the GUI char GSRport[8], HRport[8], EBport[8]; // name of the port (e.g., "COM10") CSerial GSRserial, EBserial, HRserial; int POLL; int freq_set = 0; // Variables that use the high-resolution timer (QueryPerformanceTimer();) __int64 tfreq; // freq of the hi-res timer __int64 gsr_writetime, hr_writetime, eb_writetime; // time at which request for data was written __int64 now; // -------------------------------------------------------------------------- int open_assigned_port (int which) { // given the port number determined in assign ports, actually open and set up the serial port switch (which) { case 1: // do the gsr port setup err = GSRserial.Open(GSRport); if (err != ERROR_SUCCESS) { sprintf(s," Unable to open port %s (err = %i)", GSRport, err); write(1,s); return 0;} err = GSRserial.Setup(CSerial::EBaud38400,CSerial::EData8,CSerial::EParNone,CSerial::EStop1); if (err != ERROR_SUCCESS) { sprintf(s," Unable to set up port %s (err = %i)", GSRport, err); write(1,s); return 0;} err = GSRserial.SetupHandshaking(CSerial::EHandshakeOff); if (err != ERROR_SUCCESS) { sprintf(s," Unable to set no handshaking on port %s (err = %i)", GSRport, err); write(1,s); return 0;} err = GSRserial.SetupReadTimeouts(CSerial::EReadTimeoutNonblocking); if (err != ERROR_SUCCESS) { sprintf(s," Unable to set nonblocking reads on port %s (err = %i)", GSRport, err); write(1,s); return 0;} // err = GSRserial.SetMask(CSerial::EEventRecv); // if (err != ERROR_SUCCESS) { sprintf(s," Unable to set event mask on port %s (err = %i)", GSRport, err); write(1,s); return 0;} break; case 2: err = HRserial.Open(HRport); if (err != ERROR_SUCCESS) { sprintf(s," Unable to open port %s (err = %i)", HRport, err); write(2,s); return 0;} err = HRserial.Setup(CSerial::EBaud38400,CSerial::EData8,CSerial::EParNone,CSerial::EStop1); if (err != ERROR_SUCCESS) { sprintf(s," Unable to set up port %s (err = %i)", HRport, err); write(2,s); return 0;} err = HRserial.SetupHandshaking(CSerial::EHandshakeOff); if (err != ERROR_SUCCESS) { sprintf(s," Unable to set no handshaking on port %s (err = %i)", HRport, err); write(2,s); return 0;} err = HRserial.SetupReadTimeouts(CSerial::EReadTimeoutNonblocking); if (err != ERROR_SUCCESS) { sprintf(s," Unable to set nonblocking reads on port %s (err = %i)", HRport, err); write(2,s); return 0;} // err = HRserial.SetMask(CSerial::EEventRecv); // if (err != ERROR_SUCCESS) { sprintf(s," Unable to set event mask on port %s (err = %i)", HRport, err); write(2,s); return 0;} break; case 3: err = EBserial.Open(EBport); if (err != ERROR_SUCCESS) { sprintf(s," Unable to open port %s (err = %i)", EBport, err); write(3,s); return 0;} err = EBserial.Setup(CSerial::EBaud38400,CSerial::EData8,CSerial::EParNone,CSerial::EStop1); if (err != ERROR_SUCCESS) { sprintf(s," Unable to set up port %s (err = %i)", EBport, err); write(3,s); return 0;} err = EBserial.SetupHandshaking(CSerial::EHandshakeOff); if (err != ERROR_SUCCESS) { sprintf(s," Unable to set no handshaking on port %s (err = %i)", EBport, err); write(3,s); return 0;} err = EBserial.SetupReadTimeouts(CSerial::EReadTimeoutNonblocking); if (err != ERROR_SUCCESS) { sprintf(s," Unable to set nonblocking reads on port %s (err = %i)", EBport, err); write(3,s); return 0;} // err = EBserial.SetMask(CSerial::EEventRecv); // if (err != ERROR_SUCCESS) { sprintf(s," Unable to set event mask on port %s (err = %i)", EBport, err); write(3,s); return 0;} break; default: sprintf(s,"/n %i is not an option in open_assigned_ports"); write(0,s); return 0; break; } return 1; } int mywrite (int p, int val) { // Writes out to the device and reads back the echo'd value (full duplex requires this or we're just reading out what we wrote). int k; char buf[101], str[6]; DWORD dwBytesRead; sprintf(str,"%4x%c",val,13); // make string from int given for (k=0; k<4; k++) if (str[k] == ' ') str[k]='0'; // make space into zero switch (p) { case 1: // GSR err = GSRserial.Write(str); if (err != ERROR_SUCCESS) { sprintf(s," Unable to write to GSRserial."); write(1,s); } do { err = GSRserial.Read(buf,sizeof(buf)-1,&dwBytesRead); if (err != ERROR_SUCCESS) { sprintf(s," Unable to read from GSRserial."); write(1,s); } } while (dwBytesRead < 5); break; case 2: // HR err = HRserial.Write(str); if (err != ERROR_SUCCESS) { sprintf(s," Unable to write to HRserial."); write(2,s); } do { err = HRserial.Read(buf,sizeof(buf)-1,&dwBytesRead); if (err != ERROR_SUCCESS) { sprintf(s," Unable to read from HRserial."); write(2,s); } } while (dwBytesRead < 5); break; case 3: // EB err = EBserial.Write(str); if (err != ERROR_SUCCESS) { sprintf(s," Unable to write to EBserial."); write(3,s); } do { err = EBserial.Read(buf,sizeof(buf)-1,&dwBytesRead); if (err != ERROR_SUCCESS) { sprintf(s," Unable to read from EBserial."); write(3,s); } } while (dwBytesRead < 5); break; } return 1; } int parse_buf (char *str, int len) { // parse the data here... look for 13,13,10, D,D,D,D, 13, 10, > int j, sum; sum = 0; for (j = 0; j < len; j++) { if ((str[j] == 13) && (str[j+1] == 10) && (str[j+2] != 62)) { // then assume next 4 digits form the result sprintf(s, "%c%c%c%c", str[j+2],str[j+3],str[j+4],str[j+5]); //sprintf(s, "%s", s); write (1, s); // what's the best way to go from 4 digit string to int? if ((str[j+5] >= '0') && (str[j+5] <= '9')) sum += str[j+5]-48; else if ((str[j+5] >= 'A') && (str[j+5] <= 'Z')) sum += str[j+5]-97+10; else return -2; if ((str[j+4] >= '0') && (str[j+4] <= '9')) sum += 16*(str[j+4]-48); else if ((str[j+4] >= 'A') && (str[j+4] <= 'Z')) sum += 16*(str[j+4]-97+10); else return -3; if ((str[j+3] >= '0') && (str[j+3] <= '9')) sum += 16*16*(str[j+3]-48); else if ((str[j+3] >= 'A') && (str[j+3] <= 'Z')) sum += 16*16*(str[j+3]-97+10); else return -4; if ((str[j+2] >= '0') && (str[j+2] <= '9')) sum += 16*16*16*(str[j+2]-48); else if ((str[j+2] >= 'A') && (str[j+2] <= 'Z')) sum += 16*16*16*(str[j+2]-97+10); else return -5; } } return sum; } void handle_result(int p, int res) { // Do any calculations with the results here. This is where the heart rate window is used. Note that nothing // special is done with the eye blink result, despite the fact it's returning the time in the interval at which // the blink occured. This means that eyeblink is not terribly well correlated with time right now. float beats_per_msec, beats_per_min; int sum, i; if (!freq_set) { // Init the hi-res timer QueryPerformanceFrequency((LARGE_INTEGER*) &tfreq); freq_set = 1; } QueryPerformanceCounter((LARGE_INTEGER*) &now); sprintf(s,"read:%i",res); write(p,s); switch (p) { case 1: // graph stuff onscreen graph(p, (float) res); // write info to file write_to_file(1, res, GSR_INTERVAL, gsr_writetime/(1.0*tfreq), now/(1.0*tfreq)); break; case 2: // write info to file write_to_file(2, res, HR_INTERVAL, hr_writetime/(1.0*tfreq), now/(1.0*tfreq)); // graph average hr over a window of hr_avg_win_wid results if (hr_buff_count > (hr_avg_win_wid-1)) { sum = 0; for (i = 0; i < hr_avg_win_wid; i++) { sum += HRbuff[i]; // collect sum of response in buffer if (i < (hr_avg_win_wid-1)) HRbuff[i] = HRbuff[i+1]; // remove oldest, add newest else HRbuff[i] = res; } if (sum < 1) beats_per_msec = beats_per_min = 0.0; else { beats_per_msec = (float) (sum / (1.0 * HR_INTERVAL * hr_avg_win_wid)); // not entirely accurate because of gaps between reads beats_per_min = beats_per_msec * 1000 * 60; sprintf(s,"%i : %6.2f, %6.2f",res, beats_per_min, beats_per_msec); write(p,s); graph(p, beats_per_min); } } else { HRbuff[hr_buff_count] = res; hr_buff_count++; } break; case 3: // graph stuff if (res) graph(p,100); // show bar if blink occurred in interval else graph(p,0); // write info to file write_to_file(3, res, EB_INTERVAL, eb_writetime/(1.0*tfreq), now/(1.0*tfreq)); break; } } int polling (void) { // Polls the opened ports until the user sends a stop (via the GUI) HANDLE hevtOverlapped_gsr, hevtOverlapped_hr, hevtOverlapped_eb, hevtStop; OVERLAPPED ov_gsr, ov_hr, ov_eb; int res; char buf[101]; DWORD dwBytesRead; open_assigned_port(1); open_assigned_port(2); open_assigned_port(3); sprintf(s," Polling the devices."); write(0,s); hevtOverlapped_gsr = ::CreateEvent(0,TRUE,FALSE,GSRport); // Create a handle for the overlapped operations hevtOverlapped_hr = ::CreateEvent(0,TRUE,FALSE,HRport); hevtOverlapped_eb = ::CreateEvent(0,TRUE,FALSE,EBport); ov_gsr.hEvent = hevtOverlapped_gsr; // Setup the overlapped structure ov_hr.hEvent = hevtOverlapped_hr; ov_eb.hEvent = hevtOverlapped_eb; hevtStop = ::CreateEvent(0,TRUE,FALSE,"Overlapped_Stop_Event"); // Stop event handle GSRserial.WaitEvent(&ov_gsr); // Wait for an event HRserial.WaitEvent(&ov_hr); EBserial.WaitEvent(&ov_eb); // write out some value to the device mywrite(1,GSR_INTERVAL); Sleep(10); QueryPerformanceCounter((LARGE_INTEGER*) &gsr_writetime); mywrite(2,HR_INTERVAL); Sleep(10); QueryPerformanceCounter((LARGE_INTEGER*) &hr_writetime); mywrite(3,EB_INTERVAL); Sleep(10); QueryPerformanceCounter((LARGE_INTEGER*) &eb_writetime); // Now just keep reading from ports until I get something POLL = 1; while (POLL==1) { err = GSRserial.Read(buf,sizeof(buf)-1,&dwBytesRead); if (err != ERROR_SUCCESS) { sprintf(s," Unable to read from GSRserial."); write(1,s); POLL=0; } if (dwBytesRead) { res = parse_buf(buf, dwBytesRead); if(res >= 0) handle_result(1,res); mywrite(1,GSR_INTERVAL); // write out again QueryPerformanceCounter((LARGE_INTEGER*) &gsr_writetime); } err = HRserial.Read(buf,sizeof(buf)-1,&dwBytesRead); if (err != ERROR_SUCCESS) { sprintf(s," Unable to read from HRserial."); write(2,s); POLL=0; } if (dwBytesRead) { res = parse_buf(buf, dwBytesRead); if(res >= 0) handle_result(2,res); mywrite(2,HR_INTERVAL); // write out again QueryPerformanceCounter((LARGE_INTEGER*) &hr_writetime); } err = EBserial.Read(buf,sizeof(buf)-1,&dwBytesRead); if (err != ERROR_SUCCESS) { sprintf(s," Unable to read from EBserial."); write(3,s); POLL=0; } if (dwBytesRead) { res = parse_buf(buf, dwBytesRead); if(res >= 0) handle_result(3,res); mywrite(3,EB_INTERVAL); // write out again QueryPerformanceCounter((LARGE_INTEGER*) &eb_writetime); } POLL=do_ui_check(); } GSRserial.Close(); HRserial.Close(); EBserial.Close(); return 1; } int assign_ports(void) { // Since the port numbers for the USB-to-Serial port adaports are assigned dynamically by windows, we // need to figure out what device is where. This checks the availability of each port and tries writing // the query string ('?') out. The device will reply saying what it is and then we can assign the port. // Note that we only loop through COM ports 4 to 14 -- this freezes when it tries to write to the real serial // ports (COM1 and COM2). int p, c = 0, obj; char port[6]; CSerial serial; DWORD dwBytesRead = 0; char szBuffer[101]; // loop through com ports, open anything that's available for (p = 4; p < 14; p++) { sprintf(port,"COM%i",p); err = serial.CheckPort(port); if (err == CSerial::EPortAvailable) { sprintf(s," %s: Available (%i)", port, err); write(0,s); // Open the port err = serial.Open(port); if (err != ERROR_SUCCESS) { sprintf(s," Unable to open port %s (err = %i)", port, err); write(0,s); } else { // if available, set up 38400, 8N1, no handshaking err = serial.Setup(CSerial::EBaud38400,CSerial::EData8,CSerial::EParNone,CSerial::EStop1); if (err != ERROR_SUCCESS) { sprintf(s," Unable to set up port %s (err = %i)", port, err); write(0,s); } else { err = serial.SetupHandshaking(CSerial::EHandshakeOff); if (err != ERROR_SUCCESS) { sprintf(s," Unable to set no handshaking on port %s (err = %i)", port, err); write(0,s); } else { // set up non blocking reads err = serial.SetupReadTimeouts(CSerial::EReadTimeoutNonblocking); if (err != ERROR_SUCCESS) { sprintf(s," Unable to set nonblocking reads on port %s (err = %i)", port, err); write(0,s); } else { // set up event mask to just get data and not other crap err = serial.SetMask(CSerial::EEventRecv); if (err != ERROR_SUCCESS) { sprintf(s," Unable to set event mask on port %s (err = %i)", port, err); write(0,s); } else { // set up the wait event stuff (will do actual waiting later) HANDLE hevtOverlapped = ::CreateEvent(0,TRUE,FALSE,"a"); // Create a handle for the overlapped operations HANDLE hevtStop = ::CreateEvent(0,TRUE,FALSE,"Overlapped_Stop_Event"); OVERLAPPED ov = {0}; ov.hEvent = hevtOverlapped; // Setup the overlapped structure serial.WaitEvent(&ov); // Wait for an event // sprintf(s," Waiting for something. -- "); write(0,s); HANDLE ahWait[2]; ahWait[0] = hevtStop; ahWait[1] = hevtOverlapped; // send a '?' // sprintf(s," Sending a \'?\'"); write(0,s); err = serial.Write("?"); if (err != ERROR_SUCCESS) { sprintf(s," Unable to write \'?\' on port %s (err = %i)", port, err); write(0,s); } int wait_for_answer = 0; while (wait_for_answer < 4) { // try 4 times to get something. wait_for_answer++; // wait for a while and see if something comes back obj = ::WaitForMultipleObjects(2,ahWait,FALSE,1000); /*switch (obj) { case WAIT_OBJECT_0: write(1,"Wait object 0"); break; case WAIT_OBJECT_0+1: write(1,"Wait object 1"); break; case WAIT_ABANDONED_0: write(1,"Abandoned 0"); break; case WAIT_ABANDONED_0+1:write(1,"Abandoned 1"); break; case WAIT_TIMEOUT: write(1,"Stop"); break; default: write(1,"Failed WaitForMultipleObjects"); break; }*/ if (obj == WAIT_OBJECT_0+1) { // there's data from port ResetEvent(ahWait[obj]); dwBytesRead = 0; do { err = serial.Read(szBuffer,sizeof(szBuffer)-1,&dwBytesRead); if (err != ERROR_SUCCESS) { sprintf(s,"Unable to read from COM-port."); write(0,s); } if (dwBytesRead > 0) szBuffer[dwBytesRead] = '\0'; } while (dwBytesRead < 10); // sprintf(s,"bytes read: %i",dwBytesRead); write(0,s); if (dwBytesRead > 10) break; } else { ResetEvent(ahWait[obj]); err = serial.Write("?"); // if not seeing stuff, try re-writing the ? and looking again. if (err != ERROR_SUCCESS) { sprintf(s," Unable to write \'?\' on port %s (err = %i)", port, err); write(0,s); } } } // sprintf(s,"finished for loop, %i",p); write(0,s); serial.Close(); // find out what we got c = 0; if (strstr(szBuffer,"Gsres")) c = 1; if (strstr(szBuffer,"Heart")) c = 2; if (strstr(szBuffer,"Blink")) c = 3; switch (c) { case 1: sprintf(s,"GSR is on COM%i", p); write(1,s); GSRportnum = p; sprintf(GSRport,"COM%i",GSRportnum); update_label(1,GSRport); break; case 2: sprintf(s,"HR is on COM%i", p); write(2,s); HRportnum = p; sprintf(HRport,"COM%i",HRportnum); update_label(2,HRport);break; case 3: sprintf(s,"EB is on COM %i", p); write(3,s); EBportnum = p; sprintf(EBport,"COM%i",EBportnum); update_label(3,EBport); break; } } } } } } } else { sprintf(s," %s: Not Available (%i)", port, err); write(0,s); } } if (!GSRportnum || !HRportnum || !EBportnum) { write(0,"Unable to assign ports for all devices."); write(0,"Try hitting reset buttons and pressing \'Assign\' again."); return 0; } return 1; } int comm_quit (void) { // Clean up everything sprintf(s,""); write(0,s); // flush ports (not sure is necessary) err = GSRserial.Flush(); if (err != ERROR_SUCCESS) { sprintf(s," Failed to flush GSR port"); write(0,s); return 0; } err = HRserial.Flush(); if (err != ERROR_SUCCESS) { sprintf(s," Failed to flush HR port"); write(0,s); return 0; } err = EBserial.Flush(); if (err != ERROR_SUCCESS) { sprintf(s," Failed to flush EB port"); write(0,s); return 0; } // close serial ports err = GSRserial.Close(); if (err != ERROR_SUCCESS) { sprintf(s," Failed to close GSR port"); write(0,s); return 0; } err = HRserial.Close(); if (err != ERROR_SUCCESS) { sprintf(s," Failed to close HR port"); write(0,s); return 0; } err = EBserial.Close(); if (err != ERROR_SUCCESS) { sprintf(s," Failed to close EB port"); write(0,s); return 0; } return 1; }