Serial freeze

Hi everybody!
I'm building a temperature logger prototype using arduino mega 2560.
It's connected via USB to a windows XP PC that run a log-software written in C++ (MinGW), which use the windows API to manage the serial port.
It happens that sometimes the serial communication freeze, the serial port is still busy and the only way to make it work again is to physically disconnect and change the USB port.

I really don't know how to manage this problem. I tried to upgrade the firmware and force an hardware reset with the watchdog when disconnection occurs (the C++ software send a confirm char to arduino when the software receive data), but nothing change.
I'm searching a free serial sniffing software, but i can't find a good one (maybe you can suggest one?).

This is a big problem, 'cause the prototype is installed in a chemical analytical lab and need to work 24/24 hours.
Do you have some suggestions???

May be It's buffer overflow?
May be you send new requests until old was completed?
May be you have spikes(electric noice) and USB port stop working?
So, it may be:

  1. program mistake
  2. Noice on USB connector

When you disconnect the serial port and reconnect it the Mega will restart. This may disguise a problem with the code in the Mega.

As you have not posted the program I cannot say any more about it.
And please use the code button </>so your code looks like thisand is easy t
...R

Dmitron1036:

  1. program mistake
  2. Noice on USB connector

You are dead right about that., Indeed it might even be
3) A windows problem and nothing to do with Arduino.
But we will never know, and no suggestions will be forthcoming, because the code is clearly a secret.

Here is the code code...
When Arduino is in the "acquisition" state, it sends a "B" when it is ready to send data, and if recieve a "K" then will send all the temperatures to the PC.
I added this confirmation because there is an UMTS shield that send an SMS when the PC stop to confirm the data recieved, so when a freeze occurs i am warned.
I omitted the SMS code not because it's "clearly a secret" but 'cause it's not relevant and will mess up the reading of the code.

char stato;

float tensione;
float resistenza;
float temperatura;
float sommatens[16];

float I[16]={0.001,0.001,0.001,0.001,0.001,0.001,0.001,0.001,
             0.001,0.001,0.001,0.001,0.001,0.001,0.001,0.001};
/*the current is changed in the calibration state*/


void loop() {
  
  ...
  /*** CODE FOR OTHER INTERNAL STATE ***/
  ...
  
  } else if(stato=='N') {
    if(conta==numero_campioni) {
      Serial.print('B');
      int i=0;
      char leggi[2] = {0, 0};
      float T[16];
      while( (leggi[0]!='K' ) && i++<16000) leggi[0] = Serial.read();   //wait the ok from PC program
            
      while( Serial.available() ) Serial.read();                        //clean the input buffer
  
      for(i=0;i<numChannel;i++)
      {
        tensione = sommatens[i]/numero_campioni;
        resistenza = (tensione/I[i]);
        temperatura = (-A + sqrt( square(A) - 4.0*B*(1.0-resistenza/R0)))/(2.0*B);
        T[i] = temperatura;
      }
        
      if( leggi[0] == 'K')
      {
        for(i=0;i<numChannel;i++) {
          Serial.print(T[i], 1);
          Serial.print(";");
          delay(10);
        }
      } else if ( leggi[0] == 'C' || leggi[0] == 'S' ) {
        stato = leggi[0];
      }
      Serial.println("E");
      for(i=0;i<numChannel;i++) { sommatens[i]=0.0; prova[i]=0; }
      conta=0;
    } else {
      for(i=0;i<numChannel;i++) {
        sensorValue = analogRead(sensorPin[i]);
        tensione=(((float)sensorValue*AREF)/MAXANALOG);
                      
        sommatens[i]+=tensione;
      }
      conta++;
    }
  }
}

Maybe it's better to send data only when is requested from the PC program?

You have not posted a complete program and it may be that the cause of the crash lies elsewhere.

It is impossible to tell from your snippet how much data is being sent or at what baud rate. Too much data at a too-low baud rate can cause the Serial output to block.

If you are using the String class elsewhere in the program that could be the cause as it does not behave well in the small memory of an Arduino.

Maybe in some circumstances your code writes past the end of an array and cause data corruption.

Have you tried running the cut-down version of the program to see if the problem lies within it?

I don't think it matters whether the Arduino or the PC initiates a communication as long as both parties know what to do. Sending large quantities of unsolicited data could cause a problem - but I don't think you are doing that.

You may find some ideas in Serial Input Basics or in this Python - Arduino demo. The serial basics techniques can also be used in a PC program.

...R

Thank a lot for the answer.
I'm posting now the complete code in two part, it's a bit messy, but the problem arised in the first version of the program, wich was almost the one i posted before.
Anyway the freezes occur only when arduino is in the acquisition state (after the PC program send an "N"), so the relevant code is in that area.
If you have patience to read this mess i'll be really grateful.

EDIT: i'll post the code in the next replyes 'cause it's too big

#include <avr/io.h>
#include <avr/wdt.h>
#include <math.h>
#include "SimpleTimer.h"
#include <SoftwareSerial.h>
#include "GSM_Shield_mega.h"

#include <EEPROM.h>

#define IS_GSM

#define Reset_AVR() wdt_enable(WDTO_1S); while(1) {} 

void (*reset)(void) = 0;

#define CONFIG_VERSION "ls1"

#define CONFIG_START 32
//#define TENSIONI

//#define N      4
#define AREF   5.030

#define R0 1000.0
#define A 3.90802e-3
#define B -5.802e-7

#define MAXANALOG  1023

#define SWITCH  7

int sensorPin[16];
char stato, blk;

int sensorValue;
long int prova[16];

long int i,j;
int conta;
int numero_campioni;

float tensione;
float resistenza;
float temperatura;
float sommatens[16];

float correzione_tensione[16];
float resistenza_fili[16];

float I_PT1000[16];
float I[16]={0.001,0.001,0.001,0.001,0.001,0.001,0.001,0.001,
             0.001,0.001,0.001,0.001,0.001,0.001,0.001,0.001};
             
SimpleTimer timer;
BoundTimer Btimer;
int reset_timer;
int error_timer;
int bound_timer[16];

GSM gsm;

struct Settaggi {
  char canali;
  bool sms;
  char tel[14];
  bool isSup[16];
  bool isInf[16];
  float Sup[16];
  float Inf[16];
} settaggi = {
  16,
  0,
  "+393333333333",
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};

void error_callback()
{
    Serial.println("ERRORE");
    #if defined(IS_GSM)
    if(settaggi.sms) {
      char testo[] = "TempLog ha smesso di funzionare.\nLa centralina si è disconnessa dal computer.";
      gsm.SendSMS(settaggi.tel, testo);
	  gsm.SendSMS("+393333333333", testo);  
	  gsm.SendSMS("+393333333333", testo);  
	  gsm.SendSMS("+393333333333", testo);  
      delay(2000);
    }
    #endif
    inizializza();
    Serial.end();
    reset_callback();
}

void bound_callback(char c, bool sup )
{
  /*#if defined(IS_GSM)
  if(settaggi.sms) {
   char testo[50];
   sprintf(testo, "La sonda %i ha superato la soglia %s", c, (sup ? "superiore" : "inferiore"));
   gsm.SendSMS(settaggi.tel, testo);
  }
  #endif*/
}

void reset_callback()
{
  Reset_AVR();
}

