├── LICENSE.md ├── Hardware └── SparkFun-Blynk-Board-ESP8266-top-dimensions.pdf ├── .gitignore ├── Firmware ├── Examples │ ├── Bridge_Example │ │ ├── README.md │ │ ├── Bridge_Example_2 │ │ │ └── Bridge_Example_2.ino │ │ └── Bridge_Example_1 │ │ │ └── Bridge_Example_1.ino │ └── Laundry_Monitor │ │ └── Laundry_Monitor.ino ├── README.md └── BlynkBoard_Core_Firmware │ ├── BlynkBoard_settings.h │ ├── BlynkBoard_Setup.ino │ ├── BlynkBoard_Core_Firmware.ino │ ├── BlynkBoard_ConfigMode.ino │ └── BlynkBoard_BlynkMode.ino └── README.md /LICENSE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkfun/Blynk_Board_ESP8266/HEAD/LICENSE.md -------------------------------------------------------------------------------- /Hardware/SparkFun-Blynk-Board-ESP8266-top-dimensions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkfun/Blynk_Board_ESP8266/HEAD/Hardware/SparkFun-Blynk-Board-ESP8266-top-dimensions.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore list for Eagle, a PCB layout tool 2 | 3 | # Backup files 4 | *.s#? 5 | *.b#? 6 | *.l#? 7 | 8 | # Eagle project file 9 | # It contains a serial number and references to the file structure 10 | # on your computer. 11 | # comment the following line if you want to have your project file included. 12 | eagle.epf 13 | 14 | # CAM files 15 | *.$$$ 16 | *.cmp 17 | *.ly2 18 | *.l15 19 | *.sol 20 | *.plc 21 | *.stc 22 | *.sts 23 | *.crc 24 | *.crs 25 | 26 | *.dri 27 | *.drl 28 | *.gpi 29 | *.pls 30 | 31 | *.drd 32 | *.drd.* 33 | 34 | *.info 35 | 36 | *.eps 37 | -------------------------------------------------------------------------------- /Firmware/Examples/Bridge_Example/README.md: -------------------------------------------------------------------------------- 1 | These two example sketches are nearly identical. The key difference is the 2 | authentication tokens will be swapped in the two. This example code for the 3 | SparkFun Blynk Board demonstrates the functionality of the Bridge widget. 4 | Using two of these boards to send signals from one board to the other, or from 5 | one connected mobile device to either Blynk Board or another mobile device if 6 | available. Simple wireless buttons and a more complex chat app are part of this 7 | demo. 8 | 9 | For more information please see the [tutorial](https://learn.sparkfun.com/tutorials/blynk-board-bridge-widget-demo/) on the SparkFun website. 10 | -------------------------------------------------------------------------------- /Firmware/README.md: -------------------------------------------------------------------------------- 1 | # SparkFun Blynk Board - ESP8266 Core Firmware 2 | 3 | The [SparkFun Blynk Board - ESP8266](https://www.sparkfun.com/products/13794) firmware allows you to configure a Blynk Board's WiFi network and Blynk auth token. Then, once the board is equipped with network access and a connection to the Blynk Cloud, the firmware is equipped with ten Blynk experiments, which allows you to test out the Blynk Board and the Blynk app without ever bothering to re-program the board's ESP8266 WiFi/microcontroller. 4 | 5 | ### Firmware Contents 6 | 7 | To better navigate the firmware's functionality, the source code is divided into a number of separate files: 8 | 9 | * **[BlynkBoard_Core_Firmware.ino](https://github.com/sparkfun/Blynk_Board_ESP8266/Firmware/BlynkBoard_Core_Firmware.ino)** -- Main source file, where `setup()` and `loop()` are defined. Also controls the RGB LED status indicator. 10 | * **[BlynkBoard_Setup.ino](https://github.com/sparkfun/Blynk_Board_ESP8266/Firmware/BlynkBoard_Setup.ino)** -- Hardware setup and EEPROM/flash reading/writing. 11 | * **[BlynkBoard_ConfigMode.ino](https://github.com/sparkfun/Blynk_Board_ESP8266/Firmware/BlynkBoard_ConfigMode.ino)** -- Configuration mode functions; AP and serial WiFi/Blynk configuration. 12 | * **[BlynkBoard_BlynkMode.ino](https://github.com/sparkfun/Blynk_Board_ESP8266/Firmware/BlynkBoard_BlynkMode.ino)** -- Blynk mode functions, including definition of all Blynk Board introductory experiments. 13 | * **[BlynkBoard_settings.h](https://github.com/sparkfun/Blynk_Board_ESP8266/Firmware/BlynkBoard_settings.h)** -- Various constant settings, including RGB colors and connection timeouts. 14 | 15 | ### Using Arduino to Upload to the Blynk Board 16 | 17 | For help adding Blynk Board support to your Arduino IDE, check out our [Blynk Board Arduino Development Guide](https://learn.sparkfun.com/tutorials/blynk-board-arduino-development-guide). 18 | 19 | You can install the board definitions using the **Arduino Board Manager**. Just paste the link below into the "Additional Board URL's" textbox, in Arduino properties: 20 | 21 | http://arduino.esp8266.com/stable/package_esp8266com_index.json 22 | 23 | After installing the Arduino ESP8266 board definitions, you'll find a option for "SparkFun Blynk Board" under the **Tools** > **Board** menu - select that, and upload some code! 24 | 25 | ### Custom Libraries Used 26 | 27 | If you are installing the Bynk Board Core Firmware, you will need to install a few Arduino libraries. For more infomration, check out the tutorial for intsalling an Arduino Library. 28 | 29 | * **[Installing an Arduino Library Guide](https://learn.sparkfun.com/tutorials/installing-an-arduino-library)** - Basic information on how to install an Arduino library. 30 | 31 | The following libraries were used with the default Blynk Board Core Firmware: 32 | 33 | * [Blynk Arduino Library](https://github.com/blynkkk/blynk-library/releases/tag/v0.3.4) 34 | * [SparkFun HTU21D](https://github.com/sparkfun/SparkFun_HTU21D_Breakout_Arduino_Library/releases/tag/V_1.1.1) 35 | * [Adafruit NeoPixel](https://github.com/adafruit/Adafruit_NeoPixel) 36 | * [SparkFun TSL2561 Library](https://github.com/sparkfun/SparkFun_TSL2561_Arduino_Library/releases/tag/V_1.1.0) 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SparkFun Blynk Board - ESP8266 2 | ======================================== 3 | 4 | [![SparkFun Blynk Board - ESP8266](https://cdn.sparkfun.com//assets/parts/1/1/3/6/4/13794-07.jpg)](https://www.sparkfun.com/products/13794) 5 | 6 | [*SparkFun Blynk Board - ESP8266 (WRL-13794)*](https://www.sparkfun.com/products/13794) 7 | 8 | The SparkFun Blynk Board is specially designed to work with the [Blynk mobile app](http://blynk.cc), for iOS or Android devices. Combining the Blynk Board with the Blynk app, you'll be able to easily complete a variety of Internet-of-Things (IoT) based projects: control LEDs from your phone! Wire up a door switch and receive phone notifications when it opens or closes. Plug a soil moisture sensor into a plant, and have it tweet at you when it's thirsty! 9 | 10 | [![BotaniTweet Project](https://cdn.sparkfun.com/r/600-600/assets/learn_tutorials/4/9/0/12-01-project.jpg)](https://learn.sparkfun.com/tutorials/blynk-board-project-guide/) 11 | 12 | All of these projects - and more! - are pre-loaded into the Blynk Board. All you have to do is provision it (using the Blynk app), and start tinkering! 13 | 14 | Repository Contents 15 | ------------------- 16 | 17 | * **/Firmware** - Blynk Board core firmware and example code. 18 | * **/Hardware** - Eagle design files (.brd, .sch) 19 | * **/Production** - Production panel files (.brd) 20 | 21 | Documentation 22 | -------------- 23 | * **[Getting Started Guide](https://learn.sparkfun.com/tutorials/getting-started-with-the-sparkfun-blynk-board)** - Learn how to connect your Blynk Board to Wi-Fi, and start Blynking! 24 | * **[Blynk Board Project Guide](https://learn.sparkfun.com/tutorials/blynk-board-project-guide/introduction)** - Explore over a dozen Blynk projects pre-loaded into the Blynk Board. 25 | * **[Blynk Board Arduino Development Guide](https://learn.sparkfun.com/tutorials/blynk-board-arduino-development-guide)** - Learn how to begin programming the Blynk Board using Arduino, so you can create projects of your own! 26 | * **[SparkFun Graphical Datasheets](https://github.com/sparkfun/Graphical_Datasheets/tree/master/Datasheets/ESP8266/BlynkBoard)** -Graphical Datasheets for the SparkFun Blynk Board. 27 | 28 | Product Versions 29 | ---------------- 30 | * [SparkFun Blynk Board - ESP8266 (v1.0) (WRL-13794)](https://www.sparkfun.com/products/13794)- Initial release of the SparkFun Blynk Board - ESP8266. 31 | * IoT Starter Kit with Blynk Board 32 | * [(KIT-14682)](https://www.sparkfun.com/products/14682) - Updated parts kit with thicker resistor leads. 33 | * [(KIT-13865)](https://www.sparkfun.com/products/13865) - A parts kit including the SparkFun Blynk Board. 34 | 35 | Version History 36 | --------------- 37 | * [HW v1.0, FW v1.0.3](https://github.com/sparkfun/Blynk_Board_ESP8266/releases/tag/HW_v1.0-FW_v1.0.3) - Minor update for ESP8266 Community board add on (v2.6.0 and above) when using interrupts. 38 | * [HW v1.0, FW v1.0.2](https://github.com/sparkfun/Blynk_Board_ESP8266/releases/tag/HW_v1.0-FW_v1.0.2) - Initial release of the SparkFun Blynk Board hardware and firmware. 39 | 40 | License Information 41 | ------------------- 42 | 43 | This product is _**open source**_! 44 | 45 | Please review the LICENSE.md file for license information. 46 | 47 | If you have any questions or concerns on licensing, please contact technical support on our [SparkFun forums](https://forum.sparkfun.com/viewforum.php?f=152). 48 | 49 | Distributed as-is; no warranty is given. 50 | 51 | - Your friends at SparkFun. 52 | 53 | __ 54 | -------------------------------------------------------------------------------- /Firmware/Examples/Bridge_Example/Bridge_Example_2/Bridge_Example_2.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | Bridge_Example_2.ino 3 | Brent Wilkins @ SparkFun Electronics 4 | March 11, 2016 5 | https://github.com/sparkfun/Blynk_Board_ESP8266/Firmware 6 | This file, is example code for the SparkFun Blynk Board which demonstrates the 7 | functionality of the Bridge widget. Using two of these boards to send signals 8 | from one board to the other, or from one connected mobile device to either 9 | Blynk Board or another mobile device if available. Simple wireless buttons and 10 | a more complex chat app are part of this demo. 11 | Resources: 12 | Blynk Arduino Library: https://github.com/blynkkk/blynk-library/releases/tag/v0.3.3 13 | License: 14 | This is released under the MIT license (http://opensource.org/licenses/MIT). 15 | Please see the included LICENSE.md for more information. 16 | Development environment specifics: 17 | Arduino IDE 1.6.7 18 | SparkFun BlynkBoard - ESP8266 19 | ******************************************************************************/ 20 | 21 | // Comment next line out to disable serial prints and save space 22 | #define BLYNK_PRINT Serial // Enables Serial Monitor MUST be before #includes... 23 | #include 24 | #include // http://librarymanager/All#blynk 25 | 26 | char auth[] = "LocalAuthToken"; // Put your Auth Token here 27 | char remoteAuth[] = "RemoteAuthToken"; // Auth token of bridged device 28 | 29 | // Physical Pins: 30 | #define BUTTON_PIN 0 // GPIO0 31 | #define LED_PIN 5 // GPIO5 32 | 33 | // Virtual Pins: 34 | #define TERMINAL 0 // V0 35 | #define LOCAL_LED_BUTTON 1 // V1 36 | #define REMOTE_LED_BUTTON 2 // V2 37 | #define TERMINAL_RECEIVE 3 // V3 38 | #define BRIDGE 4 // V4 39 | #define LOCAL_LED_RECIEVE 5 // V5 40 | #define REMOTE_LED_STATUS_UPDATE 6 // V6 41 | 42 | volatile bool wasButtonPressed = false; 43 | bool ledState = LOW; 44 | bool remoteLedState = LOW; 45 | 46 | // Attach virtual serial terminal to TERMINAL Virtual Pin 47 | WidgetTerminal terminal(TERMINAL); 48 | 49 | // Configure bridge on LOCAL_BRIDGE virtual pin 50 | WidgetBridge bridge(BRIDGE); 51 | 52 | void setup() 53 | { 54 | Serial.begin(9600); // See the connection status in Serial Monitor 55 | 56 | // Here your Arduino connects to the Blynk Cloud. 57 | Blynk.begin(auth, "SSID", "PASSWORD"); 58 | 59 | // TODO: Set defaults after weird state changes 60 | // Wait until connected 61 | while (Blynk.connect() == false); 62 | 63 | bridge.setAuthToken(remoteAuth); 64 | 65 | // Setup the onboard button 66 | pinMode(BUTTON_PIN, INPUT_PULLUP); 67 | // Attach BUTTON_PIN interrupt to our handler 68 | attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), onButtonPress, FALLING); 69 | 70 | // Setup the onboard blue LED 71 | pinMode(LED_PIN, OUTPUT); 72 | 73 | // This will print Blynk Software version to the Terminal Widget when 74 | // your hardware gets connected to Blynk Server 75 | terminal.println(F("Blynk v" BLYNK_VERSION ": Device started")); 76 | terminal.println("-------------"); 77 | terminal.println("Start chatting!"); 78 | terminal.flush(); 79 | } 80 | 81 | /* Note: The SparkFun Blynk board add-on is now supported by 82 | the ESP8266 Community! If you are installing the ESP8266 83 | board add-on v2.6.0+ and using an interrupt service routine, 84 | you must include `ICACHE_RAM_ATTR` before the function 85 | definition. In this case, onButtonPress() is the ISR. */ 86 | 87 | // Interrupt service routine to capture button press event 88 | ICACHE_RAM_ATTR void onButtonPress() 89 | { 90 | // Invert state, since button is "Active LOW" 91 | wasButtonPressed = !digitalRead(BUTTON_PIN); 92 | } 93 | 94 | // Virtual button on connected app was used to change local LED. 95 | // Update LED, and tell remote devices about it. 96 | BLYNK_WRITE(LOCAL_LED_BUTTON) 97 | { 98 | ledState = param.asInt(); // Update local state to match app state 99 | BLYNK_LOG("LED state is now %s", ledState ? "HIGH" : "LOW"); 100 | digitalWrite(LED_PIN, ledState); // Set state of virtual button to LED 101 | // Send updated status to remote board. 102 | bridge.virtualWrite(REMOTE_LED_STATUS_UPDATE, ledState); 103 | } 104 | 105 | // Virtual button on connected app was used to change remote LED. 106 | // Tell remote devices about the change. 107 | BLYNK_WRITE(REMOTE_LED_BUTTON) 108 | { 109 | remoteLedState = param.asInt(); // Update state with info from remote 110 | BLYNK_LOG("New remote LED state: %s", param.asInt() ? "HIGH" : "LOW"); 111 | // Send updated status to remote board. 112 | bridge.virtualWrite(LOCAL_LED_RECIEVE, remoteLedState); 113 | } 114 | 115 | // You can send commands from Terminal to your hardware. Just use 116 | // the same Virtual Pin as your Terminal Widget 117 | BLYNK_WRITE(TERMINAL) 118 | { 119 | // Send from this terminal to remote terminal 120 | bridge.virtualWrite(TERMINAL_RECEIVE, param.asStr()); 121 | } 122 | 123 | // Receive a string on LOCAL_RECEIVE and write that value to the connected 124 | // phone's terminal 125 | BLYNK_WRITE(TERMINAL_RECEIVE) 126 | { 127 | terminal.println(param.asStr()); // Write received string to local terminal 128 | terminal.flush(); 129 | } 130 | 131 | // Remote device triggered an LED change. Update the LED and app status. 132 | BLYNK_WRITE(LOCAL_LED_RECIEVE) 133 | { 134 | // This can be seen in the Serial Monitor 135 | BLYNK_LOG("Remote device triggered LED change"); 136 | 137 | ledState = param.asInt(); 138 | // Turn on LED 139 | digitalWrite(LED_PIN, ledState); 140 | // Set state of virtual button to match remote LED 141 | Blynk.virtualWrite(LOCAL_LED_BUTTON, ledState); 142 | } 143 | 144 | // Remote LED status changed. Show this in the app. 145 | BLYNK_WRITE(REMOTE_LED_STATUS_UPDATE) 146 | { 147 | remoteLedState = param.asInt(); 148 | Blynk.virtualWrite(REMOTE_LED_BUTTON, remoteLedState); 149 | } 150 | 151 | void loop() 152 | { 153 | Blynk.run(); // All the Blynk Magic happens here... 154 | 155 | if (wasButtonPressed) { 156 | // This can be seen in the Serial Monitor 157 | BLYNK_LOG("Physical button was pressed."); 158 | // Toggle state 159 | remoteLedState ^= HIGH; 160 | // Send new state to remote board LED 161 | bridge.virtualWrite(LOCAL_LED_RECIEVE, remoteLedState); 162 | // Update state of button on local mobile app 163 | Blynk.virtualWrite(REMOTE_LED_BUTTON, remoteLedState); 164 | // Clear state variable set in ISR 165 | wasButtonPressed = false; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /Firmware/Examples/Bridge_Example/Bridge_Example_1/Bridge_Example_1.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | Bridge_Example_1.ino 3 | Brent Wilkins @ SparkFun Electronics 4 | March 11, 2016 5 | https://github.com/sparkfun/Blynk_Board_ESP8266/Firmware 6 | This file, is example code for the SparkFun Blynk Board which demonstrates the 7 | functionality of the Bridge widget. Using two of these boards to send signals 8 | from one board to the other, or from one connected mobile device to either 9 | Blynk Board or another mobile device if available. Simple wireless buttons and 10 | a more complex chat app are part of this demo. 11 | Resources: 12 | Blynk Arduino Library: https://github.com/blynkkk/blynk-library/releases/tag/v0.3.3 13 | License: 14 | This is released under the MIT license (http://opensource.org/licenses/MIT). 15 | Please see the included LICENSE.md for more information. 16 | Development environment specifics: 17 | Arduino IDE 1.6.7 18 | SparkFun BlynkBoard - ESP8266 19 | ******************************************************************************/ 20 | 21 | // Comment next line out to disable serial prints and save space 22 | #define BLYNK_PRINT Serial // Enables Serial Monitor MUST be before #includes... 23 | #include 24 | #include // http://librarymanager/All#blynk 25 | 26 | char auth[] = "LocalAuthToken"; // Put your Auth Token here 27 | char remoteAuth[] = "RemoteAuthToken"; // Auth token of bridged device 28 | 29 | // Physical Pins: 30 | #define BUTTON_PIN 0 // GPIO0 31 | #define LED_PIN 5 // GPIO5 32 | 33 | // Virtual Pins: 34 | #define TERMINAL 0 // V0 35 | #define LOCAL_LED_BUTTON 1 // V1 36 | #define REMOTE_LED_BUTTON 2 // V2 37 | #define TERMINAL_RECEIVE 3 // V3 38 | #define BRIDGE 4 // V4 39 | #define LOCAL_LED_RECIEVE 5 // V5 40 | #define REMOTE_LED_STATUS_UPDATE 6 // V6 41 | 42 | volatile bool wasButtonPressed = false; 43 | bool ledState = LOW; 44 | bool remoteLedState = LOW; 45 | 46 | // Attach virtual serial terminal to TERMINAL Virtual Pin 47 | WidgetTerminal terminal(TERMINAL); 48 | 49 | // Configure bridge on LOCAL_BRIDGE virtual pin 50 | WidgetBridge bridge(BRIDGE); 51 | 52 | void setup() 53 | { 54 | Serial.begin(9600); // See the connection status in Serial Monitor 55 | 56 | // Here your Arduino connects to the Blynk Cloud. 57 | Blynk.begin(auth, "SSID", "PASSWORD"); 58 | 59 | // TODO: Set defaults after weird state changes 60 | // Wait until connected 61 | while (Blynk.connect() == false); 62 | 63 | bridge.setAuthToken(remoteAuth); 64 | 65 | // Setup the onboard button 66 | pinMode(BUTTON_PIN, INPUT_PULLUP); 67 | // Attach BUTTON_PIN interrupt to our handler 68 | attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), onButtonPress, FALLING); 69 | 70 | // Setup the onboard blue LED 71 | pinMode(LED_PIN, OUTPUT); 72 | 73 | // This will print Blynk Software version to the Terminal Widget when 74 | // your hardware gets connected to Blynk Server 75 | terminal.println(F("Blynk v" BLYNK_VERSION ": Device started")); 76 | terminal.println("-------------"); 77 | terminal.println("Start chatting!"); 78 | terminal.flush(); 79 | } 80 | 81 | 82 | /* Note: The SparkFun Blynk board add-on is now supported by 83 | the ESP8266 Community! If you are installing the ESP8266 84 | board add-on v2.6.0+ and using an interrupt service routine, 85 | you must include `ICACHE_RAM_ATTR` before the function 86 | definition. In this case, onButtonPress() is the ISR. */ 87 | 88 | // Interrupt service routine to capture button press event 89 | ICACHE_RAM_ATTR void onButtonPress() 90 | { 91 | // Invert state, since button is "Active LOW" 92 | wasButtonPressed = !digitalRead(BUTTON_PIN); 93 | } 94 | 95 | // Virtual button on connected app was used to change local LED. 96 | // Update LED, and tell remote devices about it. 97 | BLYNK_WRITE(LOCAL_LED_BUTTON) 98 | { 99 | ledState = param.asInt(); // Update local state to match app state 100 | BLYNK_LOG("LED state is now %s", ledState ? "HIGH" : "LOW"); 101 | digitalWrite(LED_PIN, ledState); // Set state of virtual button to LED 102 | // Send updated status to remote board. 103 | bridge.virtualWrite(REMOTE_LED_STATUS_UPDATE, ledState); 104 | } 105 | 106 | // Virtual button on connected app was used to change remote LED. 107 | // Tell remote devices about the change. 108 | BLYNK_WRITE(REMOTE_LED_BUTTON) 109 | { 110 | remoteLedState = param.asInt(); // Update state with info from remote 111 | BLYNK_LOG("New remote LED state: %s", param.asInt() ? "HIGH" : "LOW"); 112 | // Send updated status to remote board. 113 | bridge.virtualWrite(LOCAL_LED_RECIEVE, remoteLedState); 114 | } 115 | 116 | // You can send commands from Terminal to your hardware. Just use 117 | // the same Virtual Pin as your Terminal Widget 118 | BLYNK_WRITE(TERMINAL) 119 | { 120 | // Send from this terminal to remote terminal 121 | bridge.virtualWrite(TERMINAL_RECEIVE, param.asStr()); 122 | } 123 | 124 | // Receive a string on LOCAL_RECEIVE and write that value to the connected 125 | // phone's terminal 126 | BLYNK_WRITE(TERMINAL_RECEIVE) 127 | { 128 | terminal.println(param.asStr()); // Write received string to local terminal 129 | terminal.flush(); 130 | } 131 | 132 | // Remote device triggered an LED change. Update the LED and app status. 133 | BLYNK_WRITE(LOCAL_LED_RECIEVE) 134 | { 135 | // This can be seen in the Serial Monitor 136 | BLYNK_LOG("Remote device triggered LED change"); 137 | 138 | ledState = param.asInt(); 139 | // Turn on LED 140 | digitalWrite(LED_PIN, ledState); 141 | // Set state of virtual button to match remote LED 142 | Blynk.virtualWrite(LOCAL_LED_BUTTON, ledState); 143 | } 144 | 145 | // Remote LED status changed. Show this in the app. 146 | BLYNK_WRITE(REMOTE_LED_STATUS_UPDATE) 147 | { 148 | remoteLedState = param.asInt(); 149 | Blynk.virtualWrite(REMOTE_LED_BUTTON, remoteLedState); 150 | } 151 | 152 | void loop() 153 | { 154 | Blynk.run(); // All the Blynk Magic happens here... 155 | 156 | if (wasButtonPressed) { 157 | // This can be seen in the Serial Monitor 158 | BLYNK_LOG("Physical button was pressed."); 159 | // Toggle state 160 | remoteLedState ^= HIGH; 161 | // Send new state to remote board LED 162 | bridge.virtualWrite(LOCAL_LED_RECIEVE, remoteLedState); 163 | // Update state of button on local mobile app 164 | Blynk.virtualWrite(REMOTE_LED_BUTTON, remoteLedState); 165 | // Clear state variable set in ISR 166 | wasButtonPressed = false; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /Firmware/BlynkBoard_Core_Firmware/BlynkBoard_settings.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | BlynBoard_settings.h 3 | BlynkBoard Firmware: Board configuration and settings 4 | Jim Lindblom @ SparkFun Electronics 5 | February 24, 2016 6 | https://github.com/sparkfun/Blynk_Board_ESP8266/Firmware 7 | 8 | This file, part of the BlynkBoard Firmware, determines run-time characteristics 9 | of the BlynkBoard. That includes RGB mode colors, blink rates, and connection 10 | timeouts. 11 | 12 | Resources: 13 | Adafruit_NeoPixel Library - https://github.com/adafruit/Adafruit_NeoPixel 14 | ESP8266 Ticker Library (included with ESP8266 Arduino board definitions) 15 | 16 | License: 17 | This is released under the MIT license (http://opensource.org/licenses/MIT). 18 | Please see the included LICENSE.md for more information. 19 | 20 | Development environment specifics: 21 | Arduino IDE 1.6.7 22 | SparkFun BlynkBoard - ESP8266 23 | ******************************************************************************/ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #define BLYNKBOARD_FIRMWARE_VERSION "1.0.3" 30 | #define BLYNKBOARD_HARDWARE_VERSION "1.0.0" 31 | 32 | #define SERIAL_TERMINAL_BAUD 9600 33 | 34 | #ifdef DEBUG_ENABLED 35 | #define BLYNK_PRINT Serial 36 | #define BB_DEBUG(msg) {\ 37 | Serial.print("[" + String(millis()) + "] "); \ 38 | Serial.println(msg); } 39 | #else 40 | #define BB_DEBUG(msg) 41 | #endif 42 | 43 | #define BB_PRINT(msg) {\ 44 | Serial.print("[" + String(millis()) + "] "); \ 45 | Serial.println(msg); } 46 | 47 | #define BLYNK_AUTH_TOKEN_SIZE 32 48 | 49 | /////////////// 50 | // Run Modes // 51 | /////////////// 52 | enum runModes{ 53 | MODE_SELF_TEST, 54 | MODE_WAIT_CONFIG, 55 | MODE_CONFIG, 56 | MODE_BUTTON_HOLD, 57 | MODE_CONFIG_DEVICE_CONNECTED, 58 | MODE_CONNECTING_WIFI, 59 | MODE_CONNECTING_BLYNK, 60 | MODE_BLYNK_RUN, 61 | MODE_BLYNK_ERROR 62 | }; 63 | runModes runMode, previousMode; 64 | 65 | ////////////////////////////////// 66 | // EEPROM and NV-Memory Defines // 67 | ////////////////////////////////// 68 | #define EEPROM_SIZE 8 69 | #define EEPROM_CONFIG_FLAG_ADDRESS 0 70 | #define EEPROM_SELF_TEST_ADDRESS 1 71 | #define EEPROM_SSID_GENERATED_ADDRESS 2 72 | #define EEPROM_SSID_SUFFX_0 3 73 | #define EEPROM_SSID_SUFFX_1 4 74 | #define EEPROM_SSID_SUFFX_2 5 75 | #define EEPROM_SSID_SUFFX_3 6 76 | #define EEPROM_AP_SETUP_FAIL_FLAG 7 77 | const String BLYNK_AUTH_SPIFF_FILE = "/blynk.txt"; 78 | const String BLYNK_HOST_SPIFF_FILE = "/blynk_host.txt"; 79 | const String BLYNK_PORT_SPIFF_FILE = "/blynk_port.txt"; 80 | 81 | /////////////////////////////// 82 | // Config Server Definitions // 83 | /////////////////////////////// 84 | #define BLYNK_WIFI_CONFIG_PORT 80 85 | #define BLYNK_BOARD_URL "blynkme.cc" 86 | 87 | ////////////////////////////// 88 | // Blynk Server Definitions // 89 | ////////////////////////////// 90 | #define BB_BLYNK_HOST_DEFAULT BLYNK_DEFAULT_DOMAIN 91 | #define BB_BLYNK_PORT_DEFAULT BLYNK_DEFAULT_PORT 92 | // If blynk strings aren't global, reconnect's may see them as invalid 93 | static String g_blynkAuthStr; 94 | static String g_blynkHostStr; 95 | uint16_t g_blynkPort; 96 | 97 | /////////////////// 98 | // WiFi Settings // 99 | /////////////////// 100 | #define WIFI_STA_CONNECT_TIMEOUT 30 // WiFi connection timeout (in seconds) 101 | #define BLYNK_CONNECT_TIMEOUT 15000 // Blynk connection timeout (15 s) 102 | IPAddress defaultAPIP(192, 168, 4, 1); 103 | IPAddress defaultAPSub(255, 255, 255, 0); 104 | 105 | ////////////////////////// 106 | // Hardware Definitions // 107 | ////////////////////////// 108 | #define WS2812_PIN 4 // Pin connected to WS2812 LED 109 | #define NUMRGB 1 // Number of WS2812's in the string 110 | Adafruit_NeoPixel rgb = Adafruit_NeoPixel(NUMRGB, WS2812_PIN, NEO_GRB + NEO_KHZ800); 111 | #define BUTTON_PIN 0 112 | #define BLUE_LED_PIN 5 113 | #define ADC_VOLTAGE_DIVIDER 3.2 114 | // ms time that button should be held down to trigger re-config: 115 | #define BUTTON_HOLD_TIME_MIN 3000 116 | #define SELF_TEST_FLAG_VALUE 0x27 117 | #define AP_SETUP_FAIL_FLAG_VALUE 0x42 118 | #define DEFAULT_MAX_BRIGHTNESS 32 119 | uint8_t selfTestResult = 0; 120 | #define SELF_TEST_SUCCESS_VALUE 0xF 121 | 122 | /////////////////////// 123 | // RGB Status Colors // 124 | /////////////////////// 125 | #define RGB_STATUS_MODE_WAIT_CONFIG 0x202020 // Light white - Start mode 126 | #define RGB_STATUS_MODE_BUTTON_HOLD 0x202020 // Light white - breathing 127 | #define RGB_STATUS_AP_MODE_DEFAULT 0x200000 // Light red - Default AP mode 128 | #define RGB_STATUS_AP_MODE_DEVICE_ON 0x200020 // Light purple - Device connected to AP 129 | #define RGB_STATUS_CONNECTING_WIFI 0x000020 // Light blue - Connecting to WiFi 130 | #define RGB_STATUS_CONNECTED_WIFI 0x000080 // Dark blue - connected to WiFi 131 | #define RGB_STATUS_CANT_CONNECT 0x200000 // Light red - failed to connect to WiFi 132 | #define RGB_STATUS_CONNECTING_BLYNK 0x00270C // Blynk Green - connecting to Blynk cloud 133 | #define RGB_STATUS_CONNECTED_BLYNK 0x00270C // Blynk Green - Connected to Blynk cloud 134 | #define RGB_STATUS_CANT_CONNECT_BLYNK 0x202000 // Light yellow - Failed to connect to Blynk 135 | 136 | ///////////////////////////// 137 | // RGB Status Blink Period // 138 | ///////////////////////////// 139 | #define RGB_PERIOD_SELF_TEST 500 140 | #define RGB_PERIOD_START 1000 141 | #define RGB_PERIOD_BUTTON_HOLD BUTTON_HOLD_TIME_MIN 142 | #define RGB_PERIOD_AP 1000 143 | #define RGB_PERIOD_AP_STOP 2000 144 | #define RGB_PERIOD_AP_DEFAULT 1000 145 | #define RGB_PERIOD_AP_DEVICE_ON 500 146 | #define RGB_PERIOD_CONNECTING 250 147 | #define RGB_PERIOD_RUNNING 5000 148 | #define RGB_PERIOD_BLYNK_CONNECTING 1000 149 | #define RGB_PERIOD_BLINK_ERROR 1000 150 | 151 | /////////////////// 152 | // BlynkRGB SSID // 153 | /////////////////// 154 | Ticker blinker; // Timer to blink LED 155 | uint8_t blinkCount = 0; // Timer iteration counter 156 | bool rgbSetByProject = false; 157 | 158 | #define WS2812_OFF 0x000000 159 | #define WS2812_RED 0x200000 160 | #define WS2812_GREEN 0x002000 161 | #define WS2812_BLUE 0x000020 162 | #define WS2812_YELLOW 0x202000 163 | #define WS2812_PURPLE 0x200040 164 | #define WS2812_NUM_COLORS 5 // Number of possible colors 165 | 166 | const uint32_t SSID_COLORS[WS2812_NUM_COLORS] = {WS2812_RED, 167 | WS2812_GREEN, WS2812_BLUE, WS2812_YELLOW, WS2812_PURPLE 168 | }; 169 | const char SSID_COLOR_CHAR[WS2812_NUM_COLORS] = { 170 | 'R', 'G', 'B', 'Y', 'P' 171 | }; // Map colors to a character 172 | 173 | #define SSID_SUFFIX_LENGTH 4 174 | const char SSID_PREFIX[] = "BlynkMe"; 175 | uint8_t ssidSuffixIndex[SSID_SUFFIX_LENGTH] = {0, 0, 0, 0}; 176 | char BoardSSID[33]; 177 | 178 | //////////////////////// 179 | // Serial Config Mode // 180 | //////////////////////// 181 | #define SERIAL_RX_BUFFER_SIZE 128 182 | #define NUM_SERIAL_CONFIG_CHARS 4 183 | const char CONFIG_CHAR_WIFI_NETWORK = 'w'; 184 | const char CONFIG_CHAR_WIFI_SCAN = 's'; 185 | const char CONFIG_CHAR_BLYNK = 'b'; 186 | const char CONFIG_CHAR_HELP = 'h'; 187 | const char CONFIG_CHAR_SUBMIT = '\r'; 188 | const char CONFIG_CHARS[NUM_SERIAL_CONFIG_CHARS] = { 189 | CONFIG_CHAR_WIFI_SCAN, CONFIG_CHAR_WIFI_NETWORK, 190 | CONFIG_CHAR_BLYNK, CONFIG_CHAR_HELP 191 | }; 192 | enum { 193 | SERIAL_CONFIG_WAITING, 194 | SERIAL_CONFIG_WIFI_SCAN, 195 | SERIAL_CONFIG_WIFI_NETWORK, 196 | SERIAL_CONFIG_WIFI_PASSWORD, 197 | SERIAL_CONFIG_BLYNK, 198 | SERIAL_CONFIG_BLYNK_HOST, 199 | SERIAL_CONFIG_BLYNK_PORT 200 | } serialConfigMode = SERIAL_CONFIG_WAITING; 201 | const char SERIAL_MESSAGE_WIFI_NETWORK[] = "\r\nType your WiFi network SSID and hit enter.\r\n> "; 202 | const char SERIAL_MESSAGE_WIFI_PASSWORD[] = "\r\nType your WiFi network password and hit enter.\r\n" \ 203 | "(If connecting to an open network, leave blank.)\r\n> " ; 204 | const char SERIAL_MESSAGE_BLYNK_HOST[] = "\r\nType your Blynk Server and hit enter.\r\n" \ 205 | "Leave blank to use default: blynk-cloud.com\r\n> "; 206 | const char SERIAL_MESSAGE_BLYNK_PORT[] = "\r\nType your Blynk Port and hit enter.\r\n" \ 207 | "Leave blank to use default: 8442.\r\n> "; 208 | const char SERIAL_MESSAGE_BLYNK[] = "\r\nEnter your 32-character Blynk Auth token.\r\n> "; 209 | const char SERIAL_MESSAGE_HELP[] = "\r\nBlynk Board - ESP8266 Serial Config\r\n" \ 210 | " s: Scan for a WiFi network\r\n" \ 211 | " b: Configure Blynk token, host, and port\r\n" \ 212 | " w: Configure WiFi network\r\n" \ 213 | " h: (This) Help menu\r\n\r\n> "; 214 | 215 | ///////////////////////// 216 | // Error/Success Codes // 217 | ///////////////////////// 218 | enum { 219 | WIFI_BLYNK_SUCCESS = 1, 220 | ERROR_CONNECT_WIFI = -1, 221 | ERROR_CONNECT_BLYNK = -2 222 | }; 223 | 224 | -------------------------------------------------------------------------------- /Firmware/Examples/Laundry_Monitor/Laundry_Monitor.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | Laundry_Monitor.ino 3 | Blynk Board Laundry Monitor 4 | Jim Lindblom @ SparkFun Electronics 5 | March 30, 2016 6 | https://github.com/sparkfun/Blynk_Board_ESP8266/Firmware 7 | 8 | Combine the Blynk Board with an MMA8452Q Accelerometer and the Blynk app 9 | to create a phone-notifying laundry monitor! 10 | 11 | Resources: 12 | SparkFun MMA8452Q Accelerometer Library - https://github.com/sparkfun/SparkFun_MMA8452Q_Arduino_Library 13 | Blynk Arduino Library - https://github.com/blynkkk/blynk-library 14 | ESP8266WiFi Library (included with ESP8266 Arduino board definitions) 15 | Adafruit_NeoPixel Library - https://github.com/adafruit/Adafruit_NeoPixel 16 | 17 | License: 18 | This is released under the MIT license (http://opensource.org/licenses/MIT). 19 | Please see the included LICENSE.md for more information. 20 | 21 | Development environment specifics: 22 | Arduino IDE 1.6.7 23 | SparkFun BlynkBoard - ESP8266 24 | ******************************************************************************/ 25 | #include 26 | #include 27 | #include // Must include Wire library for I2C 28 | #include // Includes the SFE_MMA8452Q library 29 | #include 30 | 31 | ////////// 32 | // WiFi // 33 | ////////// // Enter your WiFi credentials here: 34 | const char WiFiSSID[] = "WiFiNetworkName"; 35 | const char WiFiPSWD[] = "WiFiPassword"; 36 | 37 | /////////// 38 | // Blynk // 39 | /////////// // Your Blynk auth token here 40 | const char BlynkAuth[] = "0a1b2c3d4e5f"; 41 | bool notifyFlag = false; 42 | #define VIRTUAL_ENABLE_PUSH V0 43 | #define VIRTUAL_SHAKE_THRESHOLD V1 44 | #define VIRTUAL_START_TIME V2 45 | #define VIRTUAL_STOP_TIME V3 46 | #define VIRTUAL_LCD V4 47 | #define VIRTUAL_SHAKE_VALUE V5 48 | WidgetLCD lcd(VIRTUAL_LCD); 49 | bool pushEnabled = false; 50 | void printLaundryTime(void); 51 | 52 | ///////////////////// 53 | // Shake Detection // 54 | ///////////////////// 55 | unsigned int shakeThreshold = 50; 56 | unsigned int shakeStartTimeHysteresis = 1000; 57 | unsigned int shakeStopTimeHysteresis = 10; 58 | unsigned long shakeStateChangeTime = 0; 59 | unsigned long shakeStartTime = 0; 60 | bool loadTimer = true; 61 | 62 | enum { 63 | NO_SHAKING_LONG, // Haven't been shaking for a long time 64 | NO_SHAKING, // Hasn't been any shaking 65 | PRE_SHAKING, // Started shaking, pre-hysteresis 66 | SHAKING, // Currently shaking 67 | POST_SHAKING // Stopped shaking, pre-hysteresis 68 | } shakingState = NO_SHAKING; 69 | 70 | // Possible return values from the shake sensor 71 | enum sensorShakeReturn { 72 | SENSOR_SHAKING, 73 | SENSOR_NOT_SHAKING, 74 | SENSOR_NOT_READY 75 | }; 76 | sensorShakeReturn checkShake(void); 77 | void shakeLoop(void); 78 | 79 | //////////////////////////// 80 | // MMA8452Q Accelerometer // 81 | //////////////////////////// 82 | MMA8452Q accel; 83 | int16_t lastX, lastY, lastZ; 84 | void initAccel(void); 85 | 86 | ////////////////////////// 87 | // Hardware Definitions // 88 | ////////////////////////// 89 | const int LED_PIN = 5; 90 | const int RGB_PIN = 4; 91 | Adafruit_NeoPixel rgb = Adafruit_NeoPixel(1, RGB_PIN, NEO_GRB + NEO_KHZ800); 92 | void setLED(uint8_t red, uint8_t green, uint8_t blue); 93 | 94 | void setup() 95 | { 96 | Serial.begin(9600); 97 | 98 | pinMode(LED_PIN, OUTPUT); 99 | digitalWrite(LED_PIN, LOW); // Turn off blue LED 100 | rgb.begin(); // Set up WS2812 101 | initAccel(); // Set up accelerometer 102 | setLED(0, 0, 32); // LED blue 103 | 104 | // Initialize Blynk, and wait for a connection before doing anything else 105 | Serial.println("Connecting to WiFi and Blynk"); 106 | Blynk.begin(BlynkAuth, WiFiSSID, WiFiPSWD); 107 | while (!Blynk.connected()) 108 | Blynk.run(); 109 | Serial.println("Blynk connected! Laundry monitor starting."); 110 | setLED(0, 32, 0); // LED green 111 | } 112 | 113 | void loop() 114 | { 115 | shakeLoop(); // Check if we're shaking, and update variables accordingly 116 | Blynk.run(); // Blynk run as much as possible 117 | } 118 | 119 | bool firstConnect = true; 120 | BLYNK_CONNECTED() 121 | { 122 | if (firstConnect) // When we first connect to Blynk 123 | { 124 | // Two options here. Either sync values from phone to Blynk Board: 125 | //Blynk.syncAll(); // Uncomment to enable. 126 | // Or set phone variables to default values of the globals: 127 | Blynk.virtualWrite(VIRTUAL_SHAKE_THRESHOLD, shakeThreshold); 128 | Blynk.virtualWrite(VIRTUAL_STOP_TIME, shakeStopTimeHysteresis); 129 | Blynk.virtualWrite(VIRTUAL_START_TIME, shakeStartTimeHysteresis); 130 | Blynk.virtualWrite(VIRTUAL_ENABLE_PUSH, pushEnabled); 131 | 132 | // Print a splash screen: 133 | lcd.clear(); 134 | lcd.print(0, 0, "Laundry Monitor "); 135 | lcd.print(0, 1, " Ready "); 136 | } 137 | } 138 | 139 | BLYNK_WRITE(VIRTUAL_ENABLE_PUSH) 140 | { 141 | int enable = param.asInt(); 142 | if (enable > 0) 143 | { 144 | pushEnabled = true; 145 | Serial.println("Push notification enabled"); 146 | } 147 | else 148 | { 149 | pushEnabled = false; 150 | Serial.println("Push notification disabled"); 151 | } 152 | } 153 | 154 | BLYNK_WRITE(VIRTUAL_SHAKE_THRESHOLD) 155 | { 156 | int inputThreshold = param.asInt(); 157 | 158 | shakeThreshold = constrain(inputThreshold, 1, 2048); 159 | 160 | Serial.println("Shake threshold set to: " + String(shakeThreshold)); 161 | } 162 | 163 | BLYNK_WRITE(VIRTUAL_START_TIME) 164 | { 165 | int inputStartTime = param.asInt(); 166 | 167 | if (inputStartTime <= 0) inputStartTime = 1; 168 | shakeStartTimeHysteresis = inputStartTime; 169 | 170 | Serial.println("Shake start time set to: " + String(shakeStartTimeHysteresis) + " ms"); 171 | } 172 | 173 | BLYNK_WRITE(VIRTUAL_STOP_TIME) 174 | { 175 | int inputStopTime = param.asInt(); 176 | 177 | if (inputStopTime <= 0) inputStopTime = 1; 178 | shakeStopTimeHysteresis = inputStopTime; 179 | 180 | Serial.println("Shake stop time set to: " + String(shakeStopTimeHysteresis) + " seconds"); 181 | } 182 | 183 | void printLaundryTime(void) 184 | { 185 | unsigned long runTime = millis() - shakeStartTime; 186 | int runSeconds = (runTime / 1000) % 60; 187 | int runMinutes = ((runTime / 1000) / 60) % 60; 188 | int runHours = ((runTime / 1000) / 60 ) / 60; 189 | 190 | // Create a string like HHHH:MM:SS 191 | String runTimeString = " " + String(runHours) + ":"; 192 | if (runMinutes < 10) runTimeString += "0"; // Leading 0 minutes 193 | runTimeString += String(runMinutes) + ":"; 194 | if (runSeconds < 10) runTimeString += "0"; // Leading 0 seconds 195 | runTimeString += String(runSeconds); 196 | 197 | // Fill out the rest of the string to 16 chars 198 | int lineLength = runTimeString.length(); 199 | for (int i=lineLength; i<16; i++) 200 | { 201 | runTimeString += " "; 202 | } 203 | 204 | if (shakingState == PRE_SHAKING) 205 | lcd.print(0, 0, "Laundry starting"); 206 | else if (shakingState == SHAKING) 207 | lcd.print(0, 0, "Laundry running "); 208 | else if (shakingState == NO_SHAKING) 209 | lcd.print(0, 0, "Laundry stopping"); 210 | else if (shakingState == NO_SHAKING_LONG) 211 | lcd.print(0, 0, "Laundry done! "); 212 | lcd.print(0, 1, runTimeString); 213 | } 214 | 215 | void shakeLoop(void) 216 | { 217 | sensorShakeReturn sensorState = checkShake(); 218 | if (sensorState == SENSOR_SHAKING) // If the sensor is shaking 219 | { 220 | switch (shakingState) 221 | { 222 | case NO_SHAKING_LONG: 223 | case NO_SHAKING: // If we haven't been shaking 224 | setLED(32, 0, 32); // LED purple 225 | shakingState = PRE_SHAKING; // Set mode to pre-shaking 226 | shakeStateChangeTime = millis(); // Log state change time 227 | if (loadTimer) 228 | { 229 | loadTimer = false; 230 | shakeStartTime = millis(); // Log time we started shaking 231 | } 232 | printLaundryTime(); 233 | break; 234 | case PRE_SHAKING: // If we're pre-hysteresis shaking 235 | if (millis() - shakeStateChangeTime >= shakeStartTimeHysteresis) 236 | { // If we've passed hysteresis time 237 | shakingState = SHAKING; // Set mode to shaking 238 | digitalWrite(LED_PIN, HIGH); // Turn blue LED on 239 | Serial.println("Shaking!"); 240 | notifyFlag = true; // Flag that we need to notify when shaking stops 241 | setLED(32, 0, 0); // LED red 242 | } 243 | break; 244 | case SHAKING: // If we're already shaking 245 | printLaundryTime(); // Update laundry timer 246 | break; // Do nothing 247 | case POST_SHAKING: // If we didn't stop shaking before hysteresis 248 | shakingState = SHAKING; // Go back to shaking 249 | break; 250 | } 251 | } 252 | else if (sensorState == SENSOR_NOT_SHAKING) // If the sensor is not shaking 253 | { 254 | switch (shakingState) 255 | { 256 | case NO_SHAKING_LONG: // If we haven't been shaking for a long time 257 | break; // Do nothing 258 | case NO_SHAKING: // If we haven't been shaking 259 | if (millis() - shakeStateChangeTime >= (shakeStopTimeHysteresis * 1000)) 260 | { // Check if it's been a long time 261 | setLED(0, 32, 0); // Turn LED green 262 | shakingState = NO_SHAKING_LONG; 263 | if (notifyFlag == true) // If notify flag was set during shaking 264 | { 265 | loadTimer = true; 266 | printLaundryTime(); // Update LCD 267 | notifyFlag = false; // Clear notify flag 268 | if (pushEnabled) // If push is enabled 269 | Blynk.notify("Washer/dryer is done!"); // Notify! 270 | } 271 | } 272 | break; 273 | case PRE_SHAKING: // If we're pre-hysteresis shaking 274 | shakingState = NO_SHAKING; // Go back to no shaking 275 | setLED(0, 32, 0); 276 | break; 277 | case SHAKING: // If we're already shaking 278 | shakingState = POST_SHAKING; // Go to hysteresis cooldown 279 | shakeStateChangeTime = millis(); 280 | break; // Do nothing 281 | case POST_SHAKING: // If we're in the shake cooldown state 282 | if (millis() - shakeStateChangeTime >= shakeStartTimeHysteresis) 283 | { 284 | digitalWrite(5, LOW); // LED off 285 | shakingState = NO_SHAKING; 286 | printLaundryTime(); 287 | setLED(32, 16, 0); 288 | Serial.println("Stopped."); 289 | } 290 | break; 291 | } 292 | } 293 | } 294 | 295 | sensorShakeReturn checkShake(void) 296 | { 297 | static unsigned long lastShakeCheck = 0; 298 | float shake = 0; 299 | if (accel.available()) // If new accel data is available 300 | { 301 | int16_t x, y, z; 302 | 303 | accel.read(); // read the data in 304 | x = accel.x; 305 | y = accel.y; 306 | z = accel.z; 307 | 308 | // To determine if we're shaking, compare the sum of 309 | // x,y,z accels to the sum of the previous accels. 310 | shake = abs(x + y + z - lastX - lastY - lastZ); 311 | 312 | // Write the value to Blynk: 313 | Blynk.virtualWrite(VIRTUAL_SHAKE_VALUE, shake); 314 | 315 | // Update previous values: 316 | lastX = x; 317 | lastY = y; 318 | lastZ = z; 319 | } 320 | else // If sensore didn't have new data 321 | { // Return not ready 322 | return SENSOR_NOT_READY; 323 | } 324 | // If shake value exceeded threshold 325 | if (shake >= shakeThreshold) 326 | return SENSOR_SHAKING; // Return "shaking" 327 | else 328 | return SENSOR_NOT_SHAKING; // Or return "not shaking" 329 | } 330 | 331 | void initAccel(void) 332 | { 333 | // Use a slow update rate to throttle the shake sensor. 334 | // ODR_6 will set the accelerometer's update rate to 6Hz 335 | // Use +/-2g scale -- the lowest -- to get most sensitivity 336 | accel.init(SCALE_2G, ODR_6); // Initialize accelerometer 337 | 338 | while (!accel.available()) // Wait for data to be available 339 | yield(); // Let the system do other things 340 | accel.read(); // Read data from accel 341 | lastX = accel.x; // Initialize last values 342 | lastY = accel.y; 343 | lastZ = accel.z; 344 | } 345 | 346 | void setLED(uint8_t red, uint8_t green, uint8_t blue) 347 | { 348 | rgb.setPixelColor(0, rgb.Color(red, green, blue)); 349 | rgb.show(); 350 | } 351 | -------------------------------------------------------------------------------- /Firmware/BlynkBoard_Core_Firmware/BlynkBoard_Setup.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | BlynkBoard_Setup.h 3 | BlynkBoard Firmware: Board initialization functions 4 | Jim Lindblom @ SparkFun Electronics 5 | February 24, 2016 6 | https://github.com/sparkfun/Blynk_Board_ESP8266/Firmware 7 | 8 | This file, part of the BlynkBoard Firmware, defines the hardware setup and 9 | initialization functions. That includes EEPROM and flash (SPIFFS) reading 10 | and writing. 11 | 12 | Resources: 13 | ESP8266 File System (FS.h) - (included with ESP8266 Arduino board definitions) 14 | 15 | License: 16 | This is released under the MIT license (http://opensource.org/licenses/MIT). 17 | Please see the included LICENSE.md for more information. 18 | 19 | Development environment specifics: 20 | Arduino IDE 1.6.7 21 | SparkFun BlynkBoard - ESP8266 22 | ******************************************************************************/ 23 | 24 | bool initHardware(void) 25 | { 26 | // Short delay at the start avoids intermittent Microsoft Serial 27 | // Ballpoint driver from showing up. 28 | delay(1000); 29 | Serial.begin(SERIAL_TERMINAL_BAUD); 30 | BB_PRINT(""); 31 | BB_PRINT("SparkFun Blynk Board Hardware v" + String(BLYNKBOARD_HARDWARE_VERSION)); 32 | BB_PRINT("SparkFun Blynk Board Firmware v" + String(BLYNKBOARD_FIRMWARE_VERSION)); 33 | 34 | randomSeed(ESP.getChipId()); 35 | 36 | // Initialize RGB LED and turn it off: 37 | rgb.begin(); 38 | rgb.setPixelColor(0, rgb.Color(0, 0, 0)); 39 | rgb.show(); 40 | // Attach a timer to the RGB LED: 41 | blinker.attach_ms(RGB_PERIOD_AP, blinkRGBTimer); 42 | 43 | // Initialize the button, and setup a hardware interrupt 44 | // Look for CHANGE - when the button is pressed or released. 45 | pinMode(BUTTON_PIN, INPUT_PULLUP); 46 | attachInterrupt(BUTTON_PIN, buttonChange, CHANGE); 47 | 48 | // Initialize the LED pin: 49 | pinMode(BLUE_LED_PIN, OUTPUT); // Set pin as an output 50 | digitalWrite(BLUE_LED_PIN, LOW); // Turn the LED off 51 | 52 | if (!SPIFFS.begin()) 53 | { 54 | BB_DEBUG("Failed to initialize SPIFFS"); 55 | return false; 56 | } 57 | 58 | EEPROM.begin(EEPROM_SIZE); 59 | return true; 60 | } 61 | 62 | bool checkConfigFlag(void) 63 | { 64 | byte configFlag = EEPROM.read(EEPROM_CONFIG_FLAG_ADDRESS); 65 | if (configFlag == 1) 66 | return true; 67 | 68 | return false; 69 | } 70 | 71 | bool checkFailAPSetupFlag(void) 72 | { 73 | if (EEPROM.read(EEPROM_AP_SETUP_FAIL_FLAG) != AP_SETUP_FAIL_FLAG_VALUE) 74 | return false; 75 | 76 | return true; 77 | } 78 | 79 | void writeAPSetupFlag(bool pass) 80 | { 81 | uint8_t apSetupFlag; 82 | if (pass) apSetupFlag = AP_SETUP_FAIL_FLAG_VALUE; 83 | else apSetupFlag = 0; 84 | EEPROM.write(EEPROM_AP_SETUP_FAIL_FLAG, apSetupFlag); 85 | EEPROM.commit(); 86 | } 87 | 88 | bool writeBlynkConfig(String authToken, String host, uint16_t port) 89 | { 90 | File authFile = SPIFFS.open(BLYNK_AUTH_SPIFF_FILE, "w"); 91 | if (authFile) 92 | { 93 | BB_DEBUG("Opened " + String(BLYNK_AUTH_SPIFF_FILE)); 94 | authFile.print(authToken); 95 | authFile.close(); 96 | BB_DEBUG("Wrote file."); 97 | } 98 | else 99 | { 100 | BB_DEBUG("Failed to open file to write."); 101 | return false; 102 | } 103 | 104 | File hostFile = SPIFFS.open(BLYNK_HOST_SPIFF_FILE, "w"); 105 | if (hostFile) 106 | { 107 | BB_DEBUG("Opened " + String(BLYNK_HOST_SPIFF_FILE)); 108 | hostFile.print(host); 109 | hostFile.close(); 110 | BB_DEBUG("Wrote " + String(BLYNK_HOST_SPIFF_FILE)); 111 | } 112 | else 113 | { 114 | BB_DEBUG("Failed to open " + String(BLYNK_HOST_SPIFF_FILE)); 115 | return false; 116 | } 117 | 118 | File portFile = SPIFFS.open(BLYNK_PORT_SPIFF_FILE, "w"); 119 | if (portFile) 120 | { 121 | BB_DEBUG("Opened " + String(BLYNK_PORT_SPIFF_FILE)); 122 | portFile.print(port); 123 | portFile.close(); 124 | BB_DEBUG("Wrote " + String(BLYNK_PORT_SPIFF_FILE)); 125 | } 126 | else 127 | { 128 | BB_DEBUG("Failed to open " + String(BLYNK_PORT_SPIFF_FILE)); 129 | return false; 130 | } 131 | 132 | return true; 133 | } 134 | 135 | String getBlynkAuth(void) 136 | { 137 | String retAuth; 138 | 139 | if (SPIFFS.exists(BLYNK_AUTH_SPIFF_FILE)) 140 | { 141 | BB_DEBUG("Opening auth file."); 142 | File authFile = SPIFFS.open(BLYNK_AUTH_SPIFF_FILE, "r"); 143 | if(authFile) 144 | { 145 | BB_DEBUG("File opened."); 146 | size_t authFileSize = authFile.size(); 147 | // Only return auth token if it's the right size (32 bytes) 148 | if (authFileSize == BLYNK_AUTH_TOKEN_SIZE) 149 | { 150 | while (authFile.available()) 151 | { 152 | retAuth += (char)authFile.read(); 153 | } 154 | } 155 | authFile.close(); 156 | } 157 | else 158 | { 159 | BB_DEBUG("Failed to open auth file."); 160 | } 161 | } 162 | else 163 | { 164 | BB_DEBUG("File does not exist."); 165 | } 166 | 167 | return retAuth; 168 | } 169 | 170 | String getBlynkHost(void) 171 | { 172 | String retHost; 173 | 174 | if (SPIFFS.exists(BLYNK_HOST_SPIFF_FILE)) 175 | { 176 | File hostFile = SPIFFS.open(BLYNK_HOST_SPIFF_FILE, "r"); 177 | if(hostFile) 178 | { 179 | BB_DEBUG("Opened" + String(BLYNK_HOST_SPIFF_FILE)); 180 | while (hostFile.available()) 181 | retHost += (char)hostFile.read(); 182 | hostFile.close(); 183 | } 184 | else 185 | { 186 | BB_DEBUG("Failed to open " + String(BLYNK_HOST_SPIFF_FILE)); 187 | } 188 | } 189 | else 190 | { 191 | BB_DEBUG(String(BLYNK_HOST_SPIFF_FILE) + " does not exist."); 192 | } 193 | 194 | return retHost; 195 | } 196 | 197 | int16_t getBlynkPort(void) 198 | { 199 | String retPort; 200 | 201 | if (SPIFFS.exists(BLYNK_PORT_SPIFF_FILE)) 202 | { 203 | File portFile = SPIFFS.open(BLYNK_PORT_SPIFF_FILE, "r"); 204 | if(portFile) 205 | { 206 | BB_DEBUG("Opened" + String(BLYNK_PORT_SPIFF_FILE)); 207 | while (portFile.available()) 208 | retPort += (char)portFile.read(); 209 | portFile.close(); 210 | } 211 | else 212 | { 213 | BB_DEBUG("Failed to open " + String(BLYNK_PORT_SPIFF_FILE)); 214 | } 215 | } 216 | else 217 | { 218 | BB_DEBUG(String(BLYNK_PORT_SPIFF_FILE) + " does not exist."); 219 | } 220 | 221 | return retPort.toInt(); 222 | } 223 | 224 | int8_t setupBlynkStation(String network, String psk, String blynkAuth, 225 | String blynkHost, uint16_t blynkPort) 226 | { 227 | WiFi.disconnect(); 228 | WiFi.enableSTA(true); 229 | WiFi.disconnect(); 230 | 231 | if (!WiFi.begin(network.c_str(), psk.c_str())) 232 | return ERROR_CONNECT_WIFI; 233 | 234 | if (!WiFiConnectWithTimeout(WIFI_STA_CONNECT_TIMEOUT)) 235 | { 236 | BB_DEBUG("Timed out connecting to WiFi."); 237 | WiFi.enableSTA(false); // Disable station mode 238 | runMode = MODE_CONFIG; // Back to config LED blink 239 | return ERROR_CONNECT_WIFI; 240 | } 241 | 242 | if (!BlynkConnectWithTimeout(blynkAuth.c_str(), blynkHost.c_str(), 243 | blynkPort, BLYNK_CONNECT_TIMEOUT)) 244 | { 245 | BB_DEBUG("Timed out connecting to Blynk."); 246 | runMode = MODE_CONFIG; // Back to config LED blink 247 | return ERROR_CONNECT_BLYNK; 248 | } 249 | 250 | writeBlynkConfig(blynkAuth, blynkHost, blynkPort); 251 | EEPROM.write(EEPROM_CONFIG_FLAG_ADDRESS, 1); 252 | EEPROM.commit(); 253 | 254 | // Initialize hardware to get it ready for Blynk mode: 255 | blynkSetup(); 256 | 257 | return WIFI_BLYNK_SUCCESS; 258 | } 259 | 260 | long WiFiConnectWithTimeout(unsigned long timeout) 261 | { 262 | runMode = MODE_CONNECTING_WIFI; 263 | 264 | long timeIn = timeout; 265 | // Relying on persistent ESP8266 WiFi credentials. 266 | BB_PRINT("Connecting to: " + WiFi.SSID()); 267 | while ((WiFi.status() != WL_CONNECTED) && (--timeIn > 0)) 268 | { 269 | if (runMode != MODE_CONNECTING_WIFI) 270 | return 0; 271 | delay(1000); 272 | Serial.print('.'); 273 | } 274 | Serial.println(); 275 | 276 | if (timeIn <= 0) 277 | { 278 | runMode = MODE_WAIT_CONFIG; 279 | } 280 | 281 | return timeIn; 282 | } 283 | 284 | long BlynkConnectWithTimeout(const char * blynkAuth, const char * blynkServer, 285 | uint16_t blynkPort, unsigned long timeout) 286 | { 287 | runMode = MODE_CONNECTING_BLYNK; 288 | // Button interrupt is interfering with something. If the ISR is entered 289 | // during this loop, an exception occurs. Disable interrupt: 290 | //detachInterrupt(BUTTON_PIN); 291 | 292 | long retVal = 1; 293 | unsigned long timeoutMs = millis() + timeout; 294 | 295 | Blynk.config(blynkAuth, blynkServer, blynkPort); 296 | 297 | while ((!Blynk.connected()) && (timeoutMs > millis())) 298 | { 299 | if (runMode != MODE_CONNECTING_BLYNK) 300 | { 301 | attachInterrupt(BUTTON_PIN, buttonChange, CHANGE); 302 | return 0; 303 | } 304 | 305 | Blynk.run(); 306 | } 307 | 308 | if (millis() >= timeoutMs) // If we' timed out 309 | { 310 | retVal = 0; 311 | BB_DEBUG("Timed out connecting to Blynk"); 312 | runMode = MODE_WAIT_CONFIG; 313 | } 314 | 315 | attachInterrupt(BUTTON_PIN, buttonChange, CHANGE); 316 | return retVal; 317 | } 318 | 319 | bool checkSelfTestFlag(void) 320 | { 321 | if (EEPROM.read(EEPROM_SELF_TEST_ADDRESS) == SELF_TEST_FLAG_VALUE) 322 | return true; 323 | return false; 324 | } 325 | 326 | bool setSelfTestFlag(void) 327 | { 328 | EEPROM.write(EEPROM_SELF_TEST_ADDRESS, SELF_TEST_FLAG_VALUE); 329 | EEPROM.commit(); 330 | return true; 331 | } 332 | 333 | void performSelfTest(void) 334 | { 335 | pinMode(BLUE_LED_PIN, OUTPUT); 336 | digitalWrite(BLUE_LED_PIN, HIGH); // Turn blue LED on 337 | rgb.updateLength(2); // Connect to testbed LED 338 | rgb.setPixelColor(0, 0); 339 | rgb.setPixelColor(1, 0); 340 | rgb.show(); // Turn both WS2812's off 341 | 342 | ////////////////////////// 343 | // WiFi connection test // 344 | ////////////////////////// 345 | WiFi.enableSTA(true); 346 | WiFi.disconnect(); 347 | WiFi.enableSTA(true); 348 | WiFi.begin("sparkfun-guest","sparkfun6333"); 349 | unsigned timeout = 10000; 350 | while ((WiFi.status() != WL_CONNECTED) && (--timeout)) 351 | delay(1); 352 | if (timeout > 0) 353 | { 354 | Serial.println("Connected to test network"); 355 | selfTestResult |= (1<<0); 356 | } 357 | else 358 | { 359 | Serial.println("Failed to connect to WiFi"); 360 | } 361 | 362 | ///////////////// 363 | // Si7021 Test // 364 | ///////////////// 365 | thSense.begin(); 366 | if (scanI2C(0x40)) // Si7021 is i2c address 0x40 367 | { 368 | Serial.println("Si7021 test succeeded"); 369 | selfTestResult |= (1<<1); 370 | } 371 | else 372 | { 373 | Serial.println("Si7021 test failed"); 374 | } 375 | 376 | ////////////// 377 | // ADC Test // 378 | ////////////// 379 | float adcRaw = analogRead(A0); // Read in A0 380 | float voltage = ((float)adcRaw / 1024.0) * ADC_VOLTAGE_DIVIDER; 381 | if((voltage < 2.25) && (voltage > 1.75)) 382 | { 383 | Serial.println("ADC test passed" + String(voltage)); 384 | selfTestResult |= (1<<2); 385 | } 386 | else 387 | { 388 | Serial.println("ADC test failed: " + String(voltage)); 389 | } 390 | 391 | ///////////// 392 | // IO test // 393 | ///////////// 394 | // Pins 16, 12, and 13 should be tied together. 395 | // Pin 16 will be written high/low, and pins 12/13 will be read. 396 | uint8_t ioTest = 0; 397 | timeout = 1000; // 1s timeout 398 | pinMode(16, OUTPUT); 399 | pinMode(12, INPUT); 400 | pinMode(13, INPUT); 401 | 402 | // High test: 403 | digitalWrite(16, HIGH); 404 | delay(100); 405 | while ((--timeout)) 406 | { 407 | if ((digitalRead(12) == HIGH) && (digitalRead(13) == HIGH)) 408 | break; 409 | } 410 | if (timeout > 0) 411 | ioTest |= (1<<0); 412 | 413 | // Low test: 414 | digitalWrite(16, LOW); 415 | timeout = 1000; 416 | delay(100); 417 | while ((--timeout)) 418 | { 419 | if ((digitalRead(12) == LOW) && (digitalRead(13) == LOW)) 420 | break; 421 | } 422 | if (timeout > 0) 423 | ioTest |= (1<<1); 424 | 425 | if (ioTest == 3) 426 | { 427 | Serial.println("IO test passed"); 428 | selfTestResult |= (1<<3); 429 | } 430 | else 431 | { 432 | Serial.println("IO test failed"); 433 | } 434 | 435 | if (selfTestResult == 0xF) // If it passed 436 | { 437 | while (1) 438 | { 439 | rgb.setPixelColor(1, 0x008000); 440 | delay(1000); 441 | rgb.setPixelColor(1, 0); 442 | delay(1000); 443 | } 444 | } 445 | else // if it failed 446 | { 447 | while (1) 448 | { 449 | // WiFi failed - purple 450 | if (!(selfTestResult & (1<<0))) 451 | { 452 | rgb.setPixelColor(1, 0x800080); 453 | rgb.show(); 454 | delay(1000); 455 | } 456 | // Si7021 failed - yellow 457 | if (!(selfTestResult & (1<<1))) 458 | { 459 | rgb.setPixelColor(1, 0x802000); 460 | rgb.show(); 461 | delay(1000); 462 | } 463 | // ADC failed - red 464 | if (!(selfTestResult & (1<<2))) 465 | { 466 | rgb.setPixelColor(1, 0x800000); 467 | rgb.show(); 468 | delay(1000); 469 | } 470 | // IO test failed - orange 471 | if (!(selfTestResult & (1<<3))) 472 | { 473 | rgb.setPixelColor(1, 0x806000); 474 | rgb.show(); 475 | delay(1000); 476 | } 477 | 478 | // Blink off 479 | rgb.setPixelColor(1, 0); 480 | rgb.show(); 481 | delay(1000); 482 | } 483 | } 484 | } 485 | 486 | void resetEEPROM(void) 487 | { 488 | EEPROM.write(EEPROM_CONFIG_FLAG_ADDRESS, 0); 489 | EEPROM.commit(); 490 | SPIFFS.remove(BLYNK_AUTH_SPIFF_FILE); 491 | SPIFFS.remove(BLYNK_HOST_SPIFF_FILE); 492 | SPIFFS.remove(BLYNK_PORT_SPIFF_FILE); 493 | } 494 | -------------------------------------------------------------------------------- /Firmware/BlynkBoard_Core_Firmware/BlynkBoard_Core_Firmware.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | BlynkBoard_Core_Firmware.ino 3 | BlynkBoard Firmware: Main Source 4 | Jim Lindblom @ SparkFun Electronics 5 | February 24, 2016 6 | https://github.com/sparkfun/Blynk_Board_ESP8266/Firmware 7 | 8 | This file, part of the BlynkBoard Firmware, is the top-level source code. 9 | It includes the main loop() and setup() function. It also maintains the 10 | WS2812 status LED 11 | 12 | Resources: 13 | ESP8266WiFi Library (included with ESP8266 Arduino board definitions) 14 | ESP8266WiFiClient Library (included with ESP8266 Arduino board definitions) 15 | ESP8266WebServer Library (included with ESP8266 Arduino board definitions) 16 | Adafruit_NeoPixel Library - https://github.com/adafruit/Adafruit_NeoPixel 17 | 18 | License: 19 | This is released under the MIT license (http://opensource.org/licenses/MIT). 20 | Please see the included LICENSE.md for more information. 21 | 22 | Development environment specifics: 23 | Arduino IDE 1.6.7 24 | SparkFun BlynkBoard - ESP8266 25 | ESP8266 Arduino Core - version 2.6.0 and above <- Critical, must be up-to-date 26 | ******************************************************************************/ 27 | 28 | //#define DEBUG_ENABLED 29 | //#define SELF_TEST_ENABLED //this is used for SparkFun's Production for QC tests 30 | //#define CAPTIVE_PORTAL_ENABLE 31 | 32 | #include "BlynkBoard_settings.h" 33 | #include 34 | #include 35 | #include 36 | #include // http://librarymanager/All#neopixel 37 | #include 38 | #include // http://librarymanager/All#blynk 39 | #include 40 | #include "FS.h" 41 | #include 42 | 43 | ///////////////////////// 44 | // Function Prototypes // 45 | ///////////////////////// 46 | // BlynkBoard_ConfigMode functions: 47 | void handleRoot(void); 48 | void handleReset(void); 49 | void handleBoardInfo(void); 50 | void handleConfig(void); 51 | void setupServer(void); 52 | void generateSSIDSuffix(bool newSSID); 53 | void handleConfigServer(void); 54 | void checkForStations(void); 55 | bool setupAP(char * ssidName); 56 | void checkSerialConfig(void); 57 | void executeSerialCommand(void); 58 | uint32_t rgbModeSelfTest(void); 59 | 60 | // BlynkBoard_BlynkMode functions: 61 | void buttonUpdate(void); 62 | void blynkSetup(void); 63 | void blynkLoop(void); 64 | 65 | // BlynkBoard_setup functions: 66 | bool initHardware(void); 67 | bool checkConfigFlag(void); 68 | bool checkFailAPSetupFlag(void); 69 | void writeAPSetupFlag(bool pass); 70 | bool writeBlynkConfig(String authToken, String host, uint16_t port); 71 | String getBlynkAuth(void); 72 | String getBlynkHost(void); 73 | int16_t getBlynkPort(void); 74 | int8_t setupBlynkStation(String network, String psk, 75 | String blynkAuth, 76 | String blynkHost = BB_BLYNK_HOST_DEFAULT, 77 | uint16_t blynkPort = BB_BLYNK_PORT_DEFAULT); 78 | void resetEEPROM(void); 79 | long WiFiConnectWithTimeout(unsigned long timeout); 80 | long BlynkConnectWithTimeout(const char * blynkAuth, const char * blynkDomain = BB_BLYNK_HOST_DEFAULT, 81 | uint16_t blynkPort = BB_BLYNK_PORT_DEFAULT, 82 | unsigned long timeout = BLYNK_CONNECT_TIMEOUT); 83 | bool checkSelfTestFlag(void); 84 | bool setSelfTestFlag(void); 85 | void performSelfTest(void); 86 | 87 | // BlynkBoard_Core_Firmware functions: 88 | void buttonRelease(void); 89 | void buttonChange(void); 90 | void blinkRGBTimer(void); 91 | void setRGB(uint32_t color); 92 | uint32_t rgbModeConfig(void); 93 | uint32_t blinkRGB(uint32_t onColor, uint32_t period); 94 | uint32_t breatheRGB(uint32_t colorMax, unsigned int breathePeriod); 95 | 96 | void setup() 97 | { 98 | // runMode keeps track of the Blynk Board's current operating mode. 99 | // It may be either MODE_WAIT_CONFIG, MODE_CONFIG, MODE_CONFIG_DEVICE_CONNECTED, 100 | // MODE_CONNECTING_WIFI, MODE_CONNECTING_BLYNK, MODE_BLYNK_RUN, 101 | // or MODE_BLYNK_ERROR. 102 | 103 | // Initializes: Serial terminal, randomSeed, WS2812 RGB LED, 104 | // GP0 button, GP5 LED, SPIFFS (flash storage), and EEPROM. 105 | initHardware(); 106 | 107 | #ifdef SELF_TEST_ENABLED 108 | if (!checkSelfTestFlag()) 109 | { 110 | BB_PRINT("Performing self test"); 111 | runMode = MODE_SELF_TEST; // Set mode to control RGB LED 112 | // Self-test Will end in an infinite loop on either success or fail 113 | performSelfTest(); 114 | } 115 | #endif 116 | 117 | runMode = MODE_WAIT_CONFIG; 118 | previousMode = runMode; // Previous mode keeps track of the previous runMode 119 | 120 | #ifdef DEBUG_ENABLED 121 | if (0 == digitalRead(BUTTON_PIN)) 122 | { 123 | BB_DEBUG("Button pressed => reset config"); 124 | resetEEPROM(); 125 | } 126 | #endif 127 | 128 | // Check if we're coming back from a 129 | if (!checkFailAPSetupFlag()) 130 | { 131 | BB_DEBUG("Failed to setup AP last time"); 132 | runMode = MODE_CONFIG; 133 | } 134 | else if (checkConfigFlag()) // Checks EEPROM to determine if the Blynk/WiFi are configured 135 | { // If the flag has been set, 136 | g_blynkAuthStr = getBlynkAuth(); // read the stored auth token 137 | BB_PRINT("Auth token: " + g_blynkAuthStr); 138 | g_blynkHostStr = getBlynkHost(); // Read the stored host 139 | BB_PRINT("Blynk Host: " + g_blynkHostStr); 140 | g_blynkPort = getBlynkPort(); // Read the stored port 141 | BB_PRINT("Blynk Port: " + String(g_blynkPort)); 142 | 143 | // As long as the auth token, host, and port aren't null 144 | if ((g_blynkAuthStr != 0) && (g_blynkHostStr != 0) && (g_blynkPort != 0)) 145 | { 146 | // Connect to WiFi network stored in ESP8266's persistent flash 147 | if (WiFiConnectWithTimeout(WIFI_STA_CONNECT_TIMEOUT)) 148 | { // If we successfully connect to WiFi, connect to Blynk 149 | BB_PRINT("Connected to WiFi!"); 150 | if (BlynkConnectWithTimeout(g_blynkAuthStr.c_str(), g_blynkHostStr.c_str(), 151 | g_blynkPort, BLYNK_CONNECT_TIMEOUT)) 152 | { // If we successfully connect to Blynk 153 | BB_PRINT("Connected to Blynk!"); 154 | blynkSetup(); // Run the Blynk setup function [BlynkBoard_BlynkMode] 155 | } 156 | } 157 | } 158 | else 159 | { 160 | runMode = MODE_CONFIG; 161 | } 162 | } 163 | else 164 | { 165 | runMode = MODE_CONFIG; 166 | } 167 | } 168 | 169 | void loop() 170 | { 171 | switch (runMode) 172 | { 173 | case MODE_SELF_TEST: 174 | break; 175 | case MODE_WAIT_CONFIG: // Do nothing, wait for GP0 button 176 | break; 177 | case MODE_CONFIG: // Config mode - no connected device 178 | checkForStations(); // Check for any new connected device 179 | checkSerialConfig(); // Check for serial config messages 180 | if (previousMode != MODE_CONFIG) 181 | { 182 | generateSSIDSuffix(false); // Start the AP with the persistent BlynkMe-CCCC SSID 183 | setupServer(); // Start the config server up: 184 | previousMode = MODE_CONFIG; 185 | Serial.print(SERIAL_MESSAGE_HELP); 186 | } 187 | break; 188 | case MODE_CONFIG_DEVICE_CONNECTED: // Config mode - connected device 189 | checkForStations(); // Check if any stations disconnect 190 | handleConfigServer(); // Serve up the config webpage 191 | checkSerialConfig(); // Check for serial config messages 192 | break; 193 | case MODE_BLYNK_RUN: // Main Blynk Demo run mode 194 | if (previousMode != MODE_BLYNK_RUN) // If this is the first execution of BLYNK_RUN since a state change 195 | { 196 | previousMode = MODE_BLYNK_RUN; // previousMode is used by MODE_BLYNK_ERROR 197 | } 198 | Blynk.run(); 199 | if (Blynk.connected()) // If Blynk is connected 200 | { 201 | blynkLoop(); // Do the blynkLoop [BlynkBoard_BlynkMode] 202 | } 203 | else 204 | { 205 | runMode = MODE_BLYNK_ERROR; // Otherwise switch to MODE_BLYNK_ERROR mode 206 | BB_DEBUG("Blynk Disconnected"); 207 | } 208 | break; 209 | case MODE_BLYNK_ERROR: // Error connecting to Blynk 210 | if (previousMode != MODE_BLYNK_ERROR) // If we just got here 211 | { 212 | g_blynkAuthStr = getBlynkAuth(); // read the stored auth token 213 | g_blynkHostStr = getBlynkHost(); // Read the stored host 214 | g_blynkPort = getBlynkPort(); // Read the stored port 215 | Blynk.config(g_blynkAuthStr.c_str(), g_blynkHostStr.c_str(), g_blynkPort); 216 | blinker.attach_ms(1, blinkRGBTimer); // Turn on the RGB blink timer 217 | previousMode = MODE_BLYNK_ERROR; // Update previousMode so blink timer isn't re-called 218 | } 219 | Blynk.run(); // Try to do a Blynk run 220 | if (Blynk.connected()) // If it establishes a connection 221 | { 222 | runMode = MODE_BLYNK_RUN; // Change to Blynk Run mode 223 | BB_DEBUG("Blynk Connected"); 224 | } 225 | break; 226 | default: // Modes not defined: MODE_CONNECTING_WIFI, MODE_CONNECTING_BLYNK 227 | break; 228 | } 229 | } 230 | 231 | // buttonRelease is called when a button has been held 232 | // for BUTTON_HOLD_TIME_MIN (3s) and released 233 | void buttonRelease(void) 234 | { 235 | // A button press has different effects in different modes: 236 | switch (runMode) 237 | { 238 | case MODE_WAIT_CONFIG: // If we're in "wait for config mode" 239 | BB_DEBUG("Switching to config mode"); 240 | previousMode = runMode; 241 | runMode = MODE_CONFIG; // A button release will switch to config mode 242 | break; 243 | case MODE_CONFIG: // If we're in config mode: 244 | BB_DEBUG("Generating a new SSID suffix"); 245 | generateSSIDSuffix(true); // Create a new SSID suffix 246 | break; 247 | case MODE_CONNECTING_WIFI: 248 | case MODE_CONNECTING_BLYNK: 249 | BB_DEBUG("Switching to config mode"); 250 | runMode = MODE_CONFIG; 251 | break; 252 | } 253 | } 254 | 255 | /* Note: The SparkFun Blynk board add-on is now supported by 256 | the ESP8266 Community! If you are installing the ESP8266 257 | board add-on v2.6.0+ and using an interrupt service routine, 258 | you must include `ICACHE_RAM_ATTR` before the function 259 | definition. In this case, buttonChange() is the ISR. */ 260 | 261 | ICACHE_RAM_ATTR void buttonChange(void) 262 | { 263 | static unsigned long buttonPressTime = 0; 264 | if (digitalRead(BUTTON_PIN)) // Button rising - released 265 | { 266 | runMode = previousMode; // Leave button press mode 267 | blinkCount = 0; 268 | BB_DEBUG("Button released"); 269 | 270 | if (runMode == MODE_SELF_TEST) 271 | { 272 | if (selfTestResult == SELF_TEST_SUCCESS_VALUE) 273 | { 274 | // Set selftest flag 275 | setSelfTestFlag(); 276 | previousMode = runMode; 277 | runMode = MODE_CONFIG; 278 | generateSSIDSuffix(true); // Create a new SSID suffix 279 | } 280 | } 281 | else 282 | { 283 | unsigned long buttonHoldTime = millis() - buttonPressTime; 284 | if (buttonHoldTime >= BUTTON_HOLD_TIME_MIN) 285 | { 286 | // If the button has been held for minimum time (3s) 287 | // execute the button release code: 288 | buttonRelease(); 289 | } 290 | } 291 | } 292 | else // Button falling - pressed 293 | { 294 | BB_DEBUG("Button pressed"); 295 | buttonPressTime = millis(); // Log the press time 296 | if ((runMode == MODE_WAIT_CONFIG) || (runMode == MODE_CONFIG) || 297 | (runMode == MODE_CONNECTING_WIFI) || (runMode == MODE_CONNECTING_BLYNK)) 298 | { // If in config, wait-for-config mode, or trying to connect 299 | previousMode = runMode; // Store current mode 300 | runMode = MODE_BUTTON_HOLD; // set to button-hold mode 301 | blinkCount = 0; // Restart LED 302 | } 303 | } 304 | } 305 | 306 | // This function is tied to the blinker Ticker. It will either blink or 307 | // breathe the RGB LED, then re-set the Ticker to be called at either 308 | // the same, or a new return time. 309 | void blinkRGBTimer(void) 310 | { 311 | uint32_t returnTime = 0; 312 | rgbSetByProject = false; 313 | switch (runMode) 314 | { 315 | case MODE_SELF_TEST: 316 | returnTime = rgbModeSelfTest(); 317 | break; 318 | case MODE_WAIT_CONFIG: // Waiting for config mode, blink white: 319 | returnTime = blinkRGB(RGB_STATUS_MODE_WAIT_CONFIG, RGB_PERIOD_SELF_TEST); 320 | break; 321 | case MODE_CONFIG: 322 | returnTime = rgbModeConfig(); // Blink the unique RGBYP code 323 | break; 324 | case MODE_BUTTON_HOLD: 325 | returnTime = breatheRGB(RGB_STATUS_MODE_BUTTON_HOLD, RGB_PERIOD_BUTTON_HOLD); 326 | break; 327 | case MODE_CONFIG_DEVICE_CONNECTED: // Device connected in config mode, blink purple: 328 | returnTime = blinkRGB(RGB_STATUS_AP_MODE_DEVICE_ON, RGB_PERIOD_AP_DEVICE_ON); 329 | break; 330 | case MODE_CONNECTING_WIFI: // Connecting to a WiFi AP, blink green 331 | returnTime = blinkRGB(RGB_STATUS_CONNECTING_WIFI, RGB_PERIOD_CONNECTING); 332 | break; 333 | case MODE_CONNECTING_BLYNK: // Connecting to Blynk cloud, blink blue 334 | returnTime = blinkRGB(RGB_STATUS_CONNECTING_BLYNK, RGB_PERIOD_BLYNK_CONNECTING); 335 | break; 336 | case MODE_BLYNK_RUN: // In Blynk run mode, breathe blue 337 | returnTime = breatheRGB(RGB_STATUS_CONNECTED_BLYNK, RGB_PERIOD_RUNNING); 338 | break; 339 | case MODE_BLYNK_ERROR: // Error connecting to Blynk, blink yellow 340 | returnTime = blinkRGB(RGB_STATUS_CANT_CONNECT_BLYNK, RGB_PERIOD_BLINK_ERROR); 341 | break; 342 | } 343 | if (returnTime > 0) // Call this function again in returnTime ms 344 | blinker.attach_ms(returnTime, blinkRGBTimer); 345 | } 346 | // Blink the LED with a unique-ish combination of R/G/B/Y/P 347 | // Returns a time, in ms, when this function should be called again 348 | uint32_t rgbModeConfig(void) 349 | { 350 | uint32_t retVal = 0; 351 | 352 | // Assume blinkCount is anywhere between 0-255 353 | // If it's greater than our suffix length (4) times two 354 | if (blinkCount >= SSID_SUFFIX_LENGTH * 2) 355 | { 356 | setRGB(WS2812_OFF); // Turn the LED off 357 | retVal = RGB_PERIOD_AP_STOP; // Come back in a longer time 358 | blinkCount = 0; // Reset blinkCount 359 | } 360 | else // If we're blinking a color, or mid-color 361 | { 362 | if (blinkCount % 2 == 0) // If even, blink the color 363 | setRGB(SSID_COLORS[ssidSuffixIndex[blinkCount / 2]]); 364 | else // If odd, turn the LED off 365 | setRGB(WS2812_OFF); 366 | retVal = RGB_PERIOD_AP / 2; // Come back in a shorter time 367 | blinkCount++; // Increment blinkCount 368 | } 369 | 370 | return retVal; // Return the blink time 371 | } 372 | 373 | // Continuously blink through r/g/b 374 | uint32_t rgbModeSelfTest(void) 375 | { 376 | if (blinkCount > 2) blinkCount = 0; 377 | 378 | if (blinkCount == 0) setRGB(0x200000); // Red 379 | else if (blinkCount == 1) setRGB(0x002000); // Green 380 | else if (blinkCount == 2) setRGB(0x000020); // Blue 381 | 382 | blinkCount++; // Increment blinkCount 383 | blinkCount %= 3; // Limit blinkCount range to 0-2 384 | return RGB_PERIOD_START; 385 | } 386 | 387 | // Blink the LED a specific color with a set period. 388 | // 50% duty cycle ~ even on/off time. 389 | // Returns a time, in ms, when this function should be called again 390 | uint32_t blinkRGB(uint32_t onColor, uint32_t period) 391 | { 392 | // Assume blinkCount is anywhere between 0-255 393 | if (blinkCount % 2 == 0) // If blinkCount is even (0) 394 | { 395 | setRGB(onColor); // Turn the LED on 396 | blinkCount = 1; // Turn the LED off next time 397 | } 398 | else // If blinkCount is odd (1) 399 | { 400 | setRGB(WS2812_OFF); // Turn LED off 401 | blinkCount = 0; // Turn the LED on next time 402 | } 403 | 404 | return period / 2; // Come back in half the period 405 | } 406 | 407 | // Breathe the LED between off and a maximum 32-bit color value 408 | // Returns a time, in ms, when this function should be called again 409 | uint32_t breatheRGB(uint32_t colorMax, unsigned int breathePeriod) 410 | { 411 | // Find the three r/g/b colors from colorMax: 412 | uint8_t redMax = (colorMax & 0xFF0000) >> 16; 413 | uint8_t greenMax = (colorMax & 0x00FF00) >> 8; 414 | uint8_t blueMax = (colorMax & 0x0000FF); 415 | 416 | // Determine the color brightness, based on blinkCount. 417 | // brightness will steadily rise from 0 to 128, then steadily fall back to 0 418 | uint8_t brightness; 419 | if (blinkCount < 128) // If it's 0-128 420 | brightness = blinkCount; // Leave it be 421 | else // If it's 129->255 422 | brightness = 255 - blinkCount; // Adjust it to 127-0 423 | 424 | // Multiply our three colors by the brightness: 425 | redMax *= ((float)brightness / 128.0); 426 | greenMax *= ((float)brightness / 128.0); 427 | blueMax *= ((float)brightness / 128.0); 428 | // And turn the LED to that color: 429 | setRGB(rgb.Color(redMax, greenMax, blueMax)); 430 | 431 | blinkCount++; // Increment blinkCount 432 | // This function relies on the 8-bit, unsigned blinkCount rolling over. 433 | // If it's 255, it'll go back to 0. 434 | return breathePeriod / 256; 435 | } 436 | 437 | // All-in-one RGB-setting function 438 | void setRGB(uint32_t color) 439 | { 440 | rgb.setPixelColor(0, color); 441 | rgb.show(); 442 | } 443 | -------------------------------------------------------------------------------- /Firmware/BlynkBoard_Core_Firmware/BlynkBoard_ConfigMode.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | BlynkBoard_ConfigMode.ino 3 | Blynk Board Firmware: Config Mode Source 4 | Jim Lindblom @ SparkFun Electronics 5 | February 24, 2016 6 | https://github.com/sparkfun/Blynk_Board_ESP8266/Firmware 7 | 8 | This file, part of the BlynkBoard Firmware, implements all "Config Mode" 9 | functions of the BlynkBoard Core Firmware. That includes setting up an access 10 | point (AP), serving the config server, and listening for serial configuration 11 | commands. 12 | 13 | Resources: 14 | ESP8266WiFi Library (included with ESP8266 Arduino board definitions) 15 | ESP8266WiFiClient Library (included with ESP8266 Arduino board definitions) 16 | ESP8266WebServer Library (included with ESP8266 Arduino board definitions) 17 | 18 | License: 19 | This is released under the MIT license (http://opensource.org/licenses/MIT). 20 | Please see the included LICENSE.md for more information. 21 | 22 | Development environment specifics: 23 | Arduino IDE 1.6.7 24 | SparkFun BlynkBoard - ESP8266 25 | ******************************************************************************/ 26 | #include "string.h" 27 | ESP8266WebServer server(BLYNK_WIFI_CONFIG_PORT); 28 | const String SSIDWebFormHdr = R"raw_string( 29 | 30 | 31 | 32 | 33 | 34 | SparkFun Blynk Board Config 35 | 92 | 93 | 94 | )raw_string"; 95 | 96 | const String SSIDWebFormStart = R"raw_string( 97 |
98 |

SparkFun Blynk Board Config

99 | )raw_string"; 100 | 101 | const String SSIDWebFormBtm = R"raw_string( 102 |

Enter the password for your network:
(leave blank if the network is open)
103 |

104 |

Enter the Blynk auth token for your project:
105 |

106 |

Enter the Blynk host and port for your Blynk project:
107 | (leave as-is for defaults)
108 |

109 |
110 | 111 | 121 | 122 | )raw_string"; 123 | 124 | const String SSIDWebRspFtr = R"raw_string( 125 |

RGB LED Status

126 |

● Blue: Connecting to WiFi
127 | ● Blynk green (blink): Connecting to blink
128 | ● Blynk green (fade in/out): Connected to Blynk!
129 | ● Purple (blink): Failed to connect to WiFi or Blynk. Phone re-connected.
130 | ● R/G/B/Y/P color code: Failed to connect to WiFi or Blynk
131 | ● Yellow (blink): Error connecting to Blynk server.

132 |
133 |

If the LED is "breathing" Blynk-green, it's connected! Switch to the Blynk app to start blinking!

134 |

If the LED has reverted back to purple or the R/G/B/Y/P color code, go back to blynkme.cc and try again.

135 |

If the LED is blinking yellow, the Blynk Board encountered an error. Unplug it, wait a couple seconds, then plug it back in. It should attempt to connect again.

136 | 137 | )raw_string"; 138 | 139 | 140 | DNSServer dnsServer; 141 | const byte DNS_PORT = 53; 142 | 143 | bool setupAP(char * ssidName) 144 | { 145 | WiFi.mode(WIFI_AP); 146 | WiFi.softAPConfig(defaultAPIP, defaultAPIP, defaultAPSub); 147 | WiFi.softAP(ssidName); 148 | 149 | IPAddress myIP = WiFi.softAPIP(); 150 | BB_PRINT("AP IP address: " + String(myIP[0]) + "." + 151 | String(myIP[1]) + "." + String(myIP[2]) + "." + String(myIP[3])); 152 | 153 | //! ESP8266 bug: IP may be 0 -- makes AP un-connectable 154 | if (myIP == (uint32_t)0) 155 | { 156 | writeAPSetupFlag(false); 157 | return false; 158 | } 159 | else 160 | { 161 | writeAPSetupFlag(true); 162 | return true; 163 | } 164 | } 165 | 166 | void handleRoot(void) // On root request to server, send form 167 | { 168 | String webPage = SSIDWebFormHdr; 169 | webPage += SSIDWebFormStart; 170 | BB_DEBUG("Initiating WiFi network scan."); 171 | WiFi.enableSTA(true); 172 | 173 | // WiFi.scanNetworks will return the number of networks found 174 | int n = WiFi.scanNetworks(); 175 | if (n != 0) 176 | { 177 | BB_DEBUG("Scan found " + String(n) + " networks"); 178 | webPage += "

Select your WiFi network:
"; 179 | webPage += "
"; 204 | webPage += "

"; 205 | } 206 | else 207 | { 208 | BB_DEBUG("Scan didn't find any networks."); 209 | webPage += "

Type in your network name:
"; 210 | webPage += "

"; 211 | } 212 | webPage += SSIDWebFormBtm; 213 | WiFi.enableSTA(false); 214 | 215 | server.send(200, "text/html", webPage); 216 | BB_DEBUG("Root served."); 217 | } 218 | 219 | void handleReset(void) 220 | { 221 | resetEEPROM(); 222 | server.send(200, "text/html", "EEPROM reset"); 223 | } 224 | 225 | void handleBoardInfo(void) 226 | { 227 | char buff[256]; 228 | const char* fmt = 229 | R"json({ 230 | "board": "Blynk Board", 231 | "vendor": "SparkFun", 232 | "fw_ver": "%s", 233 | "hw_ver": "%s", 234 | "blynk_ver": "%s" 235 | })json"; 236 | 237 | snprintf(buff, sizeof(buff), fmt, 238 | BLYNKBOARD_FIRMWARE_VERSION, 239 | BLYNKBOARD_HARDWARE_VERSION, 240 | BLYNK_VERSION 241 | ); 242 | server.send(200, "application/json", buff); 243 | } 244 | 245 | void handleConfig(void) // handler for "/config" server request 246 | { 247 | // Gather the arguments into String variables 248 | String ssidManual = server.arg("ssidManual"); // Network name - entered manually 249 | String ssidScan = server.arg("ssid"); // Network name - entered from select list 250 | String pass = server.arg("pass"); // Network password 251 | g_blynkAuthStr = server.arg("blynk"); // Blynk auth code 252 | g_blynkHostStr = server.arg("host"); 253 | g_blynkPort = server.arg("port").toInt(); 254 | 255 | String ssid; // Select between the manually or scan entered 256 | if (ssidScan != "") // Prefer scan entered 257 | ssid = ssidScan; 258 | else if (ssidManual != "") // Otherwise, if manually entered is valid 259 | ssid = ssidManual; // Use manually entered 260 | else 261 | return; 262 | 263 | BB_PRINT("SSID: " + ssid); 264 | BB_PRINT("Pass: " + pass); 265 | BB_PRINT("Auth: " + g_blynkAuthStr); 266 | BB_PRINT("Host: " + g_blynkHostStr); 267 | BB_PRINT("Port: " + String(g_blynkPort)); 268 | 269 | // Send a response back to the requester 270 | String rsp = SSIDWebFormHdr; 271 | rsp += "

WiFi/Blynk Connecting...

Your Blynk Board is attempting to connect to "; 272 | rsp += ssid + "

"; 273 | rsp += "

Then it'll use the Blynk auth token " + g_blynkAuthStr + ""; 274 | rsp += " to connect to " + g_blynkHostStr + ":" + String(g_blynkPort) + ".

"; 275 | rsp += SSIDWebRspFtr; 276 | 277 | server.send(200, "text/html", rsp); 278 | 279 | if ((g_blynkHostStr != "") && (g_blynkPort != 0)) // If host and port are not null/0 280 | { 281 | BB_DEBUG("Connecting to " + g_blynkHostStr); 282 | setupBlynkStation(ssid, pass, g_blynkAuthStr, g_blynkHostStr, g_blynkPort); // Connect using those 283 | } 284 | else 285 | { 286 | BB_DEBUG("Connecting to default server"); 287 | setupBlynkStation(ssid, pass, g_blynkAuthStr); // Otherwise connect using defaults 288 | } 289 | } 290 | 291 | void setupServer(void) 292 | { 293 | // Set up DNS Server 294 | dnsServer.setTTL(300); // Time-to-live 300s 295 | dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure); // Return code for non-accessible domains 296 | #ifdef CAPTIVE_PORTAL_ENABLE 297 | dnsServer.start(DNS_PORT, "*", WiFi.softAPIP()); // Point all to our IP 298 | server.onNotFound(handleRoot); 299 | #else 300 | dnsServer.start(DNS_PORT, BLYNK_BOARD_URL, WiFi.softAPIP()); // Point blynkme.cc to our IP 301 | #endif 302 | 303 | server.on("/", handleRoot); 304 | server.on("/config", handleConfig); 305 | server.on("/reset", handleReset); 306 | server.on("/board_info.json", handleBoardInfo); 307 | server.begin(); 308 | 309 | BB_DEBUG("HTTP server started"); 310 | } 311 | 312 | void generateSSIDSuffix(bool newSuffix) 313 | { 314 | // If asking for a new suffix, or suffix hasn't been generated 315 | if ((newSuffix) || (EEPROM.read(EEPROM_SSID_GENERATED_ADDRESS) != 42)) 316 | { 317 | for (int i=0; i 0) 359 | runMode = MODE_CONFIG_DEVICE_CONNECTED; 360 | else 361 | runMode = MODE_CONFIG; 362 | } 363 | 364 | bool SerialWiFiScan(void) 365 | { 366 | int n = WiFi.scanNetworks(); 367 | if (n != 0) 368 | { 369 | char index; 370 | Serial.println("Scan found " + String(n) + " networks:"); 371 | Serial.println("0: Not listed (hidden network)"); 372 | for (int i=0; i