Changing display OLED drivers

Hi,
I am a novice with Arduino coding C++, I really need some help to make this works and display on my OLED from SH110X to U8G2_SH1106. I tried to play with codes but I have no idea.
Please see codes below, really appreciate it.

Original:
#include <Stepper.h>
#include <Encoder.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>

#define motorInterfaceType 1
#define i2c_Address 0x3c


const int LINMOT_STEPPERS_STEP_PIN = 19;  // LINMOT: Linear motion
const int LINMOT_STEPPERS_DIR_PIN = 18;

const int EXTRUDER_STEPPER_STEP_PIN = 16;  // The stepper that moves the wire in the extruder.
const int EXTRUDER_STEPPER_DIR_PIN = 17;

const int ENCODER_DT_PIN = 0;
const int ENCODER_CLK_PIN = 23;
const int ENCODER_BTN_PIN = 4;

// For calibration only. The two buttons are used to move the top blade up and down manually.
// Once calibrated, you can remove the buttons from the circuit.
const int BTN1_PIN = 27;
const int BTN2_PIN = 26;

const int LINMOT_STEPPERS_STEPS = 1;  // Steppers step(s) movement at a time.
const int EXTRUDER_STEPPER_STEPS = 1;

const int LINMOT_STEPPERS_SPEED = 2000;
const int EXTRUDER_STEPPER_SPEED = 2000;


const int SCREEN_WIDTH = 128;  // OLED display width, in pixels
const int SCREEN_HEIGHT = 64;  // OLED display height, in pixels
const int TEXT_SIZE = 2;
const int TEXT_OFFSET = 3;

// Values for drawing the wire at the top of the OLED screen.
const int WIRE_STRAND_LENGTH = 30;
const int WIRE_STRAND_Y_POS = 7;
const int WIRE_INSULATION_WIDTH = SCREEN_WIDTH - (WIRE_STRAND_LENGTH * 2);
const int WIRE_INSULATION_HEIGHT = 14;

// These are just references to the corresponding component index in the comps array;
const int STRIPPING_LENGTH1_INDEX = 0;
const int WIRE_LENGTH_INDEX = 1;
const int STRIPPING_LENGTH2_INDEX = 2;
const int QUANTITY_INDEX = 3;
const int STRIPPING_DEPTH_INDEX = 4;
const int START_BTN_INDEX = 5;

const int CUTTING_STEPS = 17750;  // Steps to move blade to fully cut the wire.
const int STRIPPING_MULTIPLIER = 300;  // The chosen stripping depth value on the screen is multiplied by this value.
const int WIRE_MOVEMENT_MULTI = 414;  // How much to move wire per unit on OLED, turn on CALIBRATION_MODE to find this value.
const int DELAY_BETWEEN_CUTS = 100;  // Delay in ms between each cut in the quantity.

// To calibrate the wire movement distance. Use the first cell to enter the distance in mm to move the wire.
// Then adjust WIRE_MOVEMENT_MULTI to get the correct wire length.
const boolean CALIBRATION_MODE = true;


Stepper linMotSteppers(200, LINMOT_STEPPERS_DIR_PIN, LINMOT_STEPPERS_STEP_PIN);
Stepper extruderStepper(200, EXTRUDER_STEPPER_DIR_PIN, EXTRUDER_STEPPER_STEP_PIN);

Encoder encoder(ENCODER_DT_PIN, ENCODER_CLK_PIN);

Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);


int linMotSteppersCurrStep = 0;  // Current position/step of the stepper motor.

// A component is a cell with a value on the OLED display.
struct Component {
    int x, y;  // Position
    int w, h;  // Size
    int value;  // Current value of the cell.
    boolean highlighted;  // Whether the cell is the currently highlighted cell.
    boolean selected;  // Whether the cell is currently the selected one, where its value will be controlled by the encoder.
    boolean btn;  // If it is a button or not.
};


Component comps[] = {{0, 20, 40, 20, 0, 0, 0, 0},
                     {44, 20, 40, 20, 0, 0, 0, 0},
                     {88, 20, 40, 20, 0, 0, 0, 0},
                     {0, 44, 40, 20, 0, 0, 0, 0},
                     {44, 44, 40, 20, 0, 0, 0, 0},
                     {88, 44, 40, 20, 0, 0, 0, 1}};

int numOfComps = sizeof(comps) / sizeof(comps[0]);


int encoderPos;  // Current position/value of the encoder.
int encoderLastPos;  // For OLED drawing.
int encoderLastPosMain;  // For main loop.

boolean encBtnState = false;
boolean encBtnPrevState = false;  // For OLED drawing.
boolean encBtnPrevStateMain = false;  // For main loop.



void setup() {
    linMotSteppers.setSpeed(LINMOT_STEPPERS_SPEED);
    extruderStepper.setSpeed(EXTRUDER_STEPPER_SPEED);

    pinMode(ENCODER_BTN_PIN, INPUT_PULLUP);

    pinMode(BTN1_PIN, INPUT_PULLUP);
    pinMode(BTN2_PIN, INPUT_PULLUP);

    display.begin(i2c_Address, true);
}


void loop() {
    int encPos = getEncoderPos();
    boolean encBtnState = digitalRead(ENCODER_BTN_PIN);

    // Only update OLED screen if encoder is moved or pressed.
    if((encPos != encoderLastPosMain) || (encBtnState != encBtnPrevStateMain)) {
        handleOLEDDisplay();
    }
    
    if(comps[START_BTN_INDEX].selected) {
        runAutoCuttingStripping();
    }

    encoderLastPosMain = encPos;
    encBtnPrevStateMain = encBtnState;


    if(!digitalRead(BTN1_PIN)) {
        moveBlade(1);
    }
    if(!digitalRead(BTN2_PIN)) {
        moveBlade(-1);
    }    
}


void handleOLEDDisplay() {
    display.clearDisplay();

    drawWire();

    boolean btnState = digitalRead(ENCODER_BTN_PIN);

    // Handling whether encoder is changing cell or value of the cell.
    if(!btnState && (btnState != encBtnPrevState)) {
        encBtnState = !encBtnState;

        if(encBtnState) {
            encoderLastPos = encoderPos;
        } else {
            encoder.write(encoderLastPos * 4);
        }
    }
    
    encBtnPrevState = btnState;

    if (!encBtnState) {
        encoderPos = getEncoderPos();
    }

    handleAllComponents();
    
    display.display();
}


void drawWire() {
    display.drawLine(0, WIRE_STRAND_Y_POS, WIRE_STRAND_LENGTH, WIRE_STRAND_Y_POS, SH110X_WHITE);
    display.fillRect(WIRE_STRAND_LENGTH, 0, WIRE_INSULATION_WIDTH, WIRE_INSULATION_HEIGHT, SH110X_WHITE);
    display.drawLine(WIRE_STRAND_LENGTH+WIRE_INSULATION_WIDTH, WIRE_STRAND_Y_POS, SCREEN_WIDTH, WIRE_STRAND_Y_POS, SH110X_WHITE);
}


void handleAllComponents() {
    for(int i = 0; i < numOfComps; i++) {
        Component &comp = comps[i];
        
        if(encoderPos == i) {
            comp.highlighted = true;
            
            if(encBtnState) {
                if(!comp.selected && !comp.btn) {
                    encoder.write(comp.value * 4);
                }
                
                comp.selected = true;
                
                int newEncPos = getEncoderPos();
                comp.value = newEncPos;
                
            } else {
                comp.selected = false;
            }
            
        } else {
            comp.highlighted = false;
            comp.selected = false;
        }
        
        drawComponent(comp);
    }
}


