Last active
August 19, 2019 09:34
-
-
Save w0rm49/fc15f6dc06c4f7265e82363ab8067152 to your computer and use it in GitHub Desktop.
Working time counter
This file contains hidden or 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
| /** | |
| * connect ssd1306 to i2c pins | |
| * button (or jumper) between pin 2 and ground | |
| * | |
| * how to reset counter: | |
| * 1. press and hold button | |
| * 2. reset or enable arduino | |
| * 3. hold button at least 2 seconds, | |
| * 4. reboot again | |
| */ | |
| #include <Wire.h> | |
| #include <EEPROM.h> | |
| #include <Adafruit_GFX.h> | |
| #include <Adafruit_SSD1306.h> | |
| #define RESET_PIN 2 | |
| #define SCREEN_ADDRESS 0x3C | |
| #define SCROLLBAR_Y 55 | |
| #define HOURS_Y 8 | |
| Adafruit_SSD1306 display(128, 64, &Wire, -1); | |
| uint16_t totalMinutes = 0; | |
| uint16_t hours = 0; | |
| uint8_t minutes = 0; | |
| unsigned long mlsLast = 0; | |
| unsigned long mls = 0; | |
| uint16_t eepromAddr = 0; | |
| union sector { | |
| uint16_t data[sizeof(uint16_t) * 4]; | |
| uint8_t bytes[sizeof(uint32_t) * 2]; | |
| }; | |
| void setup() { | |
| if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { | |
| panic(); | |
| } | |
| display.setTextColor(WHITE); | |
| resetRoutine(); | |
| readSavedCounter(); | |
| display.setTextSize(5); | |
| draw(); | |
| mlsLast = millis(); | |
| } | |
| void loop() { | |
| mls = millis(); | |
| if (mls - mlsLast < 60000) { | |
| return; | |
| } | |
| mlsLast = mls; | |
| totalMinutes++; | |
| draw(); | |
| writeValueToSector(eepromAddr, 0, totalMinutes); | |
| delay(3000); | |
| writeValueToSector(eepromAddr, 1, totalMinutes); | |
| delay(3000); | |
| writeValueToSector(eepromAddr, 2, totalMinutes); | |
| } | |
| void draw() { | |
| hours = totalMinutes / 60; | |
| minutes = totalMinutes - (hours * 60); | |
| display.clearDisplay(); | |
| display.setCursor(22, HOURS_Y); | |
| if (hours < 10) { | |
| display.print(F("0")); | |
| } | |
| if (hours < 100) { | |
| display.print(F("0")); | |
| } | |
| if (hours > 999) { | |
| display.print(F("999")); | |
| } else { | |
| display.println(hours); | |
| } | |
| //frame | |
| display.drawLine(2, SCROLLBAR_Y, 125, SCROLLBAR_Y, WHITE); | |
| display.drawLine(2, SCROLLBAR_Y + 6, 125, SCROLLBAR_Y + 6, WHITE); | |
| display.drawLine(2, SCROLLBAR_Y, 2, SCROLLBAR_Y + 6, WHITE); | |
| display.drawLine(125, SCROLLBAR_Y, 125, SCROLLBAR_Y + 6, WHITE); | |
| //progress bar | |
| uint8_t m2 = 4 + minutes * 2 + 1; | |
| display.drawLine(4, SCROLLBAR_Y + 2, m2, SCROLLBAR_Y + 2, WHITE); | |
| display.drawLine(4, SCROLLBAR_Y + 3, m2, SCROLLBAR_Y + 3, WHITE); | |
| display.drawLine(4, SCROLLBAR_Y + 4, m2, SCROLLBAR_Y + 4, WHITE); | |
| display.display(); | |
| } | |
| void panic() { | |
| pinMode(LED_BUILTIN, OUTPUT); | |
| for(;;) { | |
| digitalWrite(LED_BUILTIN, HIGH); | |
| delay(300); | |
| digitalWrite(LED_BUILTIN, LOW); | |
| delay(300); | |
| } | |
| } | |
| void resetRoutine() { | |
| pinMode(RESET_PIN, INPUT_PULLUP); | |
| uint8_t val = digitalRead(RESET_PIN); | |
| if (val == HIGH) { | |
| return; | |
| } | |
| display.clearDisplay(); | |
| display.setTextSize(1); | |
| display.setCursor(0, HOURS_Y); | |
| display.print(F("hold to reset")); | |
| display.display(); | |
| delay(2000); | |
| display.clearDisplay(); | |
| display.setCursor(0, HOURS_Y); | |
| if (digitalRead(RESET_PIN) == HIGH) { | |
| display.println(F("reset failed")); | |
| display.println(F("try again")); | |
| } else { | |
| uint16_t addr = pickValueFromSector(127); | |
| addr++; | |
| if (addr > 126) { | |
| addr = 0; | |
| } | |
| writeValueToFullSector(127, addr); | |
| writeValueToFullSector(addr, 0); | |
| display.println(F("release button")); | |
| display.println(F("and reboot")); | |
| } | |
| display.display(); | |
| //hang | |
| for(;;); | |
| } | |
| void readSavedCounter() { | |
| uint16_t addr = pickValueFromSector(127); | |
| if (addr > 126) { | |
| writeValueToFullSector(127, 0); | |
| writeValueToFullSector(0, 0); | |
| eepromAddr = 0; | |
| totalMinutes = 0; | |
| } else { | |
| eepromAddr = addr; | |
| totalMinutes = pickValueFromSector(addr); | |
| } | |
| } | |
| uint16_t pickValueFromSector(uint8_t num) { | |
| sector sect; | |
| uint16_t realAddress = num * 8; | |
| EEPROM.get(realAddress, sect); | |
| if ((sect.data[0] == sect.data[1]) || (sect.data[0] == sect.data[2])) { | |
| return sect.data[0]; | |
| } | |
| if (sect.data[1] == sect.data[2]) { | |
| return sect.data[1]; | |
| } | |
| //we can only guess here | |
| return sect.data[0]; | |
| } | |
| void writeValueToSector(uint8_t num, uint8_t offset, uint16_t value) { | |
| uint16_t realAddress = num * 8 + offset * sizeof(uint16_t); | |
| EEPROM.put(realAddress, value); | |
| } | |
| void writeValueToFullSector(uint8_t num, uint16_t value) { | |
| sector sect; | |
| uint16_t realAddress = num * 8; | |
| sect.data[0] = value; | |
| sect.data[1] = value; | |
| sect.data[3] = value; | |
| sect.data[4] = 0; | |
| EEPROM.put(realAddress, sect); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment