Es gibt ein Update zum meinem Waschmaschinenprojekt. Zur Erinnerung: Es ging darum, bei einer recht alten Waschmaschine eine Benachrichtigung am Handy zu bekommen, sobald der Waschvorgang beendet ist.
Die Spielerei hat anfangs recht gut funktioniert, im Laufe der Zeit jedoch immer schlechter. Meine Erklärung: Der Vibrationssensor hat der Belastung nicht standgehalten. Der erste Sensor ging wirklich kaputt, ein zweiter wurde auch laufend schlechter. Schlechter bedeutet, dass die Werte immer mehr streuten und daher das zeitliche Vibrationsprofil, über das der letzte Schleudervorgang und damit das Ende des Waschvorgangs detektiert wurde, immer verrauschter und uneindeutiger wurde. Es gab Fehlalarme.

3-Achsen-Beschleunigungssensor GY-61

Über ein anderes Bastelprojekt bin ich dann auf einen einfachen 3-Achsen-Beschleunigungssensor gestoßen, den GY-61. Der gibt an drei analogen Ausgängen einfach die Beschleunigung für drei orthogonale Achsen (x, y, z) aus. Im Prinzip so ein Teil, welches heute in jedem Smartphone z.B. die Bildschirmorientierung steuert. Gravitation ist ja auch nichts anderes als eine Beschleunigung; dreht man das Handy, dann wirkt diese Beschleunigung (zumindest anteilig) in eine andere Richtung.
Ich habe nun den alten Vibrationssensor durch den neuen Beschleunigungssensor ersetzt. Von den drei Achsen kann ich allerdings nur ein einzige nutzen, weil der Wemos D1 mini nur einen analogen Eingang bietet. Hier ein paar Bilder vom Umbau:
#gallery-1 {
margin: auto;
}
#gallery-1 .gallery-item {
float: left;
margin-top: 10px;
text-align: center;
width: 25%;
}
#gallery-1 img {
border: 2px solid #cfcfcf;
}
#gallery-1 .gallery-caption {
margin-left: 0;
}
/* see gallery_shortcode() in wp-includes/media.php */

Die Werte, die der Sensor in den drei Schleuderphasen der Waschmaschine liefert sind um Welten besser als das, was der Vibrationssensor jemals geliefert hat. Hier der Plot eines typischen Waschvorgangs:
Die Werte werden für mich zur Kontrolle an meinen Raspberry Pi geschickt und mit Hilfe der Bibliothek dygraphs geplottet. Blau: Beschleunigung. Rot: Der Schwellwert. Gelb: Dauer über dem Schwellwert. Rot: Auslösen der Benachrichtigung.

Man sieht hier schön, dass der letzte Schleudergang stärker und vor allem länger ist als die ersten beiden. Seit dem Einbau des neuen Sensors wurde damit das Ende des Waschvorgangs hundertprozentig richtig erkannt.
Ach ja, und hier noch der aktualisierte Code, falls der jemanden interessiert:

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

// PIN config:
#define PinVibration A0
// #define PinVibration D3
#define PinReset D0

// timing varaibles:
unsigned long delay_between_measurements = 50; // ms
unsigned long averaging_time = 4500; // ms
unsigned long time_between_data_points = 5000; // ms => 12 measurement points per minute
unsigned long notification_delay = 4; // min

// other variables:
double vibration = 0.0; // vibration value
double vibration_log_thres = 20.0; // if vibration is higher than this value a log entry is created
double vibration_thres = 100.0; // if vibration is higher than this value the last vibration_last[] entry is set to 1
// Erfahrungswerte:
//
// 120 ist zu hoch, das liefert z.B. bei Bettwäsche schon mal keinen Wert, die läuft sehr rund
// 80 ist zu niedrig, das löst ggf. gerade so 2x aus.
bool vibration_last [28]; // => array over the last 2.xx minutes (depends on variable time_between_data_points)
unsigned long means = 0; // number of means per measurement
unsigned long notification_delay_start = 0; //start time of notification delay (milliseconds)

// WIFI credentials:
const char* wifi_host = „Waschmaschine“;
const char* wifi_ssid = „MY_SSID“;
const char* wifi_password = „MY_WIFI_PASSWORD“;

// define URLs (BTW: use HTTP instead of HTTPS!):
String URL_log = „http://MY_URL/waschmaschine/“;
String URL_notification[] = {
„http://maker.ifttt.com/trigger/waschmaschinefertig/with/key/MY_KEY_1“,
„http://maker.ifttt.com/trigger/waschmaschinefertig/with/key/MY_KEY_2“,
};
// we need this line to walk through the string loop using „ArraySize()“:
template< typename T, size_t N > size_t ArraySize (T (&) [N]){ return N; } // see http://forum.arduino.cc/index.php?topic=157398.0

void setup() {

// set PINs and PIN modes:
pinMode(PinVibration, INPUT);
pinMode(PinReset, OUTPUT);
digitalWrite(PinReset, HIGH);
pinMode(BUILTIN_LED, OUTPUT);

// start serial output:
Serial.begin(115200);

// connect to WiFi network:
connect2Wifi();
}

void loop() {

// measure a vibration value:
measure_vibration();

// log the vibration value:
if (vibration > vibration_log_thres || sum_over_thres() >= 1) {
send_log();
}

// start notification delay if end of long vibration is detected:
if (notification_delay_start == 0 && sum_over_thres() == sizeof(vibration_last)) {
notification_delay_start = millis();
Serial.println(„[Notification] End of spinning detected!“);
Serial.println(„[Notification] Start sending a notification in “ + (String)notification_delay + “ minutes.“);

}

// send notification if end of notification delay is reached:
if (notification_delay_start > 0 && millis() – notification_delay_start >= notification_delay * 60 * 1000) {
notification_delay_start = 0;
send_notification();

// Sometimes the board does no longer respond, maybe due to an overflow of the millis-varaibles.
// Try to reset the board 10s after sending the notifications:
// RESET the board:
delay(10*1000);
digitalWrite(PinReset, LOW);

}

// wait rest of the time:
delay(time_between_data_points – averaging_time);

}

///////////////////// function: measure the vibration value
void measure_vibration() {
Serial.println(„=================================================================================“);
Serial.printf(„[Vibration] Starting vibration measurement for %.1f seconds“, averaging_time / 1000.);

vibration = 0;
means = 0;
unsigned long starttime = millis();
/*while (millis() – starttime < averaging_time) {
means ++;
if (means % 10 == 0) Serial.print(„.“);
vibration = vibration + (analogRead(PinVibration) – vibration) / means;
// delay(10);
// vibration = vibration + (pulseIn(PinVibration, HIGH) – vibration) / means; // see http://www.theorycircuit.com/sw-420-vibration-sensor-arduino-interface/
delay(delay_between_measurements-10);
}*/
int x = 0;
int xlast = 0;
int xdiff = 0;
while (millis() – starttime < averaging_time) {
means ++;
if (means % 10 == 0) Serial.print(„.“);
x = analogRead(PinVibration);
xdiff = abs(x – xlast);
vibration = vibration + (xdiff – vibration) / means;
xlast = x;
delay(delay_between_measurements-10);
}

// explanation:
// we use an bool array that contains zeros.
// with every measurement the values of the bool array are shifted one step to the end
// if a vibration measurement gives a value larger than the threshold => write a 1 to the first position of the bool array.
// if the washing machine is spinning the array is filled with ones.
// if the sum over the array values exceeds a defined value the notifications are sent
shift_vibration_array();
if (vibration >= vibration_thres) {
vibration_last[0] = 1;
}

Serial.println();
Serial.print( „[Vibration] vibration = „); Serial.print(vibration); Serial.println();
Serial.print( „[Vibration] vibration_log_thres = „); Serial.print(vibration_log_thres); Serial.println();
Serial.print( „[Vibration] vibration_thres = „); Serial.print(vibration_thres); Serial.println();
Serial.printf( „[Vibration] means = %in“, means);
Serial.println(„[Vibration] vibration_last = “ + gen_vibration_string());
Serial.println(„[Vibration] progress = “ + (String)sum_over_thres() + „/“ + (String)sizeof(vibration_last));

}

///////////////////// function: send notification(s)
void send_notification() {

// Serial.println(„[Notification] End of spinning detected!“);
// Serial.println(„[Notification] Start sending a notification in “ + (String)notification_delay + “ minutes.“);
Serial.println(„[Notification] Sending the notification(s).“);
//delay(notification_delay * 60 * 1000);

// send notifications:
for (int i = 0; i < ArraySize(URL_notification); i++) {
Serial.println(„[Notification] Sending notification #“ + (String)(i+1) + „:“);
send_HTTP_request(URL_notification[i]);
}

// reset the vibration array (to avoid multiple alarms):
for (int i = 0; i < sizeof(vibration_last); i++) { vibration_last[i] = 0; }
}

///////////////////// function: send log values to server
void send_log() {

send_HTTP_request(URL_log +
„?means=“ + (String)means +
„&vibration=“ + (String)vibration +
„&vibration_log_thres=“ + (String)vibration_log_thres +
„&vibration_thres=“ + (String)vibration_thres +
„&vibration_last=“ + gen_vibration_string() +
„&sum_over_thres=“ + (String)sum_over_thres() +
„&mac=“ + WiFi.macAddress() +
„&hostname=“ + WiFi.hostname() +
„&ip=“ + WiFi.localIP().toString() +
„“);
}

///////////////////// function: send a http request
void send_HTTP_request(String url) {

HTTPClient http;

Serial.println(„[HTTP] Request URL: “ + url);
http.begin(url);

// start connection and send HTTP header
int httpCode = http.GET();

if (httpCode > 0) { // httpCode will be negative on error
Serial.printf(„[HTTP] Return code: %dn“, httpCode);

if (httpCode == HTTP_CODE_OK) { Serial.println(http.getString()); }
}
else {
Serial.printf(„[HTTP] GET… failed, error: %sn“, http.errorToString(httpCode).c_str());
}

http.end();
}

///////////////////// function: shift the values of the vobration array
void shift_vibration_array() {
// move all array elements one step to the end
// the first element is set to zero
for (int i = sizeof(vibration_last) – 1; i > 0; i–) {
vibration_last[i] = vibration_last[i – 1];
}
vibration_last[0] = 0;
}

///////////////////// function: calculate the sum of the vibration array
int sum_over_thres() {
// calculate the sum of values 1 in the vibration array
int vibration_sum = 0;
for (int i = 0; i < sizeof(vibration_last); i++) {
vibration_sum += vibration_last[i];
}
return vibration_sum;
}

///////////////////// function: return the values of the vibration array as string
String gen_vibration_string() {

String vibration_string = „“;
for (int i = 0; i < sizeof(vibration_last); i++) {
vibration_string = vibration_string + (String)vibration_last[i];
}
return vibration_string;
}

///////////////////// function: establish the WiFi connection
void connect2Wifi() {
Serial.println();
Serial.printf(„n[WiFi] Connecting to SSID ‚%s‘ „, wifi_ssid);

WiFi.mode(WIFI_STA);
WiFi.hostname(wifi_host);
WiFi.begin(wifi_ssid, wifi_password);

while (WiFi.status() != WL_CONNECTED) {
digitalWrite(BUILTIN_LED, LOW);
delay(100);
Serial.print(„.“);
digitalWrite(BUILTIN_LED, HIGH);
delay(100);
}
Serial.println(“ Connected!“);
Serial.println(„[WiFi] Hostname: “ + WiFi.hostname());
Serial.println(„[WiFi] MAC: “ + WiFi.macAddress());
Serial.println(„[WiFi] IP: “ + WiFi.localIP().toString());
Serial.println();

digitalWrite(BUILTIN_LED, LOW);
}