My mom needed a new kitchen timer. I built her one last minute style with stuff I had in my parts drawers. Firmware development was quite hasty (i.e. written on the road) but seems to get the job done. I used a few FR4 scraps to reinforce the Teensy's USB port, as I figured this was the most likely source of breakage (its USB connection is powering the whole deal). I wired the red switch wrong, so it's just for show I guess. Speaker is held in place using a pair of yellow tension wires and a compressed foam pad at its base.
BOM
Teensy 3.2
Adafruit 128x32 OLED I2C display
0402 LEDs: 630nm, [some yellowish one]nm, and 470nm
two large switches from a recent surplus haul
10k panel mount potentiometer
8-ohm, 2" paper cone speaker
a large electrolytic capacitor for said speaker
various current-limiting and pulldown resistors
a bunch of 0.062 single-sided FR4
a self-adhesive foam pad
right-angle header to secure the processor board
more pictures
alarm video
A bit of constrained random noise. The function is seeded, so the 'melody' should be identical each time the alarm sounds.
firmware
mom-timer_fw01, as uploaded 12/24/2015. I'm using Adafruit's graphics library along with their drivers for the display, as grabbed from Paul's site. The code includes a rather lengthy instructional intro that mentions the fact that I miswired the red button; as such, the user interface is quite simple. Feel free to ask questions about this, as I probably could add a lot in terms of comments:
note: I didn't add a conclusion to this log update beyond the code block, so feel free to stop here if you're bored [recommended].
/*
mom-timer
by zach fredin
12-23-2015
my mom needed a kitchen timer, and i needed to get a christmas present for her. this timer is a bit overpowered, but maybe a future firmware update will increase its functionality. it's based on a Teensy 3.2 board, and includes two pushbutton switches, three LEDs, a potentiometer, a speaker, and an Adafruit SSD1306 32x128 I2C OLED display.
MIT license for the main function. The two Adafruit libraries are covered by this license which I think I am using correctly in this context:
<<<
Software License Agreement (BSD License)Copyright (c) 2012, Adafruit Industries
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
>>>
*/
#include <SPI.h>
#include <Wire.h>
#include <Bounce.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 2
#define BUTTON_GREEN 12 //note that, uh, green is low when pressed
#define BUTTON_RED 0
#define SPEAKER 7
#define KNOB A2
#define LED_BLUE 4
#define LED_RED 5
#define LED_YELLOW 6
// set up OLED display and button debouncers
Adafruit_SSD1306 display(OLED_RESET);
Bounce buttonGreen = Bounce(BUTTON_GREEN, 10);
Bounce buttonRed = Bounce(BUTTON_RED, 10);
// input variables
int potState = 0;
int potStateScaled = 0; // scales input to 0-59
int buttonStateGreen = 0;
int buttonStateGreenPrev = 0;
int buttonStateRed = 0;
int buttonStateRedPrev = 0;
// timer variables
int seconds = 0;
int minutes = 0;
long previousMillis = 0;
int LEDflashstate = 0;
/* state variables
MODE = 0: setup_seconds
MODE = 1: setup_minutes
MODE = 2: setup_confirm
MODE = 3: run
MODe = 4: pause
MODe = 5: alarm
*/
int MODE = 0;
void setup() {
display.begin(SSD1306_SWITCHCAPVCC, 0X3C);
pinMode(BUTTON_GREEN, INPUT);
pinMode(BUTTON_RED, INPUT);
pinMode(KNOB, INPUT);
pinMode(SPEAKER, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
pinMode(LED_RED, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
randomSeed(500); // same alarm each time!
showIntro();
MODE = 0;
}
void loop() {
unsigned long currentMillis = millis();
getInputState();
if(MODE == 0) { // setup_minutes
digitalWrite(LED_BLUE, LOW);
digitalWrite(LED_RED, LOW);
digitalWrite(LED_YELLOW, LOW);
minutes = scaleInput(potState);
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0,0);
display.println("set minutes");
display.setTextSize(2);
display.print(minutes);
display.println(":__");
display.setTextSize(1);
display.println("green to confirm");
display.display();
if ((buttonStateGreen == LOW) & (buttonStateGreenPrev == HIGH)) {
MODE += 1;
getInputState();
}
}
if(MODE == 1) { // setup_seconds
seconds = scaleInput(potState);
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0,0);
display.println("set seconds");
display.setTextSize(2);
display.print(minutes);
display.print(":");
display.println(seconds);
display.setTextSize(1);
display.println("green to confirm");
display.display();
if ((buttonStateGreen == LOW) & (buttonStateGreenPrev == HIGH)) {
MODE += 1;
getInputState();
}
}
if(MODE == 2) { // setup_confirm
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0,0);
display.println("timer ready:");
display.setTextSize(2);
if (minutes < 10) {
display.print("0");
}
display.print(minutes);
display.print(":");
if (seconds < 10) {
display.print("0");
}
display.println(seconds);
display.setTextSize(1);
display.println("green to start!");
display.display();
if ((buttonStateGreen == LOW) & (buttonStateGreenPrev == HIGH)) {
MODE += 1;
getInputState();
}
}
if(MODE == 3) { // run
if((buttonStateGreen == LOW) & (buttonStateGreenPrev == HIGH)) {
MODE += 1;
getInputState();
}
display.clearDisplay();
display.setTextSize(4);
display.setCursor(0,0);
if (minutes < 10) {
display.print("0");
}
display.print(minutes);
display.print(":");
if (seconds < 10) {
display.print("0");
}
display.println(seconds);
display.display();
if(currentMillis - previousMillis > 1000) { // "calibrated"...
previousMillis = currentMillis;
if (seconds % 2 == 0) {
digitalWrite(LED_YELLOW, HIGH);
}
else {
digitalWrite(LED_YELLOW, LOW);
}
if (seconds == 0) {
minutes -= 1;
seconds = 60;
}
seconds -= 1;
}
if((seconds == 0) & (minutes == 0)) {
MODE = 5;
}
}
if(MODE == 4) { // pause
display.clearDisplay();
display.setTextSize(2);
display.setCursor(0,0);
if (minutes < 10) {
display.print("0");
}
display.print(minutes);
display.print(":");
if (seconds < 10) {
display.print("0");
}
display.println(seconds);
display.setTextSize(1);
display.println("timer is paused!");
display.println("green to unpause.");
display.display();
if((buttonStateGreen == LOW) & (buttonStateGreenPrev == HIGH)) {
MODE -= 1;
getInputState();
}
if((buttonStateRed == HIGH) & (buttonStateRedPrev == LOW)) {
display.clearDisplay();
display.display();
delay(1000);
MODE = 0;
getInputState();
}
}
else if(MODE == 5) { //Alarm mode
display.clearDisplay();
display.setTextSize(3);
display.setCursor(0,0);
display.println("ALARM!");
display.setTextSize(1);
display.println("green to reset.");
display.display();
if(currentMillis - previousMillis > 200) {
previousMillis = currentMillis;
if (LEDflashstate == 0) {
digitalWrite(LED_BLUE, HIGH);
digitalWrite(LED_RED, LOW);
LEDflashstate = 1;
tone(SPEAKER, random(200, 600), 200);
}
else {
digitalWrite(LED_BLUE, LOW);
digitalWrite(LED_RED, HIGH);
LEDflashstate = 0;
tone(SPEAKER, random(600, 1800), 200);
}
}
if((buttonStateGreen == LOW) & (buttonStateRedPrev == HIGH)) {
display.clearDisplay();
display.display();
delay(1000);
MODE = 0;
getInputState();
}
}
}
int scaleInput(int rawValue) {
// okay, so this cuts out ~10% of the pot's range.. oh well, bitshifts are the best.
if(rawValue < 960) {
return rawValue >> 4;
}
else {
return 59;
}
}
void getInputState() {
// store previous button values
buttonStateGreenPrev = buttonStateGreen;
buttonStateRedPrev = buttonStateRed;
// update button and knob values
buttonGreen.update();
buttonRed.update();
potState = analogRead(KNOB);
buttonStateGreen = buttonGreen.read();
buttonStateRed = buttonRed.read();
}
void showIntro() {
// splash screen and operating instructions (includes delays)
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println("mom-timer");
display.setTextSize(1);
display.println("firmware v0.1");
display.println("merry xmas, mom");
display.display();
tone(SPEAKER, 440, 100);
digitalWrite(LED_BLUE, HIGH);
delay(3000);
display.clearDisplay();
digitalWrite(LED_BLUE, LOW);
display.setCursor(0,0);
display.println("TIMER SETUP MODE");
display.println("set time with knob.");
display.println("green to confirm");
display.println("and start countdown.");
display.display();
tone(SPEAKER, 440, 100);
digitalWrite(LED_YELLOW, HIGH);
delay(3000);
display.clearDisplay();
digitalWrite(LED_YELLOW, LOW);
display.setCursor(0,0);
display.println("TIMER RUN MODE");
display.println("green button pauses");
display.println("and resets. red");
display.println("button was miswired.");
display.display();
tone(SPEAKER, 440, 100);
digitalWrite(LED_RED, HIGH);
delay(3000);
display.clearDisplay();
digitalWrite(LED_RED, LOW);
}
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.