2 x SSD1306 128x64 OLED displays on an rc 'afterburner' controller - Not enough sram?

Bit of a different topic to my last post, I'm currently building an 'afterburner' rc plane that'll be using an arduino pro micro to control the servo operating a fuel valve based on throttle input from a radio receiver.
As these things usually go, more and more got added on to the project and now it's sat in a nice little enclosure on a PCB next to a Rush F7 flight controller topped with 2 oled displays. It'll be up in the air hopefully so screens aren't useful except for ground testing. Looks cool though which is an excuse in itself right?.

At this point I've got the code pretty much ready for actual testing. I'll probably need to do something to scale the fuel servo output since I doubt it's going to be a linear increase that's needed as the rpm goes up, but that's for later.

The issue I've got is I can't run both screens at 128 x 64. I've got a graph on one screen (display1) and some status data on the other (display 2). At first the best I could get was 128 x 64 on one and 96 x 32 on the other, after removing a bunch of my initial Serial.print debugging lines I bumped that 2nd display up to 128 x 32 which now runs fine and stretches to fill the screen but could be better.
From some googling this seems to be due to the displays requiring 1024b of sram each at 128x64, with the pro micro having a total of 2560b available.

Are there any other libraries or differing methods to get both displays up to 128x64? Also any suggestions on bits where I've gone wrong or could write out better would be great, the only other arduino project I've done was a button box and that wasn't a pretty sketch either.

--
As a side note, once I arm the system and it starts processing throttle values to servo positions I can visibly see the runtime slows down probably 4-5x, is this a result of me using an incorrect or slow method? Or am I just asking too much of the system? The graph plots a new point once per loop which I can see takes much longer when armed vs disarmed.

for the #includes,

#include <OLED_SSD1306_Chart.h>

also contains all the Adafruit stuff for the other display so isn't added as a separate #include.

Memory Stats after uploading:

Sketch uses 25648 bytes (89%) of program storage space. Maximum is 28672 bytes.
Global variables use 807 bytes (31%) of dynamic memory,  leaving 1753 bytes for local variables. Maximum is 2560 bytes.

Sketch:

//Servo Library
#include <Servo.h>

//display Stuff
#include <Arduino.h>
#include <Wire.h>
#include <OLED_SSD1306_Chart.h>
#define OLED_RESET -1
#define BAUDRATE 9600
#define SDA_PIN 2
#define SCL_PIN 3
OLED_SSD1306_Chart display1(128, 64, &Wire, OLED_RESET);
Adafruit_SSD1306 display2(128, 32, &Wire, OLED_RESET);
char actualThickness;

// RC Channels
int throttle = A6;
int safety = A2;
int ch;
int minValue;
int maxValue;
int defaultValue;
int rpm;
int KV = 2400;
int cellCount = 4;
int VCC = ((4.2 * cellCount));

// Fuel Management
Servo fuelValve;
int fuelServo = 10;
int fuelVal;
int throttleVal;
int throttlePercent;
int fuelFlow;

// Ignition
int arcPin = 8;
int flameSensor = A1;
int flameVal = 0;
int flameOut = 4;
int safetyVal = 0;
int armed = 0;
int idle = 80;
int purgeRequired = 1;
int ignitionRequired = 1;
int timeOut = 0;
int prevState = 0;
int i = 0;

//Channel Reading
int readChannel(int channelInput, int minLimit, int maxLimit, int defaultValue) {
  int ch = pulseIn(channelInput, HIGH, 30000);
  if (ch < 100) return defaultValue;
  return map(ch, 1000, 2000, minLimit, maxLimit);
}

void setup() {

  Serial.begin(BAUDRATE);
  Wire.begin();
  initialiseDisplay2();
  delay(2000); // Boot Delay for FC
  initialiseDisplay();
  pinMode(throttle, INPUT);
  pinMode(safety, INPUT);
  pinMode(flameSensor, INPUT);;
  fuelValve.attach(fuelServo);
}

void loop() {

  genVals();
  preFlight();
  inFlight();
  updateDisplay();
  updateDisplay2();

}

void initialiseDisplay() {
  display1.begin(SSD1306_SWITCHCAPVCC, 0x3c);
  display1.clearDisplay();
  display1.setChartCoordinates(0, 60);
  display1.setChartWidthAndHeight(123, 40);
  display1.setXIncrement(1);
  display1.setYLimits(0, 100);
  display1.setYLimitLabels("0", "100");
  display1.setYLabelsVisible(true);
  display1.setAxisDivisionsInc(8, 5);
  display1.setPlotMode(SINGLE_PLOT_MODE);
  actualThickness = NORMAL_LINE;
  display1.setLineThickness(actualThickness);
  display1.drawChart();
  display1.setCursor(1, 0);
  display1.setTextSize(2);
  display1.println(F("FUEL FLOW"));
  display1.display();
}

