Parsing data packets

Hello everyone,

I am trying to build some software with labview to read the data packets coming from the Arduino. I have a mega hooked up to an Olimex ECG Shield. SHIELD-EKG-EMG - Open Source Hardware Board. I am having trouble understanding the example code.

The serial monitor spits out a bunch of characters. I think I see the start bit, 7 data bits, and a stop bit but its all in char. How do I make since of this data? Any help would be appreciated. Thanks!

/**********************************************************/
/* Demo program for:                                      */
/*    Board: SHIELD-EKG/EMG + Olimexino328                */
/*  Manufacture: OLIMEX                                   */
/*  COPYRIGHT (C) 2012                                    */
/*  Designed by:  Penko Todorov Bozhkov                   */
/*   Module Name:   Sketch                                */
/*   File   Name:   ShieldEkgEmgDemo.ino                  */
/*   Revision:  Rev.A                                     */
/*    -> Added is suppport for all Arduino boards.        */
/*       This code could be recompiled for all of them!   */
/*   Date: 19.12.2012                                     */
/*   Built with Arduino C/C++ Compiler, version: 1.0.3    */
/**********************************************************/
/**********************************************************
Purpose of this programme is to give you an easy way to 
connect Olimexino328 to ElectricGuru(TM), see:
https://www.olimex.com/Products/EEG/OpenEEG/EEG-SMT/resources/ElecGuru40.zip
where you'll be able to observe yours own EKG or EMG signal.
It is based on:
***********************************************************
* ModularEEG firmware for one-way transmission, v0.5.4-p2
* Copyright (c) 2002-2003, Joerg Hansmann, Jim Peters, Andreas Robinson
* License: GNU General Public License (GPL) v2
***********************************************************
For proper communication packet format given below have to be supported:
///////////////////////////////////////////////
////////// Packet Format Version 2 ////////////
///////////////////////////////////////////////
// 17-byte packets are transmitted from Olimexino328 at 256Hz,
// using 1 start bit, 8 data bits, 1 stop bit, no parity, 57600 bits per second.

// Minimial transmission speed is 256Hz * sizeof(Olimexino328_packet) * 10 = 43520 bps.

struct Olimexino328_packet
{
  uint8_t	sync0;		// = 0xa5
  uint8_t	sync1;		// = 0x5a
  uint8_t	version;	// = 2 (packet version)
  uint8_t	count;		// packet counter. Increases by 1 each packet.
  uint16_t	data[6];	// 10-bit sample (= 0 - 1023) in big endian (Motorola) format.
  uint8_t	switches;	// State of PD5 to PD2, in bits 3 to 0.
};
*/
/**********************************************************/
#include <compat/deprecated.h>
#include <FlexiTimer2.h>
//http://www.arduino.cc/playground/Main/FlexiTimer2

// All definitions
#define NUMCHANNELS 6
#define HEADERLEN 4
#define PACKETLEN (NUMCHANNELS * 2 + HEADERLEN + 1)
#define SAMPFREQ 256                      // ADC sampling rate 256
#define TIMER2VAL (1024/(SAMPFREQ))       // Set 256Hz sampling frequency                    
#define LED1  13
#define CAL_SIG 9

// Global constants and variables
volatile unsigned char TXBuf[PACKETLEN];  //The transmission packet
volatile unsigned char TXIndex;           //Next byte to write in the transmission packet.
volatile unsigned char CurrentCh;         //Current channel being sampled.
volatile unsigned char counter = 0;	  //Additional divider used to generate CAL_SIG
volatile unsigned int ADC_Value = 0;	  //ADC current value

//~~~~~~~~~~
// Functions
//~~~~~~~~~~

/****************************************************/
/*  Function name: Toggle_LED1                      */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Switches-over LED1.                   */
/****************************************************/
void Toggle_LED1(void){

 if((digitalRead(LED1))==HIGH){ digitalWrite(LED1,LOW); }
 else{ digitalWrite(LED1,HIGH); }
 
}


/****************************************************/
/*  Function name: toggle_GAL_SIG                   */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Switches-over GAL_SIG.                */
/****************************************************/
void toggle_GAL_SIG(void){
  
 if(digitalRead(CAL_SIG) == HIGH){ digitalWrite(CAL_SIG, LOW); }
 else{ digitalWrite(CAL_SIG, HIGH); }
 
}


