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
-
CLK : Output A (digital pin)
-
DT : Output B (digital pin)
-
SW : Button Press (digital pin)
-
VCC : 5V power
-
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.
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.