void initialiseDisplay2() {
  display2.begin(SSD1306_SWITCHCAPVCC, 0x3d);
  display2.clearDisplay();
  display2.setTextSize(1);
  display2.setTextColor(WHITE);
  display2.setCursor(0, 0);
  display2.println("FCM V1");
  display2.println("DO NOT ARM");
  display2.println("Booting up...");
  display2.display();
}

void updateDisplay() {
  if (!display1.updateChart(fuelFlow))
  {
    display1.clearDisplay();
    if (actualThickness == NORMAL_LINE)
    {
      actualThickness = LIGHT_LINE;
    }
    else if (actualThickness == LIGHT_LINE)
    {
      actualThickness = NORMAL_LINE;
    }
    display1.setLineThickness(actualThickness);
    display1.drawChart();
    display1.setCursor(1, 0);
    display1.setTextSize(2);
    display1.println(F("FUEL FLOW"));
  }
}

void updateDisplay2() {

  display2.clearDisplay();
  display2.setTextSize(1);
  display2.setCursor(0, 0);
  if (armed == 1) {
    display2.print("ARMED");
  }
  else display2.print("DISARMED");

  display2.setCursor(0, 8);
  if (purgeRequired == 1) {
    display2.print("Purge Required");
  }
  else display2.print("Purged");

  display2.setCursor(0, 16);
  if (ignitionRequired == 1) {
    display2.print("Flameout");
  }
  else {
    display2.print("Afterburner Active");
  }
  display2.setCursor(0, 24);
  display2.print("RPM: ");
  display2.setCursor(25, 24);
  //display2.print(rpm);
  display2.print(timeOut * 100); // Dummy rpm value
  display2.display();
}

void genVals() {
  throttleVal = readChannel(throttle, 0, 1000, 0);
  safetyVal = readChannel(safety, 0, 1000, 0);
  fuelVal = map(throttleVal, 0, 1000, 80, 50);
  //fuelFlow = map(fuelVal, 80, 50, 0, 100);
  fuelFlow = map(timeOut, 0, 180, 0, 100); // Dummy chart/rpm value
  flameVal = analogRead(flameSensor);
  throttlePercent = map(throttleVal, 0, 1000, 0, 100);
  rpm = (((KV * VCC) / 100) * throttlePercent);

  //Serial.print(rpmOut);
}

void preFlight() {
  if (flameVal <= 1) {
    ignitionRequired = 1;
  }
  if (safetyVal >= 100) {
    armed = 1;
  }
  else if (safetyVal <= 100) {
    armed = 0;
  }
  if (armed == 0) {
    fuelValve.write(90);
    digitalWrite(arcPin, LOW);
    prevState = 0;

    if (timeOut == 180) {
      purgeRequired = 1;
      timeOut = 0;
    }
    else timeOut++;
  }
  if (armed == 1) {
    if (prevState == 0) {
      prevState = 1;
    }
    if (purgeRequired == 1) {

      purge();
      purgeRequired = 0;
    }
    timeOut = 0;
  }
}

void inFlight() {
  if (ignitionRequired == 1 && armed == 1) {
    ignite();
  }
  if (throttleVal <= 100 && armed == 1) {
    fuelValve.write(idle);
  }
  if (throttleVal > 100 && armed == 1) {
    fuelValve.write(fuelVal);
  }
}

void purge() {
  fuelValve.write(65);
  delay(500);
  fuelValve.write(90);
  delay(500);
  fuelValve.write(65);
  delay(750);
  fuelValve.write(90);
  delay(1000);
}

void ignite() {
  if (ignitionRequired = 1) {
    for (int i = 0; i <= 4; i++) {
      digitalWrite(arcPin, HIGH);
      delay(100);
      digitalWrite(arcPin, LOW);
      delay(100);
    }
  }
}

I have no idea what your OLED_SSD1306_Chart class does. But I bet it expects GFX style graphics.
So you are probably stuck with Adafruit for your graphics OLED.

In which case you could use a text-only library for your print statements.

The alternative is to use U8g2lib for both displays with _1_ or _2_ type constructors.
But this involves re-writing all your print statements and all your graphics statements. Probably your OLED_SSD1306_Chart statements too.

Or you move to STM32 or SAM boards. I presume that you want something 43x18mm or similar.
Which has native USB and plenty of SRAM, Flash, CPU power, ...

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.