Arduino Wank-o-Meter

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