├── .gitattributes └── All_in_One_New_Blynk ├── BlynkState.h ├── ResetButton.h ├── OTA.h ├── Settings.h ├── All_in_One_New_Blynk.ino ├── ConfigStore.h ├── Indicator.h ├── BlynkEdgent.h └── ConfigMode.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /All_in_One_New_Blynk/BlynkState.h: -------------------------------------------------------------------------------- 1 | 2 | enum State { 3 | MODE_WAIT_CONFIG, 4 | MODE_CONFIGURING, 5 | MODE_CONNECTING_NET, 6 | MODE_CONNECTING_CLOUD, 7 | MODE_RUNNING, 8 | MODE_OTA_UPGRADE, 9 | MODE_SWITCH_TO_STA, 10 | MODE_RESET_CONFIG, 11 | MODE_ERROR, 12 | 13 | MODE_MAX_VALUE 14 | }; 15 | 16 | #if defined(APP_DEBUG) 17 | const char* StateStr[MODE_MAX_VALUE+1] = { 18 | "WAIT_CONFIG", 19 | "CONFIGURING", 20 | "CONNECTING_NET", 21 | "CONNECTING_CLOUD", 22 | "RUNNING", 23 | "OTA_UPGRADE", 24 | "SWITCH_TO_STA", 25 | "RESET_CONFIG", 26 | "ERROR", 27 | 28 | "INIT" 29 | }; 30 | #endif 31 | 32 | namespace BlynkState 33 | { 34 | volatile State state = MODE_MAX_VALUE; 35 | 36 | State get() { return state; } 37 | bool is (State m) { return (state == m); } 38 | void set(State m); 39 | }; 40 | -------------------------------------------------------------------------------- /All_in_One_New_Blynk/ResetButton.h: -------------------------------------------------------------------------------- 1 | /************************************************************** 2 | * This is a DEMO. You can use it only for development and testing. 3 | * 4 | * If you would like to add these features to your product, 5 | * please contact Blynk for Business: 6 | * 7 | * http://www.blynk.io/ 8 | * 9 | **************************************************************/ 10 | 11 | volatile bool g_buttonPressed = false; 12 | volatile uint32_t g_buttonPressTime = -1; 13 | 14 | void button_action(void) 15 | { 16 | BlynkState::set(MODE_RESET_CONFIG); 17 | } 18 | 19 | void button_change(void) 20 | { 21 | #if BOARD_BUTTON_ACTIVE_LOW 22 | bool buttonState = !digitalRead(BOARD_BUTTON_PIN); 23 | #else 24 | bool buttonState = digitalRead(BOARD_BUTTON_PIN); 25 | #endif 26 | 27 | if (buttonState && !g_buttonPressed) { 28 | g_buttonPressTime = millis(); 29 | g_buttonPressed = true; 30 | DEBUG_PRINT("Hold the button to reset configuration..."); 31 | } else if (!buttonState && g_buttonPressed) { 32 | g_buttonPressed = false; 33 | uint32_t buttonHoldTime = millis() - g_buttonPressTime; 34 | if (buttonHoldTime >= BUTTON_HOLD_TIME_ACTION) { 35 | button_action(); 36 | } else { 37 | // User action 38 | } 39 | g_buttonPressTime = -1; 40 | } 41 | } 42 | 43 | void button_init() 44 | { 45 | #if BOARD_BUTTON_ACTIVE_LOW 46 | pinMode(BOARD_BUTTON_PIN, INPUT_PULLUP); 47 | #else 48 | pinMode(BOARD_BUTTON_PIN, INPUT_PULLDOWN); 49 | #endif 50 | attachInterrupt(BOARD_BUTTON_PIN, button_change, CHANGE); 51 | } 52 | -------------------------------------------------------------------------------- /All_in_One_New_Blynk/OTA.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | String overTheAirURL; 7 | 8 | extern BlynkTimer timer; 9 | 10 | BLYNK_WRITE(InternalPinOTA) { 11 | overTheAirURL = param.asString(); 12 | 13 | timer.setTimeout(2000L, [](){ 14 | // Start OTA 15 | Blynk.logEvent("sys_ota", "OTA started"); 16 | 17 | // Disconnect, not to interfere with OTA process 18 | Blynk.disconnect(); 19 | 20 | BlynkState::set(MODE_OTA_UPGRADE); 21 | }); 22 | } 23 | 24 | void enterOTA() { 25 | BlynkState::set(MODE_OTA_UPGRADE); 26 | 27 | DEBUG_PRINT(String("Firmware update URL: ") + overTheAirURL); 28 | 29 | HTTPClient http; 30 | http.begin(overTheAirURL); 31 | 32 | int httpCode = http.GET(); 33 | if (httpCode != HTTP_CODE_OK) { 34 | DEBUG_PRINT("HTTP response should be 200"); 35 | BlynkState::set(MODE_ERROR); 36 | return; 37 | } 38 | int contentLength = http.getSize(); 39 | if (contentLength <= 0) { 40 | DEBUG_PRINT("Content-Length not defined"); 41 | BlynkState::set(MODE_ERROR); 42 | return; 43 | } 44 | 45 | bool canBegin = Update.begin(contentLength); 46 | if (!canBegin) { 47 | DEBUG_PRINT("Not enough space to begin OTA"); 48 | BlynkState::set(MODE_ERROR); 49 | return; 50 | } 51 | 52 | Client& client = http.getStream(); 53 | int written = Update.writeStream(client); 54 | if (written != contentLength) { 55 | DEBUG_PRINT(String("OTA written ") + written + " / " + contentLength + " bytes"); 56 | BlynkState::set(MODE_ERROR); 57 | return; 58 | } 59 | 60 | if (!Update.end()) { 61 | DEBUG_PRINT("Error #" + String(Update.getError())); 62 | BlynkState::set(MODE_ERROR); 63 | return; 64 | } 65 | 66 | if (!Update.isFinished()) { 67 | DEBUG_PRINT("Update failed."); 68 | BlynkState::set(MODE_ERROR); 69 | return; 70 | } 71 | 72 | DEBUG_PRINT("=== Update successfully completed. Rebooting."); 73 | restartMCU(); 74 | } 75 | -------------------------------------------------------------------------------- /All_in_One_New_Blynk/Settings.h: -------------------------------------------------------------------------------- 1 | /* 2 | * General options 3 | */ 4 | 5 | #define BOARD_HARDWARE_VERSION "1.0.0" 6 | 7 | /* 8 | * Board configuration (see examples below). 9 | */ 10 | 11 | #if defined(USE_WROVER_BOARD) 12 | 13 | // Custom board configuration 14 | #define BOARD_BUTTON_PIN 15 // Pin where user button is attached 15 | #define BOARD_BUTTON_ACTIVE_LOW true // true if button is "active-low" 16 | 17 | #define BOARD_LED_PIN_R 0 // Set R,G,B pins - if your LED is PWM RGB 18 | #define BOARD_LED_PIN_G 2 19 | #define BOARD_LED_PIN_B 4 20 | #define BOARD_LED_INVERSE false // true if LED is common anode, false if common cathode 21 | #define BOARD_LED_BRIGHTNESS 128 // 0..255 brightness control 22 | 23 | #else 24 | 25 | // Custom board configuration 26 | #define BOARD_BUTTON_PIN 0 // Pin where user button is attached 27 | #define BOARD_BUTTON_ACTIVE_LOW true // true if button is "active-low" 28 | 29 | #define BOARD_LED_PIN 20 // Set LED pin - if you have a single-color LED attached 30 | //#define BOARD_LED_PIN_R 27 // Set R,G,B pins - if your LED is PWM RGB 31 | //#define BOARD_LED_PIN_G 26 32 | //#define BOARD_LED_PIN_B 25 33 | //#define BOARD_LED_PIN_WS2812 33 // Set if your LED is WS2812 RGB 34 | #define BOARD_LED_INVERSE false // true if LED is common anode, false if common cathode 35 | #define BOARD_LED_BRIGHTNESS 64 // 0..255 brightness control 36 | 37 | #endif 38 | 39 | 40 | /* 41 | * Advanced options 42 | */ 43 | 44 | #define BUTTON_HOLD_TIME_INDICATION 3000 45 | #define BUTTON_HOLD_TIME_ACTION 10000 46 | 47 | #define BOARD_PWM_MAX 1023 48 | 49 | #define LEDC_CHANNEL_1 1 50 | #define LEDC_CHANNEL_2 2 51 | #define LEDC_CHANNEL_3 3 52 | #define LEDC_TIMER_BITS 10 53 | #define LEDC_BASE_FREQ 12000 54 | 55 | #define CONFIG_AP_URL "blynk.setup" 56 | #define CONFIG_DEFAULT_SERVER "blynk.cloud" 57 | #define CONFIG_DEFAULT_PORT 443 58 | 59 | #define WIFI_NET_CONNECT_TIMEOUT 30000 60 | #define WIFI_CLOUD_CONNECT_TIMEOUT 30000 61 | #define WIFI_AP_CONFIG_PORT 80 62 | #define WIFI_AP_IP IPAddress(192, 168, 4, 1) 63 | #define WIFI_AP_Subnet IPAddress(255, 255, 255, 0) 64 | //#define WIFI_CAPTIVE_PORTAL_ENABLE 65 | 66 | #define USE_TICKER 67 | //#define USE_TIMER_ONE 68 | //#define USE_TIMER_THREE 69 | 70 | #define BLYNK_NO_DEFAULT_BANNER 71 | 72 | #if defined(APP_DEBUG) 73 | #define DEBUG_PRINT(...) BLYNK_LOG1(__VA_ARGS__) 74 | #else 75 | #define DEBUG_PRINT(...) 76 | #endif 77 | -------------------------------------------------------------------------------- /All_in_One_New_Blynk/All_in_One_New_Blynk.ino: -------------------------------------------------------------------------------- 1 | 2 | // Fill-in information from your Blynk Template here 3 | #define BLYNK_TEMPLATE_ID "ID" 4 | #define BLYNK_DEVICE_NAME "NAME" 5 | 6 | 7 | #define BLYNK_FIRMWARE_VERSION "0.1.0" 8 | 9 | #define BLYNK_PRINT Serial 10 | //#define BLYNK_DEBUG 11 | 12 | #define APP_DEBUG 13 | 14 | // Define Relay Pins 15 | #define r1 15 16 | #define r2 2 17 | #define r3 4 18 | #define r4 22 19 | 20 | // Define Switch Pins 21 | #define s1 32 22 | #define s2 35 23 | #define s3 34 24 | #define s4 39 25 | 26 | // Pins of Fan Regulator Knob 27 | #define f1 27 28 | #define f2 14 29 | #define f3 12 30 | #define f4 13 31 | 32 | // Pins of Relay (Fan Speed Control) 33 | #define Speed1 21 34 | #define Speed2 19 35 | #define Speed4 18 36 | 37 | bool speed1_flag = 1; 38 | bool speed2_flag = 1; 39 | bool speed3_flag = 1; 40 | bool speed4_flag = 1; 41 | bool speed0_flag = 1; 42 | 43 | // Switch Flags 44 | bool SWITCH5_FLAG = 1; 45 | bool SWITCH6_FLAG = 1; 46 | bool SWITCH7_FLAG = 1; 47 | bool SWITCH8_FLAG = 1; 48 | 49 | // Default Relay State 50 | bool relay1 = LOW; 51 | bool relay2 = LOW; 52 | bool relay3 = LOW; 53 | bool relay4 = LOW; 54 | 55 | 56 | // Uncomment your board, or configure a custom board in Settings.h 57 | //#define USE_WROVER_BOARD 58 | 59 | #include "BlynkEdgent.h" 60 | 61 | 62 | BLYNK_WRITE(V0) 63 | { 64 | int fan_speed = param.asInt(); // assigning incoming value from pin V0 to a variable 65 | Serial.print("FAN VALUE -");Serial.print(fan_speed);Serial.println("-"); 66 | if (fan_speed == 0) 67 | { 68 | speed0(); 69 | } 70 | if (fan_speed == 1) 71 | { 72 | speed1(); 73 | } 74 | if (fan_speed == 2) 75 | { 76 | speed2(); 77 | } 78 | if (fan_speed == 3) 79 | { 80 | speed3(); 81 | } 82 | if (fan_speed == 4) 83 | { 84 | speed4(); 85 | } 86 | } 87 | 88 | BLYNK_WRITE(V1) 89 | { 90 | int pinValue = param.asInt(); // assigning incoming value from pin V1 to a variable 91 | relay1 = pinValue; 92 | digitalWrite(r1 , pinValue); 93 | // process received value 94 | } 95 | 96 | BLYNK_WRITE(V2) 97 | { 98 | int pinValue = param.asInt(); // assigning incoming value from pin V1 to a variable 99 | relay2 = pinValue; 100 | Serial.print("Relay2 - ");Serial.println(pinValue); 101 | digitalWrite(r2 , pinValue); 102 | // process received value 103 | } 104 | 105 | BLYNK_WRITE(V3) 106 | { 107 | int pinValue = param.asInt(); // assigning incoming value from pin V1 to a variable 108 | relay3 = pinValue; 109 | Serial.print("Relay3 - ");Serial.println(pinValue); 110 | digitalWrite(r3 , pinValue); 111 | // process received value 112 | } 113 | 114 | BLYNK_WRITE(V4) 115 | { 116 | int pinValue = param.asInt(); // assigning incoming value from pin V1 to a variable 117 | relay4 = pinValue; 118 | digitalWrite(r4 , pinValue); 119 | // process received value 120 | } 121 | 122 | 123 | void setup() 124 | { 125 | Serial.begin(115200); 126 | delay(100); 127 | 128 | pinMode(s1, INPUT_PULLUP); 129 | pinMode(s2, INPUT_PULLUP); 130 | pinMode(s3, INPUT_PULLUP); 131 | pinMode(s4, INPUT_PULLUP); 132 | 133 | pinMode(f1, INPUT_PULLUP); 134 | pinMode(f2, INPUT_PULLUP); 135 | pinMode(f3, INPUT_PULLUP); 136 | pinMode(f4, INPUT_PULLUP); 137 | 138 | pinMode(r1, OUTPUT); 139 | pinMode(r2, OUTPUT); 140 | pinMode(r3, OUTPUT); 141 | pinMode(r4, OUTPUT); 142 | 143 | pinMode(Speed1, OUTPUT); 144 | pinMode(Speed2, OUTPUT); 145 | pinMode(Speed4, OUTPUT); 146 | 147 | digitalWrite(r1, 0); 148 | digitalWrite(r2, 0); 149 | digitalWrite(r3, 0); 150 | digitalWrite(r4, 0); 151 | 152 | BlynkEdgent.begin(); 153 | } 154 | 155 | void loop() 156 | { 157 | BlynkEdgent.run(); 158 | swfeedback(); 159 | } 160 | -------------------------------------------------------------------------------- /All_in_One_New_Blynk/ConfigStore.h: -------------------------------------------------------------------------------- 1 | 2 | #define CONFIG_FLAG_VALID 0x01 3 | #define CONFIG_FLAG_STATIC_IP 0x02 4 | 5 | #define BLYNK_PROV_ERR_NONE 0 // All good 6 | #define BLYNK_PROV_ERR_CONFIG 700 // Invalid config from app (malformed token,etc) 7 | #define BLYNK_PROV_ERR_NETWORK 701 // Could not connect to the router 8 | #define BLYNK_PROV_ERR_CLOUD 702 // Could not connect to the cloud 9 | #define BLYNK_PROV_ERR_TOKEN 703 // Invalid token error (after connection) 10 | #define BLYNK_PROV_ERR_INTERNAL 704 // Other issues (i.e. hardware failure) 11 | 12 | struct ConfigStore { 13 | uint32_t magic; 14 | char version[15]; 15 | uint8_t flags; 16 | 17 | char wifiSSID[34]; 18 | char wifiPass[64]; 19 | 20 | char cloudToken[34]; 21 | char cloudHost[34]; 22 | uint16_t cloudPort; 23 | 24 | uint32_t staticIP; 25 | uint32_t staticMask; 26 | uint32_t staticGW; 27 | uint32_t staticDNS; 28 | uint32_t staticDNS2; 29 | 30 | int last_error; 31 | 32 | void setFlag(uint8_t mask, bool value) { 33 | if (value) { 34 | flags |= mask; 35 | } else { 36 | flags &= ~mask; 37 | } 38 | } 39 | 40 | bool getFlag(uint8_t mask) { 41 | return (flags & mask) == mask; 42 | } 43 | } __attribute__((packed)); 44 | 45 | ConfigStore configStore; 46 | 47 | const ConfigStore configDefault = { 48 | 0x626C6E6B, 49 | BLYNK_FIRMWARE_VERSION, 50 | 0x00, 51 | 52 | "", 53 | "", 54 | 55 | "invalid token", 56 | CONFIG_DEFAULT_SERVER, 57 | CONFIG_DEFAULT_PORT, 58 | 0, 59 | BLYNK_PROV_ERR_NONE 60 | }; 61 | 62 | template 63 | void CopyString(const String& s, T(&arr)[size]) { 64 | s.toCharArray(arr, size); 65 | } 66 | 67 | static bool config_load_blnkopt() 68 | { 69 | static const char blnkopt[] = "blnkopt\0" 70 | BLYNK_PARAM_KV("ssid" , BLYNK_PARAM_PLACEHOLDER_64 71 | BLYNK_PARAM_PLACEHOLDER_64 72 | BLYNK_PARAM_PLACEHOLDER_64 73 | BLYNK_PARAM_PLACEHOLDER_64) 74 | BLYNK_PARAM_KV("host" , CONFIG_DEFAULT_SERVER) 75 | BLYNK_PARAM_KV("port" , BLYNK_TOSTRING(CONFIG_DEFAULT_PORT)) 76 | "\0"; 77 | 78 | BlynkParam prov(blnkopt+8, sizeof(blnkopt)-8-2); 79 | BlynkParam::iterator ssid = prov["ssid"]; 80 | BlynkParam::iterator pass = prov["pass"]; 81 | BlynkParam::iterator auth = prov["auth"]; 82 | BlynkParam::iterator host = prov["host"]; 83 | BlynkParam::iterator port = prov["port"]; 84 | 85 | if (!(ssid.isValid() && auth.isValid())) { 86 | return false; 87 | } 88 | 89 | // reset to defaut before loading values from blnkopt 90 | configStore = configDefault; 91 | 92 | if (ssid.isValid()) { CopyString(ssid.asStr(), configStore.wifiSSID); } 93 | if (pass.isValid()) { CopyString(pass.asStr(), configStore.wifiPass); } 94 | if (auth.isValid()) { CopyString(auth.asStr(), configStore.cloudToken); } 95 | if (host.isValid()) { CopyString(host.asStr(), configStore.cloudHost); } 96 | if (port.isValid()) { configStore.cloudPort = port.asInt(); } 97 | 98 | return true; 99 | } 100 | 101 | #include 102 | Preferences preferences; 103 | 104 | void config_load() 105 | { 106 | memset(&configStore, 0, sizeof(configStore)); 107 | preferences.getBytes("config", &configStore, sizeof(configStore)); 108 | if (configStore.magic != configDefault.magic) { 109 | DEBUG_PRINT("Using default config."); 110 | configStore = configDefault; 111 | return; 112 | } 113 | } 114 | 115 | bool config_save() 116 | { 117 | preferences.putBytes("config", &configStore, sizeof(configStore)); 118 | DEBUG_PRINT("Configuration stored to flash"); 119 | return true; 120 | } 121 | 122 | bool config_init() 123 | { 124 | preferences.begin("blynk", false); 125 | config_load(); 126 | return true; 127 | } 128 | 129 | void enterResetConfig() 130 | { 131 | DEBUG_PRINT("Resetting configuration!"); 132 | configStore = configDefault; 133 | config_save(); 134 | eraseMcuConfig(); 135 | BlynkState::set(MODE_WAIT_CONFIG); 136 | } 137 | 138 | void config_set_last_error(int error) { 139 | // Only set error if not provisioned 140 | if (!configStore.getFlag(CONFIG_FLAG_VALID)) { 141 | configStore = configDefault; 142 | configStore.last_error = error; 143 | BLYNK_LOG2("Last error code: ", error); 144 | config_save(); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /All_in_One_New_Blynk/Indicator.h: -------------------------------------------------------------------------------- 1 | 2 | #if defined(BOARD_LED_PIN_WS2812) 3 | #include // Library: https://github.com/adafruit/Adafruit_NeoPixel 4 | 5 | Adafruit_NeoPixel rgb = Adafruit_NeoPixel(1, BOARD_LED_PIN_WS2812, NEO_GRB + NEO_KHZ800); 6 | #endif 7 | 8 | void indicator_run(); 9 | 10 | #if !defined(BOARD_LED_BRIGHTNESS) 11 | #define BOARD_LED_BRIGHTNESS 255 12 | #endif 13 | 14 | #if defined(BOARD_LED_PIN_WS2812) || defined(BOARD_LED_PIN_R) 15 | #define BOARD_LED_IS_RGB 16 | #endif 17 | 18 | #define DIMM(x) ((x)*(BOARD_LED_BRIGHTNESS)/255) 19 | #define RGB(r,g,b) (DIMM(r) << 16 | DIMM(g) << 8 | DIMM(b) << 0) 20 | 21 | class Indicator { 22 | public: 23 | 24 | enum Colors { 25 | COLOR_BLACK = RGB(0x00, 0x00, 0x00), 26 | COLOR_WHITE = RGB(0xFF, 0xFF, 0xE7), 27 | COLOR_BLUE = RGB(0x0D, 0x36, 0xFF), 28 | COLOR_BLYNK = RGB(0x2E, 0xFF, 0xB9), 29 | COLOR_RED = RGB(0xFF, 0x10, 0x08), 30 | COLOR_MAGENTA = RGB(0xA7, 0x00, 0xFF), 31 | }; 32 | 33 | Indicator() { 34 | m_Counter = 0; 35 | initLED(); 36 | } 37 | 38 | uint32_t run() { 39 | State currState = BlynkState::get(); 40 | 41 | // Reset counter if indicator state changes 42 | if (m_PrevState != currState) { 43 | m_PrevState = currState; 44 | m_Counter = 0; 45 | } 46 | 47 | if (g_buttonPressed) { 48 | if (millis() - g_buttonPressTime > BUTTON_HOLD_TIME_ACTION) { return beatLED(COLOR_WHITE, (int[]){ 100, 100 }); } 49 | if (millis() - g_buttonPressTime > BUTTON_HOLD_TIME_INDICATION) { return waveLED(COLOR_WHITE, 1000); } 50 | } 51 | switch (currState) { 52 | case MODE_RESET_CONFIG: 53 | case MODE_WAIT_CONFIG: return beatLED(COLOR_BLUE, (int[]){ 50, 500 }); 54 | case MODE_CONFIGURING: return beatLED(COLOR_BLUE, (int[]){ 200, 200 }); 55 | case MODE_CONNECTING_NET: return beatLED(COLOR_BLYNK, (int[]){ 50, 500 }); 56 | case MODE_CONNECTING_CLOUD: return beatLED(COLOR_BLYNK, (int[]){ 100, 100 }); 57 | case MODE_RUNNING: return waveLED(COLOR_BLYNK, 5000); 58 | case MODE_OTA_UPGRADE: return beatLED(COLOR_MAGENTA, (int[]){ 50, 50 }); 59 | default: return beatLED(COLOR_RED, (int[]){ 80, 100, 80, 1000 } ); 60 | } 61 | } 62 | 63 | protected: 64 | 65 | /* 66 | * LED drivers 67 | */ 68 | 69 | #if defined(BOARD_LED_PIN_WS2812) // Addressable, NeoPixel RGB LED 70 | 71 | void initLED() { 72 | rgb.begin(); 73 | setRGB(COLOR_BLACK); 74 | } 75 | 76 | void setRGB(uint32_t color) { 77 | rgb.setPixelColor(0, color); 78 | rgb.show(); 79 | } 80 | 81 | #elif defined(BOARD_LED_PIN_R) // Normal RGB LED (common anode or common cathode) 82 | 83 | void initLED() { 84 | ledcAttachPin(BOARD_LED_PIN_R, LEDC_CHANNEL_1); 85 | ledcAttachPin(BOARD_LED_PIN_G, LEDC_CHANNEL_2); 86 | ledcAttachPin(BOARD_LED_PIN_B, LEDC_CHANNEL_3); 87 | 88 | ledcSetup(LEDC_CHANNEL_1, LEDC_BASE_FREQ, LEDC_TIMER_BITS); 89 | ledcSetup(LEDC_CHANNEL_2, LEDC_BASE_FREQ, LEDC_TIMER_BITS); 90 | ledcSetup(LEDC_CHANNEL_3, LEDC_BASE_FREQ, LEDC_TIMER_BITS); 91 | } 92 | 93 | void setRGB(uint32_t color) { 94 | uint8_t r = (color & 0xFF0000) >> 16; 95 | uint8_t g = (color & 0x00FF00) >> 8; 96 | uint8_t b = (color & 0x0000FF); 97 | #if BOARD_LED_INVERSE 98 | ledcWrite(LEDC_CHANNEL_1, BOARD_PWM_MAX - r); 99 | ledcWrite(LEDC_CHANNEL_2, BOARD_PWM_MAX - g); 100 | ledcWrite(LEDC_CHANNEL_3, BOARD_PWM_MAX - b); 101 | #else 102 | ledcWrite(LEDC_CHANNEL_1, r); 103 | ledcWrite(LEDC_CHANNEL_2, g); 104 | ledcWrite(LEDC_CHANNEL_3, b); 105 | #endif 106 | } 107 | 108 | #elif defined(BOARD_LED_PIN) // Single color LED 109 | 110 | void initLED() { 111 | ledcSetup(LEDC_CHANNEL_1, LEDC_BASE_FREQ, LEDC_TIMER_BITS); 112 | ledcAttachPin(BOARD_LED_PIN, LEDC_CHANNEL_1); 113 | } 114 | 115 | void setLED(uint32_t color) { 116 | #if BOARD_LED_INVERSE 117 | ledcWrite(LEDC_CHANNEL_1, BOARD_PWM_MAX - color); 118 | #else 119 | ledcWrite(LEDC_CHANNEL_1, color); 120 | #endif 121 | } 122 | 123 | #else 124 | 125 | #warning Invalid LED configuration. 126 | 127 | #endif 128 | 129 | /* 130 | * Animations 131 | */ 132 | 133 | uint32_t skipLED() { 134 | return 20; 135 | } 136 | 137 | #if defined(BOARD_LED_IS_RGB) 138 | 139 | template 140 | uint32_t beatLED(uint32_t onColor, const T& beat) { 141 | const uint8_t cnt = sizeof(beat)/sizeof(beat[0]); 142 | setRGB((m_Counter % 2 == 0) ? onColor : (uint32_t)COLOR_BLACK); 143 | uint32_t next = beat[m_Counter % cnt]; 144 | m_Counter = (m_Counter+1) % cnt; 145 | return next; 146 | } 147 | 148 | uint32_t waveLED(uint32_t colorMax, unsigned breathePeriod) { 149 | uint8_t redMax = (colorMax & 0xFF0000) >> 16; 150 | uint8_t greenMax = (colorMax & 0x00FF00) >> 8; 151 | uint8_t blueMax = (colorMax & 0x0000FF); 152 | 153 | // Brightness will rise from 0 to 128, then fall back to 0 154 | uint8_t brightness = (m_Counter < 128) ? m_Counter : 255 - m_Counter; 155 | 156 | // Multiply our three colors by the brightness: 157 | redMax *= ((float)brightness / 128.0); 158 | greenMax *= ((float)brightness / 128.0); 159 | blueMax *= ((float)brightness / 128.0); 160 | // And turn the LED to that color: 161 | setRGB((redMax << 16) | (greenMax << 8) | blueMax); 162 | 163 | // This function relies on the 8-bit, unsigned m_Counter rolling over. 164 | m_Counter = (m_Counter+1) % 256; 165 | return breathePeriod / 256; 166 | } 167 | 168 | #else 169 | 170 | template 171 | uint32_t beatLED(uint32_t, const T& beat) { 172 | const uint8_t cnt = sizeof(beat)/sizeof(beat[0]); 173 | setLED((m_Counter % 2 == 0) ? DIMM(BOARD_PWM_MAX) : 0); 174 | uint32_t next = beat[m_Counter % cnt]; 175 | m_Counter = (m_Counter+1) % cnt; 176 | return next; 177 | } 178 | 179 | uint32_t waveLED(uint32_t, unsigned breathePeriod) { 180 | uint8_t brightness = (m_Counter < 128) ? m_Counter : 255 - m_Counter; 181 | 182 | setLED(BOARD_PWM_MAX * (DIMM((float)brightness) / (BOARD_PWM_MAX/2))); 183 | 184 | // This function relies on the 8-bit, unsigned m_Counter rolling over. 185 | m_Counter = (m_Counter+1) % 256; 186 | return breathePeriod / 256; 187 | } 188 | 189 | #endif 190 | 191 | private: 192 | uint8_t m_Counter; 193 | State m_PrevState; 194 | }; 195 | 196 | Indicator indicator; 197 | 198 | /* 199 | * Animation timers 200 | */ 201 | 202 | #if defined(USE_TICKER) 203 | 204 | #include 205 | 206 | Ticker blinker; 207 | 208 | void indicator_run() { 209 | uint32_t returnTime = indicator.run(); 210 | if (returnTime) { 211 | blinker.attach_ms(returnTime, indicator_run); 212 | } 213 | } 214 | 215 | void indicator_init() { 216 | blinker.attach_ms(100, indicator_run); 217 | } 218 | 219 | #elif defined(USE_TIMER_ONE) 220 | 221 | #include 222 | 223 | void indicator_run() { 224 | uint32_t returnTime = indicator.run(); 225 | if (returnTime) { 226 | Timer1.initialize(returnTime*1000); 227 | } 228 | } 229 | 230 | void indicator_init() { 231 | Timer1.initialize(100*1000); 232 | Timer1.attachInterrupt(indicator_run); 233 | } 234 | 235 | #elif defined(USE_TIMER_THREE) 236 | 237 | #include 238 | 239 | void indicator_run() { 240 | uint32_t returnTime = indicator.run(); 241 | if (returnTime) { 242 | Timer3.initialize(returnTime*1000); 243 | } 244 | } 245 | 246 | void indicator_init() { 247 | Timer3.initialize(100*1000); 248 | Timer3.attachInterrupt(indicator_run); 249 | } 250 | 251 | #else 252 | 253 | #warning LED indicator needs a functional timer! 254 | 255 | void indicator_run() {} 256 | void indicator_init() {} 257 | 258 | #endif 259 | -------------------------------------------------------------------------------- /All_in_One_New_Blynk/BlynkEdgent.h: -------------------------------------------------------------------------------- 1 | 2 | extern "C" { 3 | void app_loop(); 4 | void eraseMcuConfig(); 5 | void restartMCU(); 6 | } 7 | 8 | #include "Settings.h" 9 | #include 10 | 11 | #ifndef BLYNK_NEW_LIBRARY 12 | #error "Old version of Blynk library is in use. Please replace it with the new one." 13 | #endif 14 | 15 | #define DEBUG_SW 0 16 | 17 | 18 | // Function Definition 19 | 20 | void speed0(); 21 | void speed1(); 22 | void speed2(); 23 | void speed3(); 24 | void speed4(); 25 | 26 | 27 | #if !defined(BLYNK_TEMPLATE_ID) || !defined(BLYNK_DEVICE_NAME) 28 | #error "Please specify your BLYNK_TEMPLATE_ID and BLYNK_DEVICE_NAME" 29 | #endif 30 | 31 | #include "BlynkState.h" 32 | #include "ConfigStore.h" 33 | #include "ResetButton.h" 34 | #include "ConfigMode.h" 35 | #include "Indicator.h" 36 | #include "OTA.h" 37 | void irsense(); 38 | void swfeedback(); 39 | inline 40 | void BlynkState::set(State m) { 41 | if (state != m && m < MODE_MAX_VALUE) { 42 | DEBUG_PRINT(String(StateStr[state]) + " => " + StateStr[m]); 43 | state = m; 44 | 45 | // You can put your state handling here, 46 | // i.e. implement custom indication 47 | } 48 | } 49 | 50 | void printDeviceBanner() 51 | { 52 | Blynk.printBanner(); 53 | DEBUG_PRINT("--------------------------"); 54 | DEBUG_PRINT(String("Product: ") + BLYNK_DEVICE_NAME); 55 | DEBUG_PRINT(String("Hardware: ") + BOARD_HARDWARE_VERSION); 56 | DEBUG_PRINT(String("Firmware: ") + BLYNK_FIRMWARE_VERSION " (build " __DATE__ " " __TIME__ ")"); 57 | if (configStore.getFlag(CONFIG_FLAG_VALID)) { 58 | DEBUG_PRINT(String("Token: ...") + (configStore.cloudToken + 28)); 59 | } 60 | DEBUG_PRINT(String("Device: ") + BLYNK_INFO_DEVICE + " @ " + ESP.getCpuFreqMHz() + "MHz"); 61 | DEBUG_PRINT(String("MAC: ") + WiFi.macAddress()); 62 | DEBUG_PRINT(String("Flash: ") + ESP.getFlashChipSize() / 1024 + "K"); 63 | DEBUG_PRINT(String("ESP sdk: ") + ESP.getSdkVersion()); 64 | DEBUG_PRINT(String("Chip rev: ") + ESP.getChipRevision()); 65 | DEBUG_PRINT(String("Free mem: ") + ESP.getFreeHeap()); 66 | DEBUG_PRINT("--------------------------"); 67 | } 68 | 69 | void runBlynkWithChecks() { 70 | Blynk.run(); 71 | if (BlynkState::get() == MODE_RUNNING) { 72 | if (!Blynk.connected()) { 73 | if (WiFi.status() == WL_CONNECTED) { 74 | BlynkState::set(MODE_CONNECTING_CLOUD); 75 | } else { 76 | BlynkState::set(MODE_CONNECTING_NET); 77 | } 78 | } 79 | } 80 | } 81 | 82 | class Edgent { 83 | 84 | public: 85 | void begin() 86 | { 87 | indicator_init(); 88 | button_init(); 89 | config_init(); 90 | 91 | WiFi.persistent(false); 92 | WiFi.enableSTA(true); // Needed to get MAC 93 | 94 | printDeviceBanner(); 95 | 96 | if (configStore.getFlag(CONFIG_FLAG_VALID)) { 97 | BlynkState::set(MODE_CONNECTING_NET); 98 | } else if (config_load_blnkopt()) { 99 | DEBUG_PRINT("Firmware is preprovisioned"); 100 | BlynkState::set(MODE_CONNECTING_NET); 101 | } else { 102 | BlynkState::set(MODE_WAIT_CONFIG); 103 | } 104 | } 105 | 106 | void run() { 107 | app_loop(); 108 | switch (BlynkState::get()) { 109 | case MODE_WAIT_CONFIG: 110 | case MODE_CONFIGURING: enterConfigMode(); break; 111 | case MODE_CONNECTING_NET: enterConnectNet(); break; 112 | case MODE_CONNECTING_CLOUD: enterConnectCloud(); break; 113 | case MODE_RUNNING: runBlynkWithChecks(); break; 114 | case MODE_OTA_UPGRADE: enterOTA(); break; 115 | case MODE_SWITCH_TO_STA: enterSwitchToSTA(); break; 116 | case MODE_RESET_CONFIG: enterResetConfig(); break; 117 | default: enterError(); break; 118 | } 119 | } 120 | 121 | }; 122 | 123 | Edgent BlynkEdgent; 124 | BlynkTimer timer; 125 | 126 | void app_loop() { 127 | timer.run(); 128 | swfeedback(); 129 | } 130 | 131 | void swfeedback() 132 | { 133 | if (digitalRead(s1) == HIGH && SWITCH5_FLAG == 1) { 134 | digitalWrite(r1, LOW); 135 | Blynk.virtualWrite(V1, LOW); 136 | Serial.println("r1 on"); 137 | relay1 = 0; 138 | SWITCH5_FLAG = 0; 139 | } 140 | if (digitalRead(s1) == LOW && SWITCH5_FLAG == 0) { 141 | digitalWrite(r1, HIGH); 142 | Blynk.virtualWrite(V1, HIGH); 143 | Serial.println("r1 off"); 144 | relay1 = 1; 145 | SWITCH5_FLAG = 1; 146 | } 147 | if (digitalRead(s2) == HIGH && SWITCH6_FLAG == 1) { 148 | digitalWrite(r2, LOW); 149 | Blynk.virtualWrite(V2, LOW); 150 | Serial.println("r2 on"); 151 | relay2 = 0; 152 | SWITCH6_FLAG = 0; 153 | } 154 | if (digitalRead(s2) == LOW && SWITCH6_FLAG == 0) { 155 | digitalWrite(r2, HIGH); 156 | Blynk.virtualWrite(V2, HIGH); 157 | Serial.println("r2 off"); 158 | relay2 = 1; 159 | SWITCH6_FLAG = 1; 160 | } 161 | if (digitalRead(s3) == HIGH && SWITCH7_FLAG == 1) { 162 | digitalWrite(r3, LOW); 163 | Blynk.virtualWrite(V3, LOW); 164 | Serial.println("r3 on"); 165 | relay3 = 0; 166 | SWITCH7_FLAG = 0; 167 | } 168 | if (digitalRead(s3) == LOW && SWITCH7_FLAG == 0) { 169 | digitalWrite(r3, HIGH); 170 | Blynk.virtualWrite(V3, HIGH); 171 | Serial.println("r3 off"); 172 | relay3 = 1; 173 | SWITCH7_FLAG = 1; 174 | } 175 | if (digitalRead(s4) == HIGH && SWITCH8_FLAG == 1) { 176 | digitalWrite(r4, LOW); 177 | Blynk.virtualWrite(V4, LOW); 178 | Serial.println("r4 on"); 179 | relay4 = 0; 180 | SWITCH8_FLAG = 0; 181 | } 182 | if (digitalRead(s4) == LOW && SWITCH8_FLAG == 0) { 183 | digitalWrite(r4, HIGH); 184 | Blynk.virtualWrite(V4, HIGH); 185 | Serial.println("r4 off"); 186 | relay4 = 1; 187 | SWITCH8_FLAG = 1; 188 | } 189 | 190 | // FOR FAN 191 | if (digitalRead(f1) == LOW && speed1_flag == 1) 192 | { 193 | speed1(); 194 | Blynk.virtualWrite(V0, 1); 195 | speed1_flag = 0; 196 | speed2_flag = 1; 197 | speed3_flag = 1; 198 | speed4_flag = 1; 199 | speed0_flag = 1; 200 | 201 | 202 | } 203 | if (digitalRead(f2) == LOW && digitalRead(f3) == HIGH && speed2_flag == 1) 204 | { 205 | speed2(); 206 | Blynk.virtualWrite(V0, 2); 207 | speed1_flag = 1; 208 | speed2_flag = 0; 209 | speed3_flag = 1; 210 | speed4_flag = 1; 211 | speed0_flag = 1; 212 | 213 | } 214 | if (digitalRead(f2) == LOW && digitalRead(f3) == LOW && speed3_flag == 1) 215 | { 216 | speed3(); 217 | Blynk.virtualWrite(V0, 3); 218 | speed1_flag = 1; 219 | speed2_flag = 1; 220 | speed3_flag = 0; 221 | speed4_flag = 1; 222 | speed0_flag = 1; 223 | } 224 | if (digitalRead(f4) == LOW && speed4_flag == 1) 225 | { 226 | speed4(); 227 | Blynk.virtualWrite(V0, 4); 228 | speed1_flag = 1; 229 | speed2_flag = 1; 230 | speed3_flag = 1; 231 | speed4_flag = 0; 232 | speed0_flag = 1; 233 | } 234 | if (digitalRead(f1) == HIGH && digitalRead(f2) == HIGH && digitalRead(f3) == HIGH && digitalRead(f4) == HIGH && speed0_flag == 1) 235 | { 236 | speed0(); 237 | Blynk.virtualWrite(V0, 0); 238 | speed1_flag = 1; 239 | speed2_flag = 1; 240 | speed3_flag = 1; 241 | speed4_flag = 1; 242 | speed0_flag = 0; 243 | } 244 | } 245 | 246 | 247 | 248 | // Fan Speed Control 249 | 250 | void speed0() 251 | { 252 | //All Relays Off - Fan at speed 0 253 | if (DEBUG_SW)Serial.println("SPEED 0"); 254 | digitalWrite(Speed1, LOW); 255 | digitalWrite(Speed2, LOW); 256 | digitalWrite(Speed4, LOW); 257 | 258 | } 259 | 260 | void speed1() 261 | { 262 | //Speed1 Relay On - Fan at speed 1 263 | if (DEBUG_SW)Serial.println("SPEED 1"); 264 | digitalWrite(Speed1, LOW); 265 | digitalWrite(Speed2, LOW); 266 | digitalWrite(Speed4, LOW); 267 | delay(1000); 268 | digitalWrite(Speed1, HIGH); 269 | } 270 | 271 | void speed2() 272 | { 273 | //Speed2 Relay On - Fan at speed 2 274 | if (DEBUG_SW)Serial.println("SPEED 2"); 275 | digitalWrite(Speed1, LOW); 276 | digitalWrite(Speed2, LOW); 277 | digitalWrite(Speed4, LOW); 278 | delay(1000); 279 | digitalWrite(Speed2, HIGH); 280 | } 281 | 282 | void speed3() 283 | { 284 | //Speed1 & Speed2 Relays On - Fan at speed 3 285 | if (DEBUG_SW)Serial.println("SPEED 3"); 286 | digitalWrite(Speed1, LOW); 287 | digitalWrite(Speed2, LOW); 288 | digitalWrite(Speed4, LOW); 289 | delay(1000); 290 | digitalWrite(Speed1, HIGH); 291 | digitalWrite(Speed2, HIGH); 292 | 293 | } 294 | 295 | void speed4() 296 | { 297 | //Speed4 Relay On - Fan at speed 4 298 | if (DEBUG_SW)Serial.println("SPEED 4"); 299 | digitalWrite(Speed1, LOW); 300 | digitalWrite(Speed2, LOW); 301 | digitalWrite(Speed4, LOW); 302 | delay(1000); 303 | digitalWrite(Speed4, HIGH); 304 | } 305 | -------------------------------------------------------------------------------- /All_in_One_New_Blynk/ConfigMode.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #ifdef BLYNK_USE_SPIFFS 11 | #include "SPIFFS.h" 12 | #else 13 | const char* config_form = R"html( 14 | 15 | 16 | 17 | WiFi setup 18 | 44 | 45 | 46 |
47 |
48 | 49 | 50 | 51 | 52 | 53 | 54 |

