Dota 2 Timer – From Ideas to a Finished Project


Around two weeks ago I wrote a post about ideas for my Arduino project to be done for my prototyping course held by Tero Karvinen. I agreed to do this project with Péter Takács and we decided our project to be the Dota 2 Timer, considering the time and resources we had.

Finished Dota Timer
Finished Dota Timer

The final prototype in a nutshell

Understanding this prototype requires understanding some of the basics in the game itself. For new players its not so easy to keep tracks of things that might be happening around the map while playing a match. This timer is used to help the player understand more concretely some basic timing of the game. It could also be a nice addition to LAN parties  – if not payed attention to, it blinks leds every now and then anyway.

  • The timer is a box with 6 leds, an on/off switch and a pushbutton on top of it. On the side of the box there is a lcd screen and three potentiometers. There are also 2 holes in the back of the box for the usb and dc – cable.
  • The LCD shows the time in minutes:seconds since reset (flicking the on/off -switch) working as a timer to go on with the game.
  • At particular times the LCD prints out things that should be noted and flashing the leds on top of the box accordingly these prints are.
    • Rune (every 2 minutes) – blue leds are lit.
    • Creeps in the jungle (every minute and also at 0:30 mark) – red leds are lit.
    • Catapult (every 3 minutes 30 seconds). – white leds are lit.
    • Courier (at 3 minute mark) – no leds light up.
  • When the pushbutton on top of the box is pressed, the lcd takes the time at that moment, adds 8 minutes to it and prints it on the lower line. This helps player to keep track on how Roshan respawns.

Once the idea was clear to us and we knew what components we needed for this project, the coding was pretty straightforward. Sources and more pictures can be found on Peter’s post about it.

The prototype without casing.
The prototype without casing.

Information

Components needed
  • 2*16 blue/white screen
  • 6 leds
  • 3 potentiometers
  • 1 pushbutton
  • on/off switch
  • 30+ jumper wires
  • 2 breadboards (we didn’t solder the prototype, soldering would decrease the jumper wire count also)

Sources

Tero Karvinen – Prototyypin rakentaminen http://terokarvinen.com/2013/aikataulu-%E2%80%93-prototyypin-rakentaminen-bus4tn007-5-alkukevat-2014

4 thoughts on “Dota 2 Timer – From Ideas to a Finished Project”

  1. Hi, pretty cool project. I make some modifications to add 2 more buttons, one resets the sketch and the other one pauses it. I added a led for roshan spawn and the time of rosh spawn is always displayed(if any). Also it now works with the I2C lcd backpack. .gist table { margin-bottom: 0; } This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters //#define DEBUG #include <LiquidCrystal_I2C.h> #include <Wire.h> #ifdef DEBUG #include <MemoryFree.h> #endif //mark rosh dead const int buttonPin = 9; //reset const int button2Pin = 10; //pause const int button3Pin = 11; const int runeLed = 5; const int jungleLed = 6; const int catapultLed = 7; const int roshanLed = 8; boolean firstjungle = true; boolean firstTime = true; boolean rosh = true; int buttonState = 0; int button2State = 0; int button3State = 0; int rune = 2; int catapult1 = 6; int catapult2 = 3; String displayText = (" "); long day = 86400000; // 86400000 milliseconds in a day long hour = 3600000; // 3600000 milliseconds in an hour long minute = 60000; // 60000 milliseconds in a minute long second = 1000; // 1000 milliseconds in a second //pause stuff long pausetime = 0; long timelapse = 0; bool paused = false; //role selector const int analogInPin = A0; //role selector variable resistor pin int sensorValue = 0; int outputValue = 0; //Roshan int roshanMinutes; int roshanSeconds; LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); void setup() { //Buttons pinMode(buttonPin, INPUT_PULLUP); pinMode(button2Pin, INPUT_PULLUP); pinMode(button3Pin, INPUT_PULLUP); //leds pinMode(runeLed, OUTPUT); pinMode(jungleLed, OUTPUT); pinMode(catapultLed, OUTPUT); pinMode(roshanLed, OUTPUT); #ifdef DEBUG //Blink all LEDs for debug. digitalWrite(runeLed, HIGH); delay(100); digitalWrite(runeLed, LOW); delay(100); digitalWrite(jungleLed, HIGH); delay(100); digitalWrite(jungleLed, LOW); delay(100); digitalWrite(catapultLed, HIGH); delay(100); digitalWrite(catapultLed, LOW); delay(100); digitalWrite(roshanLed, HIGH); delay(100); digitalWrite(roshanLed, LOW); delay(100); //End LED debug. Serial.begin(9600); #endif lcd.begin(16, 2); lcd.clear(); } void firstJungle() { firstjungle = false; textBlink(F("1. Jungle spawn"), jungleLed); } String deterRole() { sensorValue = analogRead(analogInPin); outputValue = map(sensorValue, 0, 1023, 0, 35); #ifdef DEBUG Serial.println(outputValue); #endif String role = " "; if (outputValue == 1) { role = (F("Mid")); } else if (outputValue == 2) { role = (F("Support")); } else if (outputValue == 3) { role = (F("Jungle")); } return role; } void showTime(int seconds, int minutes) { lcd.setCursor(0, 0); lcd.print(F("Dota_Timer")); lcd.setCursor(11, 0); if (minutes < 10) { lcd.print(F("0")); lcd.print(minutes); } else { lcd.print(minutes); } lcd.print(F(":")); if (seconds < 10) { lcd.print(F("0")); lcd.print(seconds); } else { lcd.print(seconds); } } void showRole(String role) { lcd.setCursor(0, 1); if (role == (F("Mid"))) { lcd.print(role + " "); // I�M ALL OUT OF BUBBLE GUM } else if (role == (F("Support"))) { lcd.print(role); // I�M ALL OUT OF BUBBLE GUM } else { lcd.print(role + " "); // I�M ALL OUT OF BUBBLE GUM } } void executeMid(int seconds, int minutes, int rune, int catapult1, int catapult2, boolean firstTime) { //Rune if (minutes == rune && seconds == 00 && !firstTime) { textBlink(F("Runes"), runeLed); } //Catapult1 if (minutes == catapult1 && seconds == 30 && !firstTime) { textBlink(F("Catapult"), catapultLed); } //Catapult2 if (minutes == catapult2 && seconds == 00 && !firstTime) { textBlink(F("Catapult"), catapultLed); } //Courier if (minutes == 3 && seconds == 00) { textBlink(F("Courier"), jungleLed); } } void executeSupport(int seconds, int minutes, int rune, int catapult1, int catapult2, boolean firstTime) { //Rune if (minutes == rune && seconds == 00 && !firstTime) { textBlink(F("Runes"), runeLed); } //Catapult1 if (minutes == catapult1 && seconds == 30 && !firstTime) { textBlink(F("Catapult"), catapultLed); } //Catapult2 if (minutes == catapult2 && seconds == 00 && !firstTime) { textBlink(F("Catapult"), catapultLed); } //Courier if (minutes == 3 && seconds == 00) { textBlink(F("Courier"), jungleLed); } if (seconds == 00 && !firstTime) { textBlink(F("Creeps"), jungleLed); } } void executeJungle(int seconds, int minutes, boolean firstTime) { if (seconds == 00 && !firstTime) { textBlink(F("Creeps"), jungleLed); } //Courier if (minutes == 3 && seconds == 00) { textBlink(F("Courier"), jungleLed); } } void textBlink(String displayText, int led) { lcd.clear(); lcd.print(displayText); #ifdef DEBUG lcd.setCursor(0, 1); lcd.print(freeMemory()); #endif for (int i = 0; i < 6; i++) { digitalWrite(led, HIGH); delay(250); digitalWrite(led, LOW); delay(250); } lcd.clear(); } void roshan(long timeNow) { rosh = false; unsigned long roshan = timeNow; roshanMinutes = ((timeNow % day) / minute); roshanSeconds = (((timeNow % day) % hour) % minute) / second; roshanMinutes = roshanMinutes + 8; printRosh(); } void printRosh() { lcd.setCursor(9, 1); lcd.print(F("R:")); if (roshanMinutes < 10) { lcd.print(F("0")); lcd.print(roshanMinutes); } else { lcd.print(roshanMinutes); } lcd.print(F(":")); if (roshanSeconds < 10) { lcd.print(F("0")); lcd.print(roshanSeconds); } else { lcd.print(roshanSeconds); } } //poor man reset void softReset() { asm volatile (" jmp 0"); } void loop() { buttonState = digitalRead(buttonPin); button2State = digitalRead(button2Pin); button3State = digitalRead(button3Pin); //pausestuff if (button3State == LOW && !paused) { paused = true; pausetime = millis() – timelapse; delay(200); } else if (button3State == LOW && paused) { paused = false; timelapse = millis() – pausetime; delay(200); } if (paused) { return; } long timeNow = millis() – timelapse; int minutes = ((timeNow % day) / minute); int seconds = (((timeNow % day) % hour) % minute) / second; //Functions showTime(seconds, minutes); showRole(deterRole()); if (deterRole() == (F("Mid"))) { executeMid(seconds, minutes, rune, catapult1, catapult2, firstTime); } else if (deterRole() == (F("Support"))) { executeSupport(seconds, minutes, rune, catapult1, catapult2, firstTime); } else if (deterRole() == (F("Jungle"))) { executeJungle(seconds, minutes, firstTime); } //Prevent textBlink at 00.00. if (seconds > 1) { firstTime = false; } //00.30 mark Jungle. if (seconds == 30 && firstjungle) { firstJungle(); } //Roshan, Get current time and add 8 minutes, display result. if (buttonState == LOW) { roshan(timeNow); delay(200); } //Reset the timer if (button2State == LOW) { softReset(); delay(200); } //Rosh if (minutes == roshanMinutes && seconds == roshanSeconds && minutes != 0 && seconds != 0) { textBlink(F("Roshan soon"), roshanLed); rosh = true; } if (!rosh) { printRosh(); } //Rune & Catapult if (minutes == rune && seconds == 00 && !firstTime) { rune = rune + 2; } if (minutes == catapult1 && seconds == 30 && !firstTime) { catapult1 = catapult1 + 7; } if (minutes == catapult2 && seconds == 00 && !firstTime) { catapult2 = catapult2 + 7; } } view raw dota_timer.ino hosted with ❤ by GitHub Salud. pd: sorry for my poor english.

Leave a comment