This was a piss-take project that measures how fast you can ‘wank’. The concept formed because I got drunk while testing an infra-red type speed sensor. Let this be a lesson not to drink & code.
The rules of this obscene game are simple: you have 12.8 seconds to ‘wank’ as much as possible. Your score will then be checked against the high score and the results will be displayed along with one of two rude comments.
It should be noted that the definition of ‘wank’ on this web page means to break the intra-red beam in the sensor as can be seen in the video below.
What you will need:
- An Arduino and ability to program it (Uno and nano both work)
- 128×64 OLED screen (SSD1306)
- Infra-red speed sensor
- A breadboard & hook up wires
- A desire to waste time
What you will learn:
Probably very little, but the code uses:
- Arrays
- EEPROM
- Interfacing with OLED screen
Hook up guide
Again, This depends on your exact set up (eg. the Arduino Mega has different pins for the I2C bus), however in the simplest terms:
- OLED – 5v > Arduino 5v
- OLED – GND > Arduino GND
- OLED – SDA > Arduino A4
- OLED – SCL > Arduino A5
- IR sensor – ‘DO’ > Arduino D8
- IR sensor – 5v > Arduino 5v
- IR sensor – GND > Arduino GND
The code
There will probably be many inefficiencies, but it compiles, and that’s all that matters.
#include <EEPROM.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 0x3C // Check with i2c scanner
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
const int sensor = 8; //Digital out from sensor to pin 8
unsigned long startTime = 0;
unsigned long endTime = 0;
float x[17];
float y[17];
int newCount = 0;
int totalCount = 0;
int highestTotal;
int maxRPM;
int highestRPM;
float max_y;
bool recordFlag;
// ****************************************************
void setup() {
pinMode(sensor, INPUT_PULLUP);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(43, 0);
display.print("Wank");
display.setCursor(48, 21);
display.print("-o-");
display.setCursor(37, 42);
display.print("Meter");
display.display();
delay(3000);
display.clearDisplay();
x[0] = 0.00; //initial graph point
y[0] = 63.00; //initial graph point
}
//**************************************************
void displayGrid () {
display.drawLine(0, 12, 127, 12, WHITE);
display.drawLine(64, 0, 64, 12, WHITE);
display.setTextSize(1);
display.setCursor(0,0);
display.print("RPM=");
display.setCursor(66,0);
display.print("Time= ");
display.display();
}
//***************************************************
void displayStartMessage() {
display.setTextSize(2);
display.setCursor(6, 16);
display.print("Ready when you are");
display.display();
display.setTextSize(1);
}
// ************************************************
int takeMeasure() {
int stepCount = 0;
startTime = millis();
endTime = startTime + 800; //0.8s sample rate
while (millis() < endTime) {
if (digitalRead(sensor)) {
stepCount = stepCount + 1;
while (digitalRead(sensor));
}
}
return stepCount;
}
//**************************************************
void game() {
max_y = 0.0; //reset current game values
totalCount = 0;
display.clearDisplay(); //blank screen
displayGrid(); //display gridlines
for (int measureCount = 1; measureCount < 17; measureCount++) { //16 measures @ 8 pixel = 128px
newCount = takeMeasure();
totalCount = totalCount + newCount;
x[measureCount] = measureCount * 8; //y co-ordinate
y[measureCount] = map(newCount, 0, 15, 63, 11); //x Co-ordinate
//update instantaneous RPM
int currentRPM = newCount * 75;
display.setCursor(25,0);
display.fillRect(25,0,38,11,BLACK);
display.print(currentRPM);
display.display();
//check for new max RPM
if (newCount > max_y) { //compare x co-ordinate against max
max_y = newCount;
maxRPM = max_y * 75;
}
//draw graph
for (int i = 0; i < measureCount; i++) {
display.drawLine(x[i],y[i],x[i+1],y[i+1],WHITE);
}
//update time remaining
float timeRemaining = 12.8 - ( measureCount * 0.8);
display.setCursor(96,0);
display.fillRect(96,0,32,11,BLACK);
display.print(timeRemaining);
display.display();
}
display.invertDisplay(true);
delay(100);
display.invertDisplay(false);
delay(100);
display.invertDisplay(true);
delay(100);
display.invertDisplay(false);
delay(5000);
}
//**************************************************
void recordCheck() {
EEPROM.get(0, highestTotal);
EEPROM.get(2, highestRPM);
if (totalCount > highestTotal) {
EEPROM.put(0, totalCount);
recordFlag = true;
}
if (maxRPM > highestRPM) {
EEPROM.put(2, maxRPM);
recordFlag = true;
}
}
//**************************************************
void finalScreen() {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(50,0);
display.print("Your");
display.setCursor(50,11);
display.print("Score");
display.setCursor(86,0);
display.print("Best");
display.setCursor(86,11);
display.print("Ever");
display.setCursor(0,22);
display.print("Total ");
display.setCursor(0,33);
display.print("Max RPM");
display.drawLine(48,0,48,40,WHITE);
display.drawLine(84,0,84,40,WHITE);
display.drawLine(0,20,127,20,WHITE);
display.drawLine(0,31,127,31,WHITE);
display.setCursor(50,22);
display.print(totalCount);
display.setCursor(86,22);
display.print(highestTotal);
display.setCursor(50,33);
display.print(maxRPM);
display.setCursor(86,33);
display.print(highestRPM);
display.display();
if (recordFlag == true) {
delay(500);
display.setCursor(0,42);
display.print("YOU ARE A TOP WANKER!");
display.display();
} else {
delay(1000);
display.setCursor(2,42);
display.print("That was a shit wank!");
display.display();
}
delay(2500);
display.setCursor(30,55);
display.print("Wank Again?");
display.display();
while(!digitalRead(sensor)); //hang until new reading
recordFlag = false; //reset any new records
setup();
}
//********************************************************
void loop() {
displayGrid();
displayStartMessage();
while(!digitalRead(sensor)); //hang until sensor detects movement
game();
delay(1000);
recordCheck();
finalScreen();
}
Good luck! If you have any problems with the above or just want to gloat about your top scores then let me know via my contact page
References
I think it’s important to give credit where it’s due and I find the tutorials that Rui and Sara Santos provide at randomnerdtutorials.com to be thorough, insightful and full of knowledge.
For this project, I found their page on OLED screens an invaluable source for quickly looking up how to draw rectangles and lines, check their page out: Guide for I2C OLED Display with Arduino | Random Nerd Tutorials
Page created: 19/04/2024
Last edited: 23/03/2024