Skip to content
Snippets Groups Projects
Commit e0590a2c authored by jsiegle's avatar jsiegle
Browse files

Update Pulse Pal API

parent 4ea79bfa
No related branches found
No related tags found
No related merge requests found
......@@ -20,12 +20,11 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Modified by JS 1/30/2014: Updated op codes for firmware 0_4, added new functions (indicated in comments below)
#include <vector>
#include <stdio.h>
#include <stdint.h>
#include "PulsePal.h"
#define ZERO_uS (uint32_t) 0
......@@ -33,6 +32,7 @@
#define MAX_uS (uint32_t) 3600000000
#define NEWLINE 0xA
#define RETURN 0xD
#define makeLong(msb, byte2, byte3, lsb) ((msb << 24) | (byte2 << 16) | (byte3 << 8) | (lsb)) //JS 2/1/2014
PulsePal::PulsePal()
{
......@@ -41,7 +41,7 @@ PulsePal::PulsePal()
PulsePal::~PulsePal()
{
disconnectClient();
serial.close();
}
......@@ -64,108 +64,153 @@ void PulsePal::initialize()
// bool foundDevice = false;
int id = devices[0].getDeviceID();
int id = devices[0].getDeviceID();
string path = devices[0].getDevicePath();
string name = devices[0].getDeviceName();
serial.setup(id, 115200); //115200);
serial.setup(id, 115200);
std::cout << "Found!" << std::endl;
}
uint32_t PulsePal::getFirmwareVersion() // JS 1/30/2014
{
uint32_t firmwareVersion = 0;
uint8_t handshakeByte = 72;
uint8_t responseBytes[5] = { 0 };
serial.writeByte(handshakeByte);
Sleep(100);
serial.readBytes(responseBytes,5);
firmwareVersion = makeLong(responseBytes[4], responseBytes[3], responseBytes[2], responseBytes[1]);
return firmwareVersion;
}
void PulsePal::setBiphasic(uint8_t channel, bool isBiphasic)
{
uint8_t command = 0;
if (isBiphasic)
{
command = 1;
}
program(channel, 1, command);
PulsePal::currentOutputParams[channel].isBiphasic = command; //JS 2/1/2014 (Added this for all single-item programming functions)
}
void PulsePal::setPhase1Voltage(uint8_t channel, float voltage)
{
program(channel, 2, voltageToByte(voltage));
PulsePal::currentOutputParams[channel].phase1Voltage = voltage;
}
void PulsePal::setPhase2Voltage(uint8_t channel, float voltage)
{
program(channel, 3, voltageToByte(voltage));
PulsePal::currentOutputParams[channel].phase2Voltage = voltage;
}
void PulsePal::setPhase1Duration(uint8_t channel, uint32_t timeInMicroseconds)
void PulsePal::setPhase1Duration(uint8_t channel, float timeInSeconds)
{
uint32_t timeInMicroseconds = (uint32_t)(timeInSeconds * 1000000); //JS 2/1/2014
constrain(&timeInMicroseconds, FIFTY_uS, MAX_uS);
program(channel, 0, timeInMicroseconds);
program(channel, 4, timeInMicroseconds);
PulsePal::currentOutputParams[channel].phase1Duration = timeInSeconds;
}
void PulsePal::setInterPhaseInterval(uint8_t channel, uint32_t timeInMicroseconds)
void PulsePal::setInterPhaseInterval(uint8_t channel, float timeInSeconds)
{
uint32_t timeInMicroseconds = (uint32_t)(timeInSeconds * 1000000);
constrain(&timeInMicroseconds, FIFTY_uS, MAX_uS);
program(channel, 1, timeInMicroseconds);
program(channel, 5, timeInMicroseconds);
PulsePal::currentOutputParams[channel].interPhaseInterval = timeInSeconds;
}
void PulsePal::setPhase2Duration(uint8_t channel, uint32_t timeInMicroseconds)
void PulsePal::setPhase2Duration(uint8_t channel, float timeInSeconds)
{
uint32_t timeInMicroseconds = (uint32_t)(timeInSeconds * 1000000);
constrain(&timeInMicroseconds, FIFTY_uS, MAX_uS);
program(channel, 2, timeInMicroseconds);
program(channel, 6, timeInMicroseconds);
PulsePal::currentOutputParams[channel].phase2Duration = timeInSeconds;
}
void PulsePal::setInterPulseInterval(uint8_t channel, uint32_t timeInMicroseconds)
void PulsePal::setInterPulseInterval(uint8_t channel, float timeInSeconds)
{
uint32_t timeInMicroseconds = (uint32_t)(timeInSeconds * 1000000);
constrain(&timeInMicroseconds, FIFTY_uS, MAX_uS);
program(channel, 3, timeInMicroseconds);
program(channel, 7, timeInMicroseconds);
PulsePal::currentOutputParams[channel].interPhaseInterval = timeInSeconds;
}
void PulsePal::setBurstDuration(uint8_t channel, uint32_t timeInMicroseconds)
void PulsePal::setBurstDuration(uint8_t channel, float timeInSeconds)
{
uint32_t timeInMicroseconds = (uint32_t)(timeInSeconds * 1000000);
constrain(&timeInMicroseconds, ZERO_uS, MAX_uS);
program(channel, 4, timeInMicroseconds);
program(channel, 8, timeInMicroseconds);
PulsePal::currentOutputParams[channel].burstDuration = timeInSeconds;
}
void PulsePal::setBurstInterval(uint8_t channel, uint32_t timeInMicroseconds)
void PulsePal::setBurstInterval(uint8_t channel, float timeInSeconds)
{
uint32_t timeInMicroseconds = (uint32_t)(timeInSeconds * 1000000);
constrain(&timeInMicroseconds, ZERO_uS, MAX_uS);
program(channel, 5, timeInMicroseconds);
program(channel, 9, timeInMicroseconds);
PulsePal::currentOutputParams[channel].interBurstInterval = timeInSeconds;
}
void PulsePal::setStimulusTrainDuration(uint8_t channel, uint32_t timeInMicroseconds)
void PulsePal::setPulseTrainDuration(uint8_t channel, float timeInSeconds)
{
uint32_t timeInMicroseconds = (uint32_t)(timeInSeconds * 1000000);
constrain(&timeInMicroseconds, FIFTY_uS, MAX_uS);
program(channel, 6, timeInMicroseconds);
program(channel, 10, timeInMicroseconds);
PulsePal::currentOutputParams[channel].pulseTrainDuration = timeInSeconds;
}
void PulsePal::setStimulusTrainDelay(uint8_t channel, uint32_t timeInMicroseconds)
void PulsePal::setPulseTrainDelay(uint8_t channel, float timeInSeconds)
{
uint32_t timeInMicroseconds = (uint32_t)(timeInSeconds * 1000000);
constrain(&timeInMicroseconds, FIFTY_uS, MAX_uS);
program(channel, 7, timeInMicroseconds);
program(channel, 11, timeInMicroseconds);
PulsePal::currentOutputParams[channel].pulseTrainDelay = timeInSeconds;
}
void PulsePal::setBiphasic(uint8_t channel, bool isBiphasic)
void PulsePal::setTrigger1Link(uint8_t channel, uint8_t link_state) // JS 1/30/2014
{
uint8_t command = 0;
if (isBiphasic)
{
command = 1;
}
program(channel, 8, command);
program(channel, 12, link_state);
PulsePal::currentOutputParams[channel].linkTriggerChannel1 = link_state;
}
void PulsePal::setPhase1Voltage(uint8_t channel, float voltage)
void PulsePal::setTrigger2Link(uint8_t channel, uint8_t link_state) // JS 1/30/2014
{
program(channel, 9, voltageToByte(voltage));
program(channel, 13, link_state);
PulsePal::currentOutputParams[channel].linkTriggerChannel2 = link_state;
}
void PulsePal::setPhase2Voltage(uint8_t channel, float voltage)
void PulsePal::setCustomTrainID(uint8_t channel, uint8_t ID) // JS 1/30/2014
{
program(channel, 10, voltageToByte(voltage));
program(channel, 14, ID);
PulsePal::currentOutputParams[channel].customTrainID = ID;
}
void PulsePal::updateDisplay(string line1, string line2)
void PulsePal::setCustomTrainTarget(uint8_t channel, uint8_t target) // JS 1/30/2014
{
uint8_t message1 = 85;
serial.writeByte(message1);
serial.writeBytes((unsigned char*) line1.data(), line1.size());
//serial.writeByte(0);
//serial.writeByte(RETURN);
serial.writeByte(254);
serial.writeBytes((unsigned char*) line2.data(), line2.size());
//serial.writeByte(0);
//serial.writeByte(RETURN);
program(channel, 15, target);
PulsePal::currentOutputParams[channel].customTrainTarget = target;
}
void PulsePal::setCustomTrainLoop(uint8_t channel, uint8_t loop_state) // JS 1/30/2014
{
program(channel, 16, loop_state);
PulsePal::currentOutputParams[channel].customTrainLoop = loop_state;
}
void PulsePal::setTriggerMode(uint8_t channel, uint8_t mode) // JS 1/30/2014
{
program(channel, 128, mode);
PulsePal::currentInputParams[channel].triggerMode = mode;
}
void PulsePal::program(uint8_t channel, uint8_t paramCode, uint32_t paramValue)
{
std::cout << "sending 32-bit message" << std::endl;
//std::cout << "sending 32-bit message" << std::endl;
uint8_t message1[3] = {79, paramCode, channel};
uint8_t message1[3] = {74, paramCode, channel};
uint8_t message2[4];
......@@ -178,32 +223,96 @@ void PulsePal::program(uint8_t channel, uint8_t paramCode, uint32_t paramValue)
serial.writeBytes(message1, 3);
serial.writeBytes(message2, 4);
std::cout << "Message 1: " << (int) message1[0] << " " << (int) message1[1] << " " << (int) message1[2] << std::endl;
std::cout << "Message 2: " << (int) message2[0] << " " << (int) message2[1] << " " << (int) message2[2] << " " << (int) message2[3] << std::endl;
//std::cout << "Message 1: " << (int) message1[0] << " " << (int) message1[1] << " " << (int) message1[2] << std::endl;
//std::cout << "Message 2: " << (int) message2[0] << " " << (int) message2[1] << " " << (int) message2[2] << " " << (int) message2[3] << std::endl;
}
void PulsePal::program(uint8_t channel, uint8_t paramCode, uint8_t paramValue)
{
std::cout << "sending 8-bit message" << std::endl;
//std::cout << "sending 8-bit message" << std::endl;
uint8_t message1[3] = {79, paramCode, channel};
uint8_t message1[3] = {74, paramCode, channel};
serial.writeBytes(message1, 3);
serial.writeBytes(&paramValue, 1);
std::cout << "Message 1: " << (int) message1[0] << " " << (int) message1[1] << " " << (int) message1[2] << std::endl;
std::cout << "Message 2: " << paramValue << std::endl;
//std::cout << "Message 1: " << (int) message1[0] << " " << (int) message1[1] << " " << (int) message1[2] << std::endl;
//std::cout << "Message 2: " << paramValue << std::endl;
}
void PulsePal::triggerChannel(uint8_t chan)
{
const uint8_t code = 1 << (chan - 1);
uint8_t bytesToWrite[2] = {77, code};
serial.writeBytes(bytesToWrite, 2);
}
void PulsePal::triggerChannels(uint8_t channel1, uint8_t channel2, uint8_t channel3, uint8_t channel4) // JS 1/30/2014
{
uint8_t code = 0;
code = code + 1 * channel1;
code = code + 2 * channel2;
code = code + 4 * channel3;
code = code + 8 * channel4;
uint8_t bytesToWrite[2] = { 77, code };
serial.writeBytes(bytesToWrite, 2);
}
void PulsePal::updateDisplay(string line1, string line2)
{
string Prefix;
string Message;
Message.append(line1);
Message += 254;
Message.append(line2);
Prefix += 78;
Prefix += Message.size();
Prefix.append(Message);
serial.writeBytes((unsigned char*)Prefix.data(), Prefix.size());
}
void PulsePal::setFixedVoltage(uint8_t channel, float voltage) // JS 1/30/2014
{
uint8_t voltageByte = 0;
voltageByte = voltageToByte(voltage);
uint8_t message1[3] = { 79, channel, voltageByte };
serial.writeBytes(message1, 3);
}
void PulsePal::abortPulseTrains() // JS 1/30/2014
{
uint8_t message1 = 80;
serial.writeByte(message1);
}
void PulsePal::disconnectClient() // JS 1/30/2014
{
uint8_t message1 = 81;
serial.writeByte(message1);
}
void PulsePal::setContinuousLoop(uint8_t channel, uint8_t state) // JS 1/30/2014
{
uint8_t message1[3] = {82, channel, state};
serial.writeBytes(message1, 3);
}
void PulsePal::constrain(uint32_t* value, uint32_t min, uint32_t max)
{
// value must be a multiple of 50
if (*value % 50 > 0)
// value must be a multiple of 100
if (*value % 100 > 0)
{
*value = *value - (*value % 50);
*value = *value - (*value % 100);
}
if (*value < min)
......@@ -223,20 +332,133 @@ uint8_t PulsePal::voltageToByte(float voltage)
// input: -10 to 10 V
// output: 0-255
uint8_t output = (uint8_t)((voltage+10)/20)*255;
uint8_t output = uint8_t(((voltage+10)/20)*255);
return output;
}
void PulsePal::programCustomTrain(uint8_t ID, uint8_t nPulses, float customPulseTimes[], float customVoltages[]){
int nMessageBytes = (nPulses * 5) + 6;
// Convert voltages to bytes
uint8_t voltageBytes[1000] = { 0 };
float thisVoltage = 0;
for (int i = 0; i < nPulses; i++) {
thisVoltage = customVoltages[i];
voltageBytes[i] = voltageToByte(thisVoltage);
}
// Convert times to bytes
uint8_t pulseTimeBytes[4000] = { 0 };
int pos = 0;
unsigned long pulseTimeMicroseconds;
for (int i = 0; i < nPulses; i++){
pulseTimeMicroseconds = (unsigned long)(customPulseTimes[i] * 1000000);
pulseTimeBytes[pos] = (uint8_t)(pulseTimeMicroseconds); pos++;
pulseTimeBytes[pos] = (uint8_t)(pulseTimeMicroseconds >> 8); pos++;
pulseTimeBytes[pos] = (uint8_t)(pulseTimeMicroseconds >> 16); pos++;
pulseTimeBytes[pos] = (uint8_t)(pulseTimeMicroseconds >> 24); pos++;
}
uint8_t *messageBytes = new uint8_t[nMessageBytes];
if (ID == 2) {
messageBytes[0] = 76; // Op code to program custom train 2
}
else {
messageBytes[0] = 75; // Op code to program custom train 1
}
messageBytes[1] = 0; // USB packet correction byte
messageBytes[2] = (uint8_t)(nPulses);
messageBytes[3] = (uint8_t)(nPulses >> 8);
messageBytes[4] = (uint8_t)(nPulses >> 16);
messageBytes[5] = (uint8_t)(nPulses >> 24);
int timeDataEnd = 6 + (nPulses * 4);
for (int i = 6; i < timeDataEnd; i++){
messageBytes[i] = pulseTimeBytes[i - 6];
}
for (int i = timeDataEnd; i < nMessageBytes; i++){
messageBytes[i] = voltageBytes[i - timeDataEnd];
}
serial.writeBytes(messageBytes, nMessageBytes);
}
void PulsePal::programAllParams() {
uint8_t messageBytes[163] = { 0 };
messageBytes[0] = 73;
int pos = 1;
uint32_t thisTime = 0;
float thisVoltage = 0;
uint8_t thisVoltageByte = 0;
// add time params
for (int i = 1; i < 5; i++){
thisTime = (uint32_t)(currentOutputParams[i].phase1Duration * 1000000);
messageBytes[pos] = (uint8_t)(thisTime); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 8); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 16); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 24); pos++;
thisTime = (uint32_t)(currentOutputParams[i].interPhaseInterval * 1000000);
messageBytes[pos] = (uint8_t)(thisTime); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 8); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 16); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 24); pos++;
thisTime = (uint32_t)(currentOutputParams[i].phase2Duration * 1000000);
messageBytes[pos] = (uint8_t)(thisTime); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 8); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 16); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 24); pos++;
thisTime = (uint32_t)(currentOutputParams[i].interPulseInterval * 1000000);
messageBytes[pos] = (uint8_t)(thisTime); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 8); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 16); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 24); pos++;
thisTime = (uint32_t)(currentOutputParams[i].burstDuration * 1000000);
messageBytes[pos] = (uint8_t)(thisTime); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 8); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 16); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 24); pos++;
thisTime = (uint32_t)(currentOutputParams[i].interBurstInterval * 1000000);
messageBytes[pos] = (uint8_t)(thisTime); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 8); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 16); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 24); pos++;
thisTime = (uint32_t)(currentOutputParams[i].pulseTrainDuration * 1000000);
messageBytes[pos] = (uint8_t)(thisTime); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 8); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 16); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 24); pos++;
thisTime = (uint32_t)(currentOutputParams[i].pulseTrainDelay * 1000000);
messageBytes[pos] = (uint8_t)(thisTime); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 8); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 16); pos++;
messageBytes[pos] = (uint8_t)(thisTime >> 24); pos++;
}
void PulsePal::triggerChannel(uint8_t chan)
{
const uint8_t code = 1 << (chan-1);
// add single-byte params
for (int i = 1; i < 5; i++){
messageBytes[pos] = (uint8_t)currentOutputParams[i].isBiphasic; pos++;
thisVoltage = PulsePal::currentOutputParams[i].phase1Voltage;
thisVoltageByte = voltageToByte(thisVoltage);
messageBytes[pos] = thisVoltageByte; pos++;
thisVoltage = PulsePal::currentOutputParams[i].phase2Voltage;
thisVoltageByte = voltageToByte(thisVoltage);
messageBytes[pos] = thisVoltageByte; pos++;
messageBytes[pos] = (uint8_t)currentOutputParams[i].customTrainID; pos++;
messageBytes[pos] = (uint8_t)currentOutputParams[i].customTrainTarget; pos++;
messageBytes[pos] = (uint8_t)currentOutputParams[i].customTrainLoop; pos++;
}
uint8_t bytesToWrite[2] = {84, code};
// add trigger channel 1 links
for (int i = 1; i < 5; i++){
messageBytes[pos] = (uint8_t)currentOutputParams[i].linkTriggerChannel1; pos++;
}
// add trigger channel 2 links
for (int i = 1; i < 5; i++){
messageBytes[pos] = (uint8_t)currentOutputParams[i].linkTriggerChannel2; pos++;
}
serial.writeBytes(bytesToWrite, 2);
}
// add trigger channel modes
messageBytes[pos] = (uint8_t)currentInputParams[1].triggerMode; pos++;
messageBytes[pos] = (uint8_t)currentInputParams[2].triggerMode; pos++;
serial.writeBytes(messageBytes, 163);
}
\ No newline at end of file
......@@ -21,6 +21,8 @@
*/
// Modified by JS 1/30/2014: Updated op codes for firmware 0_4, added new functions (indicated in comments below)
#ifndef __PULSEPAL_H_F2B7B63E__
#define __PULSEPAL_H_F2B7B63E__
......@@ -29,11 +31,7 @@
#include "ofSerial.h"
/**
Interfaces with the PulsePal from Lucid Biosystems
(www.lucidbiosystems.com)
Interface to PulsePal
@see PulsePalOutput
*/
......@@ -41,40 +39,77 @@
class PulsePal
{
public:
// Initialization and termination
PulsePal();
~PulsePal();
void initialize();
uint32_t PulsePal::getFirmwareVersion();
void disconnectClient();
void setPhase1Duration(uint8_t channel, uint32_t timeInMicroseconds);
void setInterPhaseInterval(uint8_t channel, uint32_t timeInMicroseconds);
void setPhase2Duration(uint8_t channel, uint32_t timeInMicroseconds);
void setInterPulseInterval(uint8_t channel, uint32_t timeInMicroseconds);
void setBurstDuration(uint8_t channel, uint32_t timeInMicroseconds);
void setBurstInterval(uint8_t channel, uint32_t timeInMicroseconds);
void setStimulusTrainDuration(uint8_t channel, uint32_t timeInMicroseconds);
void setStimulusTrainDelay(uint8_t channel, uint32_t timeInMicroseconds);
// Program single parameter
void setBiphasic(uint8_t channel, bool isBiphasic);
void setPhase1Voltage(uint8_t channel, float voltage);
void setPhase2Voltage(uint8_t channel, float voltage);
void setPhase1Duration(uint8_t channel, float timeInSeconds);
void setInterPhaseInterval(uint8_t channel, float timeInSeconds);
void setPhase2Duration(uint8_t channel, float timeInSeconds);
void setInterPulseInterval(uint8_t channel, float timeInSeconds);
void setBurstDuration(uint8_t channel, float timeInSeconds);
void setBurstInterval(uint8_t channel, float timeInSeconds);
void setPulseTrainDuration(uint8_t channel, float timeInSeconds);
void setPulseTrainDelay(uint8_t channel, float timeInSeconds);
void setTrigger1Link(uint8_t channel, uint8_t link_state);
void setTrigger2Link(uint8_t channel, uint8_t link_state);
void setCustomTrainID(uint8_t channel, uint8_t ID); // ID = 0: no custom train. ID = 1-2: custom trains 1 or 2
void setCustomTrainTarget(uint8_t channel, uint8_t target); // target = 0: Custom times define pulses Target = 1: They define bursts
void setCustomTrainLoop(uint8_t channel, uint8_t loop_state); // loop_state = 0: No loop 1: loop
// Program all parameters
void programAllParams();
// Program custom pulse train
void programCustomTrain(uint8_t ID, uint8_t nPulses, float customPulseTimes[], float customVoltages[]);
// Operations and settings
void triggerChannel(uint8_t channel);
void triggerChannels(uint8_t channel1, uint8_t channel2, uint8_t channel3, uint8_t channel4);
void updateDisplay(string line1, string line2);
void setFixedVoltage(uint8_t channel, float voltage);
void abortPulseTrains();
void setContinuousLoop(uint8_t channel, uint8_t state);
void setTriggerMode(uint8_t channel, uint8_t mode);
// Fields
struct OutputParams {
int isBiphasic = 0;
float phase1Voltage = 5;
float phase2Voltage = -5;
float phase1Duration = 0.001;
float interPhaseInterval = 0.001;
float phase2Duration = 0.001;
float interPulseInterval = 0.01;
float burstDuration = 0;
float interBurstInterval = 0;
float pulseTrainDuration = 1;
float pulseTrainDelay = 0;
int linkTriggerChannel1 = 1;
int linkTriggerChannel2 = 0;
int customTrainID = 0;
int customTrainTarget = 0;
int customTrainLoop = 0;
} currentOutputParams[5]; // Use 1-indexing for the channels (output channels 1-4 = currentOutputParams[1]-currentOutputParams[4])
struct InputParams {
int triggerMode;
} currentInputParams[3]; // Use 1-indexing for the trigger channels
private:
void constrain(uint32_t* value, uint32_t min, uint32_t max);
void program(uint8_t channel, uint8_t paramCode, uint32_t paramValue);
void program(uint8_t channel, uint8_t paramCode, uint8_t paramValue);
uint8_t voltageToByte(float voltage);
ofSerial serial;
};
#endif // __PULSEPAL_H_F2B7B63E__
#endif // __PULSEPAL_H_F2B7B63E__
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment