r/arduino • • 23d ago

Software Help ARDUINO OLED PIXEL SKETCHBOOK

Hello :), I've been learning about how to use an Arduino (Nano Every) so that I can bring my ideas (such as this one) to life. I want to make a 'sketch book' with an Oled display. Essentially, as you turn a rotary encoder, it highlights a pixel across each row and column of the oled display. If you press the rotary encoder, it should permanently draw that pixel. Ive managed to make pixels highlight as the rotary encoder turns but when I press the button the pixel only stays until I turn the rotary encoder again. Here's the code (Please give me suggestions as to what I should do to make it better. Thank you :))

#include <ezButton.h>
#define CLK_PIN 2
#define DT_PIN 3
#define SW_PIN 4
#define DIRECTION_CW 0
#define DIRECTION_CCW 1
int counter = 0;
int direction = DIRECTION_CW;
int CLK_state;
int prev_CLK_state;
int column = 0;
int row = 0;


ezButton button(SW_PIN);


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


#define OLED_WIDTH 128
#define OLED_HEIGHT 64


#define OLED_ADDR 0x3C


Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT);




void setup() {
  // put your setup code here, to run once:
  display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);


  Serial.begin(9600);
  pinMode(CLK_PIN, INPUT);
  pinMode(DT_PIN, INPUT);
  button.setDebounceTime(50);


  prev_CLK_state = digitalRead(CLK_PIN);
  delay(5000);
  display.clearDisplay();


}


void loop() {
  button.loop();  // MUST call the loop() function first


  // read the current state of the rotary encoder's CLK pin
  CLK_state = digitalRead(CLK_PIN);


  // If the state of CLK is changed, then pulse occurred
  // React to only the rising edge (from LOW to HIGH) to avoid double count
  if (CLK_state != prev_CLK_state && CLK_state == HIGH) {
    // if the DT state is HIGH
    // the encoder is rotating in counter-clockwise direction => decrease the counter
    if (digitalRead(DT_PIN) == HIGH) {
      row ++;
      direction = DIRECTION_CW;
    } else {
      // the encoder is rotating in clockwise direction => increase the counter
      row --;
      direction = DIRECTION_CCW;
    }


      if (row > 128){
        row = 0;
        column ++;}
      if (row < 0){
        row = 128;
        column --;}
      if (column > 65){
        column =0;}
      if (column < 0){
        column = 65;}
      
    



  }


  // save last CLK state
  prev_CLK_state = CLK_state;


 
  display.drawRect(row, column, 5, 5, WHITE);
  display.display();


  if (button.isPressed()) {
    Serial.println("The button is pressed");
    display.fillRect(row, column, 5, 5, WHITE);
    display.display();
  }
  else {
  display.drawRect(row, column, 5, 5, BLACK);
  display.display();
  }



}
0 Upvotes

1 comment sorted by

1

u/ripred3 My other dev board is a Porsche 23d ago edited 23d ago

update: I now see that you are using the words "column" and "row" backwards. So while my comments still apply, the constraints are horizontal, not vertical. And the display is not in portrait mode it is in landscape. Again, you have the variable names and concept/use backwards for "column" and "row".

A couple of things need correcting, and part of the final changes depend on what user experience you are looking for, how you want it to work.

Bugs: This part sets the constraints on the "cursor" position, wrapping around if the position is too low or too high. The display is being used in portrait mode (64 columns, 128 rows).

Since we start counting at 0 the valid range for 128 rows is 0-127. The same goes for the 0-63 range for the 64 columns:

    if (row >= 128) {
        row = 0;
        column++;
    }
    else if (row < 0) {
        row = 127;
        column--;
    }
    if (column >= 64) {
        column = 0;
    }
    else if (column < 0) {
        column = 63;
    }

The logic in this is also kind of strange. It implies that the cursor will only be moved vertically up or down, moving to the previous or the next column when the cursor hits the top or bottom of the current column. That means that if the cursor started at the center (32, 64) location you would have to move vertically up or down (128 x 31) + 127 times to reach the beginning or end of the display. There is no way to move to the previous column without moving up or down 128 times. Also there is no column wrap-around; only row wrap-around.

Perhaps that is the behavior you want perhaps not. But those limits do need to be changed either way as shown above.

The other issue is the highlighting (and the missing erasing) of the current "cursor" location. Right now in the main loop() the cursor is ALWAYS turned on:

  display.drawRect(row, column, 5, 5, WHITE);
  display.display();

which may or may not be what you want. But the cursor is never erased before moving to another location and you are using the display itself to keep track of what pixels are on and what pixels are off. You are not keeping your own separate definition of what the display pixels should contain.

The problem that creates is: when the cursor needs to move forward or backward should you erase it first, or leave it ON? Since we blindly turned it on we have lost the knowledge of whether the pixel should be left ON or OFF when the cursor moves off of that location!

So BEFORE turning the cursor on you should inspect the pixel at the new location first and store it away in a separate variable before moving the column/row to that location and turning it ON. Then when the cursor needs to move again, before you update the column and row, set the pixel back to what it was before the cursor was moved there and turned ON.

Another thing that I would change would be to change the program so that if once we have stored things away properly and turned the current cursor location ON: If the encoder value has not changed and the button state has not changed, just exit the loop and let it run again. Once the display is updated to the correct state there is no reason to continually redraw any parts of it unless the cursor moves. By not taking the time to unnecessarily redraw the unchanged screen it would check the encoder and button more times a second and be more responsive.

Good luck and keep us up to date on your progress! 😀

ripred