Skip to content

Instantly share code, notes, and snippets.

@mostlygeek
Created January 2, 2022 23:51
Show Gist options
  • Save mostlygeek/b1fd63fbb3bb962bd7df03239351de3d to your computer and use it in GitHub Desktop.
Save mostlygeek/b1fd63fbb3bb962bd7df03239351de3d to your computer and use it in GitHub Desktop.
Code for a little robot that open/closes a door in my AV console when it gets too hot inside.
#include <OneWire.h>
#include <DallasTemperature.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
// *** -= WIFI & Web =- **************************************
ESP8266WebServer server(80); // listen on port 80
// ***********************************************************
// *** -= L298n MOTOR CONTROLLER =- **************************
// L298n motor controller
const int ACTUATOR_PIN1 = D3;
const int ACTUATOR_PIN2 = D4;
const int FAN_PIN1 = D5;
const int FAN_PIN2 = D6;
const int ACTUATOR_ON = 5800; // ms to move actuator
// *** -= DS18B20 THERMAL PROBE =- ***************************
OneWire oneWire(D2);
DallasTemperature sensors(&oneWire);
// ***********************************************************
// *** -= SYSTEM STATE =- ************************************
const float TEMP_LOW_THRESHOLD = 24.5;
const float TEMP_HIGH_THRESHOLD = 27.0; // pretty hot
const byte STATE_OFF = 0; // auto, default state
const byte STATE_COOLING_AUTO = 1; // auto cooling - on
const byte STATE_COOLING_HOLD = 2; // cooling, hold until timeout
// how frequently to check temperature
const unsigned int CHECK_TEMP_TIMEOUT = 1000;
const unsigned int HOLD_COOLING_TIMEOUT = 1000 * 60 * 15; // 15 minutes
// Global state variables
int gState = STATE_OFF;
float gTemp = 0; // temperature in celcius
unsigned long gLastTempCheck = 0;
unsigned long gCoolingHoldStart = 0;
// ***********************************************************
void setup() {
// Start the Serial Monitor
Serial.begin(115200);
// Start the DS18B20 sensor
sensors.begin();
pinMode(ACTUATOR_PIN1, OUTPUT);
pinMode(ACTUATOR_PIN2, OUTPUT);
pinMode(FAN_PIN1, OUTPUT);
pinMode(FAN_PIN2, OUTPUT);
// reset to default initial state
fanOff();
closeDoor();
// setup WiFI
WiFi.softAP(WIFI_SSID, WIFI_PW);
// WIFI gateway can be same as wifi IP. No routing anyways.
WiFi.softAPConfig(WIFI_IP, WIFI_IP, WIFI_SN);
delay(100);
server.on("/", handle_OnHome);
server.on("/toggle", handle_OnHoldCooling);
server.on("/info", handle_GetInfo);
server.onNotFound(handle_NotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
updateTemp();
updateState();
}
void updateTemp() {
// update temperature value
unsigned long now = millis();
if (abs(now - gLastTempCheck) >= CHECK_TEMP_TIMEOUT) {
gLastTempCheck = now;
gTemp = getTemperature();
Serial.print("TEMP: ");
Serial.print(gTemp);
Serial.print("ºC | High: ");
Serial.print(TEMP_HIGH_THRESHOLD);
Serial.print("ºC (");
Serial.print(TEMP_HIGH_THRESHOLD - gTemp);
Serial.print("ºC) | Low: ");
Serial.print(TEMP_LOW_THRESHOLD);
Serial.print("ºC (");
Serial.print(gTemp - TEMP_LOW_THRESHOLD);
Serial.println("ºC)");
}
}
// does state transitions
void updateState() {
// STATE_OFF -> STATE_COOLING_AUTO
if (gState == STATE_OFF && gTemp >= TEMP_HIGH_THRESHOLD) {
Serial.println("STATE_OFF -> STATE_COOLING_AUTO");
setState(STATE_COOLING_AUTO);
return;
}
// STATE_COOLING_AUTO -> STATE_OFF
if (gState == STATE_COOLING_AUTO && gTemp <= TEMP_LOW_THRESHOLD ) {
Serial.println("STATE_COOLING_AUTO -> STATE_OFF");
setState(STATE_OFF);
return;
}
// STATE_COOLING_HOLD -> STATE_OFF
if (gState == STATE_COOLING_HOLD) {
unsigned long now = millis();
if (abs(now - gCoolingHoldStart) >= HOLD_COOLING_TIMEOUT) {
Serial.println("STATE_COOLING_HOLD -> STATE_OFF");
setState(STATE_OFF);
}
return;
}
}
void setState(byte newState) {
if (newState == gState) {
return;
}
switch (newState) {
case STATE_OFF:
fanOff();
closeDoor();
gState = STATE_OFF;
Serial.println("New State: STATE_OFF");
break;
case STATE_COOLING_AUTO:
fanOn();
openDoor();
gState = STATE_COOLING_AUTO;
Serial.println("New State: STATE_COOLING_AUTO");
break;
case STATE_COOLING_HOLD:
fanOn();
openDoor();
gState = STATE_COOLING_HOLD;
gCoolingHoldStart = millis();
Serial.println("New State: STATE_COOLING_HOLD");
break;
}
}
float getTemperature() {
sensors.requestTemperatures();
return sensors.getTempCByIndex(0);
}
void fanOn() {
digitalWrite(FAN_PIN1, HIGH); // spin in correct direction
digitalWrite(FAN_PIN2, LOW);
}
void fanOff() {
digitalWrite(FAN_PIN1, LOW);
digitalWrite(FAN_PIN2, LOW);
}
void openDoor() {
digitalWrite(ACTUATOR_PIN1, LOW);
digitalWrite(ACTUATOR_PIN2, HIGH);
}
void closeDoor() {
digitalWrite(ACTUATOR_PIN1, HIGH);
digitalWrite(ACTUATOR_PIN2, LOW);
}
void handle_OnHome() {
server.send(200, "text/html", SendHTML());
}
void handle_OnHoldCooling() {
// send http response before making the changes to state
server.sendHeader("Location", "/");
server.send(303, "text/plain", "OK");
if (gState == STATE_COOLING_HOLD) {
setState(STATE_OFF);
} else {
setState(STATE_COOLING_HOLD);
}
}
void handle_GetInfo() {
String out = String(gTemp) + "|";
if (gState == STATE_OFF) {
out += "OFF";
} else if (gState == STATE_COOLING_AUTO) {
out += "Cooling Auto";
} else if (gState == STATE_COOLING_HOLD) {
float remain = (HOLD_COOLING_TIMEOUT - (millis() - gCoolingHoldStart))/1000;
out += "Cooling Hold for " + String(remain) + "s";
}
server.send(200, "text/plain", out);
}
void handle_NotFound() {
server.send(404, "text/plain", "Not found");
}
String SendHTML() {
String ptr = "<!DOCTYPE html> <html>\n";
ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
ptr += "<title>Cooler Bot - 192.168.1.1</title>\n";
ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
ptr += ".button {display: block;width: 160px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
ptr += ".button:active {background-color: #16a085;}\n";
ptr += ".button-off {background-color: #990000;}\n";
ptr += ".button-off:active {background-color: #800000;}\n";
ptr += ".button:active {background-color: #16a085;}\n";
ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
ptr += "table {width: 220px; margin-left: auto; margin-right: auto;}\n";
ptr += "td { border-bottom: 1px solid black; }\n";
ptr += "td.a { text-align: right; font-weight: bold; }\n";
ptr += "</style>\n";
// background update the temperature
ptr += "<script>\n";
ptr += "function update() { \n";
ptr += " fetch(\"/info\").then(resp => {\n";
ptr += " resp.text().then(d => {\n";
ptr += " var s = d.split(\"|\");\n";
ptr += " document.getElementById(\"curtemp\").innerText=s[0];\n";
ptr += " document.getElementById(\"state\").innerText=s[1];\n";
ptr += " setTimeout(update, 1000);\n"; // set another automatic update on success
ptr += " })\n";
ptr += " })\n";
ptr += "}\n";
ptr += "update();\n"; // start it running
ptr += "</script>\n";
ptr += "</head>\n";
ptr += "<body>\n";
ptr += "<h1>Cooler Bot</h1>\n";
ptr += "<h3>State: <span id=state>--</span></h3>";
ptr += "<table>\n<tr><td class=a>Low</td><td>"+String(TEMP_LOW_THRESHOLD) + "&deg;C</td></tr>\n";
ptr += "<tr><td class=a>Current</td><td><span id=curtemp>--</span>&deg;C</td></tr>\n";
ptr += "<tr><td class=a>High</td><td>"+ String(TEMP_HIGH_THRESHOLD)+"&deg;C</td></tr>\n";
ptr += "</table>\n";
if (gState == STATE_COOLING_HOLD) {
ptr += "<p><a class=\"button button-off\" href=\"/toggle\">STOP HOLD</a></p>";
} else {
ptr += "<p><a class=\"button\" href=\"/toggle\">HOLD COOLING</a></p>";
}
ptr += "</body>\n";
ptr += "</html>\n";
return ptr;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment