SH110X Problem displaying more than X amount of characters

I have written a program for an Arduino NANO with a 0.9" display using the SSD1306 Library. To improve the readability of the information on the display, I changed to a 1.3" display using the required SH110X library.

Now I have a problem that there is a limitation to the amount of information that I can display. This was not the case with the 0.9" SSD1306 Library.

As an example, if I add one more character to the program, even if it is a space, certain things will no longer be displayed, especially floating point numbers. If I remove this one single character then it all works perfectly.

It is as though there is a smaller display buffer assigned by the SH110X library than by the SSD1306 library.

As soon as I convert the program back to the SSD1306 version and swap the 1.3" display back to the 0.9", this restriction goes away.

It's a total pain because I have had to absolutely minimalise the information I display so that I can achieve all of the required functionality for the program to carry out it's functions and communicate with the user.

Update:27May2024
This is the Dummy Code set up for the 1.3" SH110X Library:
Go to ******************* Test Variables *****************************
Here I have created a string "Test1" of different lengths. Anything over 25 characters will cause problems in varying degrees with the floating point numbers I am displaying.

I have used the identical code simply converted to the SSD1306 Library for the 0.9" display and this problem does not occur.

// --> Include libraries
#include <Wire.h>
#include <Adafruit_SH110X.h> // ** 1.3" Display **
// #include <Adafruit_SSD1306.h> // ** 0.9" Display **

// --> Defines and constants for peripherals
// 1.3" Display
#define i2c_Address 0x3c //initialize with the I2C addr 0x3C Typically eBay OLED's
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// 0.9" Display
// #define SCREEN_WIDTH 128 // OLED display width, in pixels
// #define SCREEN_HEIGHT 64 // OLED display height, in pixels
// #define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
// #define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3C for 128x64, 0x3D for 128x32
// Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// ******************* Test Variables *****************************
String Test1 = "12345678901234567890123456789"; // Not OK!
// String Test1 = "123456789012345678901234567"; // Not OK!
// String Test1 = "12345678901234567890123456"; // Not OK!
// String Test1 = "1234567890123456789012345";  // OK

// Constants
const float LoadR100K = 100000.0;
const float LoadR15K = 14831.0;
const float DCCorr = 1.215;
const float BattVCorr = 1.96;
const float VCorrAC= 1.195;
const float IndCorr = 1.28;
const float GaussZOffset = 9.0;

// General
const String Version = "24May2024 12:00";
float CoilVoltsAC = 1.5;
float IVoltsAC = 3.9;
const float ZeroDB = 2.19;
float RefVoltsF = 1000.0;

// Start screen and Button
const byte IntPin = 3;
const float MinBattery = 8.0;
volatile bool ButtonPressed;
const byte MinMaxSamples = 200;

// Frequency Scan
const float incrementSteps = 157.32;
const float ScreenMaxV = 4.2;
const unsigned int SweepStartFreq = 20;
float StartVAC = 0.1;
float EndVAC = 1.5;

// Resonant Search Measurement
const unsigned int R100K = 5;
float MaxVAC = 3.1;
float ResoF = 12000.0;

// Inductance 
const unsigned int IndStartFreq = 20;
float L = 8.6;
const float IMult = 0.707;
const unsigned int R15K = 4;

// Resistance Measurement
float DCResistance = 8000.0;

// Setup: -----------------------------------------------------------------------------
void setup() { // The setup routine runs once when you press reset
// 1.3" Display
display.begin(i2c_Address, true);
display.setTextColor(SH110X_WHITE,SH110X_BLACK);
// 0.9" Display
// display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
// display.setTextColor(WHITE, BLACK); // Draw white text with a black background (instead of transparent) to overwrite previous info
// SSD1306_WHITE ******************* to replace SH110X_WHITE for pixel and line commands
display.clearDisplay();
display.display();

pinMode(IntPin, INPUT_PULLUP); // Set all unused Dig pins to INPUT_PULLUP to prevent noise caused by floating inputs!!!
pinMode(0, INPUT_PULLUP); pinMode(1, INPUT_PULLUP);pinMode(6, INPUT_PULLUP); pinMode(7, INPUT_PULLUP);pinMode(8, INPUT_PULLUP); pinMode(10, INPUT_PULLUP); pinMode(12, INPUT_PULLUP);
pinMode(2, INPUT);

}

 // Main routine: -----------------------------------------------------------------------------
void loop() {
  StartScreen();
  F1();
  F2();
  F3();
  F4();
  F5();
  F6();
}

void StartScreen(){
  display.setCursor(11,0);
  display.print("Press O To Proceed");
  display.setCursor(13,56);
  display.print("R:" +  Version); 
  display.display();
  attachInterrupt(digitalPinToInterrupt(IntPin),ISR_Button,LOW);// Interrupt Vector for pin 3 = 1
  while (ButtonPressed != true){BatteryTest();} // Allows battery test to run without delaying the button press as the interrupt will respond instantaneously regardless of battery test.
  ButtonPressed = false;
  Button(); // Wait to go to next function
}

void BatteryTest(){
  display.setTextSize(2);
  float BatteryV = (analogRead(A3) * (5.0 / 1023)*BattVCorr);
    if (BatteryV <= MinBattery) { // Battery too low
    display.setCursor(18,24);
    display.print("    "); // Print nothing to create a flashing function
    display.display();
    delay(150);
    display.setCursor(18,24);
    display.print("Bat=" + String(BatteryV,1) + "V ");
    display.display();
    delay(150);  
    } else {
    display.setCursor(18,24);    
    display.print("Bat=" + String(BatteryV,1) + "V ");
    display.display();
    }
}

void Button(){
  while (digitalRead(3) == HIGH){} // Waiting for button to be pressed
  display.clearDisplay(); // Erase previous screen
  display.setTextSize(1);
  display.setCursor(5,0);
  display.print ("Release O To Proceed");
  display.display();
  while (digitalRead(3) == LOW){} // Waiting for button to be released
  display.clearDisplay(); // Clear display ready for next function
  display.display(); // Clear screen for next function
}

void ISR_Button(){ // Interrupt routine for the button
  ButtonPressed=true;
  detachInterrupt(digitalPinToInterrupt(IntPin));// Interrupt Vector for pin 3 = 1;
}

void F1(){
  display.setTextSize(2);
  display.setCursor(24,12);
  display.print("GETTING");
  display.setCursor(24,32);
  display.print(" STUFF ");
  display.display();
  display.setTextSize(1);
  display.setCursor(11,0);
  display.print("Press O To Proceed");
  display.display();
  Button();
  display.setTextSize(1);
  display.clearDisplay();
  display.display();
}

void F2(){
  float SweepFreq = SweepStartFreq;
  byte xAxis = 1;
  display.drawLine(0,((ScreenMaxV-ZeroDB)*15), 128, ((ScreenMaxV-ZeroDB)*15),SH110X_WHITE); // 0 db line
  display.setCursor(46,(((ScreenMaxV-ZeroDB)*15)-4));
  display.print(" 0dBu ");
  display.display();
while (xAxis < 129){
      display.setCursor(32,0);
      display.print("F = " + String(SweepFreq,0) + "Hz");
      display.drawPixel(xAxis, (((ScreenMaxV-CoilVoltsAC)*15)), SH110X_WHITE);  
      display.display();
      SweepFreq = SweepFreq + incrementSteps;  
      xAxis ++; 
    }
  display.setCursor(11,0);
  display.print("Press O To Proceed");
  display.display();
  Button();
}

void F3(){
  float SweepFreq = ResoF - 1000.0; // ResoF taken from Frequency sweep function
  display.setCursor(34,0);
  display.print("RESONANT F");
  display.display();
  display.setCursor(12,40);
  display.setTextSize(2);
  display.print("F=" + String(SweepFreq,0) + "Hz "); 
  display.display();
  display.setTextSize(1);
  display.setCursor(11,0);
  display.print("Press O To Proceed");
  display.display();
  Button();
  display.setCursor(2,20);
  display.setTextSize(1);
  display.print("Resonant F = " + String((ResoF/1000),1) + "kHz ");
  display.setCursor(11,0);
  display.print("Press O To Proceed");
  display.display();
  Button();
  display.setTextSize(1);
  display.clearDisplay();
  display.display();
}

// ******************** Inductance Test **********************************
void F4(){
  float TVoltsAC = CoilVoltsAC*IMult;
  float IndFreq = ResoF;
  display.setTextSize(1);
  display.setCursor(34,0);
  display.print("INDUCTANCE");
  display.display();
  display.setTextSize(1);
  display.setCursor(11,8);
  display.print("Press O To Proceed");
  display.display();
  Button();
  display.setTextSize(1);
  display.setCursor(16,20);
  display.print("Test F = " + String(IndFreq,0) + " Hz  ");
  display.setTextSize(2);
  display.setCursor(6,40);
  display.print("Diff " + String(CoilVoltsAC - TVoltsAC)); // Remaining difference
  display.display();
  display.setTextSize(1);
  display.setCursor(11,0);
  display.print("Press O To Proceed");
  display.display();
  Button();     
  display.setTextSize(1);
  display.clearDisplay();
  display.display();
}

void F5(){ // Results
    float Cp = 100.0/((sq(0.0628*ResoF))*L);
    display.setCursor(0,0);
    display.println("RESULTS: Press O To >");
    display.println("R = " + String((DCResistance/1000),1) + " kR");
    display.println("L = " + String(L,1) + " H");
    display.println("Res. F = " + String(ResoF,1) + " kHz");
    display.println("Cp = " + String(Cp,0) + " pF");
    display.println("Max = " + String(8.68*(log(MaxVAC/ZeroDB)),2) + " dBu"); // 8.68 converts the natural log to Log10
    display.println("Span = " + String(8.68*(log(MaxVAC/StartVAC)),1) + " dB");
    display.print("20kHz = " + String(8.68*(log(EndVAC/ZeroDB)),1) + " dBu");
    display.display();
    Button();
}

void F6(){
  ReGauss:
  float Gauss  = -248.0;
  String stringNS = "N";
  float RawGauss = -248.0;
  display.setTextSize(2);
  display.setCursor(34,12);
  display.print("Gauss");
  display.setCursor(42,32);  
  display.print(String(Gauss,0) + "   ");
  display.setTextSize(2);  
  display.setCursor(54,50);
  display.print(stringNS);
  display.display();
  display.setTextSize(1);
  display.setCursor(11,0);
  display.print("Press O To Proceed");
  display.display();
  Button();
  goto ReGauss;
}

There is likely to be a mistake in your code, or you are running out of dynamic memory. For help, post the code using code tags, and state which Arduino you are using (there are half a dozen different Nanos).

Be sure to state the size of the displays you are using, as well.

Thanks for your reply,

If I am running out of dynamic memory, why only with the SH110X library and not with the SSD1306 library?

If there was a mistake in my code then it wouldn't start working just because I removed a single character from a print statement. Everything is identical electrically and code wise, it's only the library related information that changes to convert from the 0.9" to the 1.3".

I cannot release my code as it is the culmination of 10 months work.

If necessary I will write a piece of code just using the display related software to show the problem.

The NANO and display are eBay purchased items. The NANO is described as V3 compatible with ATmega328P.

During startup, the libraries allocate different amounts of dynamic ram for the screen buffer.

If necessary I will write a piece of code just using the display related software to show the problem.

Good idea.

If we cannot see code that has the problem and have no idea of which library you are using, then how can the forum help ?

I have used SSD1306 and SH1106 displays, same library, same hardware and have not had a problem swapping between the two displays.

But theoretically, if I can can display more characters with the SSD1306 library than the SH110x library, then the SSD1306 must be allocating more memory for itself.

Is it possible the the SH110x library is simply not allocating enough memory?
Is there a way of finding out what the libraries are allocating?

Read the open source library code, or add in and run the freeMemory function before and after initializing the display.

//Arduino free memory .cpp
extern unsigned int __heap_start;
extern void *__brkval;

/*
   The free list structure as maintained by the
   avr-libc memory allocation routines.
*/
struct __freelist {
  size_t sz;
  struct __freelist *nx;
};

/* The head of the free list structure */
extern struct __freelist *__flp;
/* Calculates the size of the free list */
int freeListSize() {
  struct __freelist* current;
  int total = 0;
  for (current = __flp; current; current = current->nx) {
    total += 2; /* Add two bytes for the memory block's header  */
    total += (int) current->sz;
  }
  return total;
}

int freeMemory() {
  int free_memory;
  if ((int)__brkval == 0) {
    free_memory = ((int)&free_memory) - ((int)&__heap_start);
  } else {
    free_memory = ((int)&free_memory) - ((int)__brkval);
    free_memory += freeListSize();
  }
  return free_memory;
}

Thanks for your reply.

I have used the standard Adafruit libraries.
Adafruit_SH110X.h
Adafruit_SSD1306.h

I will create a separate program that has this problem so that I can provide it for analysis. It will take a while though.

If so, that would have been discovered long ago. Those libraries are in use every day by very large numbers of people.

True, but maybe I am trying to display more information than has been tried up until now.

Also extremely unlikely.

Always suspect your own code before blaming the library, and you will proceed much more quickly toward the goal.

1 Like

I have spent many days trying to locate this problem because I suspected my code. The only key factor I have found is the amount of characters I assign for displaying.
Blame doesn't come into consideration???, I'm just trying to understand the possibilities so that I can find a solution.

The project is shown here so that you have an idea about what is being displayed:

I am re-writing the code to create a dummy version with the same problem.
I should have it finished by the middle of next week.

1 Like

Great project!

What's that accent you have? I think I hear a bit of Irish, Scouse, and not sure what else.

You obviously haven't used the most helpful analytical tools, or studied the library code, so this is not surprising.

The symptoms you describe are characteristic of writing to memory outside the bounds of an array, incorrect pointer usage, etc. A seemingly insignificant, unrelated code change alters where something is stored and suddenly make the problem noticeable.

Welsh from Barry! What could be a worse accent?

Thanks for the reply.
But it only happens with the SH110X Library not the SSD1306. The code and electronics (I2C) is identical. I just change between the necessary libraries and calls.

I'm new to this software and processor and am not aware of the analytical tools.
Most of the time spent researching this problem was eliminating any coding problems I may have caused .... the trial and error method.

What analytical tools do you suggest?
Studying the library code is complicated as I don't have experience at library level coding.

Barry ?

Know it well, gave a lecture on one of my electronic 'projects' to a load of 'Amateurs' at the club there.

Didn't have your car stolen then?:joy: