Week 11 - Input Devices

Solo Assignment

For this week, I decided to use a potentiometer that seconds as a button which is called a rotary encoder. I also plan to get the values displayed on an OLED or a display.

Understanding the Rotary Encoder

The rotary encoder has 5 pins on it. Source of Info

  1. CLK : Output A (digital pin)

  2. DT : Output B (digital pin)

  3. SW : Button Press (digital pin)

  4. VCC : 5V power

  5. GND : Ground

Not gonna lie, this pinout surprised me. Potentiometers typically have VCC, Ground, and a data pin that uses an analog read so having all digital pins was very surprising.

The next thing I looked into was how the CLK and DT pins worked to give me a value. According to medium.com, the CLK and DT create pulses when they connect to a common pin. Basically, there are a bunch of equally spaced copper pins within the rotary encoder that the CLK and DT can connect to causing a value.

Code

I first used my Snail board with a old code from machine week but later moved to a different board a explained in the code #2 and #3.

Final Code

Code #1

#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 64 // OLED display height, in pixels

#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3D ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define CLK_PIN A0
#define DT_PIN A3
#define SW_PIN A1

int counter = 0;
int CLK_state;
int CLK_laststate;
int storedValues[3]; // Array to store the values
int buttonPressCounter = 0; // Counter for button presses

bool valstored = false;

int distance = 1;
int delay = 1;
int start = 1;

void setup() {
// put your setup code here, to run once:
Serial.begin(9600);

if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    while (true);
}

delay(2000);         // wait for initializing
oled.clearDisplay(); // clear display

oled.setTextSize(2);          // text size
oled.setTextColor(WHITE);     // text color
oled.setCursor(0, 0);        // position to display
oled.println("Start"); // text to display
oled.display();
delay(1000);         // show on OLED
oled.clearDisplay();

pinMode(CLK_PIN, INPUT);
pinMode(DT_PIN, INPUT);
pinMode(SW_PIN, INPUT_PULLUP);

CLK_laststate = digitalRead(CLK_PIN);

}

void loop() {


oled.setTextSize(1);          // text size
oled.setTextColor(WHITE);     // text color


CLK_state = digitalRead(CLK_PIN);

if (digitalRead(SW_PIN) == LOW) {

    Serial.println("Value Stored!");
    oled.display();
    storedValues[buttonPressCounter] = counter; // Store the current value
    buttonPressCounter++; // Increment button press counter
    if (buttonPressCounter >= 3) { // If pressed three times
    // Print all stored values
        oled.clearDisplay();
        oled.setCursor(5, 10);        // position to display
        oled.print("distance: ");
        distance = (abs(storedValues[0]) * .375 + 10);
        oled.println(distance);
        oled.println(" in.");

        oled.setCursor(5, 20);
        oled.print("delay: ");
        delay = (abs(storedValues[1]));
        oled.println(abs(storedValues[1]));
        oled.println(" sec.");

        oled.setCursor(5,30);
        oled.print("Start ");
        amount = (abs(storedValues[2]));
        oled.display();
        valstored = true;
    }
    // Reset button press counter and clear stored values

    delay(250); // Debouncing delay
}

if (CLK_state != CLK_laststate){     
    if (digitalRead(DT_PIN) != CLK_state) { 
    counter ++;
    } else {
    counter --;
    }
CLK_laststate = CLK_state;

    if (buttonPressCounter == 0){
    oled.clearDisplay();
    oled.setCursor(20,30);
    oled.print("distance = ");
    oled.println(abs(counter)); // MATHS
    oled.display();
    } else if (buttonPressCounter == 1){
    oled.clearDisplay();
    oled.setCursor(20,30);
    oled.print("delay = ");
    oled.print(abs(counter));
    oled.display();
    } else if (buttonPressCounter = 2){
    oled.clearDisplay();
    oled.setCursor(20,30);
    oled.print("Start = ");
    oled.println(abs(counter)); // MATHS
    oled.display();
    }
    Serial.print("Counter: ");
    Serial.println(counter);
}

if (counter >= 40 || counter <=-40){
    counter = 0;
}


}

Code #2

When I tried compiling code 1, I got an error that said delay was being defined as something else. I quickly relized that the "delay" integer was being mixed up with the "delay()" function so I changed the integer to "Delay" with a capital D. This compiled as can be seen in my code below.

#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 64 // OLED display height, in pixels

#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3D ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define CLK_PIN A0
#define DT_PIN A3
#define SW_PIN A1

int counter = 0;
int CLK_state;
int CLK_laststate;
int storedValues[3]; // Array to store the values
int buttonPressCounter = 0; // Counter for button presses

bool valstored = false;

int distance = 1;
int Delay = 1;
int start = 1;

void setup() {
// put your setup code here, to run once:
Serial.begin(9600);

if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    while (true);
}

delay(2000);         // wait for initializing
oled.clearDisplay(); // clear display

oled.setTextSize(2);          // text size
oled.setTextColor(WHITE);     // text color
oled.setCursor(0, 0);        // position to display
oled.println("Start"); // text to display
oled.display();
delay(1000);         // show on OLED
oled.clearDisplay();

