Created
July 14, 2020 21:33
-
-
Save Jotschi/d25a2fa94bbfc478fa317bfb917236b7 to your computer and use it in GitHub Desktop.
ESP8266 Scale
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <ESP8266WiFi.h> | |
#include "HX711.h" | |
#include <ShiftedLCD.h> | |
#include <SPI.h> | |
#include <ESP8266mDNS.h> | |
#include <WiFiUdp.h> | |
#include <ArduinoOTA.h> | |
#include <ESP8266HTTPClient.h> | |
#include <WiFiClientSecureBearSSL.h> | |
#include <HampelFilter.h> | |
extern "C" { | |
#include "user_interface.h" | |
} | |
const char* ssid = "**********"; | |
const char* password = "***********"; | |
const int httpPort = 80; | |
const float threshold = 80.0; | |
//HTTPClient http; | |
byte mac[6]; | |
const char* apiId = "**********"; | |
const int LOADCELL_DOUT_PIN = 2; | |
const int LOADCELL_SCK_PIN = 4; | |
const int nMEASUREMENTS = 10; | |
// Music stuff | |
const int speakerPin = 5; | |
const int sleepTimeS = 12; | |
String notes[] = {"G4", "G4", "G4", "D#4/Eb4", "A#4/Bb4", "G4", "D#4/Eb4", "A#4/Bb4", "G4", "D5", "D5", "D5", "D#5/Eb5", "A#4/Bb4", "F#4/Gb4", "D#4/Eb4", "A#4/Bb4", "G4", "G5", "G4", "G4", "G5", "F#5/Gb5", "F5", "E5", "D#5/Eb5", "E5", "rest", "G4", "rest", "C#5/Db5", "C5", "B4", "A#4/Bb4", "A4", "A#4/Bb4", "rest", "D#4/Eb4", "rest", "F#4/Gb4", "D#4/Eb4", "A#4/Bb4", "G4" , "D#4/Eb4", "A#4/Bb4", "G4"}; | |
int beats[] = { 8, 8, 8, 6, 2, 8, 6 , 2 , 16 , 8, 8, 8, 6, 2, 8, 6, 2, 16, 8, 6, 2, 8, 6, 2, 2, 2, 2, 6, 2, 2, 8, 6, 2, 2, 2, 2, 6, 2, 2, 9, 6, 2, 8, 6, 2, 16 }; | |
int tempo = 30; | |
int len = sizeof(notes) / sizeof(notes[0]); | |
HX711 scale; | |
LiquidCrystal lcd(15); | |
void topText(String text) { | |
return topText(text, false); | |
} | |
void topText(String text, boolean clear) { | |
if (clear) { | |
lcd.clear(); | |
} | |
lcd.setCursor(0, 0); | |
lcd.print(" "); | |
lcd.setCursor(0, 0); | |
lcd.print(text); | |
} | |
void bottomText(String text) { | |
return bottomText(text, false); | |
} | |
void bottomText(String text, boolean clear) { | |
if (clear) { | |
lcd.clear(); | |
} | |
lcd.setCursor(0, 1); | |
lcd.print(" "); | |
lcd.setCursor(0, 1); | |
lcd.print(text); | |
} | |
void setup() { | |
Serial.begin(115200); | |
Serial.println("Booting"); | |
pinMode(speakerPin, OUTPUT); | |
playInitSound(); | |
// set up the LCD's number of columns and rows: | |
lcd.begin(16, 2); | |
lcd.fadeIn(2024, 100); | |
topText("Booting.", true); | |
delay(150); | |
topText("Booting..", true); | |
delay(150); | |
topText("Booting...", true); | |
delay(150); | |
// Change MAC | |
byte *mac = (byte*)malloc(6); | |
WiFi.macAddress(mac); | |
mac[0] = 0x20; | |
mac[1] = 0x11; | |
mac[2] = 0xCA; | |
mac[3] = 0x11; | |
mac[4] = 0x20; | |
mac[5] = 0xCA; | |
wifi_set_macaddr(STATION_IF, mac); | |
free(mac); | |
printMac(); | |
WiFi.mode(WIFI_STA); | |
Serial.println(); | |
Serial.print("Connecting to "); | |
Serial.println(ssid); | |
WiFi.hostname("scale"); | |
WiFi.begin(ssid, password); | |
while (WiFi.waitForConnectResult() != WL_CONNECTED) { | |
Serial.println("Connection Failed! Rebooting..."); | |
delay(5000); | |
ESP.restart(); | |
} | |
topText("Connected", true); | |
// Port defaults to 8266 | |
// ArduinoOTA.setPort(8266); | |
// Hostname defaults to esp8266-[ChipID] | |
ArduinoOTA.setHostname("scaleESP"); | |
// No authentication by default | |
// ArduinoOTA.setPassword("********"); | |
// Password can be set with it's md5 value as well | |
// MD5(admin) = ************ | |
// ArduinoOTA.setPasswordHash("*************"); | |
ArduinoOTA.onStart([]() { | |
String type; | |
if (ArduinoOTA.getCommand() == U_FLASH) { | |
type = "sketch"; | |
} else { // U_SPIFFS | |
type = "filesystem"; | |
} | |
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() | |
Serial.println("Start updating " + type); | |
lcd.backlightOn(); | |
lcd.display(); | |
topText("Updating..", true); | |
}); | |
ArduinoOTA.onEnd([]() { | |
Serial.println("\nEnd"); | |
lcd.backlightOff(); | |
lcd.clear(); | |
}); | |
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { | |
float val = (progress / (total / 100)); | |
Serial.printf("Progress: %u%%\r", val); | |
topText("Updating: " + String(val, 2), true); | |
}); | |
ArduinoOTA.onError([](ota_error_t error) { | |
Serial.printf("Error[%u]: ", error); | |
if (error == OTA_AUTH_ERROR) { | |
Serial.println("Auth Failed"); | |
} else if (error == OTA_BEGIN_ERROR) { | |
Serial.println("Begin Failed"); | |
} else if (error == OTA_CONNECT_ERROR) { | |
Serial.println("Connect Failed"); | |
} else if (error == OTA_RECEIVE_ERROR) { | |
Serial.println("Receive Failed"); | |
} else if (error == OTA_END_ERROR) { | |
Serial.println("End Failed"); | |
} | |
}); | |
ArduinoOTA.begin(); | |
Serial.println("Ready"); | |
Serial.print("IP address: "); | |
Serial.println(WiFi.localIP()); | |
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN); | |
scale.set_scale(12299.f); //Cal using 12.3kg | |
//2000 = 75 | |
//1000 = 151 | |
//3000 = 50.52 | |
//4000 = 37.85 | |
//6000 = 25.24 | |
//9000 = 16.82 | |
//10000 = 15.13 | |
//scale.tare(); | |
topText("IP: " + WiFi.localIP(), true); | |
} | |
boolean needFadeOut = true; | |
void loop() { | |
ArduinoOTA.handle(); | |
topText(" Ready", true); | |
float measurement = measure(); | |
//printMeasurement(measurement, false); | |
Serial.println(); | |
Serial.print("Measurement: "); | |
Serial.println(measurement); | |
// Check if someone is standing on the scale | |
if (measurement > threshold) { | |
lcd.fadeIn(2024, 100); | |
lcd.display(); | |
needFadeOut = true; | |
topText(" Don't move!", true); | |
bottomText(" Don't move!"); | |
playInitSound(); | |
delay(1500); | |
lcd.clear(); | |
// Collect values | |
HampelFilter dataBuffer = HampelFilter(measurement, nMEASUREMENTS, 0.50); | |
float values[nMEASUREMENTS]; | |
for (int i = 0; i < nMEASUREMENTS; i++) { | |
topText(String(i + 1) + " of " + String(nMEASUREMENTS)); | |
float val = measureAVG(); | |
values[i] = val; | |
dataBuffer.write(val); | |
bottomText("Value: " + String(val, 2)); | |
playNote("G6", 10 * tempo); | |
} | |
float med = dataBuffer.readMedian(); | |
delay(500); | |
lcd.clear(); | |
// Print results | |
int validValues = 0; | |
for (int i = 0; i < nMEASUREMENTS; i++) { | |
float val = values[i]; | |
boolean outlier = dataBuffer.checkIfOutlier(val); | |
delay(500); | |
if (!outlier) { | |
topText("[" + String(i + 1) + "]: " + String(val, 2)); | |
validValues++; | |
} else { | |
bottomText("[" + String(i + 1) + "]: " + String(val, 2) + "*"); | |
} | |
} | |
delay(500); | |
lcd.clear(); | |
topText("Median: " + String(med, 2)); | |
bottomText(String(validValues) + "/" + String(nMEASUREMENTS)); | |
boolean success = handleSend(med); | |
delay(500); | |
if (success) { | |
playSong(); | |
} else { | |
soundFailure(); | |
} | |
playDone(); | |
} | |
if (needFadeOut) { | |
lcd.fadeOut(4024, 200); | |
lcd.noDisplay(); | |
needFadeOut=false; | |
} | |
} | |
boolean handleSend(float med) { | |
if (med > threshold && sendData(med)) { | |
playNote("C4", 6 * tempo); | |
playNote("B4", 7 * tempo); | |
playNote("A4", 8 * tempo); | |
return true; | |
} else { | |
soundFailure(); | |
return false; | |
} | |
} | |
void playTone(int tone, int duration) { | |
for (long i = 0; i < duration * 1000L; i += tone * 2) { | |
digitalWrite(speakerPin, HIGH); | |
delayMicroseconds(tone); | |
digitalWrite(speakerPin, LOW); | |
delayMicroseconds(tone); | |
} | |
} | |
void playNote(String note, int duration) { | |
String noteNames[] = { "D#4/Eb4", "E4", "F4", "F#4/Gb4", "G4", "G#4/Ab4", "A4", "A#4/Bb4", "B4", "C5", "C#5/Db5", "D5", "D#5/Eb5", "E5", "F5", "F#5/Gb5", "G5", "G#5/Ab5", "A5", "A#5/Bb5", "B5", "C6", "C#6/Db6", "D6", "D#6/Eb6", "E6", "F6", "F#6/Gb6", "G6" }; | |
int tones[] = { 1607, 1516, 1431, 1351, 1275, 1203, 1136, 1072, 1012, 955, 901, 851, 803, 758, 715, 675, 637, 601, 568, 536, 506, 477, 450, 425, 401, 379, 357, 337, 318 }; | |
for (int i = 0; i < 29; i++) { | |
if (noteNames[i] == note) { | |
playTone(tones[i], duration); | |
} | |
} | |
} | |
void playSong() { | |
for (int i = 0; i < len; i++) { | |
if (notes[i] == "rest") { | |
delay(beats[i] * tempo); | |
} else { | |
playNote(notes[i], beats[i] * tempo); | |
} | |
delay(tempo / 2); | |
} | |
} | |
void printMac() { | |
String s = WiFi.macAddress(); | |
Serial.print("MACs: "); | |
Serial.println(s); | |
WiFi.macAddress(mac); | |
Serial.print("MAC: "); | |
Serial.print(mac[5], HEX); | |
Serial.print(":"); | |
Serial.print(mac[4], HEX); | |
Serial.print(":"); | |
Serial.print(mac[3], HEX); | |
Serial.print(":"); | |
Serial.print(mac[2], HEX); | |
Serial.print(":"); | |
Serial.print(mac[1], HEX); | |
Serial.print(":"); | |
Serial.println(mac[0], HEX); | |
} | |
const char* host = "script.google.com"; | |
const int httpsPort = 443; | |
bool sendData(float value) { | |
WiFiClientSecure client; | |
Serial.print("connecting to "); | |
Serial.println(host); | |
if (!client.connect(host, httpsPort)) { | |
Serial.println("connection failed"); | |
return false; | |
} | |
String valueStr = String(value, 2); | |
valueStr.replace(".", ","); | |
//String medStr = String(med, 2); | |
//medStr.replace(".", ","); | |
// + "&med=" + medStr | |
String url = "/macros/s/" + String(apiId) + "/exec?wert=" + valueStr; | |
Serial.print("requesting URL: "); | |
Serial.println(url); | |
client.print(String("GET ") + url + " HTTP/1.1\r\n" + | |
"Host: " + host + "\r\n" + | |
"User-Agent: ESPScale\r\n" + | |
"Connection: close\r\n\r\n"); | |
Serial.println("request sent"); | |
boolean firstLine = true; | |
boolean success = false; | |
while (client.connected()) { | |
String line = client.readStringUntil('\n'); | |
if (firstLine) { | |
success = line.startsWith("HTTP/1.1 302"); | |
firstLine = false; | |
} | |
if (line == "\r") { | |
Serial.println("headers received"); | |
break; | |
} | |
} | |
String line = client.readStringUntil('\n'); | |
Serial.println("reply was:"); | |
Serial.println("=========="); | |
Serial.println(line); | |
Serial.println("=========="); | |
Serial.println("closing connection"); | |
return success; | |
} | |
float measureAVG() { | |
scale.power_up(); | |
float measurement = scale.get_units(5); | |
scale.power_down(); | |
if (measurement < 0) { | |
return 0; | |
} | |
return measurement; | |
} | |
float measure() { | |
scale.power_up(); | |
float measurement = scale.get_units(); | |
scale.power_down(); | |
if (measurement < 0) { | |
return 0; | |
} | |
return measurement; | |
} | |
void playDone() { | |
playNote("E4", 8 * tempo); | |
delay(tempo / 2); | |
playNote("E4", 8 * tempo); | |
delay(tempo / 2); | |
playNote("E4", 8 * tempo); | |
} | |
void playInitSound() { | |
playNote("E4", 8 * tempo); | |
delay(tempo / 2); | |
playNote("F4", 8 * tempo); | |
delay(tempo / 2); | |
playNote("G4", 8 * tempo); | |
} | |
void soundFailure() { | |
playNote("G4", 8 * tempo); | |
delay(tempo / 2); | |
playNote("F4", 8 * tempo); | |
delay(tempo / 2); | |
playNote("E4", 8 * tempo); | |
delay(tempo / 2); | |
playNote("G4", 8 * tempo); | |
delay(tempo / 2); | |
playNote("F4", 8 * tempo); | |
delay(tempo / 2); | |
playNote("E4", 8 * tempo); | |
delay(tempo / 2); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment