FLASH overflowed if I use SSD1306 library

I'm trying a simple hello world sketch using a small SSD1306 OLED display
The microcontroller is a STM32F03x It has a Cortex m0 with 16 KBytes flash, they aren't much but it seems absurd I cannot even run a simple hello world, so I'm wondering if maybe the problem is that library isn't modular enough and bloat the code or I'm doing some other thing wrong

Here the code ( I build it with default -Os option) and I'm using latest Arduino IDE 2.0.2:

#include <SPI.h>

#include <Wire.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels

#define SCREEN_HEIGHT 32 // 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; 0x3D for 128x64, 0x3C for 128x32

Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_4X);

void setup() {
  // put your setup code here, to run once:
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
 display.println(F("Hello world"));
  display.display();      // Show initial text
  delay(1000);
}

void loop() {
  Serial.print("Hello world");
  // put your main code here, to run repeatedly:

}

Please post the complete error message(s).

What is this line doing, and where is the library included?

Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_4X);

Please include the entire error message. It is easy to do. There is a button (lower right of the IDE window) called "copy error message". Copy the error and paste into a post in code tags. Paraphrasing the error message leaves out important information.

The Adafruit SSD1306 library is pretty chonky. There are smaller ones out there. Look in the Arduino Libraries and there are some that advertise themselves as being tiny.

Ignore this line. It's a copy paste error.

the error message is