void inizializza()
{
  stato=-1;  
  blk=0;
  caricaSettaggi();
  //salvaSettaggi();
  caricaCorrenti();
  //salvaCorrenti();
  pinMode(13, OUTPUT);
  pinMode(SWITCH, INPUT);
  
  sensorPin[0]=A0;
  sensorPin[1]=A1;
  sensorPin[2]=A2;
  sensorPin[3]=A3;
  sensorPin[4]=A4;
  sensorPin[5]=A5;
  sensorPin[6]=A6;
  sensorPin[7]=A7;
  sensorPin[8]=A8;
  sensorPin[9]=A9;
  sensorPin[10]=A10;
  sensorPin[11]=A11;
  sensorPin[12]=A12;
  sensorPin[13]=A13;
  sensorPin[14]=A14;
  sensorPin[15]=A15;

  conta=0;
  numero_campioni=2000;
  
  for(i=0;i<15;i++) {
    sommatens[i]=0.0;
  }
  
  
  error_timer = timer.setTimeout(30000, error_callback);
  timer.disable(error_timer);
  
  reset_timer = timer.setInterval(259200000, reset_callback);
  //reset_timer = timer.setInterval(60000, reset_callback);
}

void setup() 
{
  Serial.begin(9600);
  digitalWrite(13,1);  
  
  inizializza();

  #if defined(IS_GSM)
  Serial.println("Init GSM module");
  gsm.TurnOn(9600);          //module power on
  gsm.InitParam(PARAM_SET_1);//configure the module  
  gsm.Echo(0);               //enable AT echo
  #endif
  
  Serial.println("READY");
  //Serial.println(UCSR0C, BIN);
  
  //delay(5000);
  
  //error_callback();
}
   
void caricaSettaggi() {
  for(int i=0; i< sizeof(settaggi); i++)
    *((char*)&settaggi + i) = EEPROM.read(CONFIG_START+i);
}

void salvaSettaggi() {
  for(int i=0; i< sizeof(settaggi); i++)
    EEPROM.write(CONFIG_START+i,  *((char*)&settaggi + i));
}

void caricaCorrenti() {
  for(int i=0; i<sizeof(float[16]); i++)
    *((char*)&I + i) = EEPROM.read(CONFIG_START + sizeof(settaggi) + i);
}

