20200130_173655-omdgkwk1hxffcesw5fb2v6xfq8sebwamqiqin6thf4
Arduino_Timer_Schematic-oi9k48imd51lyho0meh0h534xueha5bx3atodtnlv4
Arduino_Timer_Schematic
20200117_193043-omdgjaa1q77ncl5jlq47klmuwcrn2nv9uiaew17w4w
20200119_163421-omdgjt2tivxdsse8jy8qygw2s26zclxwl3c4hkg0og
20200119_181620-1-omdglanmcfyq6k8ev3eheldcn0uwjculsgisuc8kts
20200121_160206-1-omdglanmcfyq6k8ev3eheldcn0uwjculsgisuc8kts
Arduino_Timer_Enclosure-omdgqdy5d4xn2iu64smqet48h5rh8c29hnqhfaot4g
20200130_173655-omdgkwk1hxffcesw5fb2v6xfq8sebwamqiqin6thf4 Arduino_Timer_Schematic-oi9k48imd51lyho0meh0h534xueha5bx3atodtnlv4 Arduino_Timer_Schematic 20200117_193043-omdgjaa1q77ncl5jlq47klmuwcrn2nv9uiaew17w4w 20200119_163421-omdgjt2tivxdsse8jy8qygw2s26zclxwl3c4hkg0og 20200119_181620-1-omdglanmcfyq6k8ev3eheldcn0uwjculsgisuc8kts 20200121_160206-1-omdglanmcfyq6k8ev3eheldcn0uwjculsgisuc8kts Arduino_Timer_Enclosure-omdgqdy5d4xn2iu64smqet48h5rh8c29hnqhfaot4g

Further technical description can be found below and on Github. Code/files are available on GitHub:

Arduino Timer

This is an timer with an OLED display, powered by an Arduino Nano. The project uses the Adafruit_SSD1306 library for I2C communication and the Adafruit-GFX library for graphics. The timer uses two capacitive touch sensors to count how many minutes to count down from, and a piezo buzzer as alarm. It is powered by a power bank, for easy charging.

For the project I designed a pcb, that was later manufactured by jlcpcb. I currently have 4 pieces left available if there is interest.

Lastly the device was enclosed in a 3D-printed case, designed by me.

Arduino Timer

This is an timer with an OLED display, powered by an Arduino Nano. The project uses the Adafruit_SSD1306 library for I2C communication and the Adafruit-GFX library for graphics. The timer uses two capacitive touch sensors to count how many minutes to count down from, and a piezo buzzer as alarm. It is powered by a power bank, for easy charging.

For the project I designed a pcb, that was later manufactured by jlcpcb. I currently have 4 pieces left available if there is interest.

Lastly the device was enclosed in a 3D-printed case, designed by me.

20200130_173655-omdgkwk1hxffcesw5fb2v6xfq8sebwamqiqin6thf4
Arduino_Timer_Schematic-oi9k48imd51lyho0meh0h534xueha5bx3atodtnlv4
Arduino_Timer_Schematic
20200117_193043-omdgjaa1q77ncl5jlq47klmuwcrn2nv9uiaew17w4w
20200119_163421-omdgjt2tivxdsse8jy8qygw2s26zclxwl3c4hkg0og
20200119_181620-1-omdglanmcfyq6k8ev3eheldcn0uwjculsgisuc8kts
20200121_160206-1-omdglanmcfyq6k8ev3eheldcn0uwjculsgisuc8kts
Arduino_Timer_Enclosure-omdgqdy5d4xn2iu64smqet48h5rh8c29hnqhfaot4g

Further technical description can be found below and on Github. Code/files are available on GitHub:

Technical Description

From Github readme.md file

Arduino Timer

A timer with an OLED display and capacitive touch buttons, powered by an Arduino Nano. At its current state the code is neither commented or structured very well, this might change in the future.

More information: https://lucasstromberg.com. Contact me if you're interested in getting a pcb.

About

This is an timer with an OLED display, powered by an Arduino Nano. The project uses the Adafruit_SSD1306 library for I2C communication and the Adafruit-GFX library for graphics. The timer uses two capacitive touch sensors to count how many minutes to count down from, and a piezo buzzer as alarm. It is powered by a power bank, for easy charging.

The power bank I used turns off automatically after ~30s if no sufficient current is detected. Therefore I added an NPN transistor that is switched on for a 1s pulse by an Arduino GPIO pin every 15s, whereafter current is drained through the power bank over a low value resistor. This current drain pulse is enough for the power bank to be tricked into thinking it powers for example a cell phone, and therefore stays on.

For the project I designed a pcb, that was later manufactured by jlcpcb. I currently have 4 pieces left if anyone is interested.

Lastly I enclosed the project in a 3D-printed case. While the stl-files are not yet uploaded I'll try to make this happen soon.

Materials list

Schematic and PCB

I have included an image of the schematic, and gerber files to produce the pcb. I created the pcbs with Altium Designer for which I have now lost my previous student license, therefore it might be hard to gather the project files.

I currently have 4 pcbs left and would be more than happy to send one to you! Contact me on my website if you're interested and maybe we can work something out.

The pcbs are printed with my previous website domain aprelga.com. Sadly there isn't an easy way to change this now... 🙂

schematic

pcb

3D-Printed enclosure

Unfortunately only gcode for an Ender 3 is available. I will include stl-files later.

enclosure

Code

All code is available in an ino-file, and below.

//INCLUDE STUFF:
#include <Adafruit_GFX.h>
#include <Adafruit_SPITFT.h>
#include <Adafruit_SPITFT_Macros.h>
#include <gfxfont.h>
#include <Adafruit_SSD1306.h>
#include <splash.h>
#include <Wire.h>
#include <SPI.h>

//DEFINE STUFF:
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32

#define OLED_RESET 4

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

//PINS
int buttonPin1 = 6;
int buttonPin2 = 5;
int piezo = A3;
int drawPin = 7;

//VARIABLES:
int currentPage = 1;
int countDown = 0;
unsigned long reference = 0;
unsigned long drawReference = 0;
int delayAmount = 300;
int drawLength = 1000;
int drawDelay = 15000;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
    Serial.println(F("SSD1306 allocation failed"));
    for (;;); // Don't proceed, loop forever
  }

  pinMode(buttonPin1, INPUT);
  pinMode(buttonPin2, INPUT);
  pinMode(piezo, OUTPUT);
  pinMode(drawPin, OUTPUT);

  display.clearDisplay();

  animateRect();
  rePrint(0, 0);
  
  reference = millis();
}

void loop() {
  // put your main code here, to run repeatedly:
  unsigned long timeState = millis();
  
  if (countDown > 0 && (timeState - reference > 3000)){
    startTimer(countDown);
  }

  if (digitalRead(buttonPin1)){
    reference = millis();
    if (countDown < 99){
      countDown++;
    }
    if (delayAmount > 50){
      delayAmount = delayAmount - 30;
    }
    delay(delayAmount);
  } else if (digitalRead(buttonPin2)){
    reference = millis();
    if (countDown != 0){
      countDown--;
    }
    delay(delayAmount);
    if (delayAmount > 50){
      delayAmount = delayAmount - 30;
    }
  } else {
    delayAmount = 300;
  }
  
  rePrint(countDown, 0);

  drawCurrent();
}

void drawCurrent(){
  digitalWrite(drawPin, millis() - drawReference < drawLength);
  Serial.println(countDown);
  if (millis() - drawReference > drawDelay){drawReference += drawDelay;}
  
}

void animateRect() {
  for (int i = 0; i <= SCREEN_WIDTH; i++) {
    display.drawPixel(i, 0, WHITE);
    if ((i % 8) == 0) {
      display.display();
    }
  }
  for (int i = 0; i <= SCREEN_HEIGHT; i++) {
    display.drawPixel(SCREEN_WIDTH - 1, i, WHITE);
    if ((i % 8) == 0) {
      display.display();
    }
  }
  for (int i = SCREEN_WIDTH; i >= 0; i--) {
    display.drawPixel(i, SCREEN_HEIGHT - 1, WHITE);
    if ((i % 8) == 0) {
      display.display();
    }
  }
  for (int i = SCREEN_HEIGHT; i >= 0; i--) {
    display.drawPixel(0, i, WHITE);
    if ((i % 8) == 0) {
      display.display();
    }
  }
}

void drawRect(){
  display.drawRect(0,0,SCREEN_WIDTH, SCREEN_HEIGHT,WHITE);
}

void drawBoxes() {
  display.fillRect(0, 0, 14, 18, WHITE);
  display.drawRect(15, 0, 14, 18, WHITE);
  display.drawRect(30, 0, 14, 18, WHITE);
  display.drawRect(45, 0, 14, 18, WHITE);
}

void printBoxText() {
  display.setTextSize(1);
  display.setTextColor(BLACK);

  display.setCursor(5, 5);
  display.println(F("1"));

  display.setTextColor(WHITE);

  display.setCursor(20, 5);
  display.println(F("2"));

  display.setCursor(35, 5);
  display.println(F("3"));

  display.setCursor(50, 5);
  display.println(F("4"));

}

void printTimer(int minutes, int seconds) {
  display.setTextSize(3);
  display.setTextColor(WHITE);

  if (minutes < 10){
    display.setCursor(90, 5);
  } else {
    display.setCursor(73, 5);
  }

  display.println(minutes);

  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(110, 5);

  if (seconds < 10) {
    display.println("0" + String(seconds));
  } else {
    display.println(seconds);
  }

}

void rePrint(int minutes, int seconds){
  display.clearDisplay();

  drawRect();
 
  drawBoxes();

  printBoxText();

  printTimer(minutes, seconds);

  display.display();
}

void blinkPrint(int minutes, int seconds){
  for (int i = 0; i <=6; i++){
    display.clearDisplay();
    drawRect();
    drawBoxes();
    printBoxText();
    if (i % 2 == 0){
      printTimer(minutes, seconds);
    }
    display.display();
    delay(50);
  }
}

void startTimer(int minutes) {
  blinkPrint(minutes, 0);
  
  int currentMinute = minutes;
  int currentSecond = 0;

  bool atStart = true;

  unsigned long milliSecond = 1000UL;
  unsigned long milliMinute = milliSecond * 60UL;
  unsigned long milliSeconds = minutes * milliMinute;
  unsigned long startReference = millis();
  unsigned long secondReference = millis();

  bool sound = true;

  while (true) {

    drawCurrent();

    if (digitalRead(buttonPin1) || digitalRead(buttonPin2)){
      rePrint(0,0);
      sound = false;
      countDown = 0;
      blinkPrint(0, 0);
      break;
    }

    secondReference = millis();
    
    if (secondReference - startReference > milliSecond) {
      if (currentSecond == 0) {
        if (currentMinute == 0){
          break;
        }
        currentSecond = 59;
        if (atStart){
          currentMinute--;
          atStart = false;
        }
      } else {
        currentSecond--;
      }
      rePrint(currentMinute, currentSecond);
      milliSecond += 1000UL;
    }
    if (secondReference - startReference > milliMinute && currentSecond == 59) {
      currentMinute--;
      rePrint(currentMinute, currentSecond);
      milliMinute += 1000UL * 60UL;
    }
  }
  if (sound){
    playSound();
  }
}

void playSound(){
  while (!digitalRead(buttonPin1) || !digitalRead(buttonPin2)){

    drawCurrent();

    tone(piezo, 800);
    delay(50);
    noTone(piezo);
    delay(50);
    tone(piezo, 800);
    delay(50);
    noTone(piezo);
    delay(50);
    tone(piezo, 800);
    delay(50);
    noTone(piezo);

    delay(800);
  }
}