void drawComponent(Component comp) {
    if(comp.highlighted) {
        display.setTextColor(SH110X_BLACK, SH110X_WHITE);
        display.fillRect(comp.x, comp.y, comp.w, comp.h, SH110X_WHITE);
        
        if (comp.selected) {
            display.drawRect(comp.x-1, comp.y-1, comp.w+2, comp.h+2, SH110X_WHITE);
        }
        
    } else {
        display.setTextColor(SH110X_WHITE, SH110X_BLACK);
        display.drawRect(comp.x, comp.y, comp.w, comp.h, SH110X_WHITE);
    }

    if(comp.btn) {
        display.setTextSize(1);
        drawText("Start", comp.x + TEXT_OFFSET, comp.y + TEXT_OFFSET);
    } else {
        display.setTextSize(TEXT_SIZE);
        drawText(String(comp.value), comp.x + TEXT_OFFSET, comp.y + TEXT_OFFSET);
    }
}


void drawText(String text, int x, int y) {
    display.setCursor(x, y);
    display.println(text);
}


void runAutoCuttingStripping() {
    if(CALIBRATION_MODE) {
        moveWire(comps[STRIPPING_LENGTH1_INDEX].value);
        cut();
    } else {
        cut();
        delay(DELAY_BETWEEN_CUTS);

        for(int i = 0; i < comps[QUANTITY_INDEX].value; i++) {
            moveWire(comps[STRIPPING_LENGTH1_INDEX].value);
            strip();
            moveWire(comps[WIRE_LENGTH_INDEX].value);
            strip();
            moveWire(comps[STRIPPING_LENGTH2_INDEX].value);
            cut();
            delay(DELAY_BETWEEN_CUTS);
        }   
    }

    comps[START_BTN_INDEX].selected = false;
    encBtnState = false;
}


void cut() {
    moveBlade(-CUTTING_STEPS);
    moveBlade(CUTTING_STEPS);
}


void strip() {
    moveBlade(-(comps[STRIPPING_DEPTH_INDEX].value * STRIPPING_MULTIPLIER));
    moveBlade(comps[STRIPPING_DEPTH_INDEX].value * STRIPPING_MULTIPLIER);
}


void moveBlade(int steps) {
    linMotSteppers.step(steps);
    linMotSteppersCurrStep += steps;
}


void moveWire(int steps) {
    extruderStepper.step(steps * WIRE_MOVEMENT_MULTI);
}


int getEncoderPos() {
    int encPos = encoder.read() / 4;
    return encPos;
}

My OLED works with this driver:

Test file:
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>

// This line defines the controller, resolution and interface type to use from the library
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
//===============================================================================
//  Initialization
//===============================================================================
void setup(void) {
  u8g2.begin();
}
//===============================================================================
//  Main
//===============================================================================
void loop(void) {
  u8g2.firstPage();
  do {
    u8g2.setFont(u8g2_font_ncenB14_tr);  // Set the font
    u8g2.drawStr(3,35,"Hello World!");   // Write 'Hello World!'
    u8g2.drawRFrame(0,0,127,63,7);       // Draw a rectangle around it
  } while ( u8g2.nextPage() );
}
1 Like

Why do you want to change some working code ?
Have you encountered stability issues ?

I built the "Original" for a Uno in the Web Editor.

Sketch uses 15676 bytes (48%) of program storage space. Maximum is 32256 bytes.

Global variables use 586 bytes (28%) of dynamic memory, leaving 1462 bytes for local variables. Maximum is 2048 bytes.

Although Adafruit_SH110X steals about 1120 bytes of SRAM at runtime you still have 1462-1120 = 342 bytes left.

Yes, you can re-write the code for U8g2. But you will have similar memory issues with the U8G2_xxx_F_ constructor. The only way to save SRAM is to use the U8G2_xxx_1_ or U8G2_xxx_2_ constructor.

I have not studied your code. You could store comps[] array in EEPROM to save memory. Copy individual element to and from SRAM when needed.

David.

