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();
+    }
+  }
+}
+
+ + +