This is a quick treat! We recently learned that one of our children needs to take medicine twice a day for the foreseeable
+future. He’s too young to take it on his own, so the twice-a-day responsibility is split up between my partner and
+I. However, sometimes our schedules don’t overlap so succinctly, so we needed some sort of indicator to let the other know if
+the previous dose was (or wasn’t!) taken. Naturally, I sprung into action with an ESP device and components.
+
Device Overview#
+
The device works like this (see the pictures below): Two single neopixel LEDs and a
+non-latching button are connected to the ESP8266. Inside the code there is a switch-case
+section which changes states per high/low state of the button. On each press, the lights will change to one of the following:
+
+- Red/Red
+- Green/Red
+- Red/Green
+- Green/Green
+
+
For each case, I established a values for morning and evening which will be given a value of 0 or 1 - 0 to indicate that the medicine was
+not taken, and 1 to indicate that it was taken. Along with the time server data, I send an MQTT payload to my MQTT Broker.
+
Here’s what the payload looks like:
+
{
+ "time": "1630",
+ "morning": "1",
+ "evening": "0"
+}
+
Thoughts and Background#
+
I’d like to write a longer post that explains each section of the code, but I’m running out of
+time - work has been quite busy lately. If you’d like me to add a post with more code explanations, or just have questions,
+feel free to mention me on Mastoton/Fosstodon and we can chat.
+
The much shorter explanation is that I didn’t want this device to be an isolated device in my network; this was part of the
+impetus in using an ESP device as opposed to another microcontroller. So what I did was connected the ESP8266 to my wifi
+network and imported an NTP (Network Time Protocol) server to ensure I get I have the current time. Then, look at the state
+given by the case (and thus the external light indicator), and construct and transmit an MQTT payload with the state of the
+light & time, so that I can use it elsewhere. In other words, now with MQTT payloads being accessible to HomeAssistant, I can
+send myself a notification, or make an announcement on a speaker.
+
What’s neat about importing a time server is that at midnight, I reset both of the lights to red so that I don’t have to
+reset it manually when I get up in the morning.
+
In a separate post, I’ll show how I use the MQTT payload in HomeAssistant to send Pushover Notifications
+to me and my partner in case we forgot to give my child the medicine! What’s great about this little project is that it is
+scalable (add more components and sensors!) and it isn’t limited to a medicine indicator. You
+could use the button to count how many times your dog (or child!) has gone to the bathroom, how many times you have eat or
+drink water while working, and many more ideas.
+
Assuming everything has gone well for the day and we’ve given my child both doses, we should be heading to bed with both of
+those lights being green.
+
Materials Used#
+
Here is what I used to construct this:
+
+
Pictures#
+
+

+

+
+
+

+

+
+
Full Code#
+
#include <Wire.h>
+#include <Adafruit_NeoPixel.h>
+#include <ESP8266WiFi.h>
+#include <NTPClient.h>
+#include <WiFiUdp.h>
+#include <PubSubClient.h>
+#include <ArduinoJson.h>
+#include <secrets.h>
+
+#define NEO_PIN 14 // Pin for all the Neopixels
+#define SIG_PIX 2 // Data pin for Neopixels
+#define BUTTON_FWD_PIN 4
+#define mqtt_topic "home/medicine"
+
+const char* ssid = WIFI_SSID;
+const char* password = WIFI_PASS;
+const char* mqtt_server = MQTT_SERV;
+const char* mqtt_user = MQTT_USER;
+const char* mqtt_password = MQTT_PASSWORD;
+
+boolean oldState = HIGH;
+int mode = 0; // Active mode on startup
+unsigned long previousMillis = 0;
+const long interval = 5000;
+
+// NTP Definition
+const long utcOffsetInSeconds = -14400;
+WiFiUDP ntpUDP;
+NTPClient timeClient(ntpUDP, "north-america.pool.ntp.org", utcOffsetInSeconds);
+
+WiFiClient espClient;
+PubSubClient client(espClient);
+
+long lastMsg = 0;
+char msg[50];
+float value = 0;
+
+// Setup defaults for the eventual MQTT payload
+char hours[] = "";
+char minutes[] = "";
+boolean morning_status = false;
+boolean evening_status = false;
+char current_time[] = "";
+int int_time = 0;
+
+// Necessary Setup for Neopixels
+Adafruit_NeoPixel NeoJewel = Adafruit_NeoPixel(SIG_PIX, NEO_PIN, NEO_GRB + NEO_KHZ800);
+unsigned long delayTime;
+
+// Taken Medicine Color
+uint32_t pineGreen = NeoJewel.Color(14, 170, 26);
+
+// Not Yet Taken Color
+uint32_t pureRed = NeoJewel.Color(255, 0, 0);
+
+void setup() {
+ //delayTime = 1000;
+ Serial.begin(9600);
+ delay(50);
+
+ while (!Serial) {
+ // wait for serial port to connect. Needed for native USB port only
+ }
+
+ // Print Status of Wifi Connection to Serial Monitor
+ Serial.println("Attempting to connect to SSID: ");
+ Serial.print(ssid);
+ WiFi.begin(ssid, password);
+
+ // Attempt to connect to WiFi network:
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(1000);
+ Serial.print(".");
+ }
+
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.println("IP address: ");
+ Serial.println(WiFi.localIP()); //You can get IP address assigned to ESP
+ Serial.println("Mac Address: ");
+ Serial.println(WiFi.macAddress());
+ delay(100);
+ client.setServer(mqtt_server, 1883);
+ WiFi.setAutoReconnect(true);
+ WiFi.persistent(true);
+
+ // Initialize Time
+ timeClient.begin();
+
+ // Necessary code for button setup
+ pinMode(BUTTON_FWD_PIN, INPUT_PULLUP);
+
+ //Initialization of the Jewel
+ NeoJewel.setBrightness(100);
+ NeoJewel.begin();
+ NeoJewel.clear();
+ NeoJewel.show();
+}
+
+void loop() {
+ buttonpush();
+ client.loop();
+ timeClient.update();
+
+ int hours = timeClient.getHours();
+ int minutes = timeClient.getMinutes();
+ int int_time = (hours*100)+minutes;
+ String current_time = String(int_time);
+
+ if (WiFi.status() != WL_CONNECTED) {
+ delay(1000);
+ WiFi.disconnect();
+ ESP.restart();
+ }
+
+ if (!client.connected()) {
+ reconnect();
+ }
+
+ unsigned long currentMillis = millis();
+ if (currentMillis - previousMillis >= interval) {
+ previousMillis = currentMillis;
+ Serial.print(int_time);
+ publishmqtt(current_time, morning_status, evening_status);
+
+ // Reset to red colors and new payload at midnight
+ if (int_time == 0000) {
+ NeoJewel.clear();
+ NeoJewel.fill(pureRed, 0, 2);
+ NeoJewel.show();
+ morning_status = false;
+ evening_status = false;
+ }
+ }
+}
+
+void buttonpush() {
+ boolean newState = digitalRead(BUTTON_FWD_PIN);
+ if((newState == LOW) && (oldState == HIGH)) {
+ delay(20);
+ newState = digitalRead(BUTTON_FWD_PIN);
+ if(newState == LOW) {
+ if(++mode > 4) mode = 0;
+ switch(mode) {
+ case 1:
+ // Nothing Taken
+ { NeoJewel.clear();
+ NeoJewel.fill(pureRed, 0, 2);
+ NeoJewel.show();
+ morning_status = false;
+ evening_status = false;
+ break; }
+ case 2:
+ // Morning Taken
+ { NeoJewel.clear();
+ NeoJewel.fill(pineGreen, 0, 1);
+ NeoJewel.fill(pureRed, 1, 2);
+ NeoJewel.show();
+ morning_status = true;
+ evening_status = false;
+ break; }
+ case 3:
+ // Afternoon Taken
+ { NeoJewel.clear();
+ NeoJewel.fill(pineGreen, 1, 2);
+ NeoJewel.fill(pureRed, 0, 1);
+ NeoJewel.show();
+ morning_status = false;
+ evening_status = true;
+ break; }
+ case 4:
+ // Both Taken
+ { NeoJewel.clear();
+ NeoJewel.fill(pineGreen, 0, 2);
+ NeoJewel.show();
+ morning_status = true;
+ evening_status = true;
+ break; }
+ }
+ }
+ }
+ // Set the last-read button state to the old state (reset)
+ oldState = newState;
+}
+
+void publishmqtt(String current_time, boolean morning_status, boolean evening_status) {
+ StaticJsonDocument<200> doc;
+
+ doc["time"] = (String)current_time;
+ doc["morning"] = (String)morning_status;
+ doc["evening"] = (String)evening_status;
+ serializeJsonPretty(doc, Serial);
+
+ char data[200];
+ serializeJson(doc, data);
+ client.publish(mqtt_topic, data, true);
+ delay(50);
+ yield();
+}
+
+void reconnect() {
+ // Loop until we're reconnected
+ while (!client.connected()) {
+ Serial.println("Connecting to MQTT...");
+ // Create a random client ID
+ String clientId = "ESP8266Client-";
+ clientId += String(random(0xffff), HEX);
+ if (client.connect(clientId.c_str(), mqtt_user, mqtt_password)) {
+ Serial.println("connected");
+ } else {
+ Serial.println("failed with state ");
+ Serial.print(client.state());
+ delay(2000);
+ ESP.restart();
+ }
+ }
+}
+
+
+
+
+
+
+