55 | 56 |
57 |
58 | 59 | 60 | )html"; 61 | #endif 62 | 63 | WebServer server(WIFI_AP_CONFIG_PORT); 64 | DNSServer dnsServer; 65 | const byte DNS_PORT = 53; 66 | 67 | static const char serverUpdateForm[] PROGMEM = 68 | R"( 69 |
70 | 71 | 72 |
73 | )"; 74 | 75 | void restartMCU() { 76 | ESP.restart(); 77 | while(1) {}; 78 | } 79 | 80 | void eraseMcuConfig() { 81 | // Erase ESP32 NVS 82 | int err; 83 | //err=nvs_flash_init(); 84 | //BLYNK_LOG2("nvs_flash_init: ", err ? String(err) : "Success"); 85 | err=nvs_flash_erase(); 86 | BLYNK_LOG2("nvs_flash_erase: ", err ? String(err) : "Success"); 87 | } 88 | 89 | void getWiFiName(char* buff, size_t len, bool withPrefix = true) { 90 | const uint64_t chipId = ESP.getEfuseMac(); 91 | uint32_t unique = 0; 92 | for (int i=0; i<4; i++) { 93 | unique = BlynkCRC32(&chipId, sizeof(chipId), unique); 94 | } 95 | if (withPrefix) { 96 | snprintf(buff, len, "Blynk %s-%05X", BLYNK_DEVICE_NAME, unique & 0xFFFFF); 97 | } else { 98 | snprintf(buff, len, "%s-%05X", BLYNK_DEVICE_NAME, unique & 0xFFFFF); 99 | } 100 | } 101 | 102 | void enterConfigMode() 103 | { 104 | char ssidBuff[64]; 105 | getWiFiName(ssidBuff, sizeof(ssidBuff)); 106 | 107 | WiFi.mode(WIFI_OFF); 108 | delay(100); 109 | WiFi.mode(WIFI_AP); 110 | delay(2000); 111 | WiFi.softAPConfig(WIFI_AP_IP, WIFI_AP_IP, WIFI_AP_Subnet); 112 | WiFi.softAP(ssidBuff); 113 | delay(500); 114 | 115 | IPAddress myIP = WiFi.softAPIP(); 116 | DEBUG_PRINT(String("AP SSID: ") + ssidBuff); 117 | DEBUG_PRINT(String("AP IP: ") + myIP[0] + "." + myIP[1] + "." + myIP[2] + "." + myIP[3]); 118 | 119 | // Set up DNS Server 120 | dnsServer.setTTL(300); // Time-to-live 300s 121 | dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure); // Return code for non-accessible domains 122 | #ifdef WIFI_CAPTIVE_PORTAL_ENABLE 123 | dnsServer.start(DNS_PORT, "*", WiFi.softAPIP()); // Point all to our IP 124 | server.onNotFound(handleRoot); 125 | #else 126 | dnsServer.start(DNS_PORT, CONFIG_AP_URL, WiFi.softAPIP()); 127 | DEBUG_PRINT(String("AP URL: ") + CONFIG_AP_URL); 128 | #endif 129 | 130 | server.on("/update", HTTP_GET, []() { 131 | server.sendHeader("Connection", "close"); 132 | server.send(200, "text/html", serverUpdateForm); 133 | }); 134 | server.on("/update", HTTP_POST, []() { 135 | server.sendHeader("Connection", "close"); 136 | if (!Update.hasError()) { 137 | server.send(200, "text/plain", "OK"); 138 | } else { 139 | server.send(500, "text/plain", "FAIL"); 140 | } 141 | delay(1000); 142 | restartMCU(); 143 | }, []() { 144 | HTTPUpload& upload = server.upload(); 145 | if (upload.status == UPLOAD_FILE_START) { 146 | DEBUG_PRINT(String("Update: ") + upload.filename); 147 | //WiFiUDP::stop(); 148 | 149 | if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size 150 | Update.printError(BLYNK_PRINT); 151 | } 152 | } else if (upload.status == UPLOAD_FILE_WRITE) { 153 | /* flashing firmware to ESP*/ 154 | if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { 155 | Update.printError(BLYNK_PRINT); 156 | } 157 | BLYNK_PRINT.print("."); 158 | } else if (upload.status == UPLOAD_FILE_END) { 159 | BLYNK_PRINT.println(); 160 | DEBUG_PRINT("Finishing..."); 161 | if (Update.end(true)) { //true to set the size to the current progress 162 | DEBUG_PRINT("Update Success. Rebooting"); 163 | } else { 164 | Update.printError(BLYNK_PRINT); 165 | } 166 | } 167 | }); 168 | 169 | server.on("/config", []() { 170 | DEBUG_PRINT("Applying configuration..."); 171 | String ssid = server.arg("ssid"); 172 | String ssidManual = server.arg("ssidManual"); 173 | String pass = server.arg("pass"); 174 | if (ssidManual != "") { 175 | ssid = ssidManual; 176 | } 177 | String token = server.arg("blynk"); 178 | String host = server.arg("host"); 179 | String port = server.arg("port_ssl"); 180 | 181 | String ip = server.arg("ip"); 182 | String mask = server.arg("mask"); 183 | String gw = server.arg("gw"); 184 | String dns = server.arg("dns"); 185 | String dns2 = server.arg("dns2"); 186 | 187 | bool save = server.arg("save").toInt(); 188 | 189 | String content; 190 | 191 | DEBUG_PRINT(String("WiFi SSID: ") + ssid + " Pass: " + pass); 192 | DEBUG_PRINT(String("Blynk cloud: ") + token + " @ " + host + ":" + port); 193 | 194 | if (token.length() == 32 && ssid.length() > 0) { 195 | configStore.setFlag(CONFIG_FLAG_VALID, false); 196 | CopyString(ssid, configStore.wifiSSID); 197 | CopyString(pass, configStore.wifiPass); 198 | CopyString(token, configStore.cloudToken); 199 | if (host.length()) { 200 | CopyString(host, configStore.cloudHost); 201 | } 202 | if (port.length()) { 203 | configStore.cloudPort = port.toInt(); 204 | } 205 | 206 | IPAddress addr; 207 | 208 | if (ip.length() && addr.fromString(ip)) { 209 | configStore.staticIP = addr; 210 | configStore.setFlag(CONFIG_FLAG_STATIC_IP, true); 211 | } else { 212 | configStore.setFlag(CONFIG_FLAG_STATIC_IP, false); 213 | } 214 | if (mask.length() && addr.fromString(mask)) { 215 | configStore.staticMask = addr; 216 | } 217 | if (gw.length() && addr.fromString(gw)) { 218 | configStore.staticGW = addr; 219 | } 220 | if (dns.length() && addr.fromString(dns)) { 221 | configStore.staticDNS = addr; 222 | } 223 | if (dns2.length() && addr.fromString(dns2)) { 224 | configStore.staticDNS2 = addr; 225 | } 226 | 227 | if (save) { 228 | configStore.setFlag(CONFIG_FLAG_VALID, true); 229 | config_save(); 230 | 231 | content = R"json({"status":"ok","msg":"Configuration saved"})json"; 232 | } else { 233 | content = R"json({"status":"ok","msg":"Trying to connect..."})json"; 234 | } 235 | server.send(200, "application/json", content); 236 | 237 | BlynkState::set(MODE_SWITCH_TO_STA); 238 | } else { 239 | DEBUG_PRINT("Configuration invalid"); 240 | content = R"json({"status":"error","msg":"Configuration invalid"})json"; 241 | server.send(500, "application/json", content); 242 | } 243 | }); 244 | server.on("/board_info.json", []() { 245 | DEBUG_PRINT("Sending board info..."); 246 | const char* tmpl = BLYNK_TEMPLATE_ID; 247 | char ssidBuff[64]; 248 | getWiFiName(ssidBuff, sizeof(ssidBuff)); 249 | char buff[512]; 250 | snprintf(buff, sizeof(buff), 251 | R"json({"board":"%s","tmpl_id":"%s","fw_type":"%s","fw_ver":"%s","hw_ver":"%s","ssid":"%s","bssid":"%s","last_error":%d,"wifi_scan":true,"static_ip":true})json", 252 | BLYNK_DEVICE_NAME, 253 | tmpl ? tmpl : "Unknown", 254 | BLYNK_FIRMWARE_TYPE, 255 | BLYNK_FIRMWARE_VERSION, 256 | BOARD_HARDWARE_VERSION, 257 | ssidBuff, 258 | WiFi.softAPmacAddress().c_str(), 259 | configStore.last_error 260 | ); 261 | server.send(200, "application/json", buff); 262 | }); 263 | server.on("/wifi_scan.json", []() { 264 | DEBUG_PRINT("Scanning networks..."); 265 | int wifi_nets = WiFi.scanNetworks(true, true); 266 | while (wifi_nets == -1) { 267 | delay(20); 268 | wifi_nets = WiFi.scanComplete(); 269 | } 270 | DEBUG_PRINT(String("Found networks: ") + wifi_nets); 271 | 272 | String result = "[\n"; 273 | if (wifi_nets) { 274 | 275 | // Sort networks 276 | int indices[wifi_nets]; 277 | for (int i = 0; i < wifi_nets; i++) { 278 | indices[i] = i; 279 | } 280 | for (int i = 0; i < wifi_nets; i++) { 281 | for (int j = i + 1; j < wifi_nets; j++) { 282 | if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) { 283 | std::swap(indices[i], indices[j]); 284 | } 285 | } 286 | } 287 | 288 | wifi_nets = BlynkMin(15, wifi_nets); // Show top 15 networks 289 | 290 | // TODO: skip empty names 291 | 292 | char buff[256]; 293 | for (int i = 0; i < wifi_nets; i++){ 294 | int id = indices[i]; 295 | 296 | const char* sec; 297 | switch (WiFi.encryptionType(id)) { 298 | case WIFI_AUTH_WEP: sec = "WEP"; break; 299 | case WIFI_AUTH_WPA_PSK: sec = "WPA/PSK"; break; 300 | case WIFI_AUTH_WPA2_PSK: sec = "WPA2/PSK"; break; 301 | case WIFI_AUTH_WPA_WPA2_PSK: sec = "WPA/WPA2/PSK"; break; 302 | case WIFI_AUTH_OPEN: sec = "OPEN"; break; 303 | default: sec = "unknown"; break; 304 | } 305 | 306 | snprintf(buff, sizeof(buff), 307 | R"json( {"ssid":"%s","bssid":"%s","rssi":%i,"sec":"%s","ch":%i})json", 308 | WiFi.SSID(id).c_str(), 309 | WiFi.BSSIDstr(id).c_str(), 310 | WiFi.RSSI(id), 311 | sec, 312 | WiFi.channel(id) 313 | ); 314 | 315 | result += buff; 316 | if (i != wifi_nets-1) result += ",\n"; 317 | } 318 | server.send(200, "application/json", result + "\n]"); 319 | } else { 320 | server.send(200, "application/json", "[]"); 321 | } 322 | }); 323 | server.on("/reset", []() { 324 | BlynkState::set(MODE_RESET_CONFIG); 325 | server.send(200, "application/json", R"json({"status":"ok","msg":"Configuration reset"})json"); 326 | }); 327 | server.on("/reboot", []() { 328 | restartMCU(); 329 | }); 330 | 331 | #ifdef BLYNK_USE_SPIFFS 332 | if (SPIFFS.begin()) { 333 | server.serveStatic("/img/favicon.png", SPIFFS, "/img/favicon.png"); 334 | server.serveStatic("/img/logo.png", SPIFFS, "/img/logo.png"); 335 | server.serveStatic("/", SPIFFS, "/index.html"); 336 | } else { 337 | DEBUG_PRINT("Webpage: No SPIFFS"); 338 | } 339 | #endif 340 | 341 | server.begin(); 342 | 343 | while (BlynkState::is(MODE_WAIT_CONFIG) || BlynkState::is(MODE_CONFIGURING)) { 344 | delay(10); 345 | dnsServer.processNextRequest(); 346 | server.handleClient(); 347 | app_loop(); 348 | if (BlynkState::is(MODE_WAIT_CONFIG) && WiFi.softAPgetStationNum() > 0) { 349 | BlynkState::set(MODE_CONFIGURING); 350 | } else if (BlynkState::is(MODE_CONFIGURING) && WiFi.softAPgetStationNum() == 0) { 351 | BlynkState::set(MODE_WAIT_CONFIG); 352 | } 353 | } 354 | 355 | server.stop(); 356 | 357 | #ifdef BLYNK_USE_SPIFFS 358 | SPIFFS.end(); 359 | #endif 360 | } 361 | 362 | void enterConnectNet() { 363 | BlynkState::set(MODE_CONNECTING_NET); 364 | DEBUG_PRINT(String("Connecting to WiFi: ") + configStore.wifiSSID); 365 | 366 | char ssidBuff[64]; 367 | getWiFiName(ssidBuff, sizeof(ssidBuff)); 368 | String hostname(ssidBuff); 369 | hostname.replace(" ", "-"); 370 | 371 | WiFi.setHostname(hostname.c_str()); 372 | 373 | if (configStore.getFlag(CONFIG_FLAG_STATIC_IP)) { 374 | if (!WiFi.config(configStore.staticIP, 375 | configStore.staticGW, 376 | configStore.staticMask, 377 | configStore.staticDNS, 378 | configStore.staticDNS2) 379 | ) { 380 | DEBUG_PRINT("Failed to configure Static IP"); 381 | config_set_last_error(BLYNK_PROV_ERR_CONFIG); 382 | BlynkState::set(MODE_ERROR); 383 | return; 384 | } 385 | } 386 | 387 | WiFi.begin(configStore.wifiSSID, configStore.wifiPass); 388 | 389 | unsigned long timeoutMs = millis() + WIFI_NET_CONNECT_TIMEOUT; 390 | while ((timeoutMs > millis()) && (WiFi.status() != WL_CONNECTED)) 391 | { 392 | delay(10); 393 | app_loop(); 394 | if (!BlynkState::is(MODE_CONNECTING_NET)) { 395 | WiFi.disconnect(); 396 | return; 397 | } 398 | } 399 | 400 | if (WiFi.status() == WL_CONNECTED) { 401 | IPAddress localip = WiFi.localIP(); 402 | if (configStore.getFlag(CONFIG_FLAG_STATIC_IP)) { 403 | BLYNK_LOG_IP("Using Static IP: ", localip); 404 | } else { 405 | BLYNK_LOG_IP("Using Dynamic IP: ", localip); 406 | } 407 | 408 | BlynkState::set(MODE_CONNECTING_CLOUD); 409 | } else { 410 | config_set_last_error(BLYNK_PROV_ERR_NETWORK); 411 | BlynkState::set(MODE_ERROR); 412 | } 413 | } 414 | 415 | void enterConnectCloud() { 416 | BlynkState::set(MODE_CONNECTING_CLOUD); 417 | 418 | Blynk.config(configStore.cloudToken, configStore.cloudHost, configStore.cloudPort); 419 | Blynk.connect(0); 420 | 421 | unsigned long timeoutMs = millis() + WIFI_CLOUD_CONNECT_TIMEOUT; 422 | while ((timeoutMs > millis()) && 423 | (!Blynk.isTokenInvalid()) && 424 | (Blynk.connected() == false)) 425 | { 426 | delay(10); 427 | Blynk.run(); 428 | app_loop(); 429 | if (!BlynkState::is(MODE_CONNECTING_CLOUD)) { 430 | Blynk.disconnect(); 431 | return; 432 | } 433 | } 434 | 435 | if (millis() > timeoutMs) { 436 | DEBUG_PRINT("Timeout"); 437 | } 438 | 439 | if (Blynk.isTokenInvalid()) { 440 | config_set_last_error(BLYNK_PROV_ERR_TOKEN); 441 | BlynkState::set(MODE_WAIT_CONFIG); 442 | } else if (Blynk.connected()) { 443 | BlynkState::set(MODE_RUNNING); 444 | 445 | if (!configStore.getFlag(CONFIG_FLAG_VALID)) { 446 | configStore.last_error = BLYNK_PROV_ERR_NONE; 447 | configStore.setFlag(CONFIG_FLAG_VALID, true); 448 | config_save(); 449 | } 450 | } else { 451 | config_set_last_error(BLYNK_PROV_ERR_CLOUD); 452 | BlynkState::set(MODE_ERROR); 453 | } 454 | } 455 | 456 | void enterSwitchToSTA() { 457 | BlynkState::set(MODE_SWITCH_TO_STA); 458 | 459 | DEBUG_PRINT("Switching to STA..."); 460 | 461 | delay(1000); 462 | WiFi.mode(WIFI_OFF); 463 | delay(100); 464 | WiFi.mode(WIFI_STA); 465 | 466 | BlynkState::set(MODE_CONNECTING_NET); 467 | } 468 | 469 | void enterError() { 470 | BlynkState::set(MODE_ERROR); 471 | 472 | unsigned long timeoutMs = millis() + 10000; 473 | while (timeoutMs > millis() || g_buttonPressed) 474 | { 475 | delay(10); 476 | app_loop(); 477 | if (!BlynkState::is(MODE_ERROR)) { 478 | return; 479 | } 480 | } 481 | DEBUG_PRINT("Restarting after error."); 482 | delay(10); 483 | 484 | restartMCU(); 485 | } 486 | --------------------------------------------------------------------------------