Arduino kitchen timer using Nokia 5110 LCD and Pro Mini
Posted: Mon Aug 25, 2014 9:58 pm
A couple of months ago, I promised to give more details about my Arduino-based kitchen timer project, which I had presented in this video:
I've been quite busy recently, so this is just a little update showing the schematics and code. It should provide enough info to get you started. Check out my previous post for more details about connecting Nokia 5110 LCD. Note that this project uses Arduino Pro Mini at 3.3 V, so there's no need for the 4050 level shifter, which makes things much simpler.
![Image](http://forums.4fips.com/2013/4FipS.com_Arduino_Mini_Nokia_5110_LCD_Philips_PCD8544_SPI_Kitchen_Timer_Schematics_small.png)
I've been quite busy recently, so this is just a little update showing the schematics and code. It should provide enough info to get you started. Check out my previous post for more details about connecting Nokia 5110 LCD. Note that this project uses Arduino Pro Mini at 3.3 V, so there's no need for the 4050 level shifter, which makes things much simpler.
![Image](http://forums.4fips.com/2013/4FipS.com_Arduino_Mini_Nokia_5110_LCD_Philips_PCD8544_SPI_Kitchen_Timer_Schematics_small.png)
Code: Select all
/*
(c) 2013 +++ Filip Stoklas, aka FipS, http://www.4FipS.com +++
THIS CODE IS FREE - LICENSED UNDER THE MIT LICENSE
ARTICLE URL: http://forums.4fips.com/viewtopic.php?f=3&t=1202
*/
#include "U8glib.h"
#include <stdio.h>
U8GLIB_PCD8544 u8g(13, 11, 10, 9, 8); // SPI Com: SCK = 13, MOSI = 11, CS = 10, A0 = 9, Reset = 8
const int32_t c_sec_ms = 1000L;
const int32_t c_min_ms = 1000L * 60L;
const int32_t c_hour_ms = 1000L * 60L * 60L;
class Clock
{
public:
explicit Clock(int32_t time_ms = 0) : _time_ms(time_ms) { _text[0] = '\0'; }
void tick(int32_t delta_ms) { _time_ms += delta_ms; } // call once a loop()
void reset(int32_t time_ms) { _time_ms = time_ms; }
void add(int32_t h, int32_t m, int32_t s) { _time_ms += h * c_hour_ms + m * c_min_ms + s * c_sec_ms; }
int32_t time_ms() const { return _time_ms; }
bool expired() const { return _time_ms < 0; }
const char * text(bool show_sec) const
{
const bool neg = _time_ms < 0;
const int32_t abs_time_ms = neg ? -_time_ms : _time_ms;
const int sec = (abs_time_ms / c_sec_ms) % 60;
const int min = (abs_time_ms / c_min_ms) % 60;
const int hour = (abs_time_ms / c_hour_ms) % 24;
if(show_sec)
sprintf(_text, "%c%01d:%02d:%02d", neg ? '-' : ' ', hour, min, sec);
else
sprintf(_text, "%c%02d:%02d", neg ? '-' : ' ', hour, min);
return _text;
}
private:
int32_t _time_ms; // negative time supported
mutable char _text[32];
};
class Button
{
public:
explicit Button(uint8_t pin) : _pin(pin) { reset(); }
void setup() { pinMode(_pin, INPUT); }
void reset() { _down_ms = _down_rep_ms = _up_ms = -1; _clicked = _repeated = false; }
void tick(int32_t time_ms) // call once a loop()
{
const int32_t DUR_MS = 500;
const bool down = this->down();
_clicked = false;
_repeated = false;
if(down)
{
const bool edge = _down_ms < 0; // up => down transition
_down_ms = edge ? time_ms : _down_ms;
_down_rep_ms = edge ? _down_ms : _down_rep_ms;
const int32_t dur_ms = time_ms - _down_rep_ms; // since last repeat
if(dur_ms > DUR_MS)
{
_down_rep_ms += 500;
_repeated = true;
}
_up_ms = -1;
}
else if(_down_ms >= 0) // ignore initial ups
{
const bool edge = _up_ms < 0; // down => up transition
_up_ms = edge ? time_ms : _up_ms;
const int32_t dur_ms = time_ms - _down_ms; // since down
if(edge && dur_ms < DUR_MS)
_clicked = true;
_down_ms = -1;
}
}
bool down() const { return digitalRead(_pin) == HIGH; }
bool clicked() const { return _clicked; }
bool repeated() const { return _repeated; }
private:
const uint8_t _pin;
int32_t _down_ms;
int32_t _down_rep_ms;
int32_t _up_ms;
bool _clicked;
bool _repeated;
};
class Alarm
{
public:
enum Mode { Off, Alarm1, Alarm2, Alarm3 };
explicit Alarm(uint8_t pin) : _pin(pin) { set_mode(Off); }
void setup() { pinMode(_pin, OUTPUT); }
void set_mode(Mode mode)
{
if(mode == _mode)
return;
_mode = mode;
_counter = 0;
}
void tick()
{
switch(_mode)
{
case Alarm1:
{
digitalWrite(_pin, _counter % 20 == 0 ? HIGH : LOW);
break;
}
case Alarm2:
{
digitalWrite(_pin, _counter % 3 == 0 ? HIGH : LOW);
break;
}
default: // ~ Off
{
digitalWrite(_pin, LOW);
break;
}
}
++_counter;
}
private:
const uint8_t _pin;
Mode _mode;
int32_t _counter;
};
const int32_t c_setupdur_ms = 3 * c_sec_ms;
const uint8_t c_lcd_brightness = 64;
const uint8_t c_led_lcd_pin = 6;
Button g_min_btn(2);
Button g_sec_btn(3);
Alarm g_alarm(5);
Clock g_sys_clk(0);
Clock g_uptime_clk(0);
Clock g_timer_clk(0);
Clock g_setup_clk(c_setupdur_ms);
void setup()
{
g_min_btn.setup();
g_sec_btn.setup();
g_alarm.setup();
pinMode(c_led_lcd_pin, OUTPUT);
//u8g.setContrast(128);
u8g.setColorIndex(1); // pixel on
reset();
}
void reset()
{
g_timer_clk.reset(0);
analogWrite(c_led_lcd_pin, c_lcd_brightness);
g_alarm.set_mode(Alarm::Off);
}
void draw()
{
u8g.setFont(u8g_font_fur17r);
u8g.drawStr(-1, 20, g_timer_clk.text(true));
u8g.setFont(u8g_font_unifont);
u8g.drawStr(15, 42, g_uptime_clk.text(false));
}
int32_t tick_time_delta_ms()
{
static int32_t last_time_ms = -1;
const int32_t time_ms = millis() / 2; // ???
const int32_t time_delta_ms = last_time_ms >= 0 ? time_ms - last_time_ms : 0;
last_time_ms = time_ms;
return time_delta_ms;
}
void loop()
{
const int32_t time_delta_ms = tick_time_delta_ms();
g_sys_clk.tick(time_delta_ms);
static bool started = false;
static bool paused = true;
//--- INPUT ---
g_min_btn.tick(g_sys_clk.time_ms());
g_sec_btn.tick(g_sys_clk.time_ms());
//--- RESET ---
static bool reset_action = false;
{
const bool min_down = g_min_btn.down();
const bool sec_down = g_sec_btn.down();
reset_action = (min_down && sec_down) || reset_action && (min_down || sec_down);
}
if(reset_action)
{
g_min_btn.reset();
g_sec_btn.reset();
reset();
g_uptime_clk.reset(0);
g_setup_clk.reset(c_setupdur_ms);
started = false;
paused = true;
}
//--- ACTION ---
const bool add_min = g_min_btn.clicked();
const bool add_min10 = g_min_btn.repeated();
const bool add_sec = g_sec_btn.clicked();
const bool add_sec10 = g_sec_btn.repeated();
const bool any_action = add_min || add_min10 || add_sec || add_sec10;
//--- UPDATE ---
if(started)
g_uptime_clk.tick(time_delta_ms);
if(!paused)
g_setup_clk.tick(-time_delta_ms);
if(any_action)
{
g_setup_clk.reset(c_setupdur_ms);
if(g_timer_clk.expired())
{
reset();
paused = true;
}
else
{
if(add_min) g_timer_clk.add(0, 1, 0);
if(add_min10) g_timer_clk.add(0, 10, 0);
if(add_sec) g_timer_clk.add(0, 0, 1);
if(add_sec10) g_timer_clk.add(0, 0, 10);
if(g_timer_clk.time_ms() > 0)
{
started = true;
paused = false;
}
}
}
if(g_setup_clk.expired())
g_timer_clk.tick(-time_delta_ms);
//--- ALARM ---
if(g_setup_clk.expired() && g_timer_clk.time_ms() <= 10 * c_sec_ms)
analogWrite(c_led_lcd_pin, (g_sys_clk.time_ms() / 500) % 2 == 0 ? c_lcd_brightness : 255);
else
analogWrite(c_led_lcd_pin, c_lcd_brightness);
if(g_timer_clk.expired())
{
const int32_t dur_ms = -g_timer_clk.time_ms();
if(dur_ms >= 10 * c_min_ms) g_alarm.set_mode(Alarm::Off); // give up after 10 min
else if(dur_ms >= 30 * c_sec_ms) g_alarm.set_mode(Alarm::Alarm2);
else if(dur_ms >= 0) g_alarm.set_mode(Alarm::Alarm1);
}
g_alarm.tick();
//--- DRAW ---
u8g.firstPage();
do
{
draw();
}
while(u8g.nextPage());
//--- SLEEP ---
delay(100);
}