void salvaCorrenti() {
  for(int i=0; i<sizeof(float[16]); i++)
    EEPROM.write(CONFIG_START+i+sizeof(settaggi),  *((char*)&I + i));
}
void loop() 
{
            if(stato != 'N') {
 if(stato == -1) stato = Serial.read();
 if(stato=='N'){ 
 if(timer.isEnabled(error_timer)) timer.disable(error_timer);
 Serial.write('K'); 
 digitalWrite(13,0); 
 }
 //else if(stato != 'S' && stato != 'R' && stato != 'C') stato = -1;
            }
             
            if(stato=='S')
            {
 if(timer.isEnabled(error_timer)) timer.disable(error_timer);
 digitalWrite(13,1);
 Serial.print('K');
 //delay(50);
                Salva();
 digitalWrite(13,0); 
                stato=-1;
 delay(2000);
            } else if(stato == 'C') {
 if(timer.isEnabled(error_timer)) timer.disable(error_timer);
 digitalWrite(13,0);
 Serial.print('K');
 
 Calibra();
 stato=-1;
            } else if(stato=='N') {
 if(conta==numero_campioni) {
 //Serial.print('K');
 //Serial.print('K');
 delay(10);
 Serial.print('B');
                 
 char leggi[2] = {0, 0};
 float T[16];
                 
 while( (leggi[0]!='K' && leggi[0]!= 'C' && leggi[0]!='S' && leggi[0]!='N') && i++<16000) leggi[0] = Serial.read();
 
                                        while( Serial.available() ) Serial.read();

 for(i=0;i<settaggi.canali;i++)
 {
 tensione = sommatens[i]/numero_campioni;
 resistenza = (tensione/I[i]);
 temperatura = (-A + sqrt( square(A) - 4.0*B*(1.0-resistenza/R0)))/(2.0*B);
 T[i] = temperatura;
                   
 if(temperatura > settaggi.Sup[i]) {
 if(Btimer.isEnabled(bound_timer[i]) && !Btimer.getBool(bound_timer[i]))
 Btimer.deleteTimer(bound_timer[i]);
                     
 if(!Btimer.isEnabled(bound_timer[i])) 
 bound_timer[i] = Btimer.setTimeout(60000, bound_callback, i, true);
                     
 if(digitalRead(SWITCH) == HIGH) T[i] = settaggi.Sup[i] - random(1, 6)*0.1;
                     
 } else if(temperatura < settaggi.Inf[i]) {
 if(Btimer.isEnabled(bound_timer[i]) && Btimer.getBool(bound_timer[i])) 
 Btimer.deleteTimer(bound_timer[i]);
                     
 if(!Btimer.isEnabled(bound_timer[i]))
 bound_timer[i] = Btimer.setTimeout(60000, bound_callback, i, false);
                     
 if(digitalRead(SWITCH) == HIGH) T[i] = settaggi.Inf[i] + random(1, 6)*0.1;
                     
 } else if(Btimer.isEnabled(bound_timer[i])) {
 Btimer.deleteTimer(bound_timer[i]);
 }
 }
 
 if( leggi[0] == 'K')
 {
 for(i=0;i<settaggi.canali;i++) {
 #if defined(TENSIONI)
 printFloat(tensione, 1);
 #else
 printFloat(T[i], 1);
 #endif
 Serial.print(";");
 delay(10);
 }
                   
 if(timer.isEnabled(error_timer)) timer.disable(error_timer);
 digitalWrite(13,0);
 blk=0;
 } else if ( leggi[0] == 'C' || leggi[0] == 'S' ) {
 stato = leggi[0];
 timer.disable(error_timer);
 } else {
 blk = !blk;
 digitalWrite(13, blk);
 if(!timer.isEnabled(error_timer)) {
 timer.enable(error_timer);
 timer.restartTimer(error_timer);
 }
 }
 Serial.println("E");
 for(i=0;i<settaggi.canali;i++) { sommatens[i]=0.0; prova[i]=0; }
                 
 conta=0;
 } else {
 for(i=0;i<settaggi.canali;i++) {
 sensorValue = analogRead(sensorPin[i]);
 tensione=(((float)sensorValue*AREF)/MAXANALOG);
                    
 sommatens[i]+=tensione;
 }
 conta++;
 }
            } else if(stato == 'R') {
 do_report();
 stato=-1;
            } else {
 digitalWrite(13,0);
 stato=-1; 
            }
             
            timer.run();
            Btimer.run();
            //delay(1);
}

void Salva()
{
    char leggi;
 int i=0;
    
 leggi=Serial.read();
 while ( leggi == 'S' ) leggi = Serial.read();
 if(leggi != -1) *(((char*)(&settaggi) + i++))=leggi;
 leggi = Serial.read();
    while(i<sizeof(settaggi)) {
 if(leggi != -1 && ( leggi != 'S' || i > 16  ))
 *((char*)(&settaggi) + i++)=leggi;
 Serial.println((int)i);
 leggi = Serial.read();
 }
                
    salvaSettaggi();
            
    Serial.println('E');
}

You seem to have phone numbers in your code. I suggest to replace them with dummy data. I will wait until you respond before doing any more so I don't save your private data.

If you have a long program you can add your .ino file as an attachment

...R

Thank you for the suggestion... i replaced the data with some dummy phone numbers. Also here is the last part of code.

void Calibra()
{
	char c=-1, flag=1, leggi=-1, i=0;
	double t=25.0;
               
    while( (c==-1 || c=='C') && i++<8000000 ) 
		c = Serial.read();
               
               
	if( c>=0 && c < 16 ) { 
		Serial.print('K');
		 //while( Serial.read() != 'D' );
		 while( leggi!='D' && i++<8000000 ) 
			leggi = Serial.read();
		
		if(leggi=='D') {
		Serial.print('K');
		leggi=-1;
		while( leggi == 'D' || leggi == -1 ) leggi=Serial.read();
		
		*((char*)&t) = leggi;
		Serial.readBytes(((char*)&t) + 1, sizeof(t)-1);
		sommatens[c]=0;
        
		for(j=0; j<3*numero_campioni; j++)
		{
			sensorValue = analogRead(sensorPin[c]);
			tensione=(((float)sensorValue*AREF)/MAXANALOG);
			sommatens[c]+=tensione;
		}
               
		sommatens[c] /= 3*numero_campioni;
		//Serial.print(sommatens[c]);
		double curenta = sommatens[c]*4*B / (4*B*R0 + R0*(4*square(B)*square(t) + 4*B*t*A));
               
		I[c] = curenta*1.0003;
            
		salvaCorrenti();
		Serial.println('E');
		}
	}
}