pinMode(CLK_PIN, INPUT);
pinMode(DT_PIN, INPUT);
pinMode(SW_PIN, INPUT_PULLUP);

CLK_laststate = digitalRead(CLK_PIN);

}

void loop() {


oled.setTextSize(1);          // text size
oled.setTextColor(WHITE);     // text color


CLK_state = digitalRead(CLK_PIN);

if (digitalRead(SW_PIN) == LOW) {

    Serial.println("Value Stored!");
    oled.display();
    storedValues[buttonPressCounter] = counter; // Store the current value
    buttonPressCounter++; // Increment button press counter
    if (buttonPressCounter >= 3) { // If pressed three times
    // Print all stored values
        oled.clearDisplay();
        oled.setCursor(5, 10);        // position to display
        oled.print("distance: ");
        distance = (abs(storedValues[0]) * .375 + 10);
        oled.println(distance);
        oled.println(" in.");

        oled.setCursor(5, 20);
        oled.print("Delay: ");
        Delay = (abs(storedValues[1]));
        oled.println(abs(storedValues[1]));
        oled.println(" sec.");

        oled.setCursor(5,30);
        oled.print("Start ");
        start = (abs(storedValues[2]));
        oled.display();
        valstored = true;
    }
    // Reset button press counter and clear stored values

    delay(250); // Debouncing delay
}

if (CLK_state != CLK_laststate){     
    if (digitalRead(DT_PIN) != CLK_state) { 
    counter ++;
    } else {
    counter --;
    }
CLK_laststate = CLK_state;

    if (buttonPressCounter == 0){
    oled.clearDisplay();
    oled.setCursor(20,30);
    oled.print("distance = ");
    oled.println(abs(counter)); // MATHS
    oled.display();
    } else if (buttonPressCounter == 1){
    oled.clearDisplay();
    oled.setCursor(20,30);
    oled.print("delay = ");
    oled.print(abs(counter));
    oled.display();
    } else if (buttonPressCounter = 2){
    oled.clearDisplay();
    oled.setCursor(20,30);
    oled.print("Start = ");
    oled.println(abs(counter)); // MATHS
    oled.display();
    }
    Serial.print("Counter: ");
    Serial.println(counter);
}

if (counter >= 40 || counter <=-40){
    counter = 0;
}


}

Code 3

When I uploaded code 2, I ran into a major problem. The ATtiny 412 does not have enough storage to run the Adafruit library. To solve this issue, I milled a board that uses a ATtiny 3216.

Sadly, this went horribly wrong when my anger management skills failed me. I was mad from doing a bad soldering job on the pin headers and a bridge appeared between 2 pins. Now reflection back, I should have stopped and slowed down but I was already flustered with my board so I did my best to un-solder my board and eventually tried to cut with a box cutter the tiny bit of the bridge. And well.... I cut across 5 traces making the board unusable.

The next day, I decided to use a Board with the Seeed because I wouldn't have memory problems and the soldering is easy.

Seeed Xiao RP2040 Board

I designed the board in KiCad, Milled it on a Othermill, and Soldered Conn headers and the Seeed on.

Code 3 continued

After, I created a Simpler code combining my past code and a bit of chat GPT. The code is below and at the top as well.

// Define pins for the encoder and switch
#define CLK_PIN A2
#define DT_PIN A1
#define SW_PIN D9

// Variables to store encoder state
volatile int encoderPos = 0;
volatile int lastCLKState = LOW;
volatile bool lastSWState = HIGH; // Initialize switch state to HIGH (not pressed)

void setup() {
Serial.begin(9600); // Start serial communication
pinMode(CLK_PIN, INPUT_PULLUP); // Set encoder pins as inputs with pull-up resistors
pinMode(DT_PIN, INPUT_PULLUP);
pinMode(SW_PIN, INPUT_PULLUP);
}

void loop() {
// Read the state of the CLK pin
int CLKState = digitalRead(CLK_PIN);

// Detect rising edge of CLK
if (CLKState != lastCLKState && CLKState == HIGH) {
    // If the DT pin is high, the encoder is rotating clockwise
    if (digitalRead(DT_PIN) == HIGH) {
    encoderPos++;
    } else { // Otherwise, the encoder is rotating counterclockwise
    encoderPos--;
    }
}

// Read the state of the switch
bool SWState = digitalRead(SW_PIN);

// Detect falling edge of the switch (button press)
if (SWState != lastSWState && SWState == LOW) {
    // Print encoder position when the switch is pressed
    Serial.println("Switch pressed. Encoder position: " + String(encoderPos));
}

lastCLKState = CLKState;
lastSWState = SWState;
}

Working Sensor

As can be seen in the video, when the rotary encoder is pressed, the value is printed in the serial monitor.

Group Project

For the group project, we investigated how the sensor gave us the values using a oscilloscope.

Reflection

This week, I learned how to use a rotary encoder to give me inputs that I can later use. I also used this week to really focus in on my final project and figure out the details for the motor.