Week 10

Creating a Desktop Application

I’ve basically already completed this assignment during week 6, so I’ve decided to make something smaller but with the same idea. I had a snake sketch in C++ left over, so the idea was again to make snake that will be controlled by a rotary encoder. I have some experience in Pygame, so I used that. This time the Arduino didn’t work for me, so I’ve used the ESP32 board.

Connecting the Encoder

Encoder connection schematic

Scheme for connecting Keyes KY-040 Rotary Encoder to ESP32

Here you connect:

  • (+) -> (5V) or (3V)
  • (GND) -> (GND)
  • CLK -> GPIO 18
  • DT -> GPIO 19
  • SW -> GPIO 21

And here’s the code that will print the data to the serial port, adjust the port name and the baud rate to your own board.

#define CLK 18
#define DT 19
#define SW 21

volatile int counter = 0; 
int currentStateCLK;
int lastStateCLK;
int currentStateSW;
int lastStateSW;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;   // increase if the output flickers

void IRAM_ATTR handleEncoder() {
  // Read the current state of CLK
  currentStateCLK = digitalRead(CLK);

  if (currentStateCLK != lastStateCLK) {
    if (digitalRead(DT) != currentStateCLK) {
      counter++;
    } else {
      // The encoder is rotating counterclockwise
      counter--;
    }
  }

  // Save the current state of CLK
  lastStateCLK = currentStateCLK;
}

void IRAM_ATTR handleSwitch() {
  unsigned long currentTime = millis();
  // Check if the debounce delay has passed
  if ((currentTime - lastDebounceTime) > debounceDelay) {
    // Read the current state of SW
    currentStateSW = digitalRead(SW);

    if (currentStateSW != lastStateSW) {
      if (currentStateSW == LOW) {
        Serial.println("Button pressed");
      }
      // Save the current state of SW
      lastStateSW = currentStateSW;
    }

    // Update the debounce time
    lastDebounceTime = currentTime;
  }
}

void setup() {
  // Set encoder pins as inputs
  pinMode(CLK, INPUT);
  pinMode(DT, INPUT);
  pinMode(SW, INPUT_PULLUP);

  // Attach interrupt service routines to the encoder pins
  attachInterrupt(digitalPinToInterrupt(CLK), handleEncoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(SW), handleSwitch, CHANGE);

  // Initialize the serial monitor
  Serial.begin(115200);

  // Read the initial state of CLK and SW
  lastStateCLK = digitalRead(CLK);
  lastStateSW = digitalRead(SW);
}

void loop() {
  // Print the counter value
  Serial.println(counter);

  // Small delay to avoid overwhelming the serial output
  delay(100);
}

Code to print the encoders position and presses to the serial port

Game Code

The code for the game itself is very large, so I will just post the file here:

Download snake.zip

To run it, just follow the instructions in the README file.

Demo

The resulting game runs great and plays well. It has a list of settings you can change in the snake.py file. I probably could have made a separate button in the menu for settings, but that’s too much for this project. It’s really fun, even considering that there aren’t many gameplay mechanics in it.