0

I've cobbled some code together from reading multiple sources on the web. Makeatronics in particular to help me get this rotary encoder code together (shout out to them). I'm pretty close to the end, but now trying to port it over the OLED display. Not sure what the issue is, but it doesn't miss counter steps when read on the serial monitor, but does when ported to the OLED.

I increased the clock speed to 400Khz using Wire.setClock as I saw that suggested in some other places for I2C comms.

1.) Any thoughts on how I can improve the code or hardware to get this to read without missing steps?

Hardware:

  • Nano (USB Powered)
  • White .96" OLED I2C SSD1306
  • HW-040 Rotary Encoder

Libraries:

  • Arduino WIRE
  • Adafruit SSD1306 Library

The Serial Monitor Code:


#define PinCLK 2  
#define PinDT 3

volatile boolean turnDetected = false;
void rotarydetect() {
  turnDetected = true;
}

void setup() {
  attachInterrupt (digitalPinToInterrupt(2), rotarydetect, CHANGE);
  pinMode(PinCLK, INPUT_PULLUP);
  pinMode(PinDT, INPUT_PULLUP);
  Serial.begin(2000000);
}

void loop() {
  if(turnDetected) {
    turnDetected = false;

    static uint8_t enc_val = 0;
    static int16_t enc_count = 0;
    static int8_t lookup_table[] = {0,1,0,-1,-1,0,1,0,0,1,0,-1,-1,0,1,0};
    static long lastTimeToDebounce = 0;
    static int debounceTime = 1;
    static int enc_counter = 0;

    if((millis() - lastTimeToDebounce) > debounceTime) {
      

    enc_val <<= 2;
    enc_val = enc_val | ((PIND & 0b1100) >> 2);
    enc_counter = enc_counter + lookup_table[enc_val & 0b1111];

    Serial.print("Counter: ");
    Serial.println(enc_counter);
    Serial.println("");
    }
    lastTimeToDebounce = millis();
  }
}

And the OLED Code:


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



#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in

#define PinCLK 2  
#define PinDT 3

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

volatile boolean rotated = false;

void rotarydetect() {
  rotated = true;
}

void setup() {
  Wire.begin();
  Wire.setClock(400000);
  attachInterrupt (digitalPinToInterrupt(2), rotarydetect, CHANGE);
  pinMode(PinCLK, INPUT_PULLUP);
  pinMode(PinDT, INPUT_PULLUP);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
}

void loop() {
  if(rotated) {
    rotated = false;

    static uint8_t enc_val = 0;
    static int16_t enc_count = 0;
    static int8_t lookup_table[] = {0,1,0,-1,-1,0,1,0,0,1,0,-1,-1,0,1,0};
    static long lastTimeToDebounce = 0;
    static int debounceTime = 3;
    static int enc_counter = 0;

    if((millis() - lastTimeToDebounce) > debounceTime) {
      
    enc_val <<= 2;
    enc_val = enc_val | ((PIND & 0b1100) >> 2);
    enc_counter = enc_counter + lookup_table[enc_val & 0b1111];
    display.setCursor(0,0);
    display.clearDisplay();
    display.print("Counter: ");
    display.println(enc_counter);
    display.println("");
    display.display();
    
    }
    lastTimeToDebounce = millis();
  }
}

I also tried the LCD_1602 screen (still through the I2C module board), and more or less it has the same results. I also switched the code from the ISR to trigger the code loop, to a polling loop that detects when the CLK pin changes state with the LCD 1602 screen. Same array with bit shifting as above for state detection. Same results. I also removed the very low debouncing code altogether with the Wire.setClock(400000) same results.

Seems like this may be a I2C limitation for communication?

  • There is a lot more overhead in displaying a value than there is in serial output, so it may well be a combination of I2C speed and that overhead. The nano is a pretty slow processor. One thing you could do to compare them would be to use micros() to measure the length of time it takes to output the count in each case; then you'd get an estimate of how much slower the display is vs the serial port. Just store the current micros() value before the Serial or display calls, then output the difference between it and the current value to measure the microseconds output took. – romkey Jul 14 at 5:22
  • 1
    Move the code related to screen clear(i.e.display.setCursor(0,0); display.clearDisplay(); display.print("Counter: ");) to setup(), you don't need to clean the screen on every loop. All you need is to set the cursor to where you want the enc_counter to be displayed on the screen and display.println(enc_counter);. – hcheung Jul 14 at 6:15
  • 1
    @romkey Yeah, ~3000 microseconds for Serial.print versus ~37000 microseconds for the the I2C. I have a .96 OLED SPI that I will test and let you guys know the results. – Jeremy Gardner Jul 15 at 4:04
  • @hcheung - The previous microsecond test I just discussed was done with the code update you suggested so that the display isn't clearing each loop. It still lagged and missed steps unfortunately. I'll chalk this one up to a communication speed issue. – Jeremy Gardner Jul 15 at 4:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.