OLED I2C freezes after several iterations

Hello,

The context

A hamradio transceiver connected to a PC via RS232. The software on the PC retrieves some information every 500 ms or send data to the radio to control it.
I want to retrieve more information than the PC requests and display them on an OLED display.
So I have created a small circuit with an Arduino Pro Mini and a MAX232 whiwh is inserted in the RS232 line.
Library SoftwareSerial is used to add a second Serial port.

How it works

The Arduino forwards the data between both serial ports, and waits for the last information from the radio, which is "MD"

After this string is received, it sends:
"PC;" request, then process the answer (ie PC255) to display the output power
"AN0;" request, then process the answer (ie AN10) to know the antenna output selected
"KS;" request, then process the answer (ie KS26)

The information are displayed, then it loops.

By the way, it also reads IF0 and FB to compare the frequencies currently in use, then calculates the difference to display it.

My concern is that after a few loops (between 10 and sometimes 200), the OLED display freezes.
The "nb" value, displayed for debugging, don't change anymore after some iterations.
The sketch is still running correctly, the informations are correctly forwarded from one serial to the other (if it didn't work, the software on the PC would tell the communication is broken).

Plus, data is still interpreted by the sketch. For example, if AN0 answer changes, RelaisRxAnt state changes

Only the display is frozen.

The most annoying is that it worked like a charm for the past 2 years. I recently recompiled if just for moving some text of a few pixels.
The only change I can see is that Arduino IDE and libraries are newer.

/*
Le montage intercepte et transfere les commandes CAT, en 19200 8N2.
Si le VFO-B est selectionne en emission, allumage de la LED
Lorsque le dernier champ est envoye par le TRX (MD), l'Arduino envoie les requetes additionnelles :
- PC (lecture de la puissance pour affichage OLED)
- KS (lecture de la vitesse du keyer pour affichage OLED)
- AN0 (lecture de l'antenne pour declenchement du relais coax si ANT = RX)
*/

#include <SoftwareSerial.h>
#include <U8glib.h>

// Entrees/sorties
SoftwareSerial CAT(12,11); // RX, TX

U8GLIB_SSD1306_128X32 u8g(U8G_I2C_OPT_NONE); // I2C / A4-A5

const int Split_led = 10; // LED split
const int RelaisRxAnt = 9; // Relais RX

// Variables
int old_power = -1;
int power;
String str;
String text_pwr;

int bandVFOa;
float freqVFOa;
int bandVFOb;
float freqVFOb;
float upVal;
int split = 0;
int wpm;
int nb = 0;

void setup() {
 pinMode(Split_led, OUTPUT);
 pinMode(RelaisRxAnt, OUTPUT);
 Serial.begin(19200);      // regular serial
 CAT.begin(19200);         // CAT serial 
 digitalWrite(Split_led, LOW);   // LED éteinte
 digitalWrite(RelaisRxAnt, LOW); // Relais RX Ant off
}

void draw(void) {
 if (Serial.available()) {
 str = Serial.readStringUntil(';');
 str.concat(";");
 Serial.print(str);
 }

 if (CAT.available()) {
 str = CAT.readStringUntil(';');
 str.concat(";");
 CAT.print(str);
      
 
 /*** LED split ***/
 if (str == "FT0;") { // TX sur VFO-A 
 digitalWrite(Split_led, LOW);
 split = 0;
 }
 if (str == "FT1;") { // TX sur VFO-B 
 digitalWrite(Split_led, HIGH);
 split = 1;
 }

 if (str.startsWith("MD")) { // Envoi requête PC apres dernier champ reçu (MD)
 Serial.print("PC;");
 }
 
 if (str.startsWith("PC")) { // Lecture puissance TX (PCxxx;)
 String power_str = str.substring(2,str.length()-1);
 power = power_str.toInt(); 
 if (power != old_power) {   // Si la puissance a changé
 text_pwr = Convert(power);   // Correspondance xxx (0-255) -> puissance
 old_power = power;
 }
 Serial.print("AN0;");   // Envoi requête "Antenne selectionnee"
      nb++;
 }
 

 if (str.startsWith("AN")) { 
 String rx_ant = str.substring(4,str.length()-1);   // Lecture Antenne ANxxY
         String tx_ant = str.substring(3,4);                // Lecture Antenne ANxZx
 if (rx_ant == "1") { // Y = 1 donc RX -> bascule relais coax RX Ant
 digitalWrite(RelaisRxAnt, HIGH);
 } else {
 digitalWrite(RelaisRxAnt, LOW);
 }
 Serial.print("KS;"); // Envoi requete "Key Speed (WPM)"
 }
     
 if (str.startsWith("KS")) { // Lecture vitesse Keyer KSxxx
 String str_wpm = str.substring(2,str.length()-1);
 wpm = str_wpm.toInt();
 } 
 
 if (str.startsWith("IF0")) { // Lecture freq VFO A
 String str_freqVFOa = str.substring(7,12);
 freqVFOa = str_freqVFOa.toInt();
 String str_bandVFOa = str.substring(5,7);
 bandVFOa = str_bandVFOa.toInt();
 }

 if (str.startsWith("FB")) { // Lecture freq VFO B
 String str_freqVFOb = str.substring(4,9);
 freqVFOb = str_freqVFOb.toInt();
 String str_bandVFOb = str.substring(2,4);
 bandVFOb = str_bandVFOb.toInt();
 }
 
 if ((bandVFOa == bandVFOb) && (split == 1)) { // Bandes identiques, comparaison freq
 upVal = (freqVFOb-freqVFOa)/100;
 } else {
 upVal = 0;
 }
   }
 
 /////////////////////////////////////////////////////////////////////////////////////////////
 // Affichage - Important de le laisser a la fin de la boucle
 //
   u8g.setContrast(10); 

   u8g.setFont(u8g_font_ncenB18);   // Affichage valeur Puissance
   u8g.setPrintPos(4, 22); 
   u8g.print(text_pwr);
   
   u8g.setFont(u8g_font_7x14); // Affichage valeur Split
   u8g.setPrintPos(92, 30);
   if (upVal != 0) {
      u8g.print(upVal);
   } else {
      u8g.print("      ");
   }
   
   u8g.setPrintPos(92, 12);   // Affichage valeur WPM
   if (wpm != 0) {
      u8g.print(wpm);
   } else {
      u8g.print("    ");
   }

   u8g.setFont(u8g_font_6x10); // Affichage "wpm"
   u8g.setPrintPos(110, 12);
   u8g.print("wpm");

   u8g.setPrintPos(0, 32);
   u8g.print(nb);
}

void loop(void) {
 u8g.firstPage(); 
 do {
 draw();
 } while( u8g.nextPage() );
}

//////////////////////////////////////////////////////////////////////////////////////////////
// La valeur PWR envoyée est comprise entre 0 (10W) et 255 (200W)
// Conversion de cette valeur numérique en texte

String Convert(int power) {
 String real_pwr;
 if (power == 0 || (power >= 0 && power < 31)){
 real_pwr = " 10 W";
 } else if (power >= 32 && power < 44) {
 real_pwr = " 15 W";
 } else if (power >= 44 && power < 60) {
 real_pwr = " 20 W";
 } else if (power >= 60 && power < 70) {
 real_pwr = " 25 W";
 } else if (power >= 70 && power < 75) {
 real_pwr = " 30 W";
 } else if (power >= 75 && power < 81) {
 real_pwr = " 35 W";
 } else if (power >= 81 && power < 86) {
 real_pwr = " 40 W";
 } else if (power >= 86 && power < 91) {
 real_pwr = " 45 W";
 } else if (power >= 91 && power < 99) {
 real_pwr = " 50 W";
 } else if (power >= 99 && power < 105) {
 real_pwr = " 60 W";
 } else if (power >= 105 && power < 118) {
 real_pwr = " 70 W";
 } else if (power >= 118 && power < 126) {
 real_pwr = " 80 W";
 } else if (power >= 126 && power < 140) {
 real_pwr = " 90 W";
 } else if (power >= 140 && power < 145) {
 real_pwr = "100 W";
 } else if (power >= 145 && power < 150) {
 real_pwr = "110 W";
 } else if (power >= 150 && power < 160) {
 real_pwr = "120 W";
 } else if (power >= 160 && power < 166) {
 real_pwr = "130 W";
 } else if (power >= 166 && power < 170) {
 real_pwr = "140 W";
 } else if (power >= 170 && power < 177) {
 real_pwr = "150 W";
 } else if (power >= 177 && power < 193) {
 real_pwr = "160 W";
 } else if (power >= 193 && power < 201) {
 real_pwr = "170 W";
 } else if (power >= 201 && power < 211) {
 real_pwr = "180 W";
 } else if (power >= 211 && power < 224) {
 real_pwr = "190 W";
 } else if (power >= 224) {
 real_pwr = "200 W";
 }
 return real_pwr;
}

Standard answer:

Using String class.

Don't! :astonished:

Hi Paul__B,
Would you develop a little bit your answer?

The "String" class - where you have used thing such as

// Variables
int old_power = -1;
int power;
String str;
String text_pwr;

is a feature of "C" only usable on machines with generous amounts of memory such as your PC - and even then may have troubles with "Garbage collection" as strings are used and renewed. Witness how your Web browser crashes from time to time.

These matters typically show up in the manner you describe - mysterious failure in an unpredictable fashion, with no clear reason.

Use character arrays instead. Your code was actually doomed from the start, you simply did not realise it!

Thank you, I ignored.
But don't you think the whole sketch would freeze if I had a such issue?
In this case, only the display freezes.

Maybe, maybe not!

I have removed all the Strings (except some parts that I don't know how to code, but commented), same story...
the display still freezes

/*
---------------------
Injection CAT
---------------------

Le montage intercepte et transfere les commandes CAT, en 19200 8N2.
Si le VFO-B est selectionne en emission, allumage de la LED
Lorsque le dernier champ est envoye par le TRX (MD), l'Arduino envoie les requetes additionnelles :
- PC (lecture de la puissance pour affichage OLED)
- KS (lecture de la vitesse du keyer pour affichage OLED)
- AN0 (lecture de l'antenne pour declenchement du relais coax si ANT = RX)
*/

#include <SoftwareSerial.h>
#include <U8glib.h>

// DEBUG //
int debug = 0;

// Entrees/sorties
SoftwareSerial CAT(12,11);		// RX, TX

U8GLIB_SSD1306_128X32 u8g(U8G_I2C_OPT_NONE);	// I2C / A4-A5

const int Split_led = 10;			// LED split
const int RelaisRxAnt = 9;		// Relais RX

// Variables
int old_power = -1;
int power;
String str = "";
char *text_pwr = "";
char pc_arr[16];
char trx_arr[16];

int bandVFOa;
float freqVFOa;
int bandVFOb;
float freqVFOb;
float upVal;
int split = 0;
int wpm;

void setup() {
	pinMode(Split_led, OUTPUT);
	pinMode(RelaisRxAnt, OUTPUT);
	Serial.begin(19200);			      // regular serial
	CAT.begin(19200);			         // CAT serial 
	digitalWrite(Split_led, LOW);	   // LED éteinte
	digitalWrite(RelaisRxAnt, LOW);	// Relais RX Ant off
}

void draw(void) {
	if (Serial.available()) {
      size_t num_read = Serial.readBytesUntil(';', pc_arr, sizeof(pc_arr)-1 );
      pc_arr[num_read] = '\0';
      Serial.print(pc_arr);
      Serial.println(";");
	}

	if (CAT.available()) {
      size_t num_read = CAT.readBytesUntil(';', trx_arr, sizeof(trx_arr)-1 );
      trx_arr[num_read] = '\0';
      CAT.print(trx_arr);
      CAT.println(";");

		
      /*** LED split ***/	
      if (strcmp (trx_arr,"FT0") == 0) {		// TX sur VFO-A
         digitalWrite(Split_led, LOW);
         split = 0;
      }
      if (strcmp (trx_arr,"FT1") == 0) {		// TX sur VFO-A   
         digitalWrite(Split_led, HIGH);
         split = 1;
      }

      if (2 >= strlen(trx_arr) && ( strncmp("MD",trx_arr,2) == 0 )) {      // Envoi requête PC apres dernier champ reçu (MD)
         Serial.print("PC;");
      }
      
      if (2 >= strlen(trx_arr) && ( strncmp("PC",trx_arr,2) == 0 )) {			// Lecture puissance TX (PCxxx;)
         int power = atoi( &trx_arr[2] );
         if (power != old_power) {		   // Si la puissance a changé
            text_pwr = Convert(power);	   // Correspondance xxx (0-255) -> puissance
            old_power = power;
         }
         
         Serial.print("KS;");			// Envoi requete "Key Speed (WPM)"
      }
      
      if (2 >= strlen(trx_arr) && ( strncmp("KS",trx_arr,2) == 0 )) {			// Lecture vitesse Keyer KSxxx
         int wpm = atoi( &trx_arr[2] );
         Serial.print("AN0;");			   // Envoi requête "Antenne selectionnee"
      }	
      
      /*
      if (str.startsWith("AN")) {			
         String rx_ant = str.substring(4,str.length()-1);   // Lecture Antenne ANxxY
         if (rx_ant == "1") {			// Y = 1 donc RX -> bascule relais coax RX Ant
            digitalWrite(RelaisRxAnt, HIGH);
         } else {
            digitalWrite(RelaisRxAnt, LOW);
         }
      }

      if (str.startsWith("IF0")) {			// Lecture freq VFO A
         String str_freqVFOa = str.substring(7,12);
         freqVFOa = str_freqVFOa.toInt();
         String str_bandVFOa = str.substring(5,7);
         bandVFOa = str_bandVFOa.toInt();
      }

      if (str.startsWith("FB")) {				// Lecture freq VFO B
         String str_freqVFOb = str.substring(4,9);
         freqVFOb = str_freqVFOb.toInt();
         String str_bandVFOb = str.substring(2,4);
         bandVFOb = str_bandVFOb.toInt();
      }
      
      if ((bandVFOa == bandVFOb) && (split == 1)) {		// Bandes identiques, comparaison freq
         upVal = (freqVFOb-freqVFOa)/100;
      } else {
         upVal = 0;
      }
      */
   }
	
	/////////////////////////////////////////////////////////////////////////////////////////////
	// Affichage - Important de le laisser a la fin de la boucle
	//
   u8g.setContrast(10); 
		u8g.setFont(u8g_font_ncenB18);   // Affichage valeur Puissance
		u8g.setPrintPos(4, 24); 	
		u8g.print(text_pwr);
		
		u8g.setFont(u8g_font_7x14);		// Affichage valeur Split
		u8g.setPrintPos(92, 30);
		if (upVal != 0) {
			u8g.print(upVal);
		} else {
			u8g.print("      ");
		}
		
		//u8g.setFont(u8g_font_7x14);
		u8g.setPrintPos(92, 12);		   // Affichage valeur WPM
		if (wpm != 0) {
			u8g.print(wpm);
		} else {
			u8g.print("    ");
		}

		u8g.setFont(u8g_font_6x10);		// Affichage "wpm"
		u8g.setPrintPos(110, 12);
      u8g.print("wpm");
}

void loop(void) {
	u8g.firstPage(); 
	do {
		draw();
	} while( u8g.nextPage() );
}

//////////////////////////////////////////////////////////////////////////////////////////////
// La valeur PWR envoyée est comprise entre 0 (10W) et 255 (200W)
// Conversion de cette valeur numérique en texte

char * Convert(int power) {
	char *real_pwr;
	if (power == 0 || (power >= 0 && power < 31)){
		real_pwr = " 10 W";
	} else if (power >= 32 && power < 44) {
		real_pwr = " 15 W";
	} else if (power >= 44 && power < 60) {
		real_pwr = " 20 W";
	} else if (power >= 60 && power < 70) {
		real_pwr = " 25 W";
	} else if (power >= 70 && power < 75) {
		real_pwr = " 30 W";
	} else if (power >= 75 && power < 81) {
		real_pwr = " 35 W";
	} else if (power >= 81 && power < 86) {
		real_pwr = " 40 W";
	} else if (power >= 86 && power < 91) {
		real_pwr = " 45 W";
	} else if (power >= 91 && power < 99) {
		real_pwr = " 50 W";
	} else if (power >= 99 && power < 105) {
		real_pwr = " 60 W";
	} else if (power >= 105 && power < 118) {
		real_pwr = " 70 W";
	} else if (power >= 118 && power < 126) {
		real_pwr = " 80 W";
	} else if (power >= 126 && power < 140) {
		real_pwr = " 90 W";
	} else if (power >= 140 && power < 145) {
		real_pwr = "100 W";
	} else if (power >= 145 && power < 150) {
		real_pwr = "110 W";
	} else if (power >= 150 && power < 160) {
		real_pwr = "120 W";
	} else if (power >= 160 && power < 166) {
		real_pwr = "130 W";
	} else if (power >= 166 && power < 170) {
		real_pwr = "140 W";
	} else if (power >= 170 && power < 177) {
		real_pwr = "150 W";
	} else if (power >= 177 && power < 193) {
		real_pwr = "160 W";
	} else if (power >= 193 && power < 201) {
		real_pwr = "170 W";
	} else if (power >= 201 && power < 211) {
		real_pwr = "180 W";
	} else if (power >= 211 && power < 224) {
		real_pwr = "190 W";
	} else if (power >= 224) {
		real_pwr = "200 W";
	}
	return real_pwr;
}

OK, being totally desperate I finally bought another OLED Display to give a try and guess what ? Everything is now ok.
Thanks for your help and the information about String Class.

1 Like