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