First draft of the medicine post is up. Instead of posting on Fridays, I'm going to shoot for Mondays to see if there's a change in visibility. Draft is ready for review.
This commit is contained in:
BIN
content/posts/img/esp_indicator_1.png
Normal file
BIN
content/posts/img/esp_indicator_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 MiB |
BIN
content/posts/img/esp_indicator_2.png
Normal file
BIN
content/posts/img/esp_indicator_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 MiB |
BIN
content/posts/img/esp_indicator_3.png
Normal file
BIN
content/posts/img/esp_indicator_3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 MiB |
BIN
content/posts/img/esp_indicator_wires.png
Normal file
BIN
content/posts/img/esp_indicator_wires.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 MiB |
283
content/posts/medicine_indicator_light.md
Normal file
283
content/posts/medicine_indicator_light.md
Normal file
@ -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
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
## Full Code
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#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 // 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
BIN
static/esp_medicine_indicator.png
Normal file
BIN
static/esp_medicine_indicator.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
Reference in New Issue
Block a user