@david_prentice I am using ESP32, can you help me to convert from Adafruit_SH110X to U8g2?
Because my OLED won't display with SH110X.
Thanks

I compiled just fine for an ESP32. Memory is not an issue. Are your libraries up to date ?

Using library stepper_1_1_3 at version 1.1.3 in folder: /home/builder/opt/libraries/stepper_1_1_3

Using library encoder_1_4_2 at version 1.4.2 in folder: /home/builder/opt/libraries/encoder_1_4_2

Using library SPI at version 2.0.0 in folder: /home/builder/.arduino15/packages/esp32/hardware/esp32/2.0.4/libraries/SPI

Using library Wire at version 2.0.0 in folder: /home/builder/.arduino15/packages/esp32/hardware/esp32/2.0.4/libraries/Wire

Using library adafruit_gfx_library_1_11_3 at version 1.11.3 in folder: /home/builder/opt/libraries/adafruit_gfx_library_1_11_3

Using library adafruit_busio_1_13_2 at version 1.13.2 in folder: /home/builder/opt/libraries/adafruit_busio_1_13_2

Using library adafruit_sh110x_2_1_8 at version 2.1.8 in folder: /home/builder/opt/libraries/adafruit_sh110x_2_1_8

/home/builder/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/gcc8_4_0-esp-2021r2-patch3/bin/xtensa-esp32-elf-size -A /tmp/arduino-build-76412AD9C5D8614229081BF16AB0D865/sketch_sep24a.ino.elf

Sketch uses 261617 bytes (19%) of program storage space. Maximum is 1310720 bytes.

Global variables use 18032 bytes (5%) of dynamic memory, leaving 309648 bytes for local variables. Maximum is 327680 bytes.

@david_prentice I think so, I always update

Can you start with the working sketch, and then add code ?

Disconnect the steppers and encoders from the ESP32.
Change the working sketch to the full u8g2 mode, such as this example: HelloWorld.ino.
Then slowly add the graphics.

What is the problem ? Blank screen or just wrong behaviour?

I only "built" your program. I have not tried it on a real SH1106 display.

I always advise running the library examples first. It verifies your hardware as well as showing off the library features.

If you are porting a known working program it is wise to post a link to the original proven code. Explain what you want to change.

David.

@Koepel I tried sketch with and without steppers & encoders connected. I use example code u8g2 works as I described above. The HelloWorld giving an error:

exit status 1
'u8g2' was not declared in this scope

