Files
rsmsn_blog/public/posts/medicine_indicator_light.html

683 lines
60 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en" dir="auto">
<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="index, follow">
<title>ESP8266 Medicine Indicator Light | Norm-working Packets 💾</title>
<meta name="keywords" content="diy, tutorial, esp">
<meta name="description" content="Learn how to make an ESP8266 and a few simple components into an indicator light.">
<meta name="author" content="Me">
<link rel="canonical" href="../posts/medicine_indicator_light.html">
<link crossorigin="anonymous" href="../assets/css/stylesheet.b609c58d5c11bb90b1a54e04005d74ad1ddf22165eb79f5533967e57df9c3b50.css" integrity="sha256-tgnFjVwRu5CxpU4EAF10rR3fIhZet59VM5Z&#43;V9&#43;cO1A=" rel="preload stylesheet" as="style">
<link rel="icon" href="../favicon.ico">
<link rel="icon" type="image/png" sizes="16x16" href="../favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="../rsmsncircles.ico">
<link rel="apple-touch-icon" href="../apple-touch-icon.png">
<link rel="mask-icon" href="../safari-pinned-tab.svg">
<meta name="theme-color" content="#2e2e33">
<meta name="msapplication-TileColor" content="#2e2e33">
<link rel="alternate" hreflang="en" href="../posts/medicine_indicator_light.html">
<noscript>
<style>
#theme-toggle,
.top-link {
display: none;
}
</style>
</noscript><meta property="og:title" content="ESP8266 Medicine Indicator Light" />
<meta property="og:description" content="Learn how to make an ESP8266 and a few simple components into an indicator light." />
<meta property="og:type" content="article" />
<meta property="og:url" content="/posts/medicine_indicator_light.html" />
<meta property="og:image" content="/esp_medicine_indicator.png" /><meta property="article:section" content="posts" />
<meta property="article:published_time" content="2023-10-16T11:34:14-04:00" />
<meta property="article:modified_time" content="2023-10-16T11:34:14-04:00" /><meta property="og:site_name" content="Norm-working Packets" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="/esp_medicine_indicator.png" />
<meta name="twitter:title" content="ESP8266 Medicine Indicator Light"/>
<meta name="twitter:description" content="Learn how to make an ESP8266 and a few simple components into an indicator light."/>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1 ,
"name": "Posts",
"item": "/posts.html"
}
{
"@type": "ListItem",
"position": 1 ,
"name": "ESP8266 Medicine Indicator Light",
"item": "/posts/medicine_indicator_light.html"
}
]
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "ESP8266 Medicine Indicator Light",
"name": "ESP8266 Medicine Indicator Light",
"description": "Learn how to make an ESP8266 and a few simple components into an indicator light.",
"keywords": [
"diy", "tutorial", "esp"
],
"articleBody": "This is a quick treat! We recently learned that one of our children needs to take medicine twice a day for the foreseeable future. Hes 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 dont overlap so succinctly, so we needed some sort of indicator to let the other know if the previous dose was (or wasnt!) taken. Naturally, I sprung into action with an ESP device and components.\nDevice 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:\nRed/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.\nHeres what the payload looks like:\n{ \"time\": \"1630\", \"morning\": \"1\", \"evening\": \"0\" } Thoughts and Background Id like to write a longer post that explains each section of the code, but Im running out of time - work has been quite busy lately. If youd 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.\nThe much shorter explanation is that I didnt 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 \u0026 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.\nWhats neat about importing a time server is that at midnight, I reset both of the lights to red so that I dont have to reset it manually when I get up in the morning.\nIn a separate post, Ill 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! Whats great about this little project is that it is scalable (add more components and sensors!) and it isnt 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.\nAssuming everything has gone well for the day and weve given my child both doses, we should be heading to bed with both of those lights being green.\nMaterials Used Here is what I used to construct this:\nESP8266 Dev Board - I tend to like HiLetgo from Amazon. 22 AWG Solid Wire White Ping Pong ball - Cut in half Balsa Wood Tactile Button Switch Adafruit Flora RGB NeoPixel Pictures Full Code #include #include #include #include #include #include #include #include #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 \u003e= 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) \u0026\u0026 (oldState == HIGH)) { delay(20); newState = digitalRead(BUTTON_FWD_PIN); if(newState == LOW) { if(++mode \u003e 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\u003c200\u003e 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(); } } } ",
"wordCount" : "1170",
"inLanguage": "en",
"image":"/esp_medicine_indicator.png","datePublished": "2023-10-16T11:34:14-04:00",
"dateModified": "2023-10-16T11:34:14-04:00",
"author":{
"@type": "Person",
"name": "Me"
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "/posts/medicine_indicator_light.html"
},
"publisher": {
"@type": "Organization",
"name": "Norm-working Packets 💾",
"logo": {
"@type": "ImageObject",
"url": "/favicon.ico"
}
}
}
</script>
</head>
<body class=" dark" id="top">
<script>
if (localStorage.getItem("pref-theme") === "light") {
document.body.classList.remove('dark')
}
</script>
<header class="header">
<nav class="nav">
<div class="logo">
<a href="../" accesskey="h" title="Norm-working Packets 💾 (Alt + H)">Norm-working Packets 💾</a>
<div class="logo-switches">
<button id="theme-toggle" accesskey="t" title="(Alt + T)">
<svg id="moon" xmlns="http://www.w3.org/2000/svg" width="24" height="18" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
</svg>
<svg id="sun" xmlns="http://www.w3.org/2000/svg" width="24" height="18" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</button>
<ul class="lang-switch"><li>|</li>
</ul>
</div>
</div>
<ul id="menu">
<li>
<a href="../posts.html" title="Posts">
<span>Posts</span>
</a>
</li>
</ul>
</nav>
</header>
<main class="main">
<article class="post-single">
<header class="post-header">
<div class="breadcrumbs"><a href="../">Home</a>&nbsp;»&nbsp;<a href="../posts.html">Posts</a></div>
<h1 class="post-title entry-hint-parent">
ESP8266 Medicine Indicator Light
</h1>
<div class="post-description">
Learn how to make an ESP8266 and a few simple components into an indicator light.
</div>
<div class="post-meta"><span title='2023-10-16 11:34:14 -0400 EDT'>October 16, 2023</span>&nbsp;·&nbsp;6 min&nbsp;·&nbsp;1170 words&nbsp;·&nbsp;Me
</div>
</header> <div class="toc">
<details >
<summary accesskey="c" title="(Alt + C)">
<span class="details">Table of Contents</span>
</summary>
<div class="inner"><nav id="TableOfContents">
<ul>
<li><a href="#device-overview">Device Overview</a></li>
<li><a href="#thoughts-and-background">Thoughts and Background</a></li>
<li><a href="#materials-used">Materials Used</a></li>
<li><a href="#pictures">Pictures</a></li>
<li><a href="#full-code">Full Code</a></li>
</ul>
</nav>
</div>
</details>
</div>
<div class="post-content"><p>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&rsquo;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&rsquo;t overlap so succinctly, so we needed some sort of indicator to let the other know if
the previous dose was (or wasn&rsquo;t!) taken. Naturally, I sprung into action with an ESP device and components.</p>
<h2 id="device-overview">Device Overview<a hidden class="anchor" aria-hidden="true" href="#device-overview">#</a></h2>
<p>The device works like this (<a href="../posts/medicine_indicator_light.html#pictures">see the pictures below</a>): Two <a href="https://www.adafruit.com/product/1260">single neopixel LEDs</a> and a
non-latching button are connected to the ESP8266. Inside the code there is a <a href="https://www.arduino.cc/reference/tr/language/structure/control-structure/switchcase/">switch-case</a>
section which changes states per high/low state of the button. On each press, the lights will change to one of the following:</p>
<ul>
<li>Red/Red</li>
<li>Green/Red</li>
<li>Red/Green</li>
<li>Green/Green</li>
</ul>
<p>For each case, I established a values for <code>morning</code> and <code>evening</code> 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 <a href="https://mosquitto.org/">MQTT Broker</a>.</p>
<p>Here&rsquo;s what the payload looks like:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;time&#34;</span>: <span style="color:#e6db74">&#34;1630&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;morning&#34;</span>: <span style="color:#e6db74">&#34;1&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;evening&#34;</span>: <span style="color:#e6db74">&#34;0&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="thoughts-and-background">Thoughts and Background<a hidden class="anchor" aria-hidden="true" href="#thoughts-and-background">#</a></h2>
<p>I&rsquo;d like to write a longer post that explains each section of the code, but I&rsquo;m running out of
time - work has been quite busy lately. If you&rsquo;d like me to add a post with more code explanations, or just have questions,
feel free to mention me on <a href="https://fosstodon.org/@notnorm">Mastoton/Fosstodon</a> and we can chat.</p>
<p>The much shorter explanation is that I didn&rsquo;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 &amp; 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.</p>
<p>What&rsquo;s neat about importing a time server is that at midnight, I reset both of the lights to red so that I don&rsquo;t have to
reset it manually when I get up in the morning.</p>
<p>In a separate post, I&rsquo;ll show how I use the MQTT payload in HomeAssistant to send <a href="https://pushover.net">Pushover Notifications</a>
to me and my partner in case we forgot to give my child the medicine! What&rsquo;s great about this little project is that it is
scalable (add more components and sensors!) and it isn&rsquo;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.</p>
<p>Assuming everything has gone well for the day and we&rsquo;ve given my child both doses, we should be heading to bed with both of
those lights being green.</p>
<h2 id="materials-used">Materials Used<a hidden class="anchor" aria-hidden="true" href="#materials-used">#</a></h2>
<p>Here is what I used to construct this:</p>
<ul>
<li><a href="https://www.amazon.com/HiLetgo-Internet-Development-Wireless-Micropython/dp/B081CSJV2V/ref=sr_1_1?crid=7JMC6TOMCS9I&amp;keywords=hiletgo+esp8255&amp;qid=1697229200&amp;sprefix=hiletgo+esp8255%2Caps%2C89&amp;sr=8-1">ESP8266 Dev
Board</a> - I tend to like HiLetgo from Amazon.</li>
<li>22 AWG Solid Wire</li>
<li>White Ping Pong ball - Cut in half</li>
<li>Balsa Wood</li>
<li><a href="https://www.adafruit.com/product/367">Tactile Button Switch</a></li>
<li><a href="https://www.adafruit.com/product/1260">Adafruit Flora RGB NeoPixel</a></li>
</ul>
<h2 id="pictures">Pictures<a hidden class="anchor" aria-hidden="true" href="#pictures">#</a></h2>
<div class="column">
<img src="../posts/img/esp_indicator_1.png">
<img src="../posts/img/esp_indicator_2.png">
</div>
<div class="column">
<img src="../posts/img/esp_indicator_3.png">
<img src="../posts/img/esp_indicator_wires.png">
</div>
<h2 id="full-code">Full Code<a hidden class="anchor" aria-hidden="true" href="#full-code">#</a></h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c++" data-lang="c++"><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;Wire.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;Adafruit_NeoPixel.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ESP8266WiFi.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;NTPClient.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;WiFiUdp.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;PubSubClient.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ArduinoJson.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;secrets.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#define NEO_PIN 14 </span><span style="color:#75715e">// Pin for all the Neopixels
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#define SIG_PIX 2 </span><span style="color:#75715e">// Data pin for Neopixels
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#define BUTTON_FWD_PIN 4
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define mqtt_topic &#34;home/medicine&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">char</span><span style="color:#f92672">*</span> ssid <span style="color:#f92672">=</span> WIFI_SSID;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">char</span><span style="color:#f92672">*</span> password <span style="color:#f92672">=</span> WIFI_PASS;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">char</span><span style="color:#f92672">*</span> mqtt_server <span style="color:#f92672">=</span> MQTT_SERV;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">char</span><span style="color:#f92672">*</span> mqtt_user <span style="color:#f92672">=</span> MQTT_USER;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">char</span><span style="color:#f92672">*</span> mqtt_password <span style="color:#f92672">=</span> MQTT_PASSWORD;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>boolean oldState <span style="color:#f92672">=</span> HIGH;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">int</span> mode <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; <span style="color:#75715e">// Active mode on startup
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">long</span> previousMillis <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">long</span> interval <span style="color:#f92672">=</span> <span style="color:#ae81ff">5000</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// NTP Definition
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">long</span> utcOffsetInSeconds <span style="color:#f92672">=</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">14400</span>;
</span></span><span style="display:flex;"><span>WiFiUDP ntpUDP;
</span></span><span style="display:flex;"><span>NTPClient <span style="color:#a6e22e">timeClient</span>(ntpUDP, <span style="color:#e6db74">&#34;north-america.pool.ntp.org&#34;</span>, utcOffsetInSeconds);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>WiFiClient espClient;
</span></span><span style="display:flex;"><span>PubSubClient <span style="color:#a6e22e">client</span>(espClient);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">long</span> lastMsg <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">char</span> msg[<span style="color:#ae81ff">50</span>];
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">float</span> value <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Setup defaults for the eventual MQTT payload
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">char</span> hours[] <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">char</span> minutes[] <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span>boolean morning_status <span style="color:#f92672">=</span> false;
</span></span><span style="display:flex;"><span>boolean evening_status <span style="color:#f92672">=</span> false;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">char</span> current_time[] <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">int</span> int_time <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Necessary Setup for Neopixels
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>Adafruit_NeoPixel NeoJewel <span style="color:#f92672">=</span> Adafruit_NeoPixel(SIG_PIX, NEO_PIN, NEO_GRB <span style="color:#f92672">+</span> NEO_KHZ800);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">long</span> delayTime;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Taken Medicine Color
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">uint32_t</span> pineGreen <span style="color:#f92672">=</span> NeoJewel.Color(<span style="color:#ae81ff">14</span>, <span style="color:#ae81ff">170</span>, <span style="color:#ae81ff">26</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Not Yet Taken Color
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">uint32_t</span> pureRed <span style="color:#f92672">=</span> NeoJewel.Color(<span style="color:#ae81ff">255</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">setup</span>() {
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//delayTime = 1000;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Serial.begin(<span style="color:#ae81ff">9600</span>);
</span></span><span style="display:flex;"><span> delay(<span style="color:#ae81ff">50</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">while</span> (<span style="color:#f92672">!</span>Serial) {
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// wait for serial port to connect. Needed for native USB port only
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Print Status of Wifi Connection to Serial Monitor
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Serial.println(<span style="color:#e6db74">&#34;Attempting to connect to SSID: &#34;</span>);
</span></span><span style="display:flex;"><span> Serial.print(ssid);
</span></span><span style="display:flex;"><span> WiFi.begin(ssid, password);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Attempt to connect to WiFi network:
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">while</span> (WiFi.status() <span style="color:#f92672">!=</span> WL_CONNECTED) {
</span></span><span style="display:flex;"><span> delay(<span style="color:#ae81ff">1000</span>);
</span></span><span style="display:flex;"><span> Serial.print(<span style="color:#e6db74">&#34;.&#34;</span>);
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> Serial.println(<span style="color:#e6db74">&#34;&#34;</span>);
</span></span><span style="display:flex;"><span> Serial.println(<span style="color:#e6db74">&#34;WiFi connected&#34;</span>);
</span></span><span style="display:flex;"><span> Serial.println(<span style="color:#e6db74">&#34;IP address: &#34;</span>);
</span></span><span style="display:flex;"><span> Serial.println(WiFi.localIP()); <span style="color:#75715e">//You can get IP address assigned to ESP
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Serial.println(<span style="color:#e6db74">&#34;Mac Address: &#34;</span>);
</span></span><span style="display:flex;"><span> Serial.println(WiFi.macAddress());
</span></span><span style="display:flex;"><span> delay(<span style="color:#ae81ff">100</span>);
</span></span><span style="display:flex;"><span> client.setServer(mqtt_server, <span style="color:#ae81ff">1883</span>);
</span></span><span style="display:flex;"><span> WiFi.setAutoReconnect(true);
</span></span><span style="display:flex;"><span> WiFi.persistent(true);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Initialize Time
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> timeClient.begin();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Necessary code for button setup
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> pinMode(BUTTON_FWD_PIN, INPUT_PULLUP);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//Initialization of the Jewel
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> NeoJewel.setBrightness(<span style="color:#ae81ff">100</span>);
</span></span><span style="display:flex;"><span> NeoJewel.begin();
</span></span><span style="display:flex;"><span> NeoJewel.clear();
</span></span><span style="display:flex;"><span> NeoJewel.show();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">loop</span>() {
</span></span><span style="display:flex;"><span> buttonpush();
</span></span><span style="display:flex;"><span> client.loop();
</span></span><span style="display:flex;"><span> timeClient.update();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">int</span> hours <span style="color:#f92672">=</span> timeClient.getHours();
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">int</span> minutes <span style="color:#f92672">=</span> timeClient.getMinutes();
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">int</span> int_time <span style="color:#f92672">=</span> (hours<span style="color:#f92672">*</span><span style="color:#ae81ff">100</span>)<span style="color:#f92672">+</span>minutes;
</span></span><span style="display:flex;"><span> String current_time <span style="color:#f92672">=</span> String(int_time);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (WiFi.status() <span style="color:#f92672">!=</span> WL_CONNECTED) {
</span></span><span style="display:flex;"><span> delay(<span style="color:#ae81ff">1000</span>);
</span></span><span style="display:flex;"><span> WiFi.disconnect();
</span></span><span style="display:flex;"><span> ESP.restart();
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>client.connected()) {
</span></span><span style="display:flex;"><span> reconnect();
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">long</span> currentMillis <span style="color:#f92672">=</span> millis();
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (currentMillis <span style="color:#f92672">-</span> previousMillis <span style="color:#f92672">&gt;=</span> interval) {
</span></span><span style="display:flex;"><span> previousMillis <span style="color:#f92672">=</span> currentMillis;
</span></span><span style="display:flex;"><span> Serial.print(int_time);
</span></span><span style="display:flex;"><span> publishmqtt(current_time, morning_status, evening_status);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Reset to red colors and new payload at midnight
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (int_time <span style="color:#f92672">==</span> <span style="color:#ae81ff">0000</span>) {
</span></span><span style="display:flex;"><span> NeoJewel.clear();
</span></span><span style="display:flex;"><span> NeoJewel.fill(pureRed, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">2</span>);
</span></span><span style="display:flex;"><span> NeoJewel.show();
</span></span><span style="display:flex;"><span> morning_status <span style="color:#f92672">=</span> false;
</span></span><span style="display:flex;"><span> evening_status <span style="color:#f92672">=</span> false;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">buttonpush</span>() {
</span></span><span style="display:flex;"><span> boolean newState <span style="color:#f92672">=</span> digitalRead(BUTTON_FWD_PIN);
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span>((newState <span style="color:#f92672">==</span> LOW) <span style="color:#f92672">&amp;&amp;</span> (oldState <span style="color:#f92672">==</span> HIGH)) {
</span></span><span style="display:flex;"><span> delay(<span style="color:#ae81ff">20</span>);
</span></span><span style="display:flex;"><span> newState <span style="color:#f92672">=</span> digitalRead(BUTTON_FWD_PIN);
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span>(newState <span style="color:#f92672">==</span> LOW) {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span>(<span style="color:#f92672">++</span>mode <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">4</span>) mode <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">switch</span>(mode) {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">case</span> <span style="color:#ae81ff">1</span><span style="color:#f92672">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Nothing Taken
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> { NeoJewel.clear();
</span></span><span style="display:flex;"><span> NeoJewel.fill(pureRed, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">2</span>);
</span></span><span style="display:flex;"><span> NeoJewel.show();
</span></span><span style="display:flex;"><span> morning_status <span style="color:#f92672">=</span> false;
</span></span><span style="display:flex;"><span> evening_status <span style="color:#f92672">=</span> false;
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>; }
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">case</span> <span style="color:#ae81ff">2</span><span style="color:#f92672">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Morning Taken
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> { NeoJewel.clear();
</span></span><span style="display:flex;"><span> NeoJewel.fill(pineGreen, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>);
</span></span><span style="display:flex;"><span> NeoJewel.fill(pureRed, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">2</span>);
</span></span><span style="display:flex;"><span> NeoJewel.show();
</span></span><span style="display:flex;"><span> morning_status <span style="color:#f92672">=</span> true;
</span></span><span style="display:flex;"><span> evening_status <span style="color:#f92672">=</span> false;
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>; }
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">case</span> <span style="color:#ae81ff">3</span><span style="color:#f92672">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Afternoon Taken
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> { NeoJewel.clear();
</span></span><span style="display:flex;"><span> NeoJewel.fill(pineGreen, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">2</span>);
</span></span><span style="display:flex;"><span> NeoJewel.fill(pureRed, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>);
</span></span><span style="display:flex;"><span> NeoJewel.show();
</span></span><span style="display:flex;"><span> morning_status <span style="color:#f92672">=</span> false;
</span></span><span style="display:flex;"><span> evening_status <span style="color:#f92672">=</span> true;
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>; }
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">case</span> <span style="color:#ae81ff">4</span><span style="color:#f92672">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Both Taken
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> { NeoJewel.clear();
</span></span><span style="display:flex;"><span> NeoJewel.fill(pineGreen, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">2</span>);
</span></span><span style="display:flex;"><span> NeoJewel.show();
</span></span><span style="display:flex;"><span> morning_status <span style="color:#f92672">=</span> true;
</span></span><span style="display:flex;"><span> evening_status <span style="color:#f92672">=</span> true;
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>; }
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Set the last-read button state to the old state (reset)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> oldState <span style="color:#f92672">=</span> newState;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">publishmqtt</span>(String current_time, boolean morning_status, boolean evening_status) {
</span></span><span style="display:flex;"><span> StaticJsonDocument<span style="color:#f92672">&lt;</span><span style="color:#ae81ff">200</span><span style="color:#f92672">&gt;</span> doc;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> doc[<span style="color:#e6db74">&#34;time&#34;</span>] <span style="color:#f92672">=</span> (String)current_time;
</span></span><span style="display:flex;"><span> doc[<span style="color:#e6db74">&#34;morning&#34;</span>] <span style="color:#f92672">=</span> (String)morning_status;
</span></span><span style="display:flex;"><span> doc[<span style="color:#e6db74">&#34;evening&#34;</span>] <span style="color:#f92672">=</span> (String)evening_status;
</span></span><span style="display:flex;"><span> serializeJsonPretty(doc, Serial);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">char</span> data[<span style="color:#ae81ff">200</span>];
</span></span><span style="display:flex;"><span> serializeJson(doc, data);
</span></span><span style="display:flex;"><span> client.publish(mqtt_topic, data, true);
</span></span><span style="display:flex;"><span> delay(<span style="color:#ae81ff">50</span>);
</span></span><span style="display:flex;"><span> yield();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">reconnect</span>() {
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Loop until we&#39;re reconnected
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">while</span> (<span style="color:#f92672">!</span>client.connected()) {
</span></span><span style="display:flex;"><span> Serial.println(<span style="color:#e6db74">&#34;Connecting to MQTT...&#34;</span>);
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Create a random client ID
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> String clientId <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;ESP8266Client-&#34;</span>;
</span></span><span style="display:flex;"><span> clientId <span style="color:#f92672">+=</span> String(random(<span style="color:#ae81ff">0xffff</span>), HEX);
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (client.connect(clientId.c_str(), mqtt_user, mqtt_password)) {
</span></span><span style="display:flex;"><span> Serial.println(<span style="color:#e6db74">&#34;connected&#34;</span>);
</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span> Serial.println(<span style="color:#e6db74">&#34;failed with state &#34;</span>);
</span></span><span style="display:flex;"><span> Serial.print(client.state());
</span></span><span style="display:flex;"><span> delay(<span style="color:#ae81ff">2000</span>);
</span></span><span style="display:flex;"><span> ESP.restart();
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><style>
.column {
display: grid;
width: 100%;
height: 100%;
grid-template-rows: auto auto;
grid-template-columns: auto auto;
}
img {
padding: 5px;
border-radius: 10px !important;
}
</style>
</div>
<footer class="post-footer">
<ul class="post-tags">
<li><a href="../tags/diy.html">Diy</a></li>
<li><a href="../tags/tutorial.html">Tutorial</a></li>
<li><a href="../tags/esp.html">Esp</a></li>
</ul>
<nav class="paginav">
<a class="prev" href="../posts/mini_neovim.html">
<span class="title">« Prev</span>
<br>
<span>Mini Neovim</span>
</a>
<a class="next" href="../posts/birdnet_homeassistant_part2.html">
<span class="title">Next »</span>
<br>
<span>BirdNET-PI &amp; HomeAssistant: Part 2</span>
</a>
</nav>
<ul class="share-buttons">
<li>
<a target="_blank" rel="noopener noreferrer" aria-label="share ESP8266 Medicine Indicator Light on x"
href="https://x.com/intent/tweet/?text=ESP8266%20Medicine%20Indicator%20Light&amp;url=%2fposts%2fmedicine_indicator_light.html&amp;hashtags=diy%2ctutorial%2cesp">
<svg version="1.1" viewBox="0 0 512 512" xml:space="preserve" height="30px" width="30px" fill="currentColor">
<path
d="M512 62.554 L 512 449.446 C 512 483.97 483.97 512 449.446 512 L 62.554 512 C 28.03 512 0 483.97 0 449.446 L 0 62.554 C 0 28.03 28.029 0 62.554 0 L 449.446 0 C 483.971 0 512 28.03 512 62.554 Z M 269.951 190.75 L 182.567 75.216 L 56 75.216 L 207.216 272.95 L 63.9 436.783 L 125.266 436.783 L 235.9 310.383 L 332.567 436.783 L 456 436.783 L 298.367 228.367 L 432.367 75.216 L 371.033 75.216 Z M 127.633 110 L 164.101 110 L 383.481 400.065 L 349.5 400.065 Z" />
</svg>
</a>
</li>
<li>
<a target="_blank" rel="noopener noreferrer" aria-label="share ESP8266 Medicine Indicator Light on linkedin"
href="https://www.linkedin.com/shareArticle?mini=true&amp;url=%2fposts%2fmedicine_indicator_light.html&amp;title=ESP8266%20Medicine%20Indicator%20Light&amp;summary=ESP8266%20Medicine%20Indicator%20Light&amp;source=%2fposts%2fmedicine_indicator_light.html">
<svg version="1.1" viewBox="0 0 512 512" xml:space="preserve" height="30px" width="30px" fill="currentColor">
<path
d="M449.446,0c34.525,0 62.554,28.03 62.554,62.554l0,386.892c0,34.524 -28.03,62.554 -62.554,62.554l-386.892,0c-34.524,0 -62.554,-28.03 -62.554,-62.554l0,-386.892c0,-34.524 28.029,-62.554 62.554,-62.554l386.892,0Zm-288.985,423.278l0,-225.717l-75.04,0l0,225.717l75.04,0Zm270.539,0l0,-129.439c0,-69.333 -37.018,-101.586 -86.381,-101.586c-39.804,0 -57.634,21.891 -67.617,37.266l0,-31.958l-75.021,0c0.995,21.181 0,225.717 0,225.717l75.02,0l0,-126.056c0,-6.748 0.486,-13.492 2.474,-18.315c5.414,-13.475 17.767,-27.434 38.494,-27.434c27.135,0 38.007,20.707 38.007,51.037l0,120.768l75.024,0Zm-307.552,-334.556c-25.674,0 -42.448,16.879 -42.448,39.002c0,21.658 16.264,39.002 41.455,39.002l0.484,0c26.165,0 42.452,-17.344 42.452,-39.002c-0.485,-22.092 -16.241,-38.954 -41.943,-39.002Z" />
</svg>
</a>
</li>
<li>
<a target="_blank" rel="noopener noreferrer" aria-label="share ESP8266 Medicine Indicator Light on reddit"
href="https://reddit.com/submit?url=%2fposts%2fmedicine_indicator_light.html&title=ESP8266%20Medicine%20Indicator%20Light">
<svg version="1.1" viewBox="0 0 512 512" xml:space="preserve" height="30px" width="30px" fill="currentColor">
<path
d="M449.446,0c34.525,0 62.554,28.03 62.554,62.554l0,386.892c0,34.524 -28.03,62.554 -62.554,62.554l-386.892,0c-34.524,0 -62.554,-28.03 -62.554,-62.554l0,-386.892c0,-34.524 28.029,-62.554 62.554,-62.554l386.892,0Zm-3.446,265.638c0,-22.964 -18.616,-41.58 -41.58,-41.58c-11.211,0 -21.361,4.457 -28.841,11.666c-28.424,-20.508 -67.586,-33.757 -111.204,-35.278l18.941,-89.121l61.884,13.157c0.756,15.734 13.642,28.29 29.56,28.29c16.407,0 29.706,-13.299 29.706,-29.701c0,-16.403 -13.299,-29.702 -29.706,-29.702c-11.666,0 -21.657,6.792 -26.515,16.578l-69.105,-14.69c-1.922,-0.418 -3.939,-0.042 -5.585,1.036c-1.658,1.073 -2.811,2.761 -3.224,4.686l-21.152,99.438c-44.258,1.228 -84.046,14.494 -112.837,35.232c-7.468,-7.164 -17.589,-11.591 -28.757,-11.591c-22.965,0 -41.585,18.616 -41.585,41.58c0,16.896 10.095,31.41 24.568,37.918c-0.639,4.135 -0.99,8.328 -0.99,12.576c0,63.977 74.469,115.836 166.33,115.836c91.861,0 166.334,-51.859 166.334,-115.836c0,-4.218 -0.347,-8.387 -0.977,-12.493c14.564,-6.47 24.735,-21.034 24.735,-38.001Zm-119.474,108.193c-20.27,20.241 -59.115,21.816 -70.534,21.816c-11.428,0 -50.277,-1.575 -70.522,-21.82c-3.007,-3.008 -3.007,-7.882 0,-10.889c3.003,-2.999 7.882,-3.003 10.885,0c12.777,12.781 40.11,17.317 59.637,17.317c19.522,0 46.86,-4.536 59.657,-17.321c3.016,-2.999 7.886,-2.995 10.885,0.008c3.008,3.011 3.003,7.882 -0.008,10.889Zm-5.23,-48.781c-16.373,0 -29.701,-13.324 -29.701,-29.698c0,-16.381 13.328,-29.714 29.701,-29.714c16.378,0 29.706,13.333 29.706,29.714c0,16.374 -13.328,29.698 -29.706,29.698Zm-160.386,-29.702c0,-16.381 13.328,-29.71 29.714,-29.71c16.369,0 29.689,13.329 29.689,29.71c0,16.373 -13.32,29.693 -29.689,29.693c-16.386,0 -29.714,-13.32 -29.714,-29.693Z" />
</svg>
</a>
</li>
<li>
<a target="_blank" rel="noopener noreferrer" aria-label="share ESP8266 Medicine Indicator Light on facebook"
href="https://facebook.com/sharer/sharer.php?u=%2fposts%2fmedicine_indicator_light.html">
<svg version="1.1" viewBox="0 0 512 512" xml:space="preserve" height="30px" width="30px" fill="currentColor">
<path
d="M449.446,0c34.525,0 62.554,28.03 62.554,62.554l0,386.892c0,34.524 -28.03,62.554 -62.554,62.554l-106.468,0l0,-192.915l66.6,0l12.672,-82.621l-79.272,0l0,-53.617c0,-22.603 11.073,-44.636 46.58,-44.636l36.042,0l0,-70.34c0,0 -32.71,-5.582 -63.982,-5.582c-65.288,0 -107.96,39.569 -107.96,111.204l0,62.971l-72.573,0l0,82.621l72.573,0l0,192.915l-191.104,0c-34.524,0 -62.554,-28.03 -62.554,-62.554l0,-386.892c0,-34.524 28.029,-62.554 62.554,-62.554l386.892,0Z" />
</svg>
</a>
</li>
<li>
<a target="_blank" rel="noopener noreferrer" aria-label="share ESP8266 Medicine Indicator Light on whatsapp"
href="https://api.whatsapp.com/send?text=ESP8266%20Medicine%20Indicator%20Light%20-%20%2fposts%2fmedicine_indicator_light.html">
<svg version="1.1" viewBox="0 0 512 512" xml:space="preserve" height="30px" width="30px" fill="currentColor">
<path
d="M449.446,0c34.525,0 62.554,28.03 62.554,62.554l0,386.892c0,34.524 -28.03,62.554 -62.554,62.554l-386.892,0c-34.524,0 -62.554,-28.03 -62.554,-62.554l0,-386.892c0,-34.524 28.029,-62.554 62.554,-62.554l386.892,0Zm-58.673,127.703c-33.842,-33.881 -78.847,-52.548 -126.798,-52.568c-98.799,0 -179.21,80.405 -179.249,179.234c-0.013,31.593 8.241,62.428 23.927,89.612l-25.429,92.884l95.021,-24.925c26.181,14.28 55.659,21.807 85.658,21.816l0.074,0c98.789,0 179.206,-80.413 179.247,-179.243c0.018,-47.895 -18.61,-92.93 -52.451,-126.81Zm-126.797,275.782l-0.06,0c-26.734,-0.01 -52.954,-7.193 -75.828,-20.767l-5.441,-3.229l-56.386,14.792l15.05,-54.977l-3.542,-5.637c-14.913,-23.72 -22.791,-51.136 -22.779,-79.287c0.033,-82.142 66.867,-148.971 149.046,-148.971c39.793,0.014 77.199,15.531 105.329,43.692c28.128,28.16 43.609,65.592 43.594,105.4c-0.034,82.149 -66.866,148.983 -148.983,148.984Zm81.721,-111.581c-4.479,-2.242 -26.499,-13.075 -30.604,-14.571c-4.105,-1.495 -7.091,-2.241 -10.077,2.241c-2.986,4.483 -11.569,14.572 -14.182,17.562c-2.612,2.988 -5.225,3.364 -9.703,1.12c-4.479,-2.241 -18.91,-6.97 -36.017,-22.23c-13.314,-11.876 -22.304,-26.542 -24.916,-31.026c-2.612,-4.484 -0.279,-6.908 1.963,-9.14c2.016,-2.007 4.48,-5.232 6.719,-7.847c2.24,-2.615 2.986,-4.484 4.479,-7.472c1.493,-2.99 0.747,-5.604 -0.374,-7.846c-1.119,-2.241 -10.077,-24.288 -13.809,-33.256c-3.635,-8.733 -7.327,-7.55 -10.077,-7.688c-2.609,-0.13 -5.598,-0.158 -8.583,-0.158c-2.986,0 -7.839,1.121 -11.944,5.604c-4.105,4.484 -15.675,15.32 -15.675,37.364c0,22.046 16.048,43.342 18.287,46.332c2.24,2.99 31.582,48.227 76.511,67.627c10.685,4.615 19.028,7.371 25.533,9.434c10.728,3.41 20.492,2.929 28.209,1.775c8.605,-1.285 26.499,-10.833 30.231,-21.295c3.732,-10.464 3.732,-19.431 2.612,-21.298c-1.119,-1.869 -4.105,-2.99 -8.583,-5.232Z" />
</svg>
</a>
</li>
<li>
<a target="_blank" rel="noopener noreferrer" aria-label="share ESP8266 Medicine Indicator Light on telegram"
href="https://telegram.me/share/url?text=ESP8266%20Medicine%20Indicator%20Light&amp;url=%2fposts%2fmedicine_indicator_light.html">
<svg version="1.1" xml:space="preserve" viewBox="2 2 28 28" height="30px" width="30px" fill="currentColor">
<path
d="M26.49,29.86H5.5a3.37,3.37,0,0,1-2.47-1,3.35,3.35,0,0,1-1-2.47V5.48A3.36,3.36,0,0,1,3,3,3.37,3.37,0,0,1,5.5,2h21A3.38,3.38,0,0,1,29,3a3.36,3.36,0,0,1,1,2.46V26.37a3.35,3.35,0,0,1-1,2.47A3.38,3.38,0,0,1,26.49,29.86Zm-5.38-6.71a.79.79,0,0,0,.85-.66L24.73,9.24a.55.55,0,0,0-.18-.46.62.62,0,0,0-.41-.17q-.08,0-16.53,6.11a.59.59,0,0,0-.41.59.57.57,0,0,0,.43.52l4,1.24,1.61,4.83a.62.62,0,0,0,.63.43.56.56,0,0,0,.4-.17L16.54,20l4.09,3A.9.9,0,0,0,21.11,23.15ZM13.8,20.71l-1.21-4q8.72-5.55,8.78-5.55c.15,0,.23,0,.23.16a.18.18,0,0,1,0,.06s-2.51,2.3-7.52,6.8Z" />
</svg>
</a>
</li>
<li>
<a target="_blank" rel="noopener noreferrer" aria-label="share ESP8266 Medicine Indicator Light on ycombinator"
href="https://news.ycombinator.com/submitlink?t=ESP8266%20Medicine%20Indicator%20Light&u=%2fposts%2fmedicine_indicator_light.html">
<svg version="1.1" xml:space="preserve" width="30px" height="30px" viewBox="0 0 512 512" fill="currentColor"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
<path
d="M449.446 0C483.971 0 512 28.03 512 62.554L512 449.446C512 483.97 483.97 512 449.446 512L62.554 512C28.03 512 0 483.97 0 449.446L0 62.554C0 28.03 28.029 0 62.554 0L449.446 0ZM183.8767 87.9921H121.8427L230.6673 292.4508V424.0079H281.3328V292.4508L390.1575 87.9921H328.1233L256 238.2489z" />
</svg>
</a>
</li>
</ul>
</footer>
</article>
</main>
<footer class="footer">
<span>&copy; 2024 <a href="../">Norm-working Packets 💾</a></span>
<span>
Powered by
<a href="https://gohugo.io/" rel="noopener noreferrer" target="_blank">Hugo</a> &
<a href="https://github.com/adityatelange/hugo-PaperMod/" rel="noopener" target="_blank">PaperMod</a>
</span>
</footer>
<a href="#top" aria-label="go to top" title="Go to Top (Alt + G)" class="top-link" id="top-link" accesskey="g">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentColor">
<path d="M12 6H0l6-6z" />
</svg>
</a>
<script>
let menu = document.getElementById('menu')
if (menu) {
menu.scrollLeft = localStorage.getItem("menu-scroll-position");
menu.onscroll = function () {
localStorage.setItem("menu-scroll-position", menu.scrollLeft);
}
}
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener("click", function (e) {
e.preventDefault();
var id = this.getAttribute("href").substr(1);
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView({
behavior: "smooth"
});
} else {
document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView();
}
if (id === "top") {
history.replaceState(null, null, " ");
} else {
history.pushState(null, null, `#${id}`);
}
});
});
</script>
<script>
var mybutton = document.getElementById("top-link");
window.onscroll = function () {
if (document.body.scrollTop > 800 || document.documentElement.scrollTop > 800) {
mybutton.style.visibility = "visible";
mybutton.style.opacity = "1";
} else {
mybutton.style.visibility = "hidden";
mybutton.style.opacity = "0";
}
};
</script>
<script>
document.getElementById("theme-toggle").addEventListener("click", () => {
if (document.body.className.includes("dark")) {
document.body.classList.remove('dark');
localStorage.setItem("pref-theme", 'light');
} else {
document.body.classList.add('dark');
localStorage.setItem("pref-theme", 'dark');
}
})
</script>
<script>
document.querySelectorAll('pre > code').forEach((codeblock) => {
const container = codeblock.parentNode.parentNode;
const copybutton = document.createElement('button');
copybutton.classList.add('copy-code');
copybutton.innerHTML = 'copy';
function copyingDone() {
copybutton.innerHTML = 'copied!';
setTimeout(() => {
copybutton.innerHTML = 'copy';
}, 2000);
}
copybutton.addEventListener('click', (cb) => {
if ('clipboard' in navigator) {
navigator.clipboard.writeText(codeblock.textContent);
copyingDone();
return;
}
const range = document.createRange();
range.selectNodeContents(codeblock);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
try {
document.execCommand('copy');
copyingDone();
} catch (e) { };
selection.removeRange(range);
});
if (container.classList.contains("highlight")) {
container.appendChild(copybutton);
} else if (container.parentNode.firstChild == container) {
} else if (codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.nodeName == "TABLE") {
codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.appendChild(copybutton);
} else {
codeblock.parentNode.appendChild(copybutton);
}
});
</script>
</body>
</html>