Blynk Board Washer/Dryer Alarm
Program the Blynk Board
If you haven’t set your computer up to program the Blynk Board using Arduino yet, read through our Blynk Board Arduino Development Guide first. Following that, you should have the Blynk Board hardware definitions and most of the libraries you’ll need added to your Arduino IDE.
Install the SparkFun MMA8452Q Arduino Library
The latest version of our MMA8452Q Arduino library can be downloaded from the SparkFun_MMA8452Q_Arduino_Library GitHub repository (click the "Download ZIP" button there). Or you can skip the detour, by clicking the button below.
The library will be downloaded as a ZIP file. To install it in Arduino, navigate to Sketch > Include Library > Add .ZIP Library.... Then select the MMA8452Q ZIP folder you just downloaded.
Copy/Paste the Code
Our "Blynk Board Laundry Monitor" source code is also available on GitHub. Download it from there, or copy/paste from the box below:
////////// // WiFi // ////////// // Enter your WiFi credentials here: const char WiFiSSID[] = "WiFiNetworkName"; const char WiFiPSWD[] = "WiFiPassword"; /////////// // Blynk // /////////// // Your Blynk auth token here const char BlynkAuth[] = "0a1b2c3d4e5f"; bool notifyFlag = false; WidgetLCD lcd(VIRTUAL_LCD); bool pushEnabled = false; void printLaundryTime(void); ///////////////////// // Shake Detection // ///////////////////// unsigned int shakeThreshold = 50; unsigned int shakeStartTimeHysteresis = 1000; unsigned int shakeStopTimeHysteresis = 10; unsigned long shakeStateChangeTime = 0; unsigned long shakeStartTime = 0; bool loadTimer = true; enum { NO_SHAKING_LONG, // Haven't been shaking for a long time NO_SHAKING, // Hasn't been any shaking PRE_SHAKING, // Started shaking, pre-hysteresis SHAKING, // Currently shaking POST_SHAKING // Stopped shaking, pre-hysteresis } shakingState = NO_SHAKING; // Possible return values from the shake sensor enum sensorShakeReturn { SENSOR_SHAKING, SENSOR_NOT_SHAKING, SENSOR_NOT_READY }; sensorShakeReturn checkShake(void); void shakeLoop(void); //////////////////////////// // MMA8452Q Accelerometer // //////////////////////////// MMA8452Q accel; int16_t lastX, lastY, lastZ; void initAccel(void); ////////////////////////// // Hardware Definitions // ////////////////////////// const int LED_PIN = 5; const int RGB_PIN = 4; Adafruit_NeoPixel rgb = Adafruit_NeoPixel(1, RGB_PIN, NEO_GRB + NEO_KHZ800); void setLED(uint8_t red, uint8_t green, uint8_t blue); void setup() { Serial.begin(9600); pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); // Turn off blue LED rgb.begin(); // Set up WS2812 initAccel(); // Set up accelerometer setLED(0, 0, 32); // LED blue // Initialize Blynk, and wait for a connection before doing anything else Serial.println("Connecting to WiFi and Blynk"); Blynk.begin(BlynkAuth, WiFiSSID, WiFiPSWD); while (!Blynk.connected()) Blynk.run(); Serial.println("Blynk connected! Laundry monitor starting."); setLED(0, 32, 0); // LED green } void loop() { shakeLoop(); // Check if we're shaking, and update variables accordingly Blynk.run(); // Blynk run as much as possible } bool firstConnect = true; BLYNK_CONNECTED() { if (firstConnect) // When we first connect to Blynk { // Two options here. Either sync values from phone to Blynk Board: //Blynk.syncAll(); // Uncomment to enable. // Or set phone variables to default values of the globals: Blynk.virtualWrite(VIRTUAL_SHAKE_THRESHOLD, shakeThreshold); Blynk.virtualWrite(VIRTUAL_STOP_TIME, shakeStopTimeHysteresis); Blynk.virtualWrite(VIRTUAL_START_TIME, shakeStartTimeHysteresis); Blynk.virtualWrite(VIRTUAL_ENABLE_PUSH, pushEnabled); // Print a splash screen: lcd.clear(); lcd.print(0, 0, "Laundry Monitor "); lcd.print(0, 1, " Ready "); } } BLYNK_WRITE(VIRTUAL_ENABLE_PUSH) { int enable = param.asInt(); if (enable > 0) { pushEnabled = true; Serial.println("Push notification enabled"); } else { pushEnabled = false; Serial.println("Push notification disabled"); } } BLYNK_WRITE(VIRTUAL_SHAKE_THRESHOLD) { int inputThreshold = param.asInt(); shakeThreshold = constrain(inputThreshold, 1, 2048); Serial.println("Shake threshold set to: " + String(shakeThreshold)); } BLYNK_WRITE(VIRTUAL_START_TIME) { int inputStartTime = param.asInt(); if (inputStartTime <= 0) inputStartTime = 1; shakeStartTimeHysteresis = inputStartTime; Serial.println("Shake start time set to: " + String(shakeStartTimeHysteresis) + " ms"); } BLYNK_WRITE(VIRTUAL_STOP_TIME) { int inputStopTime = param.asInt(); if (inputStopTime <= 0) inputStopTime = 1; shakeStopTimeHysteresis = inputStopTime; Serial.println("Shake stop time set to: " + String(shakeStopTimeHysteresis) + " seconds"); } void printLaundryTime(void) { unsigned long runTime = millis() - shakeStartTime; int runSeconds = (runTime / 1000) % 60; int runMinutes = ((runTime / 1000) / 60) % 60; int runHours = ((runTime / 1000) / 60 ) / 60; // Create a string like HHHH:MM:SS String runTimeString = " " + String(runHours) + ":"; if (runMinutes < 10) runTimeString += "0"; // Leading 0 minutes runTimeString += String(runMinutes) + ":"; if (runSeconds < 10) runTimeString += "0"; // Leading 0 seconds runTimeString += String(runSeconds); // Fill out the rest of the string to 16 chars int lineLength = runTimeString.length(); for (int i=lineLength; i<16; i++) { runTimeString += " "; } if (shakingState == PRE_SHAKING) lcd.print(0, 0, "Laundry starting"); else if (shakingState == SHAKING) lcd.print(0, 0, "Laundry running "); else if (shakingState == NO_SHAKING) lcd.print(0, 0, "Laundry stopping"); else if (shakingState == NO_SHAKING_LONG) lcd.print(0, 0, "Laundry done! "); lcd.print(0, 1, runTimeString); } void shakeLoop(void) { sensorShakeReturn sensorState = checkShake(); if (sensorState == SENSOR_SHAKING) // If the sensor is shaking { switch (shakingState) { case NO_SHAKING_LONG: case NO_SHAKING: // If we haven't been shaking setLED(32, 0, 32); // LED purple shakingState = PRE_SHAKING; // Set mode to pre-shaking shakeStateChangeTime = millis(); // Log state change time if (loadTimer) { loadTimer = false; shakeStartTime = millis(); // Log time we started shaking } printLaundryTime(); break; case PRE_SHAKING: // If we're pre-hysteresis shaking if (millis() - shakeStateChangeTime >= shakeStartTimeHysteresis) { // If we've passed hysteresis time shakingState = SHAKING; // Set mode to shaking digitalWrite(LED_PIN, HIGH); // Turn blue LED on Serial.println("Shaking!"); notifyFlag = true; // Flag that we need to notify when shaking stops setLED(32, 0, 0); // LED red } break; case SHAKING: // If we're already shaking printLaundryTime(); // Update laundry timer break; // Do nothing case POST_SHAKING: // If we didn't stop shaking before hysteresis shakingState = SHAKING; // Go back to shaking break; } } else if (sensorState == SENSOR_NOT_SHAKING) // If the sensor is not shaking { switch (shakingState) { case NO_SHAKING_LONG: // If we haven't been shaking for a long time break; // Do nothing case NO_SHAKING: // If we haven't been shaking if (millis() - shakeStateChangeTime >= (shakeStopTimeHysteresis * 1000)) { // Check if it's been a long time setLED(0, 32, 0); // Turn LED green shakingState = NO_SHAKING_LONG; if (notifyFlag == true) // If notify flag was set during shaking { loadTimer = true; printLaundryTime(); // Update LCD notifyFlag = false; // Clear notify flag if (pushEnabled) // If push is enabled Blynk.notify("Washer/dryer is done!"); // Notify! } } break; case PRE_SHAKING: // If we're pre-hysteresis shaking shakingState = NO_SHAKING; // Go back to no shaking setLED(0, 32, 0); break; case SHAKING: // If we're already shaking shakingState = POST_SHAKING; // Go to hysteresis cooldown shakeStateChangeTime = millis(); break; // Do nothing case POST_SHAKING: // If we're in the shake cooldown state if (millis() - shakeStateChangeTime >= shakeStartTimeHysteresis) { digitalWrite(5, LOW); // LED off shakingState = NO_SHAKING; printLaundryTime(); setLED(32, 16, 0); Serial.println("Stopped."); } break; } } } sensorShakeReturn checkShake(void) { static unsigned long lastShakeCheck = 0; float shake = 0; if (accel.available()) // If new accel data is available { int16_t x, y, z; accel.read(); // read the data in x = accel.x; y = accel.y; z = accel.z; // To determine if we're shaking, compare the sum of // x,y,z accels to the sum of the previous accels. shake = abs(x + y + z - lastX - lastY - lastZ); // Write the value to Blynk: Blynk.virtualWrite(VIRTUAL_SHAKE_VALUE, shake); // Update previous values: lastX = x; lastY = y; lastZ = z; } else // If sensore didn't have new data { // Return not ready return SENSOR_NOT_READY; } // If shake value exceeded threshold if (shake >= shakeThreshold) return SENSOR_SHAKING; // Return "shaking" else return SENSOR_NOT_SHAKING; // Or return "not shaking" } void initAccel(void) { // Use a slow update rate to throttle the shake sensor. // ODR_6 will set the accelerometer's update rate to 6Hz // Use +/-2g scale -- the lowest -- to get most sensitivity accel.init(SCALE_2G, ODR_6); // Initialize accelerometer while (!accel.available()) // Wait for data to be available yield(); // Let the system do other things accel.read(); // Read data from accel lastX = accel.x; // Initialize last values lastY = accel.y; lastZ = accel.z; } void setLED(uint8_t red, uint8_t green, uint8_t blue) { rgb.setPixelColor(0, rgb.Color(red, green, blue)); rgb.show(); }
Before uploading, however, you'll need to adjust a few strings.
Configure Your WiFi Network
Near the top of the sketch, you'll find a couple string constants named WiFiSSID
and WiFiPSWD
. Change the WiFiNetworkName
and WiFiPassword
values to match your WiFi network name (SSID) and password.
const char WiFiSSID[] = "WiFiNetworkName"; const char WiFiPSWD[] = "WiFiPassword";
Configure Your Blynk Token
Next, you'll need to configure the BlynkAuth
string to match your Blynk auth token. You can find this in the settings section of your Blynk project -- click the hexagon-shaped "nut" icon in the upper-right.
Use the E-Mail feature to send yourself a copy of the auth token, which you can copy and paste into the code:
const char BlynkAuth[] = "0a1b2c3d4e5f..."
Upload the Code
Finally, upload the code -- making sure your serial port is correctly selected and the board type is set to SparkFun Blynk Board.