@david_prentice Blank screen, I purchased same display as he recommended (Oled 1.3" IC2 128x64) but I might have a different driver chipset

Original codes from designer:

Which ESP32 pins are you using ?

If you are not using the exact components from the GitHub project:
Post a link to the actual SH1106 display that you bought.
Post a link to the actual ESP32 board that you bought.

I am going trolley bashing. I will try it on real hardware when I come back.

David.

You have to select the right display from the long list.

Since you had it already working with this:

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

You can use that one, remove the comments.

U8g2 examples work fine.
Adafruit_SH110X example works ok.
Sketch from #1 produces blank screen on ESP32.

The sketch from #1 works on a Uno. (builds and displays on OLED. obviously pins are rubbish)

So there is something strange going on. I suspect that my ESP module is upset by one of the Stepper or Encoder pins but I have not studied the Exception dump.

Rebooting...
ets Jun  8 2016 00:22:57

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:1344
load:0x40078000,len:13864
load:0x40080400,len:3608
entry 0x400805f0
E (124) gpio: esp_ipc_call_blocking failed (0x103)
E (124) gpio: gpio_install_isr_service(449): GPIO isr service already installed
Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.

Core  0 register dump:
PC      : 0x400f0543  PS      : 0x00060533  A0      : 0x800d52ef  A1      : 0x3ffe3b20  
A2      : 0x00000000  A3      : 0x3ffc1a70  A4      : 0x3ffc1a70  A5      : 0x00060523  
A6      : 0x00060520  A7      : 0x00000001  A8      : 0x800d4eac  A9      : 0x3ffe3ae0  
A10     : 0x3ffbdca4  A11     : 0x00800000  A12     : 0x00000017  A13     : 0x3ffe3b20  
A14     : 0x007bdc90  A15     : 0x003fffff  SAR     : 0x00000009  EXCCAUSE: 0x0000001c  
EXCVADDR: 0x00000000  LBEG    : 0x400866a1  LEND    : 0x400866b1  LCOUNT  : 0xfffffff7  


Backtrace:0x400f0540:0x3ffe3b200x400d52ec:0x3ffe3b40 0x400d2f14:0x3ffe3b70 0x400d2f95:0x3ffe3ba0 0x400d14a9:0x3ffe3bc0 0x400d16b5:0x3ffe3be0 0x400da76f:0x3ffe3c10 0x40082fbd:0x3ffe3c40 0x400792ca:0x3ffe3c90  |<-CORRUPTED

@david_prentice I don't know if his code is works or not but I can upload it without error, just no display. I assumed I have a different chipset.
I bought it from Amazon:
OLED Display Module SPI IIC I2C Serial 128x64 SSH1106 SSD1306:

As you can see from#13 the ESP32 creates an Exception.

Open your Serial Terminal @ 115200 baud. You will see it crashing.

But first off. Build and run the 110X library example. It should be ok.

If the items on your desk match the photos in the Amazon link you have a genuine SH1106 display.

Oh, I looked at the project video. It looks interesting. Mind you, it is not that difficult to cut wires by hand. Perhaps if I had to make 100 wires ...

@david_prentice
I think i have it on 9600.
It is interesting project. I am ok with the hardware and schematic.
I want to learn more about Arduino stuff.
I don't see any errors on my sketch log just no display.

@david_prentice
Both sample codes work okay for me as well. I know the oled is fine and I have sucessful upload his code, some reason I don't see display on my oled. I couldn't figure out what causing it seems no errors on my end.

Change Serial Terminal to 115200 baud. Works in IDE.

In the Web Editor Serial does not work properly at 115200.

@Koepel
Uncomment it works: (I see displayed Hello World!)

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

@david_prentice
you're right!
I see the error code keep looping in serial console at 11520. sketch from original code:

ELF file SHA256: 0000000000000000
Rebooting...
rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:1344
load:0x40078000,len:13864
load:0x40080400,len:3608
entry 0x400805f0
E (124) gpio: esp_ipc_call_blocking failed (0x103)
E (124) gpio: gpio_install_isr_service(449): GPIO isr service already installed
Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.

Core  0 register dump:
PC      : 0x400f0543  PS      : 0x00060533  A0      : 0x800d52ef  A1      : 0x3ffe3b20  
A2      : 0x00000000  A3      : 0x3ffc1a70  A4      : 0x3ffc1a70  A5      : 0x00060523  
A6      : 0x00060520  A7      : 0x00000001  A8      : 0x800d4eac  A9      : 0x3ffe3ae0  
A10     : 0x3ffbdca4  A11     : 0x00800000  A12     : 0x00000017  A13     : 0x3ffe3b20  
A14     : 0x007bdc90  A15     : 0x003fffff  SAR     : 0x00000009  EXCCAUSE: 0x0000001c  
EXCVADDR: 0x00000000  LBEG    : 0x400866a1  LEND    : 0x400866b1  LCOUNT  : 0xfffffff7  


Backtrace:0x400f0540:0x3ffe3b200x400d52ec:0x3ffe3b40 0x400d2f14:0x3ffe3b70 0x400d2f95:0x3ffe3ba0 0x400d14a9:0x3ffe3bc0 0x400d16b5:0x3ffe3be0 0x400da76f:0x3ffe3c10 0x40082fbd:0x3ffe3c40 0x400792ca:0x3ffe3c90  |<-CORRUPTED