Skip to content

Instantly share code, notes, and snippets.

@dusjagr
Created February 13, 2019 16:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dusjagr/73773c650bf341842f70b08953fae009 to your computer and use it in GitHub Desktop.
Save dusjagr/73773c650bf341842f70b08953fae009 to your computer and use it in GitHub Desktop.
HeartBeat to KORG-sync
//
// Spike Recorder Arduino code 30. Sep. 2015
// V1.1
// Backyard Brains
// Stanislav Mircic
// https://backyardbrains.com/
// This code is made for Heart and Brain SpikerShield and similar products that need to communicate with
// Spike Recorder desktop software via USB (virtual serial port).
// Sample rate depends on number of channels that are enabled. It is 10kHz divided with number of channels
// So, if only one channel is enabled sample rate is 10kHz. If two channels are enabled sample rate will be 5kHz etc.
//
// Edited by Gaudi und dusjagr, Feb 2019
//
#define CURRENT_SHIELD_TYPE "HWT:HEARTSS;"
#define EKG A0 //we are reading from AnalogIn 0
#define BUFFER_SIZE 100 //sampling buffer size
#define SIZE_OF_COMMAND_BUFFER 30 //command buffer size
#define LENGTH_OF_MESSAGE_IMPULS 100 // length of message impuls in ms
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
int i,state,state2;
long hrate_t, hrate_c;
long display_t=0;
float hrate;
int buffersize = BUFFER_SIZE;
int head = 0;//head index for sampling circular buffer
int tail = 0;//tail index for sampling circular buffer
byte writeByte;
char commandBuffer[SIZE_OF_COMMAND_BUFFER];//receiving command buffer
int16_t reading[BUFFER_SIZE]; //Sampling buffer
#define ESCAPE_SEQUENCE_LENGTH 6
byte escapeSequence[ESCAPE_SEQUENCE_LENGTH] = {255,255,1,1,128,255};
byte endOfescapeSequence[ESCAPE_SEQUENCE_LENGTH] = {255,255,1,1,129,255};
int messageImpulsPin = 5;
int messageImpulseTimer = 0;
long debouncing_time = 15; //Debouncing Time in Milliseconds
volatile unsigned long last_micros;
int redButton = 4;
int greenButton = 7;
int redLED = 12;
int greenLED = 8;
int redLEDCounter = 0;
int speaker = 6;
int greenLEDCounter = 0;
int redButtonReady = 1;
int greenButtonReady = 1;
////This sets up serial communication values can 9600, 14400, 19200, 28800, 31250, 38400, 57600, and 115200, also 300, 600, 1200, 2400, 4800, but that's too slow for us
/// Interrupt number - very important in combination with bit rate to get accurate data
int interrupt_Number=198;// Output Compare Registers value = (16*10^6) / (Fs*8) - 1 set to 1999 for 1000 Hz sampling, set to 3999 for 500 Hz sampling, set to 7999 for 250Hz sampling, 199 for 10000 Hz Sampling
int numberOfChannels = 1;//current number of channels sampling
int tempSample = 0;
float tempSampleHigh = 0;
int tempSampleLow = 0;
int tempSampleAvarage = 600;
int commandMode = 0;//flag for command mode. Don't send data when in command mode
void setup(){
Serial.begin(115200); //Serial communication baud rate (alt. 115200)
delay(300); //whait for init of serial
Serial.println("StartUp!");
Serial.setTimeout(2);
pinMode(messageImpulsPin, OUTPUT);
// initialize digital pin LED_BUILTIN as an output.
pinMode(redLED, OUTPUT);
pinMode(greenLED, OUTPUT);
pinMode(speaker, OUTPUT);
// TIMER SETUP- the timer interrupt allows preceise timed measurements of the reed switch
//for mor info about configuration of arduino timers see http://arduino.cc/playground/Code/Timer1
cli();//stop interrupts
//Make ADC sample faster. Change ADC clock
//Change prescaler division factor to 16
sbi(ADCSRA,ADPS2);//1
cbi(ADCSRA,ADPS1);//0
cbi(ADCSRA,ADPS0);//0
//set timer1 interrupt at 10kHz
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0;
OCR1A = interrupt_Number;// Output Compare Registers
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS11 bit for 8 prescaler
TCCR1B |= (1 << CS11);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei();//allow interrupts
//END TIMER SETUP
}
ISR(TIMER1_COMPA_vect) {
//Interrupt at the timing frequency you set above to measure to measure AnalogIn, and filling the buffers
if(messageImpulseTimer>0)
{
messageImpulseTimer--;
if(messageImpulseTimer==0)
{
digitalWrite(messageImpulsPin, LOW);
}
}
if(commandMode!=1)
{
//Put samples in sampling buffer "reading". Since Arduino Mega has 10bit ADC we will split every sample to 2 bytes
//First byte will contain 3 most significant bits and second byte will contain 7 least significat bits.
//First bit in all byte will not be used for data but for marking begining of the frame of data (array of samples from N channels)
//Only first byte in frame will have most significant bit set to 1
//Sample first channel and put it into buffer
tempSample = analogRead(A0)-tempSampleAvarage;
if (tempSample>tempSampleHigh) tempSampleHigh = tempSample;
if (tempSample<tempSampleLow) tempSampleLow = tempSample;
if (tempSample>(tempSampleHigh*0.8))
//if (tempSample>300)
{
// tone(11, 400, 10);
digitalWrite(redLED, HIGH); // turn the LED on (HIGH is the voltage level)
digitalWrite(greenLED, LOW); // turn the LED on (HIGH is the voltage level)
if (state==0){
hrate_t=hrate_c;
hrate_c=0;};
state=1;
} else
if (tempSample<(tempSampleLow*0.8))
//if (tempSample<-300)
{
// tone(11, 700, 10);
digitalWrite(greenLED, HIGH); // turn the LED on (HIGH is the voltage level)
digitalWrite(redLED, LOW); // turn the LED on (HIGH is the voltage level)
state=0;
} ;
//tempSample=i;
hrate_c++;
display_t++;
reading[head] = tempSample;//Mark begining of the frame by setting MSB to 1
head = head+1;
if(head==BUFFER_SIZE)
{
head = 0;
}
if(numberOfChannels>1)
{
//Sample 2. channel and put it into buffer
tempSample = analogRead(A1);
reading[head] = (tempSample>>7) & 0x7F;
head = head+1;
if(head==BUFFER_SIZE)
{
head = 0;
}
reading[head] = tempSample & 0x7F;
head = head+1;
if(head==BUFFER_SIZE)
{
head = 0;
}
}
}
}
void serialEvent()
{
commandMode = 1;//frag that we are receiving commands through serial
TIMSK1 &= ~(1 << OCIE1A);//disable timer for sampling
// read untill \n from the serial port:
String inString = Serial.readStringUntil('\n');
//convert string to null terminate array of chars
inString.toCharArray(commandBuffer, SIZE_OF_COMMAND_BUFFER);
commandBuffer[inString.length()] = 0;
// breaks string str into a series of tokens using delimiter ";"
// Namely split strings into commands
char* command = strtok(commandBuffer, ";");
while (command != 0)
{
// Split the command in 2 parts: name and value
char* separator = strchr(command, ':');
if (separator != 0)
{
// Actually split the string in 2: replace ':' with 0
*separator = 0;
--separator;
if(*separator == 'c')//if we received command for number of channels
{
separator = separator+2;
numberOfChannels = atoi(separator);//read number of channels
}
if(*separator == 's')//if we received command for sampling rate
{
//do nothing. Do not change sampling rate at this time.
//We calculate sampling rate further below as (max Fs)/(Number of channels)
}
if(*separator == 'p')//if we received command for impuls
{
//Set impuls
separator = separator+2;
digitalWrite(messageImpulsPin, HIGH);
messageImpulseTimer = (LENGTH_OF_MESSAGE_IMPULS * 10)/numberOfChannels;
}
if(*separator == 'b')//if we received command for impuls
{
//sendMessage("HWT:PLANTSS;");
//sendMessage("HWT:MUSCLESS;");
sendMessage(CURRENT_SHIELD_TYPE);
}
}
// Find the next command in input string
command = strtok(0, ";");
}
//calculate sampling rate
OCR1A = (interrupt_Number+1)*numberOfChannels - 1;
TIMSK1 |= (1 << OCIE1A);//enable timer for sampling
commandMode = 0;
}
//push message to main sending buffer
//timer for sampling must be dissabled when
//we call this function
void sendMessage(const char * message)
{
int i;
//send escape sequence
for(i=0;i< ESCAPE_SEQUENCE_LENGTH;i++)
{
reading[head++] = escapeSequence[i];
if(head==BUFFER_SIZE)
{
head = 0;
}
}
//send message
i = 0;
while(message[i] != 0)
{
reading[head++] = message[i++];
if(head==BUFFER_SIZE)
{
head = 0;
}
}
//send end of escape sequence
for(i=0;i< ESCAPE_SEQUENCE_LENGTH;i++)
{
reading[head++] = endOfescapeSequence[i];
if(head==BUFFER_SIZE)
{
head = 0;
}
}
}
void loop(){
while(head!=tail && commandMode!=1)//While there are data in sampling buffer whaiting
{
i++;
if (i==100)
{
Serial.print(reading[tail]);
Serial.print(" ");
Serial.print(state*100);
hrate=hrate_t;
hrate=10000/hrate;
hrate=hrate*60;
if (hrate>100) hrate=100;
//Serial.print(" ");
//Serial.print(hrate*10);
//Serial.print(hrate_t);
Serial.print(" ");
Serial.print(tempSampleHigh*0.8);
Serial.print(" ");
Serial.println(tempSampleLow*0.8);
tempSampleHigh=tempSampleHigh*0.99;
tempSampleLow=tempSampleLow*0.999;
i=0;
}
//Move thail for one byte
tail = tail+1;
if(tail==BUFFER_SIZE)
{
tail = 0;
}
}
if ((display_t>9000)&(state2==1))
{
digitalWrite(speaker, HIGH); // turn the LED on (HIGH is the voltage level)
display_t=0;
state2=0;
};
if ((display_t>1000)&(state2==0))
{
digitalWrite(speaker, LOW); // turn the LED on (HIGH is the voltage level)
display_t=0;
state2=1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment