Skip to content

Instantly share code, notes, and snippets.

@Jotschi
Created July 14, 2020 21:33
Show Gist options
  • Save Jotschi/d25a2fa94bbfc478fa317bfb917236b7 to your computer and use it in GitHub Desktop.
Save Jotschi/d25a2fa94bbfc478fa317bfb917236b7 to your computer and use it in GitHub Desktop.
ESP8266 Scale
#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