arduino-sketch-1E4DE4852614969736F4D52D7459C1AD/mytest.ino.elf section .text' will not fit in region FLASH'
region `FLASH' overflowed by 15280 bytes

I'll give a look

Please post code that will compile. Even after removing the "copy paste error", you are missing several definitions.

After fixing the most obvious problems, this compiles to 13386 bytes for an Arduino Uno. I have trouble imagining why an M0 would require over 2x as much flash memory, and am curious to learn what you get.

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // 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; 0x3D for 128x64, 0x3C for 128x32

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
  // put your setup code here, to run once:
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;); // Don't proceed, loop forever
  }
  display.println(F("Hello world"));
  display.display();      // Show initial text
  delay(1000);
}

void loop() {
  Serial.print("Hello world");
  // put your main code here, to run repeatedly:

}

I'm sorry The line
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_
should be
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

I have pasted the commented line.

It's super weird that it requires so much more flash compared to Arduino UNO however sems that problem is definitely the size of libraries and I'm not the only to have hard time due to low flash with this board.

Replacing Adafruit_SSD1306 library with miniOled library and removing all serial libraries now I'm able to run my Hello World
This library allows to replace Wire lib with SoftWire that allows to save further precious KB, however if want to do something it is needed same approach also for whatever other library I need to use, and in my case this unfortunately conflict with this other lib that uses Wire, leaving it few byte out of the limit.

How to edit this library to use SoftWire on 2 different SDA SCL pins? (SoftWire Wire(PA6, PA5); are used for OLED and sensor is on PA10, PA9)

UNO == AVR == 8-bit RISC

STM32F0 == STM == 32-bit see: STM link

Consider that 32-bit libraries are also 32-bit as is the C++ init code.

Good point. It appears that that particular MCU is a very poor choice for even this simple task.

Not all uses of SSD1306 OLED requires lots of resources as can be seen in this Conway example:

// Conway's Game Of Life 256x256
// PaulRB http://stm32duino.com/viewtopic.php?f=19&t=137&hilit=conway
// Jun 2015

/*
  Compiled with ArduinoIDE 1.7.3 on Linux Mint 17.3 Cinnamon tested 20160201
    Sketch uses 14,932 bytes (12%) of program storage space. Maximum is 122,880 bytes.
    Global variables use 10,968 bytes of dynamic memory.
*/

#include <SPI.h>

//Pins controlling SSD1306 Graphic OLED
#define OLED_DC     1
#define OLED_CS     0
#define OLED_RESET  2
#define OLED_COLS   128
#define OLED_ROWS   8

#define MATRIX_COLS 256
#define MATRIX_ROWS 32

union MatrixData {
  unsigned long long l[MATRIX_ROWS/8];
  byte b[MATRIX_ROWS];
};

#define SW_U 17
#define SW_D 20
#define SW_L 18
#define SW_R 16
#define SW_C 19

int scrollHorz = 0;
int scrollVert = 0;

MatrixData Matrix[MATRIX_COLS+1]; // Cell data in ram

void setup() {
  
  pinMode(OLED_DC, OUTPUT);
  pinMode(OLED_CS, OUTPUT);
  pinMode(OLED_RESET, OUTPUT);

  SPI.begin();
  SPI.setBitOrder(MSBFIRST); // Set the SPI bit order
  SPI.setDataMode(SPI_MODE0); //Set the SPI data mode 0
  SPI.setClockDivider(SPI_CLOCK_DIV2); // 2.25  MHz

  digitalWrite(OLED_RESET, HIGH);
  delay(1);
  digitalWrite(OLED_RESET, LOW);
  delay(10);
  digitalWrite(OLED_RESET, HIGH);

  digitalWrite(OLED_DC, LOW);
  digitalWrite(OLED_CS, LOW);
  
  SPI.transfer(0xAE); // Display off
  SPI.transfer(0xD5); // Set display clock divider
  SPI.transfer(0x80);
  SPI.transfer(0xA8); // Set multiplex 
  SPI.transfer(0x3F);
  SPI.transfer(0xD3); // Set display offset
  SPI.transfer(0x00);
  SPI.transfer(0x40); // Set start line to zero
  SPI.transfer(0x8D); // Set charge pump
  SPI.transfer(0x14);
  SPI.transfer(0x20); // Set memory mode
  SPI.transfer(0x00);
  SPI.transfer(0xA0 | 0x1); // Set segment remapping
  SPI.transfer(0xC8); // Set command Scan decode
  SPI.transfer(0xDA); // Set Comm pins
  SPI.transfer(0x12);
  SPI.transfer(0x81); // Set contrast
  SPI.transfer(0xCF);
  SPI.transfer(0xd9); // Set precharge
  SPI.transfer(0xF1);
  SPI.transfer(0xDB); // Set Vcom detect
  SPI.transfer(0x40);
  SPI.transfer(0xA4); // Allow display resume
  SPI.transfer(0xA6); // Set normal display
  SPI.transfer(0xAF); // Display On
  
  digitalWrite(OLED_CS, HIGH);

  //R-pentomino
  //Matrix[64].l[4] = 0b0000010;
  //Matrix[65].l[4] = 0b0000111;
  //Matrix[66].l[4] = 0b0000100;

  //Gosper's Glider Gun
  //Matrix[64].l[4] = 0b00000000000000000000000000010000000000000000000000;
  //Matrix[65].l[4] = 0b00000000000000000000000001010000000000000000000000;
  //Matrix[66].l[4] = 0b00000000000000011000000110000000000001100000000000;
  //Matrix[67].l[4] = 0b00000000000000100010000110000000000001100000000000;
  //Matrix[68].l[4] = 0b00011000000001000001000110000000000000000000000000;
  //Matrix[69].l[4] = 0b00011000000001000101100001010000000000000000000000;
  //Matrix[70].l[4] = 0b00000000000001000001000000010000000000000000000000;
  //Matrix[71].l[4] = 0b00000000000000100010000000000000000000000000000000;
  //Matrix[72].l[4] = 0b00000000000000011000000000000000000000000000000000;

  
  randomiseMatrix();
  outputMatrix();

  Serial.begin(115200);
  pinMode(SW_U, INPUT_PULLUP);
  pinMode(SW_D, INPUT_PULLUP);
  pinMode(SW_L, INPUT_PULLUP);
  pinMode(SW_R, INPUT_PULLUP);
  pinMode(SW_C, INPUT_PULLUP);
    
}

void loop() {
  unsigned long start = millis();
  for (int i=0; i<1000; i++) {
    generateMatrix();
    outputMatrix();
  }
  //Serial.print("Gens/s:"); Serial.println(1000000UL/(millis() - start));

}

void outputMatrix() {
  
  digitalWrite(OLED_DC, LOW); //Command mode
  digitalWrite(OLED_CS, LOW); //Enable display on SPI bus
  
  SPI.transfer(0x21); // Set column address
  SPI.transfer(0);
  SPI.transfer(OLED_COLS-1);

  SPI.transfer(0x22); // Set page address
  SPI.transfer(0);
  SPI.transfer(OLED_ROWS-1);

  digitalWrite(OLED_CS, HIGH); //Disable display on SPI bus
  
  digitalWrite(OLED_DC, HIGH); // Data mode
  digitalWrite(OLED_CS, LOW); //Enable display on SPI bus

  if (digitalRead(SW_L) == LOW && scrollHorz < (MATRIX_COLS - OLED_COLS)) scrollHorz++;
  if (digitalRead(SW_R) == LOW && scrollHorz > 0) scrollHorz--;
  if (digitalRead(SW_U) == LOW && scrollVert < ((MATRIX_ROWS - OLED_ROWS)<<3)) scrollVert++;
  if (digitalRead(SW_D) == LOW && scrollVert > 0) scrollVert--;
  
  //Send matrix data for display on OLED
  for (int col = 0; col < OLED_ROWS; col++) {
    int colScrolled = col + (scrollVert>>3);
    for (int row = 0; row < OLED_COLS; row++) {
      SPI.transfer(Matrix[row+scrollHorz].b[colScrolled]);
    }   
  }
  digitalWrite(OLED_CS, HIGH);
}
   
void randomiseMatrix() {

  //Set up initial cells in matrix
  randomSeed(analogRead(0));
  for (int row = 0; row < MATRIX_COLS; row++) {
    for (int col = 0; col < MATRIX_ROWS; col++) {
      Matrix[row].b[col] = random(0xff);
    }
  }
}
   
void injectGlider() {

  int col = random(MATRIX_COLS);
  int row = random(MATRIX_ROWS);
  Matrix[col++].b[row] |= 0b0000111;
  Matrix[col++].b[row] |= 0b0000001;
  Matrix[col++].b[row] |= 0b0000010;

}

void rotateLeft(unsigned long long x[]) {
  unsigned long long c = 0;
  for (int i=0; i<4; i++) {
    unsigned long long c2 = x[i] >> 63;
    x[i] = x[i] << 1 | c;
    c = c2;
  }
  x[0] |= c;
}

void rotateRight(unsigned long long x[]) {
  unsigned long long c = 0;
  for (int i=3; i>=0; i--) {
    unsigned long long c2 = x[i] << 63;
    x[i] = x[i] >> 1 | c;
    c = c2;
  }
  x[3] |= c;
}

int generateMatrix() {

  //Variables holding data on neighbouring cells
  MatrixData NeighbourN, NeighbourNW, NeighbourNE, CurrCells, NeighbourW, NeighbourE, NeighbourS, NeighbourSW, NeighbourSE;
   
  //Variables used in calculating new cells
  unsigned long long tot1, carry, tot2, tot4, NewCells;
  
  int changes = 0; // counts the changes in the matrix
  static int prevChanges[4]; // counts the changes in the matrix on prev 4 generations
  static int staleCount = 0; // counts the consecutive occurrances of the same number of changes in the matrix

  //set up N, NW, NE, W & E neighbour data
  NeighbourN = Matrix[MATRIX_COLS-1];
  CurrCells = Matrix[0];
  Matrix[MATRIX_COLS] = CurrCells;  // copy row 0 to location after last row to remove need for wrap-around code in the loop

  NeighbourNW = NeighbourN;
  rotateLeft(NeighbourNW.l);
  NeighbourNE = NeighbourN;
  rotateRight(NeighbourNE.l);
   
  NeighbourW = CurrCells;
  rotateLeft(NeighbourW.l);
  NeighbourE = CurrCells;
  rotateRight(NeighbourE.l);
  
  //Process each row of the matrix
  for (int row = 0; row < MATRIX_COLS; row++) {
      
    //Pick up new S, SW & SE neighbours
    NeighbourS = Matrix[row + 1];

    NeighbourSW = NeighbourS;
    rotateLeft(NeighbourSW.l);

    NeighbourSE = NeighbourS;
    rotateRight(NeighbourSE.l);

    for (int i=0; i<MATRIX_ROWS/8; i++) {
      //Count the live neighbours (in parallel) for the current row of cells
      //However, if total goes over 3, we don't care (see below), so counting stops at 4
      tot1 = NeighbourN.l[i];
      tot2 = tot1 & NeighbourNW.l[i]; tot1 = tot1 ^ NeighbourNW.l[i];
      carry = tot1 & NeighbourNE.l[i]; tot1 = tot1 ^ NeighbourNE.l[i]; tot4 = tot2 & carry; tot2 = tot2 ^ carry;
      carry = tot1 & NeighbourW.l[i]; tot1 = tot1 ^ NeighbourW.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
      carry = tot1 & NeighbourE.l[i]; tot1 = tot1 ^ NeighbourE.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
      carry = tot1 & NeighbourS.l[i]; tot1 = tot1 ^ NeighbourS.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
      carry = tot1 & NeighbourSW.l[i]; tot1 = tot1 ^ NeighbourSW.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
      carry = tot1 & NeighbourSE.l[i]; tot1 = tot1 ^ NeighbourSE.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
        
      //Calculate the updated cells:
      // <2 or >3 neighbours, cell dies
      // =2 neighbours, cell continues to live
      // =3 neighbours, new cell born
      NewCells = (CurrCells.l[i] | tot1) & tot2 & ~ tot4;
      
      //Have any cells changed?
      if (NewCells != CurrCells.l[i]) {       
        //Count the change for "stale" test
        changes++;
        Matrix[row].l[i] = NewCells;
      }
    }
  
    //Current cells (before update), E , W, SE, SW and S neighbours become
    //new N, NW, NE, E, W neighbours and current cells for next loop
    NeighbourN = CurrCells;
    NeighbourNW = NeighbourW;
    NeighbourNE = NeighbourE;
    NeighbourE = NeighbourSE;
    NeighbourW = NeighbourSW;
    CurrCells = NeighbourS;
  }
    
  if (changes != prevChanges[0] && changes != prevChanges[1] && changes != prevChanges[2] && changes != prevChanges[3]) {
    staleCount = 0;
  }
  else {
    staleCount++; //Detect "stale" matrix
  }
  
  if (staleCount > 64) injectGlider(); //Inject a glider
  //Serial.println(changes);

  for (int i=3; i>0; i--) {
    prevChanges[i] = prevChanges[i-1];
  }

  prevChanges[0] = changes;
}


Take-A-Way:

  • a MCU should match the target needs which implies that some effort has been applied to document the overall requirements!

  • Not all OLED projects require a heavy hitting graphics library such as the Adafruit lib.

EXAMPLE


/*
------------------------------------------------------------------------------------
                     Credits to Mike Rankin for OLED routines...
------------------------------------------------------------------------------------
*/

static void reset_display(void)
{
  displayOff();
  clear_display();
  displayOn();
}

/*
------------------------------------------------------------------------------------
              Simple sub routine added to get OLED up and running
------------------------------------------------------------------------------------
*/
void StartUp_OLED()
{
  
  
  init_OLED();
  reset_display();
  displayOff();
  setXY(0,0);
  clear_display();
  displayOn();
  
  
}





//==========================================================//
// Turns display on.
void displayOn(void)
{
    sendcommand(0xaf);        //display on
}

//==========================================================//
// Turns display off.
void displayOff(void)
{
  sendcommand(0xae);		//display off
}

//==========================================================//
// Clears the display by sendind 0 to all the screen map.
static void clear_display(void)
{
  unsigned char i,k;
  for(k=0;k<8;k++)
  {	
    setXY(k,0);    
    {
      for(i=0;i<128;i++)     //clear all COL
      {
        SendChar(0);         //clear all COL
      }
    }
  }
}



//==========================================================//
// Actually this sends a byte, not a char to draw in the display. 
// Display's chars uses 8 byte font the small ones and 96 bytes
// for the big number font.
static void SendChar(unsigned char data) 
{
  Wire.beginTransmission(OLED_address); // begin transmitting
  Wire.write(0x40);//data mode
  Wire.write(data);
  Wire.endTransmission();    // stop transmitting
}

//==========================================================//
// Prints a display char (not just a byte) in coordinates X Y,
// being multiples of 8. This means we have 16 COLS (0-15) 
// and 8 ROWS (0-7).
static void sendCharXY(unsigned char data, int X, int Y)
{
  //if (interrupt && !doing_menu) return; // Stop printing only if interrupt is call but not in button functions
  
  setXY(X, Y);
  Wire.beginTransmission(OLED_address); // begin transmitting
  Wire.write(0x40);//data mode
  
  for(int i=0;i<8;i++)
    Wire.write(pgm_read_byte(myFont[data-0x20]+i));
    
  Wire.endTransmission();    // stop transmitting
}

//==========================================================//
// Used to send commands to the display.
static void sendcommand(unsigned char com)
{
  Wire.beginTransmission(OLED_address);     //begin transmitting
  Wire.write(0x80);                          //command mode
  Wire.write(com);
  Wire.endTransmission();                    // stop transmitting
}

//==========================================================//
// Set the cursor position in a 16 COL * 8 ROW map.
static void setXY(unsigned char row,unsigned char col)
{
  sendcommand(0xb0+row);                //set page address
  sendcommand(0x00+(8*col&0x0f));       //set low col address
  sendcommand(0x10+((8*col>>4)&0x0f));  //set high col address
}


//==========================================================//
// Prints a string regardless the cursor position.
static void sendStr(unsigned char *string)
{
  unsigned char i=0;
  while(*string)
  {
    for(i=0;i<8;i++)
    {
      SendChar(pgm_read_byte(myFont[*string-0x20]+i));
    }
    *string++;
  }
}

//==========================================================//
// Prints a string in coordinates X Y, being multiples of 8.
// This means we have 16 COLS (0-15) and 8 ROWS (0-7).
static void sendStrXY( char *string, int X, int Y)
{
  setXY(X,Y);
  unsigned char i=0;
  while(*string)
  {
    for(i=0;i<8;i++)
    {
      SendChar(pgm_read_byte(myFont[*string-0x20]+i));
    }
    *string++;
  }
}


//==========================================================//
// Inits oled and draws logo at startup
static void init_OLED(void)
{
  sendcommand(0xae);		//display off
  sendcommand(0xa6);            //Set Normal Display (default)
    // Adafruit Init sequence for 128x64 OLED module
    sendcommand(0xAE);             //DISPLAYOFF
    sendcommand(0xD5);            //SETDISPLAYCLOCKDIV
    sendcommand(0x80);            // the suggested ratio 0x80
    sendcommand(0xA8);            //SSD1306_SETMULTIPLEX
    sendcommand(0x3F);
    sendcommand(0xD3);            //SETDISPLAYOFFSET
    sendcommand(0x0);             //no offset
    sendcommand(0x40 | 0x0);      //SETSTARTLINE
    sendcommand(0x8D);            //CHARGEPUMP
    sendcommand(0x14);
    sendcommand(0x20);             //MEMORYMODE
    sendcommand(0x00);             //0x0 act like ks0108
    
    //sendcommand(0xA0 | 0x1);      //SEGREMAP   //Rotate screen 180 deg
    sendcommand(0xA0);
    
    //sendcommand(0xC8);            //COMSCANDEC  Rotate screen 180 Deg
    sendcommand(0xC0);
    
    sendcommand(0xDA);            //0xDA
    sendcommand(0x12);           //COMSCANDEC
    sendcommand(0x81);           //SETCONTRAS
    sendcommand(0xCF);           //
    sendcommand(0xd9);          //SETPRECHARGE 
    sendcommand(0xF1); 
    sendcommand(0xDB);        //SETVCOMDETECT                
    sendcommand(0x40);
    sendcommand(0xA4);        //DISPLAYALLON_RESUME        
    sendcommand(0xA6);        //NORMALDISPLAY             

  clear_display();
  sendcommand(0x2e);            // stop scroll
  //----------------------------REVERSE comments----------------------------//
  //  sendcommand(0xa0);		//seg re-map 0->127(default)
  //  sendcommand(0xa1);		//seg re-map 127->0
  //  sendcommand(0xc8);
  //  delay(1000);
  //----------------------------REVERSE comments----------------------------//
  // sendcommand(0xa7);  //Set Inverse Display  
  // sendcommand(0xae);		//display off
  sendcommand(0x20);            //Set Memory Addressing Mode
  sendcommand(0x00);            //Set Memory Addressing Mode ab Horizontal addressing mode
  // sendcommand(0x02);         // Set Memory Addressing Mode ab Page addressing mode(RESET)  
  
 //  setXY(0,0);
  // Display Logo here :)
//  for(int i=0;i<128*8;i++)     // show 128* 64 Logo
 // {
  //  SendChar(pgm_read_byte(logo+i));
 // }
 // sendcommand(0xaf);		//display on
  
 // delay(5000); 
}


/*
------------------------------------------------------------------------------------
              Added to Mikes routine Draw routines
------------------------------------------------------------------------------------
*/


void Draw_WIFI()
{
   clear_display();
   setXY(0,0);
   // Display Logo here :)
   for(int i=0;i<128*8;i++)     // show 128* 64 Logo
   {
    SendChar(pgm_read_byte(WIFI1+i));
   }
   sendcommand(0xaf);		//display on
}


void Draw_WAVES()
{
   clear_display();
   setXY(0,0);
   // Display Logo here :)
   for(int i=0;i<128*8;i++)     // show 128* 64 Logo
   {
    SendChar(pgm_read_byte(rfwaves+i));
   }
   sendcommand(0xaf);		//display on
}


void Draw_LOCKED()
{
   clear_display();
   setXY(0,0);
   // Display Logo here :)
   for(int i=0;i<128*8;i++)     // show 128* 64 Logo
   {
    SendChar(pgm_read_byte(locked+i));
   }
   sendcommand(0xaf);		//display on
}