diff --git a/content/posts/img/esp_indicator_1.png b/content/posts/img/esp_indicator_1.png new file mode 100644 index 0000000..cb9da6b Binary files /dev/null and b/content/posts/img/esp_indicator_1.png differ diff --git a/content/posts/img/esp_indicator_2.png b/content/posts/img/esp_indicator_2.png new file mode 100644 index 0000000..3e03b1b Binary files /dev/null and b/content/posts/img/esp_indicator_2.png differ diff --git a/content/posts/img/esp_indicator_3.png b/content/posts/img/esp_indicator_3.png new file mode 100644 index 0000000..fb6081b Binary files /dev/null and b/content/posts/img/esp_indicator_3.png differ diff --git a/content/posts/img/esp_indicator_wires.png b/content/posts/img/esp_indicator_wires.png new file mode 100644 index 0000000..412620a Binary files /dev/null and b/content/posts/img/esp_indicator_wires.png differ diff --git a/content/posts/medicine_indicator_light.md b/content/posts/medicine_indicator_light.md new file mode 100644 index 0000000..22be11c --- /dev/null +++ b/content/posts/medicine_indicator_light.md @@ -0,0 +1,283 @@ +--- +title: 'ESP8266 Medicine Indicator Light' +date: 2023-10-13T15:49:14-04:00 +tags: ["diy", "tutorial", "esp"] +author: "Me" +showToc: true +TocOpen: false +draft: true +hidemeta: false +description: 'Learn how to make an ESP8266 and a few simple components into an indicator light.' +disableHLJS: true +disableShare: false +disableHLJS: false +hideSummary: false +searchHidden: true +ShowReadingTime: true +ShowBreadCrumbs: true +ShowPostNavLinks: true +ShowWordCount: true +ShowRssButtonInSectionTermList: true +UseHugoToc: true +cover: + image: "esp_medicine_indicator.png" + alt: "ESP8266 Logo with a Medicine Icon, Colorized" + caption: "Learn how to turn an ESP8266 device into an indicator light" + relative: false + hidden: true +--- + +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, and the morning and evening responsibility are split up between my partner and +I, 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 some leftover components. + +## Device Overview + +The device works like this (see the pictures below): Two [single neopixel LEDs](https://www.adafruit.com/product/1260) and a +non-latching button are connected to the ESP8266. Inside the code there is a switch-case section which changes case per +button press. On each press, the light will change depending on what is setup for that case. At the same time, each case is +establishing a value 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, +Mosquitto](https://mosquitto.org/). + +Here's what the payload looks like: + +```json +{ + "time": "1630", + "morning": "1", + "evening": "0" +} +``` + +## Thoughts and Background + +I'd like to write a longer post that chops up to the code and goes into explanations of each section, 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](https://fosstodon.org/@notnorm) and we can chat. + +The much shorter explanation is that I didn't want this device to be an isolated device in my network. So what I did was +connected the ESP8266 to my wifi network and import an NTP (Network Time Protocol) server to ensure I get I have the current +time. Then, look at the state of the light, and construct and transmit an MQTT payload with the state of the light & time, so +that I can use it elsewhere. + +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](https://pushover.net) +to me and my partner in case we forgot to give my child his medicine! What's great about this little project is that it is +scalable (add more components and sensors!) and that it doesn't need to indicate if someone has taken their medicine! You +could use the button to count how many times your dog (or child!) has gone to the bathroom, how many times to eat a snack or +drink water while working, and many more ideas. + +Assuming everything has gone well for the day and we've given my child both of his doses, we should +be heading to bed with both of those lights being green. However, we'll want to take into account a few edge cases. See the +pictures below with quick explanations. + +## Materials Used + +Here is what I used to construct this: + +* [ESP8266 Dev + Board](https://www.amazon.com/HiLetgo-Internet-Development-Wireless-Micropython/dp/B081CSJV2V/ref=sr_1_1?crid=7JMC6TOMCS9I&keywords=hiletgo+esp8255&qid=1697229200&sprefix=hiletgo+esp8255%2Caps%2C89&sr=8-1) - I tend to like HiLetgo from Amazon. +* 22 AWG Solid Wire +* White Ping Pong ball - Cut in half +* Balsa Wood +* [Tactile Button Switch](https://www.adafruit.com/product/367) +* [Adafruit Flora RGB NeoPixel](https://www.adafruit.com/product/1260) + +## Pictures + +![Front of Device with one light green, the other red](../posts/img/esp_indicator_1.png) +![Front of Device with both lights green](../posts/img/esp_indicator_2.png) +![Front of Device with both lights red](../posts/img/esp_indicator_3.png) +![Underside of device with wires](../posts/img/esp_indicator_wires.png) + +## Full Code + +```c++ +#include +#include +#include +#include +#include +#include +#include +#include + +#define NEO_PIN 14 // Pin for all the Neopixels +#define SIG_PIX 2 // Number of temperature pixels +#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_PASS; + +boolean oldState = HIGH; +int mode = 0; // Active mode on startup + +// NTP Definition +// const long utcOffsetInSeconds = -18000; +const long utcOffsetInSeconds = -14400; +//char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; +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; + +char morning_status[] = "false"; +char evening_status[] = "false"; +char current_time[] = ""; + +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(500); + + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + 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_SERV, MQTT_PORT); + WiFi.setAutoReconnect(true); + WiFi.persistent(true); + + // Necessary code for NeoPixel setup + pinMode(BUTTON_FWD_PIN, INPUT_PULLUP); + + //Initialization of the Jewel + NeoJewel.setBrightness(100); + NeoJewel.begin(); + NeoJewel.clear(); + NeoJewel.show(); +} + +void loop() { + boolean newState = digitalRead(BUTTON_FWD_PIN); + if((newState == LOW) && (oldState == HIGH)) { + delay(30); + newState = digitalRead(BUTTON_FWD_PIN); + if(newState == LOW) { + if(++mode > 4) mode = 0; + switch(mode) { + case 0: + // Nothing Taken + { NeoJewel.clear(); + NeoJewel.fill(pureRed, 0, 2); + NeoJewel.show(); + String morning_status = "false"; + String evening_status = "false"; + Serial.println(morning_status + evening_status); + break; } + case 1: + // Morning Taken + { NeoJewel.clear(); + NeoJewel.fill(pineGreen, 0, 1); + NeoJewel.fill(pureRed, 1, 2); + NeoJewel.show(); + String morning_status = "true"; + String evening_status = "false"; + Serial.println(morning_status + evening_status); + break; } + case 2: + // Evening Taken + { NeoJewel.clear(); + NeoJewel.fill(pineGreen, 1, 2); + NeoJewel.fill(pureRed, 0, 1); + NeoJewel.show(); + String morning_status = "false"; + String evening_status = "true"; + Serial.println(morning_status + evening_status); + break; } + case 3: + // Both Taken + { NeoJewel.clear(); + NeoJewel.fill(pineGreen, 0, 2); + NeoJewel.show(); + String morning_status = "true"; + String evening_status = "true"; + Serial.println(morning_status + evening_status); + break; } + } + } + } + // Set the last-read button state to the old state (reset) + oldState = newState; + + if (WiFi.status() != WL_CONNECTED) { + delay(1000); + WiFi.disconnect(); + ESP.restart(); + } + +} + +void publishmqtt(String current_time, String morning_status, String 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]; + Serial.println(data); + Serial.println(""); + serializeJson(doc, data); + client.publish(mqtt_topic, data, true); + delay(2000); + 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(); + } + } +} +``` diff --git a/static/esp_medicine_indicator.png b/static/esp_medicine_indicator.png new file mode 100644 index 0000000..bc80e95 Binary files /dev/null and b/static/esp_medicine_indicator.png differ