void do_report()
{
  Serial.println("Report:");  
               
  Serial.print("Numero canali: ");
  Serial.println((short int)settaggi.canali);
               
  Serial.print("Invio sms: ");
  if(settaggi.sms==1) Serial.println("SI");
  else if(settaggi.sms == 0 ) Serial.println("NO");
  else Serial.println("ERRORE");
               
  Serial.print("Numero di telefono:");
  Serial.println(settaggi.tel);
               
  Serial.println("Soglia superiore attiva:");
  for(i=0;i<settaggi.canali;i++) {
    Serial.print("  canale ");
    Serial.print(i);
    Serial.print(": ");
    if(settaggi.isSup[i]==1) Serial.println("SI");
    else if(settaggi.isSup[i] == 0 ) Serial.println("NO");
    else Serial.println("ERRORE");
  }
               
  Serial.println("Soglia inferiore attiva:");
  for(i=0;i<settaggi.canali;i++) {
    Serial.print("  canale ");
    Serial.print(i);
    Serial.print(": ");
    if(settaggi.isInf[i]==1) Serial.println("SI");
    else if(settaggi.isInf[i] == 0 ) Serial.println("NO");
    else Serial.println("ERRORE");
  }
               
  Serial.println("Soglia superiore:");
  for(i=0;i<settaggi.canali;i++) {
    Serial.print("  canale ");
    Serial.print(i);
    Serial.print(": ");
    Serial.println(settaggi.Sup[i]);
  }
               
  Serial.println("Soglia inferiore:");
  for(i=0;i<settaggi.canali;i++) {
    Serial.print("  canale ");
    Serial.print(i);
    Serial.print(": ");
    Serial.println(settaggi.Inf[i]);
  }
               
  Serial.println("Corrente:");
  for(i=0;i<settaggi.canali;i++) {
    Serial.print("  canale ");
    Serial.print(i);
    Serial.print(": ");
    Serial.println(I[i],6);
  }
}

float temp(float RT)
{
  float t_m;

  t_m= (-A + sqrt((A*A) - 4.0*B*(1.0-RT/R0)))/(2.0*B);

  return t_m; 
}

void printFloat(float value, int places) {
  // this is used to cast digits
  int digit;
  float tens = 0.1;
  int tenscount = 0;
  int i;
  float tempfloat = value;

  // make sure we round properly. this could use pow from <math.h>, but doesn't seem worth the import
  // if this rounding step isn't here, the value  54.321 prints as 54.3209

  // calculate rounding term d:   0.5/pow(10,places)  
  float d = 0.5;
  if (value < 0)
    d *= -1.0;
  // divide by ten for each decimal place
  for (i = 0; i < places; i++)
    d/= 10.0;    
  // this small addition, combined with truncation will round our values properly
  tempfloat +=  d;

  // first get value tens to be the large power of ten less than value
  // tenscount isn't necessary but it would be useful if you wanted to know after this how many chars the number will take

  if (value < 0)
    tempfloat *= -1.0;
  while ((tens * 10.0) <= tempfloat) {
    tens *= 10.0;
    tenscount += 1;
  }


  // write out the negative if needed
  if (value < 0)
    Serial.print('-');

  if (tenscount == 0)
    Serial.print(0, DEC);

  for (i=0; i< tenscount; i++) {
    digit = (int) (tempfloat/tens);
    Serial.print(digit, DEC);
    tempfloat = tempfloat - ((float)digit * tens);
    tens /= 10.0;
  }

  // if no places after decimal, stop now and return
  if (places <= 0)
    return;

  // otherwise, write the point and continue on
  Serial.print('.');  

  // now write out each decimal place by shifting digits one by one into the ones place and writing the truncated value
  for (i = 0; i < places; i++) {
    tempfloat *= 10.0;
    digit = (int) tempfloat;
    Serial.print(digit,DEC);  
    // once written, subtract off that digit
    tempfloat = tempfloat - (float) digit;
  }
}

And here is the .ino

g_mediatensioni_salvataggi.ino (12.4 KB)

    while( (c==-1 || c=='C') && i++<8000000 )
		c = Serial.read();

Since i is a char type, which can hold valued in the range -128 to 127, what are the odds that i will not be less than 80000000? If there is no chance in hell that the condition will not be true, there really is no need to test it.

There really is little point in hoping like hell that there is serial data to read. EVERY Serial.read() call should follow a Serial.available() test for greater than 0.

You still seem to have phone numbers in the code attached to Reply #11.

The code is much too long for me to analyse quickly. You are asking us to find a needle in a haystack.

As @PaulS has said you have Serial.read() sprinkled all over the place. For a robust system for receiving data study the link I gave you in Reply #5

I don't trust this sort of thing at all

if(leggi != -1) *(((char*)(&settaggi) + i++))=leggi;

Work out the value and store it in a variable and use it in a simple IF statement. Then you can print it to see if it is what you expect.
and in my experience it is very easy to make a mistake with this sort of compound IF

if(leggi != -1 && ( leggi != 'S' || i > 16  ))

If you do it as a cascade of IFs you can use Serial.print() to see which parts are being triggered.

...R

Yes i know it's a needle in a haystack, in fact i suggest you not to lose time analyzing it because i want to recode all...
I want to re-organize the code because now it's overcomplicated for the task it's doing.
I've read your tutorials and the python example and i found them really useful.
For now I wrote a TempSensor class that handle the ADC stuffs and now i'm thinking about a SerialCom class for the two way communication (I'll post the code when it's done).
I've just a question for now: do you think i should send data in bytes rather than char?

Anyway thanks a lot for your help, i really needed your tutorials XD

TempSensor.cpp (3.67 KB)

TempSensor.h (3.11 KB)

schizoping:
do you think i should send data in bytes rather than char?

I prefer to send data in human readable form unless that really causes a performance problem. Human readable data makes debugging very much easier.

...R

I just wanted to ask you some clarification about the serial input buffer, both for arduino and for PC.
I saw there is a define in the arduino source that you can hack for more input buffer... my questions are: is there a limit to this buffer? is there an hardware buffer for the serial port? how can i know the size of both software/hardware serial buffer on windows side? (i'm using mingW c++ with windows API).
Can you link me a good lesson that goes a little in depth about this stuffs?
I don't think i need all this knowledge for the program i'm writing, but it's just to understand better the stuffs i'm using.
Thanks a lot

my questions are: is there a limit to this buffer?

There is a limit on the amount of SRAM available. The buffers (one for incoming, one for outgoing) need to live in SRAM, along with every other variable and string literal (unless efforts are taken to keep them out), and the stack.

is there an hardware buffer for the serial port?

No, the buffers are in software.

how can i know the size of both software/hardware serial buffer on windows side?

The sizes are defined by that #define statement you were looking at, for the software side. The hardware doesn't have a buffer; it deals with one character at time.

Another question, this time about the pyton example: what's the purpose of the lenght byte you send within the messages since there are the start/stop marks?

schizoping:
I just wanted to ask you some clarification about the serial input buffer, both for arduino and for PC.
I saw there is a define in the arduino source that you can hack for more input buffer..

It would only be necessary to change the buffer size in very special circumstances.

With the code in Serial Input Basics you can receive as many bytes as you have memory space for. Just change the value of the contant numChars to suit your requirement. My code will empty the serial input buffer much faster than serial data can arrive to fill the buffer.

...R