XMAS is over
On January 6, we remove our Christmas tree and lights in Belgium. So it's also time for my Christmas Wreath of Things to switch off.
But we still have our Secret IoT Service going on - and I want to enjoy that for a little longer.
So I switched over to a low profile design: an LCD that glows when one of my Secret IoT Service partners is alive,
and shows the name of the last person that sent me an I'm alive message.
In the picture below, it was Robert Peter Oakes.
What's happening?
If no one but me is on-line, the display is blank, and the backlight off.
Once I receive a message from one of my friends, the Thing wakes up:
It sets a seconds counter to 120 seconds, and remembers the name of the sender.
The display lights up, and shows who was broadcasting:
Left upper corner shows my countdown timer. Each second the timer decreases.
Right lower corner shows the sender (in this case fvan).
If I receive no new message within 120 seconds, the backlight switches off, the display is blanked
and we go back to sleep.
Code:
// needed for MQTT and Process lib
#include <Bridge.h>
// MQTT includes start
#include <SPI.h>
#include <YunClient.h>
#include <IPStack.h>
#include <Countdown.h>
#include <MQTTClient.h>
#include <string.h>
// MQTT includes end
// include the library code:
#include <LiquidCrystal.h>
#define PIN_LCDLIGHT 10
// start MQTT declarations
char printbuf[100];
YunClient c; // replace by a YunClient if running on a Yun
IPStack ipstack(c);
MQTT::Client<IPStack, Countdown> client = MQTT::Client<IPStack, Countdown>(ipstack);
char payLoadBuf[] = {'j', 'a', 'n', 'c', 'u', 'm', 'p', 's', 0};
const char* send_topic = "element14_IoT";
const char* subscribe_topic = "element14_IoT";
const char* _id = "1cfee8dd0afc_yun";
// end MQTT declarations
// start frederick declarations
unsigned int uFrederickIsAlive = 0;
// end frederick declarations
// initialize the library with the numbers of the interface pins
// LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
LiquidCrystal lcd(8, 13, 9, 4, 5, 6, 7);
char lastPayload[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
// start timer functionality ============================================================
boolean bRunSeconds = false;
boolean bIsRunningSeconds = false;
boolean bRunMinutes = false;
boolean bIsRunningMinutes = false;
boolean bRunHours = false;
boolean bIsRunningHours = false;
void runSeconds() {
bIsRunningSeconds = true;
// // Serial.print("s"); // remove when confident
// task: keep MQTT alive
client.yield(30); // this takes 30 ms. May reduce the parameter // if you get duplicates, increase
// task: handle frederick alive functionality
// task: every minute; we take one live away from frederick
if (uFrederickIsAlive) {
uFrederickIsAlive--;
sprintf(printbuf, "%3u%c", uFrederickIsAlive, 0);
lcd.setCursor(0, 0);
lcd.print(printbuf);
lcd.setCursor(0, 1);
lcd.print(lastPayload);
pinMode(PIN_LCDLIGHT, INPUT); // high impedance turns on light
} else {
pinMode(PIN_LCDLIGHT, OUTPUT); // low impedance LOW turns off light
sprintf(printbuf, " %c", 0);
lcd.setCursor(0, 0);
lcd.print(printbuf);
lcd.setCursor(0, 1);
lcd.print(printbuf);
}
Serial.println(uFrederickIsAlive);
bRunSeconds = false;
bIsRunningSeconds = false;
}
void runMinutes() {
bIsRunningMinutes = true;
// // Serial.println("m"); // remove when confident
// task: flag to Frederick that I'm alive
sendAliveMessage();
bRunMinutes = false;
bIsRunningMinutes = false;
}
void runHours() {
bIsRunningHours = true;
// // Serial.println("h"); // remove when confident
bRunHours = false;
bIsRunningHours = false;
}
void timerInit() {
// initialize timer1 for 1 second ticks; ISR(TIMER1_COMPA_vect) will be called as interrupt handler
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 62500; // compare match register 16MHz/256/1Hz
TCCR1B |= (1 << WGM12); // CTC mode
TCCR1B |= (1 << CS12); // 256 prescaler
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
interrupts(); // enable all interrupts
}
ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine
{
static unsigned int uSeconds = 0;
uSeconds++; // every call is a second
bRunSeconds = true; // so yes, flag that the seconds handler should be called
bRunMinutes = ! (uSeconds % 60); // each 60th second, flag that the minutes handler should be called
if (uSeconds > 3599) { // every hour
bRunHours = true; // flag that the hours handler should be called
uSeconds = 0; // and start over
}
}
void timerTasks() {
if (bRunSeconds && ! bIsRunningSeconds) { // timer interrupt flagged that seconds handler should be called
runSeconds(); // but we only run it if it's not active
}
if (bRunMinutes && ! bIsRunningMinutes) { // timer interrupt flagged that minutes handler should be called
runMinutes(); // but we only run it if it's not active
}
if (bRunHours && ! bIsRunningHours) { // timer interrupt flagged that hours handler should be called
runHours(); // but we only run it if it's not active
}
}
// end timer functionality =====================================
// start MQTT functionality ====================================
void mqttInit() {
// Ethernet.begin(mac); // replace by Bridge.begin() if running on a Yun
Bridge.begin();
// // Serial.println("MQTT Internet of Holiday Lights example");
connect();
}
void messageArrived(MQTT::MessageData& md) // this handler is called when a subscribed MQTT message arrives
{
MQTT::Message &message = md.message;
// debug code
sprintf(printbuf, "Message arrived: qos %d, retained %d, dup %d, packetid %d\n",
message.qos, message.retained, message.dup, message.id);
Serial.print(printbuf);
sprintf(printbuf, "Payload %s\n", (char*)message.payload);
Serial.print(printbuf);
/*
// sprintf(printbuf, "Topic len %d\n", md.topicName.lenstring);
int i;
for (i = 0; i < (md.topicName.lenstring.len); i++) {
printbuf[i] = md.topicName.lenstring.data[i];
}
printbuf[(md.topicName.lenstring.len)]= '\n';
printbuf[md.topicName.lenstring.len + 1]= 0;
*/
int iPlCheck = strncmp(payLoadBuf, (char*)message.payload,8);
if (iPlCheck) {
Serial.print(printbuf);
sprintf(lastPayload, "%16s", (char*)message.payload);
uFrederickIsAlive = 120; // we give frederick two lives (minutes) to send an 'I am alive' live message.
} else {
Serial.println("it's my own message");
}
}
void connect() // connect to the MQTT broker
{
char hostname[] = "iot.eclipse.org";
int port = 1883;
sprintf(printbuf, "Connecting to %s:%d\n", hostname, port);
// // Serial.print(printbuf);
int rc = ipstack.connect(hostname, port);
if (rc != 1)
{
sprintf(printbuf, "rc from TCP connect is %d\n", rc);
// // Serial.print(printbuf);
}
// // Serial.println("MQTT connecting");
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
data.MQTTVersion = 3;
data.clientID.cstring = (char*)_id;
rc = client.connect(data);
if (rc != 0)
{
sprintf(printbuf, "rc from MQTT connect is %d\n", rc);
// // Serial.print(printbuf);
}
// // Serial.println("MQTT connected");
rc = client.subscribe(subscribe_topic, MQTT::QOS1, messageArrived);
if (rc != 0)
{
sprintf(printbuf, "rc from MQTT subscribe is %d\n", rc);
// // Serial.print(printbuf);
}
// // Serial.println("MQTT subscribed");
}
void sendAliveMessage() {
if (!client.isConnected())
connect();
MQTT::Message message;
char buf[100];
int rc;
// Send QoS 1 message
// // Serial.println(payLoadBuf);
message.qos = MQTT::QOS1;
message.retained = false;
message.dup = false;
message.payload = (void*)payLoadBuf;
message.payloadlen = strlen(payLoadBuf)+1;
rc = client.publish(send_topic, message);
}
// end MQTT functionality ==============================
// start LCD functionality ===========================
void setupLCD() {
pinMode(PIN_LCDLIGHT, OUTPUT); //Set control pins to be outputs
digitalWrite(PIN_LCDLIGHT, LOW);
lcd.begin(16, 2);
// lcd.setCursor(0, 0);
}
// end LCD functionality ===========================
void setup()
{
Serial.begin(9600);
delay(10000); // give me time to start the yun monitor
// MQTT related tasks
mqttInit();
// initialise LCD
setupLCD();
// timer related tasks
timerInit();
}
void loop()
{
timerTasks();
// non timer related functionality comes here
}

