Close

mom-timer

A project log for weekend novelty projects

minimal advanced planning. built from stuff i already have around the shop. typically not very useful. generally completed in one weekend.

zakqwyzakqwy 12/28/2015 at 03:020 Comments

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