/****************************************************/
/*  Function name: setup                            */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Initializes all peripherals           */
/****************************************************/
void setup() {

 noInterrupts();  // Disable all interrupts before initialization
 
 // LED1
 pinMode(LED1, OUTPUT);  //Setup LED1 direction
 digitalWrite(LED1,LOW); //Setup LED1 state
 pinMode(CAL_SIG, OUTPUT);
 
 //Write packet header and footer
 TXBuf[0] = 0xa5;    //Sync 0
 TXBuf[1] = 0x5a;    //Sync 1
 TXBuf[2] = 2;       //Protocol version
 TXBuf[3] = 0;       //Packet counter
 TXBuf[4] = 0x02;    //CH1 High Byte
 TXBuf[5] = 0x00;    //CH1 Low Byte
 TXBuf[6] = 0x02;    //CH2 High Byte
 TXBuf[7] = 0x00;    //CH2 Low Byte
 TXBuf[8] = 0x02;    //CH3 High Byte
 TXBuf[9] = 0x00;    //CH3 Low Byte
 TXBuf[10] = 0x02;   //CH4 High Byte
 TXBuf[11] = 0x00;   //CH4 Low Byte
 TXBuf[12] = 0x02;   //CH5 High Byte
 TXBuf[13] = 0x00;   //CH5 Low Byte
 TXBuf[14] = 0x02;   //CH6 High Byte
 TXBuf[15] = 0x00;   //CH6 Low Byte 
 TXBuf[2 * NUMCHANNELS + HEADERLEN] =  0x01;	// Switches state

 // Timer2
 // Timer2 is used to setup the analag channels sampling frequency and packet update.
 // Whenever interrupt occures, the current read packet is sent to the PC
 // In addition the CAL_SIG is generated as well, so Timer1 is not required in this case!
 FlexiTimer2::set(TIMER2VAL, Timer2_Overflow_ISR);
 FlexiTimer2::start();
 
 // Serial Port
 Serial.begin(57600);
 //Set speed to 57600 bps
 
 // MCU sleep mode = idle.
 //outb(MCUCR,(inp(MCUCR) | (1<<SE)) & (~(1<<SM0) | ~(1<<SM1) | ~(1<<SM2)));
 
 interrupts();  // Enable all interrupts after initialization has been completed
}

/****************************************************/
/*  Function name: Timer2_Overflow_ISR              */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Determines ADC sampling frequency.    */
/****************************************************/
void Timer2_Overflow_ISR()
{
  // Toggle LED1 with ADC sampling frequency /2
  Toggle_LED1();
  
  //Read the 6 ADC inputs and store current values in Packet
  for(CurrentCh=0;CurrentCh<6;CurrentCh++){
    ADC_Value = analogRead(CurrentCh);
    TXBuf[((2*CurrentCh) + HEADERLEN)] = ((unsigned char)((ADC_Value & 0xFF00) >> 8));	// Write High Byte
    TXBuf[((2*CurrentCh) + HEADERLEN + 1)] = ((unsigned char)(ADC_Value & 0x00FF));	// Write Low Byte
  }
	 
  // Send Packet
  for(TXIndex=0;TXIndex<17;TXIndex++){
    Serial.write(TXBuf[TXIndex]);
  }
  
  // Increment the packet counter
  TXBuf[3]++;			
  
  // Generate the CAL_SIGnal
  counter++;		// increment the devider counter
  if(counter == 12){	// 250/12/2 = 10.4Hz ->Toggle frequency
    counter = 0;
    toggle_GAL_SIG();	// Generate CAL signal with frequ ~10Hz
  }
}


/****************************************************/
/*  Function name: loop                             */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Puts MCU into sleep mode.             */
/****************************************************/
void loop() {
  
 __asm__ __volatile__ ("sleep");
 
}

I think I see the start bit, 7 data bits, and a stop bit but its all in char.

Nonsense. bits and chars are completely different sizes.

How do I make since of this data?

Make sense of what data where?

void Timer2_Overflow_ISR()
{
  // Send Packet
  for(TXIndex=0;TXIndex<17;TXIndex++){
    Serial.write(TXBuf[TXIndex]);
  }

NOT in and ISR, you don’t.

The data bits, stop bit, parity and baud rate define the communication standard that the Arduino is using. The receiving software on your PC must match that.

As far as I can see if the communication works the Arduino will send packets of 17 bytes as defined Setup().

I can't immediately see what the Struct is for.

...R

I’m trying to figure out what is being sent to the serial port. this piece of code is filling the buffer with the packet header, data, and footer I assume…

for(CurrentCh=0;CurrentCh<6;CurrentCh++){
    ADC_Value = analogRead(CurrentCh);
    TXBuf[((2*CurrentCh) + HEADERLEN)] = ((unsigned char)((ADC_Value & 0xFF00) >> 8));	// Write High Byte
    TXBuf[((2*CurrentCh) + HEADERLEN + 1)] = ((unsigned char)(ADC_Value & 0x00FF));	// Write Low Byte
  }

So is the ADC_Value a char?

I attached a pic of the serial output.

I attached a pic of the serial output

Why are you using a text-based application to receive binary data? Why are you expecting to see something meaningful when you do that?

This is example code I got off of there site. It works with the elecGuru software, so I know it is possible. The software they offer is not open source, and not finished. I would like to read the data in to LabView. I just need to understand how to parse it.

Looks like I need to change the char to dec.

Looks like I need to change the char to dec.

dec is not a type.

I mean char to its dec number equivalent. Such as - is equal to 45 ! is equal to 33

I mean char to its dec number equivalent. Such as - is equal to 45 ! is equal to 33

Whatever floats your boat. Personally, I think you have a lot of other problems, like sending binary to the serial port in an ISR. But, if you feel it is better to treat the hangnail instead of the chainsaw wound that nearly severed you arm, be my guest.

I'm by no means a professional computer programmer, and am doing this for a hobby. If you have any better ideas let me know. As stated earlier I did not personally write this code. I am only trying to make sense of it. The code woks with prebuilt software, and reads a heartbeat just fine, so it must not be that bad.

so it must not be that bad.

Yes, it is.

As stated earlier I did not personally write this code.

That's a copout. You are using the code. It is, therefore, your responsibility to understand it.

PaulS that is exactly what I am trying to do. I am sitting at my computer trying to better understand it. I’m learning , all sorts of stuff. That’s the reason I posted here. So I can better understand it.

Instead of telling me how bad the program is how about you give some advice? I’m all ears.

Instead of telling me how bad the program is how about you give some advice?

You mean like not calling Serial.write() in an ISR? I already did.
Or, like not using a text based application to view binary data? I already did.
Or perhaps, learning proper terminology, so you don’t confuse bits and chars? I already did.

You’ve ignored it all. So, why should I offer more?

whats so bad about calling Serial.write in an ISR?

like not using a text based application to view binary data?

If only I could read your mind, then I would know exactly what you mean here.

Or perhaps, learning proper terminology, so you don’t confuse bits and chars? I already did.

Every Char is eventually stored as bits.

Maybe you are the one that is confused.

If you can give me some helpful pointers then do it, but the stuff you are saying is not convincing me that you are a super coding genius.

whats so bad about calling Serial.write in an ISR?

It's a great way of deadlocking your code. Serial output is interrupt-driven, but interrupts are disabled in an ISR. If the buffer ever fills, the code will lockup

whats so bad about calling Serial.write in an ISR?

Serial.write() tries to put stuff in a buffer. Room is made in the buffer by actually shifting bytes out the serial port. The actual shifting happens using interrupts. Interrupts are disabled during an ISR.

If a call to Serial.write() happens when the buffer is full, it will block waiting for room in the buffer. But, room will not be made until Serial.write() ends, and the ISR ends and interrupts are enabled again. The Arduino will appear to have hung.

Every Char is eventually stored as bits.

Yes, but you can't normally see the individual bits, as you claimed.

Maybe you are the one that is confused.

About what? This is so confusing.

but the stuff you are saying is not convincing me that you are a super coding genius.

I've never made that claim. I make my living writing computer programs, and I'm pretty good at it, and I teach other people. But, I've never claimed to be a genius.

I'll be happy to stop replying to your posts.

Thanks AWOL. I have it reading into Labview now by passing a string through the serial port with Serial.print().

Thank you for the explanation PaulS. That helps me understand what is going on a bit better.

Will I still run into the problem using Serial.print(), or is this function handled in a different way?

Would you recommend rewriting the code without it being timer driven?

Will I still run into the problem using Serial.print()

Serial.print() converts the value to a string, and then calls Serial.write() to write the string, char by char. So, yes, same problem.

Would you recommend rewriting the code without it being timer driven?

No. The ISR needs to set a flag. The loop() function needs to, on each pass, see if that flag is set. If it is, loop() should call Serial.write() to send the data, and then clear the flag.

Sounds good, I’m going to have to do some Google searching to see about setting a flag. Could I do this with a simple counter?

#include <compat/deprecated.h>
#include <FlexiTimer2.h>



#define SAMPFREQ 256                      // ADC sampling rate 256
#define TIMER2VAL (1024/(SAMPFREQ))       // Set 256Hz sampling frequency                    
#define LED1  13
#define CAL_SIG 9

// Global constants and variables

volatile unsigned char counter = 0;	  //Additional divider used to generate CAL_SIG
volatile double ADC_Value = 0;	  //ADC current value

int flag = 0;

//~~~~~~~~~~
// Functions
//~~~~~~~~~~

/****************************************************/
/*  Function name: Toggle_LED1                      */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Switches-over LED1.                   */
/****************************************************/
void Toggle_LED1(void){

 if((digitalRead(LED1))==HIGH){ digitalWrite(LED1,LOW); }
 else{ digitalWrite(LED1,HIGH); }
 
}


/****************************************************/
/*  Function name: toggle_GAL_SIG                   */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Switches-over GAL_SIG.                */
/****************************************************/
void toggle_GAL_SIG(void){
  
 if(digitalRead(CAL_SIG) == HIGH){ digitalWrite(CAL_SIG, LOW); }
 else{ digitalWrite(CAL_SIG, HIGH); }
 
}


/****************************************************/
/*  Function name: setup                            */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Initializes all peripherals           */
/****************************************************/
void setup() {

 noInterrupts();  // Disable all interrupts before initialization
 
 // LED1
 pinMode(LED1, OUTPUT);  //Setup LED1 direction
 digitalWrite(LED1,LOW); //Setup LED1 state
 pinMode(CAL_SIG, OUTPUT);
 
 
 // Timer2 is used to setup the analag channels sampling frequency and packet update.
 // Whenever interrupt occures, the current read packet is sent to the PC
 // In addition the CAL_SIG is generated as well, so Timer1 is not required in this case!
 FlexiTimer2::set(TIMER2VAL, Timer2_Overflow_ISR);
 FlexiTimer2::start();
 
 // Serial Port
 Serial.begin(57600);
 //Set speed to 57600 bps
 
 // MCU sleep mode = idle.
 //outb(MCUCR,(inp(MCUCR) | (1<<SE)) & (~(1<<SM0) | ~(1<<SM1) | ~(1<<SM2)));
 
 interrupts();  // Enable all interrupts after initialization has been completed
}

/****************************************************/
/*  Function name: Timer2_Overflow_ISR              */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Determines ADC sampling frequency.    */
/****************************************************/
void Timer2_Overflow_ISR()
{
  // Toggle LED1 with ADC sampling frequency /2
  Toggle_LED1();
  
  
 
	 
  ADC_Value = analogRead(1);
  flag++;
  
  // Increment the packet counter

  
  // Generate the CAL_SIGnal
  counter++;		// increment the devider counter
  if(counter == 12){	// 250/12/2 = 10.4Hz ->Toggle frequency
    counter = 0;
    toggle_GAL_SIG();	// Generate CAL signal with frequ ~10Hz
  }
}


/****************************************************/
/*  Function name: loop                             */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Puts MCU into sleep mode.             */
/****************************************************/
void loop() {
  if(flag == 1){
    Serial.println(ADC_Value*.0049,8);
    flag = 0;
  }
 __asm__ __volatile__ ("sleep");
 
}

I can't help feeling that people are assuming @Houser636 knows a great deal more than s/he actually does.

If I'm right it would do no harm to be a little less critical.

...R