From 9999c906cae885f10a2b95304b02fa0f6169490d Mon Sep 17 00:00:00 2001 From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com> Date: Mon, 1 Jun 2020 00:04:33 -0400 Subject: [PATCH] Major release v1.0.9 ### Major Releases v1.0.9 1. Add MultiWiFi/Blynk features for WiFi 2. Add MultiBlynk feature for GPRS/GSM 3. Add DoubleResetDetector (DRD) feature. 4. Update to use LittleFS for ESP8266 core 2.7.1+ to replace deprecated SPIFFS on ESP8266 5. Add Configurable Config Portal Title 6. Add Default Config Data. --- README.md | 765 +++++++++++--- examples/ESP32_GSM/Credentials.h | 115 +++ examples/ESP32_GSM/ESP32_GSM.ino | 208 +--- examples/ESP32_GSM/defines.h | 160 +++ examples/ESP32_GSM/dynamicParams.h | 89 ++ examples/ESP8266_GSM/Credentials.h | 115 +++ examples/ESP8266_GSM/ESP8266_GSM.ino | 204 +--- examples/ESP8266_GSM/defines.h | 174 ++++ examples/ESP8266_GSM/dynamicParams.h | 89 ++ examples/TTGO_TCALL_GSM/Credentials.h | 116 +++ examples/TTGO_TCALL_GSM/TTGO_TCALL_GSM.ino | 197 +--- examples/TTGO_TCALL_GSM/defines.h | 161 +++ examples/TTGO_TCALL_GSM/dynamicParams.h | 90 ++ keywords.txt | 4 +- library.json | 2 +- library.properties | 4 +- pics/Selection_2.png | Bin 31583 -> 25917 bytes pics/Selection_3.png | Bin 40675 -> 28908 bytes src/Adapters/BlynkGsm_ESP32M.h | 4 +- src/Adapters/BlynkGsm_ESP8266M.h | 4 +- src/BlynkSimpleEsp32_GSM_WF.h | 4 +- src/BlynkSimpleEsp32_GSM_WFM.h | 1033 ++++++++++++++----- src/BlynkSimpleEsp8266_GSM_WF.h | 4 +- src/BlynkSimpleEsp8266_GSM_WFM.h | 1052 +++++++++++++++----- src/BlynkSimpleTinyGSM_M.h | 4 +- 25 files changed, 3402 insertions(+), 1196 deletions(-) create mode 100644 examples/ESP32_GSM/Credentials.h create mode 100644 examples/ESP32_GSM/defines.h create mode 100644 examples/ESP32_GSM/dynamicParams.h create mode 100644 examples/ESP8266_GSM/Credentials.h create mode 100644 examples/ESP8266_GSM/defines.h create mode 100644 examples/ESP8266_GSM/dynamicParams.h create mode 100644 examples/TTGO_TCALL_GSM/Credentials.h create mode 100644 examples/TTGO_TCALL_GSM/defines.h create mode 100644 examples/TTGO_TCALL_GSM/dynamicParams.h diff --git a/README.md b/README.md index aa41718..1dfa87b 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,31 @@ [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing) [![GitHub issues](https://img.shields.io/github/issues/khoih-prog/BlynkGSM_Manager.svg)](http://github.com/khoih-prog/BlynkGSM_Manager/issues) +By design, Blynk user can run ESP32/ESP8266 boards with either WiFi or GSM/GPRS by using different sketches, and have to upload / update firmware to change. This library enables user to include both Blynk GSM/GPRS and WiFi libraries in one sketch, run ***both WiFi and GSM/GPRS simultaneously***, or select one to use at runtime after reboot. + +This is also a Blynk and WiFiManager Library for configuring/auto(re)connecting ESP8266/ESP32 modules to the available MultiWiFi APs and MultiBlynk servers at runtime. Connection is with or without SSL. Configuration data to be saved in either LittleFS / SPIFFS or EEPROM. Default Credentials as well as Dynamic custom parameters can be added and modified easily without coding knowledge. DoubleResetDetector is used to force Config Portal opening even if the Credentials are still valid. + +- This is the new library, adding to the current Blynk_WiFiManager. It's designed to help you eliminate `hardcoding` your Blynk credentials in `ESP32 and ESP8266` boards using GSM shield (SIM800, SIM900, etc). + +- You can update GSM Modem and Blynk Credentials any time you need to change via Configure Portal. Data are saved in LittleFS / SPIFFS or configurable locations in EEPROM. + +New recent features: + +- Optional default ***Credentials as well as Dynamic parameters to be optionally autoloaded into Config Portal*** to use or change instead of manually input. +- ***DoubleDetectDetector*** feature to force Config Portal when double reset is detected within predetermined time, default 10s. +- Configurable ***Config Portal Title*** to be either HostName, BoardName or default undistinguishable names. +- Examples are redesigned to separate Credentials / Defines / Dynamic Params / Code so that you can change Credentials / Dynamic Params quickly for each device. + + +### Major Releases v1.0.9 + +1. Add MultiWiFi/Blynk features for WiFi +2. Add MultiBlynk feature for GPRS/GSM +3. Add DoubleResetDetector (DRD) feature. +4. Update to use LittleFS for ESP8266 core 2.7.1+ to replace deprecated SPIFFS on ESP8266 +5. Add Configurable Config Portal Title +6. Add Default Config Data. + ### Releases v1.0.8 1. Fix potential dangerous bug in code and examples of v1.0.7. @@ -27,21 +52,21 @@ ### Releases v1.0.5 -1. Add more modem supports. Thanks to new [TinyGSM library v0.10.1+](https://github.com/vshymanskyy/TinyGSM). +1. Add more modem supports. Thanks to new [TinyGSM library v0.10.5+](https://github.com/vshymanskyy/TinyGSM). ## Supported modems - SIMCom SIM800 series (SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868) - SIMCom SIM900 series (SIM900A, SIM900D, SIM908, SIM968) -- SIMCom WCDMA/HSPA/HSPA+ Modules (SIM5360, SIM5320, SIM5300E, SIM5300EA) +- SIMCom WCDMA/HSPA/HSPA+ Modules (SIM5360, SIM5320, SIM5300E, SIM5300E/A) - SIMCom LTE Modules (SIM7100E, SIM7500E, SIM7500A, SIM7600C, SIM7600E) -- SIMCom SIM7000E CAT-M1/NB-IoT Module +- SIMCom SIM7000E/A/G CAT-M1/NB-IoT Module - AI-Thinker A6, A6C, A7, A20 - ESP8266 (AT commands interface, similar to GSM modems) - Digi XBee WiFi and Cellular (using XBee command mode) - Neoway M590 - u-blox 2G, 3G, 4G, and LTE Cat1 Cellular Modems (many modules including LEON-G100, LISA-U2xx, SARA-G3xx, SARA-U2xx, TOBY-L2xx, LARA-R2xx, MPCI-L2xx) -- u-blox LTE-M Modems (SARA-R4xx, SARA-N4xx, _but NOT SARA-N2xx_) +- u-blox LTE-M/NB-IoT Modems (SARA-R4xx, SARA-N4xx, _but NOT SARA-N2xx_) - Sequans Monarch LTE Cat M1/NB1 (VZM20Q) - Quectel BG96 - Quectel M95 @@ -69,18 +94,14 @@ More modems may be supported later: 1. Enhance Config Portal GUI. 2. Reduce code size. -By design, Blynk user can run ESP32/ESP8266 boards with either WiFi or GSM/GPRS by using different sketches, and have to upload / update firmware to change. This library enables user to include both Blynk GSM/GPRS and WiFi libraries in one sketch, run ***both WiFi and GSM/GPRS simultaneously***, or select one to use at runtime after reboot. - -- This is the new library, adding to the current Blynk_WiFiManager. It's designed to help you eliminate `hardcoding` your Blynk credentials in `ESP32 and ESP8266` boards using GSM shield (SIM800, SIM900, etc). - -- You can update GSM Modem and Blynk Credentials any time you need to change via Configure Portal. Data are saved in SPIFFS or configurable locations in EEPROM. +--- ## Prerequisite 1. [`Arduino IDE 1.8.12 or later` for Arduino](https://www.arduino.cc/en/Main/Software) 2. [`ESP32 core 1.0.4 or later`](https://github.com/espressif/arduino-esp32/releases) for ESP32 (Use Arduino Board Manager) 3. [`ESP8266 core 2.6.3 or later`](https://github.com/esp8266/Arduino/releases) for ES82662 (Use Arduino Board Manager) 3. [`Blynk library 0.6.1 or later`](https://github.com/blynkkk/blynk-library/releases) -4. [`TinyGSM library 0.10.1 or later`](https://github.com/vshymanskyy/TinyGSM) +4. [`TinyGSM library 0.10.5 or later`](https://github.com/vshymanskyy/TinyGSM) #### Use Arduino Library Manager @@ -95,6 +116,12 @@ By design, Blynk user can run ESP32/ESP8266 boards with either WiFi or GSM/GPRS 4. Copy whole - `BlynkGSM_Manager-master` folder to Arduino libraries directory such as `~/Arduino/libraries`. +### Important information + +Please read more information in [TinyGSM Library](https://github.com/vshymanskyy/TinyGSM) + +--- + ### How to use For ESP32 and ESP8266, in your code : @@ -116,9 +143,60 @@ For ESP32 and ESP8266, in your code : #endif ``` -4. Change `Blynk.run()` for WiFi to `Blynk_WF.run()`. +4. then add + +``` +// Only for ESP8266 +#define USE_LITTLEFS true +#define USE_SPIFFS false +``` +to use LittleFS (for ESP8266), or + +``` +#define USE_LITTLEFS false +#define USE_SPIFFS true +``` +to use SPIFFS or + +``` +#define USE_LITTLEFS false +#define USE_SPIFFS false +``` +to use EEPROM. Currently, data size in v1.0.9, with DRD and not including dynamic params, is 556 bytes from address EEPROM_START ) to save your configuration data. EEPROM_SIZE can be specified from 1024 to 4096 (2048 for ESP32) bytes. + + +5. + +``` +// Force some params in Blynk, only valid for library version 1.0.1 and later +#define TIMEOUT_RECONNECT_WIFI 10000L +#define RESET_IF_CONFIG_TIMEOUT true +#define CONFIG_TIMEOUT_RETRYTIMES_BEFORE_RESET 5 + +``` +6. To use personalized Config Portal AP SSID and Password, as well as IP Address, channel e.g. call : + +``` + // Use configurable AP IP, instead of default IP 192.168.4.1 + Blynk_WF.setConfigPortalIP(IPAddress(192, 168, 100, 1)); + // Use channel = 0 => random Config Portal WiFi channel to avoid conflict + Blynk_WF.setConfigPortalChannel(0); +``` + +7. You can specify STA-mode Static IP address, Gateway, Subnet Mask, as well as DNS server 1 and 2: + +``` +// From v1.0.5, select either one of these to set static IP + Blynk_WF.setSTAStaticIPConfig(IPAddress(192, 168, 2, 220), IPAddress(192, 168, 2, 1), IPAddress(255, 255, 255, 0)); + //Blynk_WF.setSTAStaticIPConfig(IPAddress(192, 168, 2, 220), IPAddress(192, 168, 2, 1), IPAddress(255, 255, 255, 0), + // IPAddress(192, 168, 2, 1), IPAddress(8, 8, 8, 8)); + //Blynk_WF.setSTAStaticIPConfig(IPAddress(192, 168, 2, 220), IPAddress(192, 168, 2, 1), IPAddress(255, 255, 255, 0), + // IPAddress(4, 4, 4, 4), IPAddress(8, 8, 8, 8)); +``` + +8. Change `Blynk.run()` for WiFi to `Blynk_WF.run()`. -5. Change `Blynk.run()` for GSM/GPRS to `Blynk_GSM.run()`. +9. Change `Blynk.run()` for GSM/GPRS to `Blynk_GSM.run()`. That's it. @@ -192,6 +270,7 @@ Also see examples: 2. [ESP32_GSM](examples/ESP32_GSM) 3. [ESP8266_GSM](examples/ESP8266_GSM) +--- ## So, how it works? @@ -245,69 +324,129 @@ void loop() ... } ``` +--- -## Example [TTGO_TCALL_GSM](examples/TTGO_TCALL_GSM) -Please take a look at other examples, as well. +### Important notes +1. Now you can use special chars such as ***~, !, @, #, $, %, ^, &, _, -, space,etc.***. +2. The SSIDs, Passwords, BlynkServers and Tokens must be input (or to make them different from ***blank***). Otherwise, the Config Portal will re-open until those fields have been changed. If you don't need any field, just input anything or use duplicated data from similar field. +3. WiFi password max length now is 63 chars according to WPA2 standard. Minimum password length is 8 chars. +4. Sometimes, it's hard or not possible to connect to Config Portal WiFi AP, the majority cases are caused by WiFi channel conflict if there are too many WiFi APs running around. Please use ***random ConfigPortal WiFi AP channel*** in sketch (see code snippet below) and reset the board so that another channel is used. Repeat until connection is OK ``` -#ifndef ESP32 -#error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. -#endif +// Set config portal channel, default = 1. Use 0 => random channel from 1-13 to avoid conflict + Blynk.setConfigPortalChannel(0); +``` -#define BLYNK_PRINT Serial -#define BLYNK_HEARTBEAT 60 +--- -// TTGO T-Call pin definitions -#define MODEM_RST 5 // Pin D5 mapped to pin GPIO5/SPISS/VSPI_SS of ESP32 -#define MODEM_PWKEY 4 // Pin D4 mapped to pin GPIO4/ADC10/TOUCH0 of ESP32 -#define MODEM_POWER_ON 23 // Pin D23 mapped to pin GPIO23/VSPI_MOSI of ESP32 -#define MODEM_TX 27 // Pin D27 mapped to pin GPIO27/ADC17/TOUCH7 of ESP32 -#define MODEM_RX 26 // Pin D26 mapped to pin GPIO26/ADC19/DAC2 of ESP32 -#define I2C_SDA 21 // Pin D21 mapped to pin GPIO21/SDA of ESP32 -#define I2C_SCL 22 // Pin D22 mapped to pin GPIO22/SCL of ESP32 +### How to use default Credentials and have them pre-loaded onto Config Portal -// Select your modem: -#define TINY_GSM_MODEM_SIM800 -//#define TINY_GSM_MODEM_SIM808 -//#define TINY_GSM_MODEM_SIM868 -//#define TINY_GSM_MODEM_SIM900 -//#define TINY_GSM_MODEM_SIM5300 -//#define TINY_GSM_MODEM_SIM5320 -//#define TINY_GSM_MODEM_SIM5360 -//#define TINY_GSM_MODEM_SIM7000 -//#define TINY_GSM_MODEM_SIM7100 -//#define TINY_GSM_MODEM_SIM7500 -//#define TINY_GSM_MODEM_SIM7600 -//#define TINY_GSM_MODEM_SIM7800 -//#define TINY_GSM_MODEM_UBLOX -//#define TINY_GSM_MODEM_SARAR4 -//#define TINY_GSM_MODEM_M95 -//#define TINY_GSM_MODEM_BG96 -//#define TINY_GSM_MODEM_A6 -//#define TINY_GSM_MODEM_A7 -//#define TINY_GSM_MODEM_M590 -//#define TINY_GSM_MODEM_MC60 -//#define TINY_GSM_MODEM_MC60E -//#define TINY_GSM_MODEM_XBEE -//#define TINY_GSM_MODEM_SEQUANS_MONARCH +See this example and modify as necessary -// Increase RX buffer if needed -#define TINY_GSM_RX_BUFFER 1024 +1. To load [Default Credentials](examples/TTGO_TCALL_GSM/Credentials.h) +``` +bool LOAD_DEFAULT_CONFIG_DATA = true; +``` -//#define USE_BLYNK_WM false -#define USE_BLYNK_WM true +2. To use system default to load "blank" when there is no valid Credentials +``` +bool LOAD_DEFAULT_CONFIG_DATA = false; +``` -#define USE_SPIFFS false -//#define USE_SPIFFS true +3. Example of [Default Credentials](examples/TTGO_TCALL_GSM/Credentials.h) -#define EEPROM_SIZE 2048 -#define EEPROM_START 256 +```cpp +/// Start Default Config Data ////////////////// -#include +/* + // Defined in -#if USE_BLYNK_WM -#include + #define SSID_MAX_LEN 32 + //From v1.0.10, WPA2 passwords can be up to 63 characters long. + #define PASS_MAX_LEN 64 + + typedef struct + { + char wifi_ssid[SSID_MAX_LEN]; + char wifi_pw [PASS_MAX_LEN]; + } WiFi_Credentials; + + #define BLYNK_SERVER_MAX_LEN 32 + #define BLYNK_TOKEN_MAX_LEN 36 + + typedef struct + { + char blynk_server [BLYNK_SERVER_MAX_LEN]; + char wifi_blynk_token [BLYNK_TOKEN_MAX_LEN]; + char gsm_blynk_token [BLYNK_TOKEN_MAX_LEN]; + } Blynk_Credentials; + + #define NUM_WIFI_CREDENTIALS 2 + #define NUM_BLYNK_CREDENTIALS 2 + + // Configurable items besides fixed Header + #define NUM_CONFIGURABLE_ITEMS ( 6 + (2 * NUM_WIFI_CREDENTIALS) + (3 * NUM_BLYNK_CREDENTIALS) ) + #define DEFAULT_GPRS_PIN "1234" + + typedef struct Configuration + { + char header [16]; + WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS]; + Blynk_Credentials Blynk_Creds [NUM_BLYNK_CREDENTIALS]; + int blynk_port; + // YOUR GSM / GPRS RELATED + char apn [32]; + char gprsUser [32]; + char gprsPass [32]; + char gprsPin [12]; // A PIN (Personal Identification Number) is a 4-8 digit passcode + // END OF YOUR GSM / GPRS RELATED + char board_name [24]; + int checkSum; + } Blynk_WF_Configuration; + +*/ + +bool LOAD_DEFAULT_CONFIG_DATA = true; +//bool LOAD_DEFAULT_CONFIG_DATA = false; + +Blynk_WF_Configuration defaultConfig = +{ + //char header[16], dummy, not used + "GSM", + //WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS] + //WiFi_Creds.wifi_ssid and WiFi_Creds.wifi_pw + "SSID1", "password1", + "SSID2", "password2", + // Blynk_Credentials Blynk_Creds [NUM_BLYNK_CREDENTIALS]; + // Blynk_Creds.blynk_server, Blynk_Creds.wifi_blynk_token and Blynk_Creds.gsm_blynk_token + "account.ddns.net", "wifi_token", "gsm_token", + "account.duckdns.org", "wifi_token1", "gsm_token1", + //int blynk_port; + 8080, + // YOUR GSM / GPRS RELATED + //char apn [32]; + "rogers-core-appl1.apn", + //char gprsUser [32]; + "wapuser1", + //char gprsPass [32]; + "wap", + //char gprsPin [12]; // A PIN (Personal Identification Number) is a 4-8 digit passcode + "1245678", + // END OF YOUR GSM / GPRS RELATED + //char board_name [24]; + "ESP32-GSM-WiFi", + //int checkSum, dummy, not used + 0 +}; +/////////// End Default Config Data ///////////// +``` + +### How to add dynamic parameters from sketch + +- To add custom parameters, just modify the example below + +``` #define USE_DYNAMIC_PARAMETERS true /////////////// Start dynamic Credentials /////////////// @@ -329,22 +468,22 @@ Please take a look at other examples, as well. #if USE_DYNAMIC_PARAMETERS #define MAX_MQTT_SERVER_LEN 34 -char MQTT_Server [MAX_MQTT_SERVER_LEN + 1] = ""; +char MQTT_Server [MAX_MQTT_SERVER_LEN + 1] = "mqtt.ddns.net"; #define MAX_MQTT_PORT_LEN 6 -char MQTT_Port [MAX_MQTT_PORT_LEN + 1] = ""; +char MQTT_Port [MAX_MQTT_PORT_LEN + 1] = "1883"; #define MAX_MQTT_USERNAME_LEN 34 -char MQTT_UserName [MAX_MQTT_USERNAME_LEN + 1] = ""; +char MQTT_UserName [MAX_MQTT_USERNAME_LEN + 1] = "mqtt-user"; #define MAX_MQTT_PW_LEN 34 -char MQTT_PW [MAX_MQTT_PW_LEN + 1] = ""; +char MQTT_PW [MAX_MQTT_PW_LEN + 1] = "mqtt-pass"; #define MAX_MQTT_SUBS_TOPIC_LEN 34 -char MQTT_SubsTopic [MAX_MQTT_SUBS_TOPIC_LEN + 1] = ""; +char MQTT_SubsTopic [MAX_MQTT_SUBS_TOPIC_LEN + 1] = "SubTopic_ESP32_GSM"; #define MAX_MQTT_PUB_TOPIC_LEN 34 -char MQTT_PubTopic [MAX_MQTT_PUB_TOPIC_LEN + 1] = ""; +char MQTT_PubTopic [MAX_MQTT_PUB_TOPIC_LEN + 1] = "PubTopic_ESP32_GSM"; MenuItem myMenuItems [] = { @@ -368,53 +507,62 @@ uint16_t NUM_MENU_ITEMS = 0; /////// // End dynamic Credentials /////////// -#else -#include +``` +- If you don't need to add dynamic parameters, use the following in sketch -// Your WiFi credentials. -#define ssid "****" -#define pass "****" +``` +#define USE_DYNAMIC_PARAMETERS false +``` -#define USE_LOCAL_SERVER true -//#define USE_LOCAL_SERVER false +or -#if USE_LOCAL_SERVER -#define wifi_blynk_tok "****" -#define gsm_blynk_tok "****" -//#define blynk_server "account.duckdns.org" -// Usedirect IPAddress in case GPRS can't use DDNS fast enough and can't connect -#define blynk_server "xxx.xxx.xxx.xxx" -#else -#define wifi_blynk_tok "****" -#define gsm_blynk_tok "****" -#define blynk_server "blynk-cloud.com" -#endif +``` +/////////////// Start dynamic Credentials /////////////// -#define apn "rogers-core-appl1.apn" -#define gprsUser "" //"wapuser1" -#define gprsPass "" //"wap" -#endif +MenuItem myMenuItems [] = {}; -#define BLYNK_HARDWARE_PORT 8080 +uint16_t NUM_MENU_ITEMS = 0; +/////// // End dynamic Credentials /////////// -#include +``` -// Set serial for debug console (to the Serial Monitor, default speed 115200) -#define SerialMon Serial +### Important Notes for using Dynamic Parameters' ids -// Use ESP32 Serial2 for GSM, Serial1 for TTGO T-Call -#define SerialAT Serial1 +1. These ids (such as "mqtt" in example) must be ***unique***. -// Uncomment this if you want to see all AT commands -#define DUMP_AT_COMMANDS false +Please be noted that the following ***reserved names are already used in library***: -#if DUMP_AT_COMMANDS -#include -StreamDebugger debugger(SerialAT, SerialMon); -TinyGsm modem(debugger); -#else -TinyGsm modem(SerialAT); -#endif +``` +"id" for WiFi SSID +"pw" for WiFi PW +"id1" for WiFi1 SSID +"pw1" for WiFi1 PW +"apn" for GPRS/GSM apn +"usr" for GPRS/GSM user +"pwd" for GPRS/GSM password +"pin" for GPRS/GSM pin +"sv" for Blynk Server +"wtk" for Blynk WiFi Token +"gtk" for Blynk GPRS/GSM Token +"sv1" for Blynk Server1 +"wtk1" for Blynk WiFi Token1 +"gtk1" for Blynk GPRS/GSM Token1 +"pt" for Blynk Port +"nm" for Board Name +``` + +--- + +## Example [TTGO_TCALL_GSM](examples/TTGO_TCALL_GSM) + +Please take a look at other examples, as well. + +1. File [ESP32WM_Config.ino](examples/TTGO_TCALL_GSM/TTGO_TCALL_GSM.ino) + +``` +#include "defines.h" +#include "Credentials.h" +#include "dynamicParams.h" void heartBeatPrint(void) { @@ -474,7 +622,8 @@ void setup() SerialMon.begin(115200); while (!SerialMon); - SerialMon.println(F("\nStart TTGO-TCALL-GSM")); + SerialMon.print(F("\nStart TTGO-TCALL-GSM using ")); + SerialMon.println(CurrentFileFS); // Set-up modem reset, enable, power pins pinMode(MODEM_PWKEY, OUTPUT); @@ -518,7 +667,7 @@ void setup() Serial.print(F("gprs apn = ")); Serial.println(localBlynkGSM_ESP32_config.apn); - if (String(localBlynkGSM_ESP32_config.apn) == String("nothing")) + if (String(localBlynkGSM_ESP32_config.apn) == NO_CONFIG) { Serial.println(F("No valid stored apn. Must run WiFi to Open config portal")); valid_apn = false; @@ -527,12 +676,20 @@ void setup() { valid_apn = true; - Blynk_GSM.config(modem, localBlynkGSM_ESP32_config.gsm_blynk_tok, localBlynkGSM_ESP32_config.blynk_server, BLYNK_HARDWARE_PORT); - GSM_CONNECT_OK = Blynk_GSM.connectNetwork(localBlynkGSM_ESP32_config.apn, localBlynkGSM_ESP32_config.gprsUser, - localBlynkGSM_ESP32_config.gprsPass); + for (int index = 0; index < NUM_BLYNK_CREDENTIALS; index++) + { + Blynk_GSM.config(modem, localBlynkGSM_ESP32_config.Blynk_Creds[index].gsm_blynk_token, + localBlynkGSM_ESP32_config.Blynk_Creds[index].blynk_server, localBlynkGSM_ESP32_config.blynk_port); + + GSM_CONNECT_OK = Blynk_GSM.connectNetwork(localBlynkGSM_ESP32_config.apn, localBlynkGSM_ESP32_config.gprsUser, + localBlynkGSM_ESP32_config.gprsPass); - if (GSM_CONNECT_OK) - Blynk_GSM.connect(); + if (GSM_CONNECT_OK) + { + if ( Blynk_GSM.connect() == true ) + break; + } + } } #endif } @@ -540,7 +697,7 @@ void setup() #if (USE_BLYNK_WM && USE_DYNAMIC_PARAMETERS) void displayCredentials(void) { - Serial.println("Your stored Credentials :"); + Serial.println("\nYour stored Credentials :"); for (int i = 0; i < NUM_MENU_ITEMS; i++) { @@ -585,50 +742,369 @@ void loop() } ``` -and this is the terminal debug output when running both WiFi and GSM/GPRS at the same time using example [TTGO_TCALL_GSM](examples/TTGO_TCALL_GSM) +2. File [defines.h](examples/TTGO_TCALL_GSM/defines.h) + +```cpp +#ifndef defines_h +#define defines_h + +#ifndef ESP32 + #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. +#endif + +#define BLYNK_PRINT Serial +#define BLYNK_HEARTBEAT 60 + +#define DOUBLERESETDETECTOR_DEBUG true //false +#define BLYNK_WM_DEBUG 1 + +//#define USE_SPIFFS false +#define USE_SPIFFS true + +#if USE_SPIFFS + #define CurrentFileFS F("SPIFFS") +#else + #define CurrentFileFS F("EEPROM") +// EEPROM_SIZE must be <= 2048 and >= CONFIG_DATA_SIZE (currently 172 bytes) + #define EEPROM_SIZE (2 * 1024) + // EEPROM_START + CONFIG_DATA_SIZE must be <= EEPROM_SIZE + #define EEPROM_START 0 +#endif + +// Force some params in Blynk, only valid for library version 1.0.1 and later +#define TIMEOUT_RECONNECT_WIFI 10000L +#define RESET_IF_CONFIG_TIMEOUT true +#define CONFIG_TIMEOUT_RETRYTIMES_BEFORE_RESET 5 +// Those above #define's must be placed before #include + +// TTGO T-Call pin definitions +#define MODEM_RST 5 +#define MODEM_PWKEY 4 +#define MODEM_POWER_ON 23 + +#define MODEM_TX 27 +#define MODEM_RX 26 + +#define I2C_SDA 21 +#define I2C_SCL 22 + +// Select your modem: +#define TINY_GSM_MODEM_SIM800 +//#define TINY_GSM_MODEM_SIM808 +//#define TINY_GSM_MODEM_SIM868 +//#define TINY_GSM_MODEM_SIM900 +//#define TINY_GSM_MODEM_SIM5300 +//#define TINY_GSM_MODEM_SIM5320 +//#define TINY_GSM_MODEM_SIM5360 +//#define TINY_GSM_MODEM_SIM7000 +//#define TINY_GSM_MODEM_SIM7100 +//#define TINY_GSM_MODEM_SIM7500 +//#define TINY_GSM_MODEM_SIM7600 +//#define TINY_GSM_MODEM_SIM7800 +//#define TINY_GSM_MODEM_UBLOX +//#define TINY_GSM_MODEM_SARAR4 +//#define TINY_GSM_MODEM_M95 +//#define TINY_GSM_MODEM_BG96 +//#define TINY_GSM_MODEM_A6 +//#define TINY_GSM_MODEM_A7 +//#define TINY_GSM_MODEM_M590 +//#define TINY_GSM_MODEM_MC60 +//#define TINY_GSM_MODEM_MC60E +//#define TINY_GSM_MODEM_XBEE +//#define TINY_GSM_MODEM_SEQUANS_MONARCH + +// Increase RX buffer if needed +#define TINY_GSM_RX_BUFFER 1024 + +#include + +//#define USE_BLYNK_WM false +#define USE_BLYNK_WM true + +#include + +#if USE_BLYNK_WM + + #include + +#else + #include + + // Your WiFi credentials. + #define ssid "****" + #define pass "****" + + #define USE_LOCAL_SERVER true + //#define USE_LOCAL_SERVER false + + #if USE_LOCAL_SERVER + #define wifi_blynk_tok "****" + #define gsm_blynk_tok "****" + //#define blynk_server "account.duckdns.org" + // Use direct IPAddress in case GPRS can't use DDNS fast enough and can't connect + #define blynk_server "xxx.xxx.xxx.xxx" + #else + #define wifi_blynk_tok "****" + #define gsm_blynk_tok "****" + #define blynk_server "blynk-cloud.com" + #endif + + #define apn "rogers-core-appl1.apn" + #define gprsUser "" //"wapuser1" + #define gprsPass "" //"wap" + +#endif //USE_BLYNK_WM + +#define BLYNK_HARDWARE_PORT 8080 + +// Set serial for debug console (to the Serial Monitor, default speed 115200) +#define SerialMon Serial + +#define RXD2 16 +#define TXD2 17 + +// Use ESP32 Serial2 for GSM, Serial1 for TTGO T-Call +#define SerialAT Serial1 + +// Uncomment this if you want to see all AT commands +#define DUMP_AT_COMMANDS false + +#if DUMP_AT_COMMANDS + #include + StreamDebugger debugger(SerialAT, SerialMon); + TinyGsm modem(debugger); +#else + TinyGsm modem(SerialAT); +#endif + +#define HOST_NAME "ESP32-GSM-WiFi" + +#endif //defines_h + +``` + +3. File [Credentials.h](examples/TTGO_TCALL_GSM/Credentials.h) + +```cpp +#ifndef Credentials_h +#define Credentials_h + +/// Start Default Config Data ////////////////// + +/* + // Defined in + + #define SSID_MAX_LEN 32 + //From v1.0.10, WPA2 passwords can be up to 63 characters long. + #define PASS_MAX_LEN 64 + + typedef struct + { + char wifi_ssid[SSID_MAX_LEN]; + char wifi_pw [PASS_MAX_LEN]; + } WiFi_Credentials; + + #define BLYNK_SERVER_MAX_LEN 32 + #define BLYNK_TOKEN_MAX_LEN 36 + + typedef struct + { + char blynk_server [BLYNK_SERVER_MAX_LEN]; + char wifi_blynk_token [BLYNK_TOKEN_MAX_LEN]; + char gsm_blynk_token [BLYNK_TOKEN_MAX_LEN]; + } Blynk_Credentials; + + #define NUM_WIFI_CREDENTIALS 2 + #define NUM_BLYNK_CREDENTIALS 2 + + // Configurable items besides fixed Header + #define NUM_CONFIGURABLE_ITEMS ( 6 + (2 * NUM_WIFI_CREDENTIALS) + (3 * NUM_BLYNK_CREDENTIALS) ) + #define DEFAULT_GPRS_PIN "1234" + + typedef struct Configuration + { + char header [16]; + WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS]; + Blynk_Credentials Blynk_Creds [NUM_BLYNK_CREDENTIALS]; + int blynk_port; + // YOUR GSM / GPRS RELATED + char apn [32]; + char gprsUser [32]; + char gprsPass [32]; + char gprsPin [12]; // A PIN (Personal Identification Number) is a 4-8 digit passcode + // END OF YOUR GSM / GPRS RELATED + char board_name [24]; + int checkSum; + } Blynk_WF_Configuration; + +*/ + +bool LOAD_DEFAULT_CONFIG_DATA = true; +//bool LOAD_DEFAULT_CONFIG_DATA = false; + +Blynk_WF_Configuration defaultConfig = +{ + //char header[16], dummy, not used + "GSM", + //WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS] + //WiFi_Creds.wifi_ssid and WiFi_Creds.wifi_pw + "SSID1", "password1", + "SSID2", "password2", + // Blynk_Credentials Blynk_Creds [NUM_BLYNK_CREDENTIALS]; + // Blynk_Creds.blynk_server, Blynk_Creds.wifi_blynk_token and Blynk_Creds.gsm_blynk_token + "account.ddns.net", "wifi_token", "gsm_token", + "account.duckdns.org", "wifi_token1", "gsm_token1", + //int blynk_port; + 8080, + // YOUR GSM / GPRS RELATED + //char apn [32]; + "rogers-core-appl1.apn", + //char gprsUser [32]; + "wapuser1", + //char gprsPass [32]; + "wap", + //char gprsPin [12]; // A PIN (Personal Identification Number) is a 4-8 digit passcode + "1245678", + // END OF YOUR GSM / GPRS RELATED + //char board_name [24]; + "ESP32-GSM-WiFi", + //int checkSum, dummy, not used + 0 +}; + +/////////// End Default Config Data ///////////// + + +#endif //Credentials_h +``` + +4. File [dynamicParams.h](examples/TTGO_TCALL_GSM/dynamicParams.h) + +```cpp +#ifndef dynamicParams_h +#define dynamicParams_h + +#define USE_DYNAMIC_PARAMETERS true + +/////////////// Start dynamic Credentials /////////////// + +//Defined in +/************************************** + #define MAX_ID_LEN 5 + #define MAX_DISPLAY_NAME_LEN 16 + + typedef struct + { + char id [MAX_ID_LEN + 1]; + char displayName [MAX_DISPLAY_NAME_LEN + 1]; + char *pdata; + uint8_t maxlen; + } MenuItem; +**************************************/ + +#if USE_DYNAMIC_PARAMETERS + +#define MAX_MQTT_SERVER_LEN 34 +char MQTT_Server [MAX_MQTT_SERVER_LEN + 1] = "mqtt.ddns.net"; + +#define MAX_MQTT_PORT_LEN 6 +char MQTT_Port [MAX_MQTT_PORT_LEN + 1] = "1883"; + +#define MAX_MQTT_USERNAME_LEN 34 +char MQTT_UserName [MAX_MQTT_USERNAME_LEN + 1] = "mqtt-user"; + +#define MAX_MQTT_PW_LEN 34 +char MQTT_PW [MAX_MQTT_PW_LEN + 1] = "mqtt-pass"; + +#define MAX_MQTT_SUBS_TOPIC_LEN 34 +char MQTT_SubsTopic [MAX_MQTT_SUBS_TOPIC_LEN + 1] = "SubTopic_ESP32_GSM"; + +#define MAX_MQTT_PUB_TOPIC_LEN 34 +char MQTT_PubTopic [MAX_MQTT_PUB_TOPIC_LEN + 1] = "PubTopic_ESP32_GSM"; + +MenuItem myMenuItems [] = +{ + { "mqtt", "MQTT Server", MQTT_Server, MAX_MQTT_SERVER_LEN }, + { "mqpt", "Port", MQTT_Port, MAX_MQTT_PORT_LEN }, + { "user", "MQTT UserName", MQTT_UserName, MAX_MQTT_USERNAME_LEN }, + { "mqpw", "MQTT PWD", MQTT_PW, MAX_MQTT_PW_LEN }, + { "subs", "Subs Topics", MQTT_SubsTopic, MAX_MQTT_SUBS_TOPIC_LEN }, + { "pubs", "Pubs Topics", MQTT_PubTopic, MAX_MQTT_PUB_TOPIC_LEN }, +}; + +uint16_t NUM_MENU_ITEMS = sizeof(myMenuItems) / sizeof(MenuItem); //MenuItemSize; + +#else + +MenuItem myMenuItems [] = {}; + +uint16_t NUM_MENU_ITEMS = 0; +#endif + + +/////// // End dynamic Credentials /////////// + +#endif //dynamicParams_h +``` +--- + +#### This is the terminal debug output when running both WiFi and GSM/GPRS at the same time using example [TTGO_TCALL_GSM](examples/TTGO_TCALL_GSM) ``` Start TTGO-TCALL-GSM Set GSM module baud rate Use WiFi to connect Blynk [3108] Hostname=TTGO-TCALL-GSM -[3114] CCSum=0x37d8,RCSum=0x37d8 -[3114] CrCCsum=5323,CrRCsum=5323 -[3114] Hdr=ESP32_GSM_WFM,SSID=your-ssid,PW=your-pass -[3114] Svr=account.duckdns.org,Prt=8080,WiFiToken=wifi-token -[3120] APN=rogers-core-appl1.apn,User=wapuser1 -[3124] PW=wap,PIN=12345678,GSMToken=gsm-token -[3130] BrdName=TTGO-TCALL -[3133] +[3169] LoadCfgFile +[3169] OK +[3169] CCSum=0x5ae8,RCSum=0x5ae8 +[3171] LoadCredFile +[3171] OK +[3171] CrCCsum=15b9,CrRCsum=15b9 +[3171] Buffer freed +[3173] LoadCredFile +[3173] OK +[3174] CrCCsum=15b9,CrRCsum=15b9 +[3177] Hdr=ESP32_GSM_WFM,BrdName=ESP32-GSM-WiFi +[3181] SSID=HueNet1,PW=**** +[3184] SSID1=HueNet2,PW1=**** +[3187] APN=rogers-core-appl1.apn,User=wapuser1 +[3191] PW=wap,PIN=1245678 +[3194] Server=account.ddns.net,WiFi_Token=****,GSM_Token=**** +[3204] Server1=account.duckdns.org,WiFi_Token1=****,GSM_Token1=**** +[3214] Port=8080 +[3216] ======= End Config Data ======= +[3220] Connecting MultiWifi... +[9463] WiFi connected after time: 1 +[9463] SSID=HueNet1,RSSI=-40 +[9463] Channel=2,IP=192.168.2.81 +[9464] bg: WiFi OK. Try Blynk +[9464] ___ __ __ / _ )/ /_ _____ / /__ / _ / / // / _ \/ '_/ /____/_/\_, /_//_/_/\_\ /___/ v0.6.1 on ESP32 -[3150] con2WF:start -[4651] con2WF:OK -[4651] IP=192.168.2.142,GW=192.168.2.1,SN=255.255.0.0 -[4651] DNS1=192.168.2.1,DNS2=0.0.0.0 -[4651] b:WOK.TryB -[4651] BlynkArduinoClient.connect: Connecting to account.duckdns.org:8080 -[4778] Ready (ping: 5ms). -[4846] b:WBOK +[9477] BlynkArduinoClient.connect: Connecting to account.ddns.net:8080 +[9609] Ready (ping: 12ms). +[9676] Connected to BlynkServer=account.ddns.net,Token=**** +[9677] bg: WiFi+Blynk OK gprs apn = rogers-core-appl1.apn -[4846] +[9678] ___ __ __ / _ )/ /_ _____ / /__ / _ / / // / _ \/ '_/ /____/_/\_, /_//_/_/\_\ /___/ v0.6.1 on ESP32 -[4852] InitModem -[4974] Con2Network -[4985] Network:Rogers Wireless -[4985] Conn2 rogers-core-appl1.apn -[10114] GPRSConOK -[10124] BlynkArduinoClient.connect: Connecting to account.duckdns.org:8080 -[10768] Ready (ping: 315ms). +[9691] InitModem +[9713] Con2Network +[9724] Network:Rogers Wireless +[9724] Conn2 rogers-core-appl1.apn +[14955] GPRSConOK +[14965] BlynkArduinoClient.connect: Connecting to account.duckdns.org:8080 +[15609] Ready (ping: 315ms). Your stored Credentials : MQTT Server = mqtt.duckdns.org Port = 1883 @@ -639,6 +1115,17 @@ Pubs Topics = PubsTopic1 BGBGBGBGBGBGBGBGBGBG BGBGBGBGBGBGBGBGBGBG BGBGBGBGBGBGBGBGBGBG BGBGBGBGBGBGBGBGBGBG ``` +--- + +### Major Releases v1.0.9 + +1. Add MultiWiFi/Blynk features for WiFi +2. Add MultiBlynk feature for GPRS/GSM +3. Add DoubleResetDetector (DRD) feature. +4. Update to use LittleFS for ESP8266 core 2.7.1+ to replace deprecated SPIFFS on ESP8266 +5. Add Configurable Config Portal Title +6. Add Default Config Data. + ### Releases v1.0.8 1. Fix potential dangerous bug in code and examples of v1.0.7. @@ -691,6 +1178,8 @@ BGBGBGBGBGBGBGBGBGBG BGBGBGBGBGBGBGBGBGBG BGBGBGBGBGBGBGBGBGBG BGBGBGBGBGBGBGBGB 1. Change Synch XMLHttpRequest to Async to avoid ["InvalidAccessError" DOMException](https://xhr.spec.whatwg.org/) 2. Reduce memory usage. +--- + ## TO DO 1. Same features for other boards with GSM/GPRS shield as well as other GSM/GPRS shields (SIM7x00, etc.). diff --git a/examples/ESP32_GSM/Credentials.h b/examples/ESP32_GSM/Credentials.h new file mode 100644 index 0000000..3156e68 --- /dev/null +++ b/examples/ESP32_GSM/Credentials.h @@ -0,0 +1,115 @@ +/**************************************************************************************************************************** + Credentials.h for ESP32_GSM.ino + For ESP32 boards to run GSM/GPRS and WiFi simultaneously, using config portal feature + + Library to enable GSM/GPRS and WiFi running simultaneously , with WiFi config portal. + Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases + Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager + Licensed under MIT license + Version: 1.0.9 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 17/01/2020 Initial coding. Add config portal similar to Blynk_WM library. + 1.0.1 K Hoang 27/01/2020 Change Synch XMLHttpRequest to Async (https://xhr.spec.whatwg.org/). Reduce code size + 1.0.2 K Hoang 08/02/2020 Enable GSM/GPRS and WiFi running simultaneously + 1.0.3 K Hoang 18/02/2020 Add checksum. Add clearConfigData() + 1.0.4 K Hoang 14/03/2020 Enhance Config Portal GUI. Reduce code size. + 1.0.5 K Hoang 20/03/2020 Add more modem supports. See the list in README.md + 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch + 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. + 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM + *****************************************************************************************************************************/ + +#ifndef Credentials_h +#define Credentials_h + +/// Start Default Config Data ////////////////// + +/* + // Defined in + + #define SSID_MAX_LEN 32 + //From v1.0.10, WPA2 passwords can be up to 63 characters long. + #define PASS_MAX_LEN 64 + + typedef struct + { + char wifi_ssid[SSID_MAX_LEN]; + char wifi_pw [PASS_MAX_LEN]; + } WiFi_Credentials; + + #define BLYNK_SERVER_MAX_LEN 32 + #define BLYNK_TOKEN_MAX_LEN 36 + + typedef struct + { + char blynk_server [BLYNK_SERVER_MAX_LEN]; + char wifi_blynk_token [BLYNK_TOKEN_MAX_LEN]; + char gsm_blynk_token [BLYNK_TOKEN_MAX_LEN]; + } Blynk_Credentials; + + #define NUM_WIFI_CREDENTIALS 2 + #define NUM_BLYNK_CREDENTIALS 2 + + // Configurable items besides fixed Header + #define NUM_CONFIGURABLE_ITEMS ( 6 + (2 * NUM_WIFI_CREDENTIALS) + (3 * NUM_BLYNK_CREDENTIALS) ) + #define DEFAULT_GPRS_PIN "1234" + + typedef struct Configuration + { + char header [16]; + WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS]; + Blynk_Credentials Blynk_Creds [NUM_BLYNK_CREDENTIALS]; + int blynk_port; + // YOUR GSM / GPRS RELATED + char apn [32]; + char gprsUser [32]; + char gprsPass [32]; + char gprsPin [12]; // A PIN (Personal Identification Number) is a 4-8 digit passcode + // END OF YOUR GSM / GPRS RELATED + char board_name [24]; + int checkSum; + } Blynk_WF_Configuration; + +*/ + +bool LOAD_DEFAULT_CONFIG_DATA = true; +//bool LOAD_DEFAULT_CONFIG_DATA = false; + +Blynk_WF_Configuration defaultConfig = +{ + //char header[16], dummy, not used + "GSM", + //WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS] + //WiFi_Creds.wifi_ssid and WiFi_Creds.wifi_pw + "SSID1", "password1", + "SSID2", "password2", + // Blynk_Credentials Blynk_Creds [NUM_BLYNK_CREDENTIALS]; + // Blynk_Creds.blynk_server, Blynk_Creds.wifi_blynk_token and Blynk_Creds.gsm_blynk_token + "account.ddns.net", "wifi_token", "gsm_token", + "account.duckdns.org", "wifi_token1", "gsm_token1", + //int blynk_port; + 8080, + // YOUR GSM / GPRS RELATED + //char apn [32]; + "rogers-core-appl1.apn", + //char gprsUser [32]; + "wapuser1", + //char gprsPass [32]; + "wap", + //char gprsPin [12]; // A PIN (Personal Identification Number) is a 4-8 digit passcode + "1245678", + // END OF YOUR GSM / GPRS RELATED + //char board_name [24]; + "ESP32-GSM-WiFi", + //int checkSum, dummy, not used + 0 +}; + +/////////// End Default Config Data ///////////// + + +#endif //Credentials_h diff --git a/examples/ESP32_GSM/ESP32_GSM.ino b/examples/ESP32_GSM/ESP32_GSM.ino index 15a49ee..5a8e9f2 100644 --- a/examples/ESP32_GSM/ESP32_GSM.ino +++ b/examples/ESP32_GSM/ESP32_GSM.ino @@ -6,7 +6,7 @@ Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager Licensed under MIT license - Version: 1.0.8 + Version: 1.0.9 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -19,182 +19,13 @@ 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM *****************************************************************************************************************************/ -#ifndef ESP32 -#error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. -#endif - -#define BLYNK_PRINT Serial -#define BLYNK_HEARTBEAT 60 - -// TTGO T-Call pin definitions -#define MODEM_RST 5 -#define MODEM_PWKEY 4 -#define MODEM_POWER_ON 23 - -#define MODEM_TX 27 -#define MODEM_RX 26 - -#define I2C_SDA 21 -#define I2C_SCL 22 - -// Select your modem: -#define TINY_GSM_MODEM_SIM800 -//#define TINY_GSM_MODEM_SIM808 -//#define TINY_GSM_MODEM_SIM868 -//#define TINY_GSM_MODEM_SIM900 -//#define TINY_GSM_MODEM_SIM5300 -//#define TINY_GSM_MODEM_SIM5320 -//#define TINY_GSM_MODEM_SIM5360 -//#define TINY_GSM_MODEM_SIM7000 -//#define TINY_GSM_MODEM_SIM7100 -//#define TINY_GSM_MODEM_SIM7500 -//#define TINY_GSM_MODEM_SIM7600 -//#define TINY_GSM_MODEM_SIM7800 -//#define TINY_GSM_MODEM_UBLOX -//#define TINY_GSM_MODEM_SARAR4 -//#define TINY_GSM_MODEM_M95 -//#define TINY_GSM_MODEM_BG96 -//#define TINY_GSM_MODEM_A6 -//#define TINY_GSM_MODEM_A7 -//#define TINY_GSM_MODEM_M590 -//#define TINY_GSM_MODEM_MC60 -//#define TINY_GSM_MODEM_MC60E -//#define TINY_GSM_MODEM_XBEE -//#define TINY_GSM_MODEM_SEQUANS_MONARCH - - -// Increase RX buffer if needed -#define TINY_GSM_RX_BUFFER 1024 - -#include - -#define USE_SPIFFS true -//#define USE_SPIFFS false - -//#define USE_BLYNK_WM false -#define USE_BLYNK_WM true - -#define EEPROM_SIZE 2048 -#define EEPROM_START 512 - -#include - -#if USE_BLYNK_WM -#include - -#define USE_DYNAMIC_PARAMETERS true - -/////////////// Start dynamic Credentials /////////////// - -//Defined in -/************************************** - #define MAX_ID_LEN 5 - #define MAX_DISPLAY_NAME_LEN 16 - - typedef struct - { - char id [MAX_ID_LEN + 1]; - char displayName [MAX_DISPLAY_NAME_LEN + 1]; - char *pdata; - uint8_t maxlen; - } MenuItem; -**************************************/ - -#if USE_DYNAMIC_PARAMETERS - -#define MAX_MQTT_SERVER_LEN 34 -char MQTT_Server [MAX_MQTT_SERVER_LEN + 1] = ""; - -#define MAX_MQTT_PORT_LEN 6 -char MQTT_Port [MAX_MQTT_PORT_LEN + 1] = ""; - -#define MAX_MQTT_USERNAME_LEN 34 -char MQTT_UserName [MAX_MQTT_USERNAME_LEN + 1] = ""; - -#define MAX_MQTT_PW_LEN 34 -char MQTT_PW [MAX_MQTT_PW_LEN + 1] = ""; - -#define MAX_MQTT_SUBS_TOPIC_LEN 34 -char MQTT_SubsTopic [MAX_MQTT_SUBS_TOPIC_LEN + 1] = ""; - -#define MAX_MQTT_PUB_TOPIC_LEN 34 -char MQTT_PubTopic [MAX_MQTT_PUB_TOPIC_LEN + 1] = ""; - -MenuItem myMenuItems [] = -{ - { "mqtt", "MQTT Server", MQTT_Server, MAX_MQTT_SERVER_LEN }, - { "mqpt", "Port", MQTT_Port, MAX_MQTT_PORT_LEN }, - { "user", "MQTT UserName", MQTT_UserName, MAX_MQTT_USERNAME_LEN }, - { "mqpw", "MQTT PWD", MQTT_PW, MAX_MQTT_PW_LEN }, - { "subs", "Subs Topics", MQTT_SubsTopic, MAX_MQTT_SUBS_TOPIC_LEN }, - { "pubs", "Pubs Topics", MQTT_PubTopic, MAX_MQTT_PUB_TOPIC_LEN }, -}; - -uint16_t NUM_MENU_ITEMS = sizeof(myMenuItems) / sizeof(MenuItem); //MenuItemSize; - -#else - -MenuItem myMenuItems [] = {}; - -uint16_t NUM_MENU_ITEMS = 0; -#endif - - -/////// // End dynamic Credentials /////////// - -#else -#include - -// Your WiFi credentials. -#define ssid "****" -#define pass "****" - -#define USE_LOCAL_SERVER true -//#define USE_LOCAL_SERVER false - -#if USE_LOCAL_SERVER -#define wifi_blynk_tok "****" -#define gsm_blynk_tok "****" -//#define blynk_server "account.duckdns.org" -#define blynk_server "xxx.xxx.xxx.xxx" -#else -#define wifi_blynk_tok "****" -#define gsm_blynk_tok "****" -#define blynk_server "blynk-cloud.com" -#endif - -#define apn "rogers-core-appl1.apn" -#define gprsUser "" //"wapuser1" -#define gprsPass "" //"wap" -#endif - -#define BLYNK_HARDWARE_PORT 8080 - -#include - -// Set serial for debug console (to the Serial Monitor, default speed 115200) -#define SerialMon Serial - -#define RXD2 16 -#define TXD2 17 -// Use ESP32 Serial2 for GSM -#define SerialAT Serial2 - -// Uncomment this if you want to see all AT commands -#define DUMP_AT_COMMANDS false - -//#include -//SoftwareSerial SerialAT(MODEM_RX, MODEM_TX); // RX, TX - -#if DUMP_AT_COMMANDS -#include -StreamDebugger debugger(SerialAT, SerialMon); -TinyGsm modem(debugger); -#else -TinyGsm modem(SerialAT); -#endif +#include "defines.h" +#include "Credentials.h" +#include "dynamicParams.h" void heartBeatPrint(void) { @@ -254,7 +85,8 @@ void setup() SerialMon.begin(115200); while (!SerialMon); - SerialMon.println(F("\nStart ESP32-WIFI-GSM")); + SerialMon.print(F("\nStart ESP32-WIFI-GSM using ")); + SerialMon.println(CurrentFileFS); // Set-up modem reset, enable, power pins pinMode(MODEM_PWKEY, OUTPUT); @@ -276,7 +108,7 @@ void setup() #if USE_BLYNK_WM // Use configurable AP IP, instead of default IP 192.168.4.1 - Blynk_WF.setConfigPortalIP(IPAddress(192, 168, 100, 1)); + Blynk_WF.setConfigPortalIP(IPAddress(192, 168, 240, 1)); // Use channel = 0 => random Config Portal WiFi channel to avoid conflict Blynk_WF.setConfigPortalChannel(0); // Set personalized Hostname @@ -299,7 +131,7 @@ void setup() Serial.print(F("gprs apn = ")); Serial.println(localBlynkGSM_ESP32_config.apn); - if (String(localBlynkGSM_ESP32_config.apn) == String("nothing")) + if (String(localBlynkGSM_ESP32_config.apn) == NO_CONFIG) { Serial.println(F("No valid stored apn. Must run WiFi to Open config portal")); valid_apn = false; @@ -308,12 +140,20 @@ void setup() { valid_apn = true; - Blynk_GSM.config(modem, localBlynkGSM_ESP32_config.gsm_blynk_tok, localBlynkGSM_ESP32_config.blynk_server, BLYNK_HARDWARE_PORT); - GSM_CONNECT_OK = Blynk_GSM.connectNetwork(localBlynkGSM_ESP32_config.apn, localBlynkGSM_ESP32_config.gprsUser, - localBlynkGSM_ESP32_config.gprsPass); + for (int index = 0; index < NUM_BLYNK_CREDENTIALS; index++) + { + Blynk_GSM.config(modem, localBlynkGSM_ESP32_config.Blynk_Creds[index].gsm_blynk_token, + localBlynkGSM_ESP32_config.Blynk_Creds[index].blynk_server, localBlynkGSM_ESP32_config.blynk_port); - if (GSM_CONNECT_OK) - Blynk_GSM.connect(); + GSM_CONNECT_OK = Blynk_GSM.connectNetwork(localBlynkGSM_ESP32_config.apn, localBlynkGSM_ESP32_config.gprsUser, + localBlynkGSM_ESP32_config.gprsPass); + + if (GSM_CONNECT_OK) + { + if ( Blynk_GSM.connect() == true ) + break; + } + } } #endif } @@ -321,7 +161,7 @@ void setup() #if (USE_BLYNK_WM && USE_DYNAMIC_PARAMETERS) void displayCredentials(void) { - Serial.println("Your stored Credentials :"); + Serial.println("\nYour stored Credentials :"); for (int i = 0; i < NUM_MENU_ITEMS; i++) { diff --git a/examples/ESP32_GSM/defines.h b/examples/ESP32_GSM/defines.h new file mode 100644 index 0000000..5d8f806 --- /dev/null +++ b/examples/ESP32_GSM/defines.h @@ -0,0 +1,160 @@ +/**************************************************************************************************************************** + defines.h for ESP32_GSM.ino + For ESP32 boards to run GSM/GPRS and WiFi simultaneously, using config portal feature + + Library to enable GSM/GPRS and WiFi running simultaneously , with WiFi config portal. + Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases + Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager + Licensed under MIT license + Version: 1.0.9 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 17/01/2020 Initial coding. Add config portal similar to Blynk_WM library. + 1.0.1 K Hoang 27/01/2020 Change Synch XMLHttpRequest to Async (https://xhr.spec.whatwg.org/). Reduce code size + 1.0.2 K Hoang 08/02/2020 Enable GSM/GPRS and WiFi running simultaneously + 1.0.3 K Hoang 18/02/2020 Add checksum. Add clearConfigData() + 1.0.4 K Hoang 14/03/2020 Enhance Config Portal GUI. Reduce code size. + 1.0.5 K Hoang 20/03/2020 Add more modem supports. See the list in README.md + 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch + 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. + 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM + *****************************************************************************************************************************/ + +#ifndef defines_h +#define defines_h + +#ifndef ESP32 + #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. +#endif + +#define BLYNK_PRINT Serial +#define BLYNK_HEARTBEAT 60 + +#define DOUBLERESETDETECTOR_DEBUG true //false +#define BLYNK_WM_DEBUG 1 + +//#define USE_SPIFFS false +#define USE_SPIFFS true + +#if USE_SPIFFS + #define CurrentFileFS F("SPIFFS") +#else + #define CurrentFileFS F("EEPROM") +// EEPROM_SIZE must be <= 2048 and >= CONFIG_DATA_SIZE (currently 172 bytes) + #define EEPROM_SIZE (2 * 1024) + // EEPROM_START + CONFIG_DATA_SIZE must be <= EEPROM_SIZE + #define EEPROM_START 0 +#endif + +// Force some params in Blynk, only valid for library version 1.0.1 and later +#define TIMEOUT_RECONNECT_WIFI 10000L +#define RESET_IF_CONFIG_TIMEOUT true +#define CONFIG_TIMEOUT_RETRYTIMES_BEFORE_RESET 5 +// Those above #define's must be placed before #include + +// TTGO T-Call pin definitions +#define MODEM_RST 5 +#define MODEM_PWKEY 4 +#define MODEM_POWER_ON 23 + +#define MODEM_TX 27 +#define MODEM_RX 26 + +#define I2C_SDA 21 +#define I2C_SCL 22 + +// Select your modem: +#define TINY_GSM_MODEM_SIM800 +//#define TINY_GSM_MODEM_SIM808 +//#define TINY_GSM_MODEM_SIM868 +//#define TINY_GSM_MODEM_SIM900 +//#define TINY_GSM_MODEM_SIM5300 +//#define TINY_GSM_MODEM_SIM5320 +//#define TINY_GSM_MODEM_SIM5360 +//#define TINY_GSM_MODEM_SIM7000 +//#define TINY_GSM_MODEM_SIM7100 +//#define TINY_GSM_MODEM_SIM7500 +//#define TINY_GSM_MODEM_SIM7600 +//#define TINY_GSM_MODEM_SIM7800 +//#define TINY_GSM_MODEM_UBLOX +//#define TINY_GSM_MODEM_SARAR4 +//#define TINY_GSM_MODEM_M95 +//#define TINY_GSM_MODEM_BG96 +//#define TINY_GSM_MODEM_A6 +//#define TINY_GSM_MODEM_A7 +//#define TINY_GSM_MODEM_M590 +//#define TINY_GSM_MODEM_MC60 +//#define TINY_GSM_MODEM_MC60E +//#define TINY_GSM_MODEM_XBEE +//#define TINY_GSM_MODEM_SEQUANS_MONARCH + +// Increase RX buffer if needed +#define TINY_GSM_RX_BUFFER 1024 + +#include + +//#define USE_BLYNK_WM false +#define USE_BLYNK_WM true + +#include + +#if USE_BLYNK_WM + + #include + +#else + #include + + // Your WiFi credentials. + #define ssid "****" + #define pass "****" + + #define USE_LOCAL_SERVER true + //#define USE_LOCAL_SERVER false + + #if USE_LOCAL_SERVER + #define wifi_blynk_tok "****" + #define gsm_blynk_tok "****" + //#define blynk_server "account.duckdns.org" + // Use direct IPAddress in case GPRS can't use DDNS fast enough and can't connect + #define blynk_server "xxx.xxx.xxx.xxx" + #else + #define wifi_blynk_tok "****" + #define gsm_blynk_tok "****" + #define blynk_server "blynk-cloud.com" + #endif + + #define apn "rogers-core-appl1.apn" + #define gprsUser "" //"wapuser1" + #define gprsPass "" //"wap" + +#endif //USE_BLYNK_WM + +#define BLYNK_HARDWARE_PORT 8080 + +// Set serial for debug console (to the Serial Monitor, default speed 115200) +#define SerialMon Serial + +#define RXD2 16 +#define TXD2 17 + +// Use ESP32 Serial2 for GSM +#define SerialAT Serial2 + +// Uncomment this if you want to see all AT commands +#define DUMP_AT_COMMANDS false + +#if DUMP_AT_COMMANDS + #include + StreamDebugger debugger(SerialAT, SerialMon); + TinyGsm modem(debugger); +#else + TinyGsm modem(SerialAT); +#endif + +#define HOST_NAME "ESP32-GSM-WiFi" + +#endif //defines_h diff --git a/examples/ESP32_GSM/dynamicParams.h b/examples/ESP32_GSM/dynamicParams.h new file mode 100644 index 0000000..8f528c7 --- /dev/null +++ b/examples/ESP32_GSM/dynamicParams.h @@ -0,0 +1,89 @@ +/**************************************************************************************************************************** + dynamicParams.h for ESP32_GSM.ino + For ESP32 boards to run GSM/GPRS and WiFi simultaneously, using config portal feature + + Library to enable GSM/GPRS and WiFi running simultaneously , with WiFi config portal. + Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases + Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager + Licensed under MIT license + Version: 1.0.9 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 17/01/2020 Initial coding. Add config portal similar to Blynk_WM library. + 1.0.1 K Hoang 27/01/2020 Change Synch XMLHttpRequest to Async (https://xhr.spec.whatwg.org/). Reduce code size + 1.0.2 K Hoang 08/02/2020 Enable GSM/GPRS and WiFi running simultaneously + 1.0.3 K Hoang 18/02/2020 Add checksum. Add clearConfigData() + 1.0.4 K Hoang 14/03/2020 Enhance Config Portal GUI. Reduce code size. + 1.0.5 K Hoang 20/03/2020 Add more modem supports. See the list in README.md + 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch + 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. + 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM + *****************************************************************************************************************************/ + +#ifndef dynamicParams_h +#define dynamicParams_h + +#define USE_DYNAMIC_PARAMETERS true + +/////////////// Start dynamic Credentials /////////////// + +//Defined in +/************************************** + #define MAX_ID_LEN 5 + #define MAX_DISPLAY_NAME_LEN 16 + + typedef struct + { + char id [MAX_ID_LEN + 1]; + char displayName [MAX_DISPLAY_NAME_LEN + 1]; + char *pdata; + uint8_t maxlen; + } MenuItem; +**************************************/ + +#if USE_DYNAMIC_PARAMETERS + +#define MAX_MQTT_SERVER_LEN 34 +char MQTT_Server [MAX_MQTT_SERVER_LEN + 1] = "mqtt.ddns.net"; + +#define MAX_MQTT_PORT_LEN 6 +char MQTT_Port [MAX_MQTT_PORT_LEN + 1] = "1883"; + +#define MAX_MQTT_USERNAME_LEN 34 +char MQTT_UserName [MAX_MQTT_USERNAME_LEN + 1] = "mqtt-user"; + +#define MAX_MQTT_PW_LEN 34 +char MQTT_PW [MAX_MQTT_PW_LEN + 1] = "mqtt-pass"; + +#define MAX_MQTT_SUBS_TOPIC_LEN 34 +char MQTT_SubsTopic [MAX_MQTT_SUBS_TOPIC_LEN + 1] = "SubTopic_ESP32_GSM"; + +#define MAX_MQTT_PUB_TOPIC_LEN 34 +char MQTT_PubTopic [MAX_MQTT_PUB_TOPIC_LEN + 1] = "PubTopic_ESP32_GSM"; + +MenuItem myMenuItems [] = +{ + { "mqtt", "MQTT Server", MQTT_Server, MAX_MQTT_SERVER_LEN }, + { "mqpt", "Port", MQTT_Port, MAX_MQTT_PORT_LEN }, + { "user", "MQTT UserName", MQTT_UserName, MAX_MQTT_USERNAME_LEN }, + { "mqpw", "MQTT PWD", MQTT_PW, MAX_MQTT_PW_LEN }, + { "subs", "Subs Topics", MQTT_SubsTopic, MAX_MQTT_SUBS_TOPIC_LEN }, + { "pubs", "Pubs Topics", MQTT_PubTopic, MAX_MQTT_PUB_TOPIC_LEN }, +}; + +uint16_t NUM_MENU_ITEMS = sizeof(myMenuItems) / sizeof(MenuItem); //MenuItemSize; + +#else + +MenuItem myMenuItems [] = {}; + +uint16_t NUM_MENU_ITEMS = 0; +#endif + + +/////// // End dynamic Credentials /////////// + +#endif //dynamicParams_h diff --git a/examples/ESP8266_GSM/Credentials.h b/examples/ESP8266_GSM/Credentials.h new file mode 100644 index 0000000..39f6885 --- /dev/null +++ b/examples/ESP8266_GSM/Credentials.h @@ -0,0 +1,115 @@ +/**************************************************************************************************************************** + Credentials.h for ESP8266_GSM.ino + For ESP8266 boards to run GSM/GPRS and WiFi simultaneously, using config portal feature + + Library to enable GSM/GPRS and WiFi running simultaneously , with WiFi config portal. + Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases + Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager + Licensed under MIT license + Version: 1.0.9 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 17/01/2020 Initial coding. Add config portal similar to Blynk_WM library. + 1.0.1 K Hoang 27/01/2020 Change Synch XMLHttpRequest to Async (https://xhr.spec.whatwg.org/). Reduce code size + 1.0.2 K Hoang 08/02/2020 Enable GSM/GPRS and WiFi running simultaneously + 1.0.3 K Hoang 18/02/2020 Add checksum. Add clearConfigData() + 1.0.4 K Hoang 14/03/2020 Enhance Config Portal GUI. Reduce code size. + 1.0.5 K Hoang 20/03/2020 Add more modem supports. See the list in README.md + 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch + 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. + 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM + *****************************************************************************************************************************/ + +#ifndef Credentials_h +#define Credentials_h + +/// Start Default Config Data ////////////////// + +/* + // Defined in + + #define SSID_MAX_LEN 32 + //From v1.0.10, WPA2 passwords can be up to 63 characters long. + #define PASS_MAX_LEN 64 + + typedef struct + { + char wifi_ssid[SSID_MAX_LEN]; + char wifi_pw [PASS_MAX_LEN]; + } WiFi_Credentials; + + #define BLYNK_SERVER_MAX_LEN 32 + #define BLYNK_TOKEN_MAX_LEN 36 + + typedef struct + { + char blynk_server [BLYNK_SERVER_MAX_LEN]; + char wifi_blynk_token [BLYNK_TOKEN_MAX_LEN]; + char gsm_blynk_token [BLYNK_TOKEN_MAX_LEN]; + } Blynk_Credentials; + + #define NUM_WIFI_CREDENTIALS 2 + #define NUM_BLYNK_CREDENTIALS 2 + + // Configurable items besides fixed Header + #define NUM_CONFIGURABLE_ITEMS ( 6 + (2 * NUM_WIFI_CREDENTIALS) + (3 * NUM_BLYNK_CREDENTIALS) ) + #define DEFAULT_GPRS_PIN "1234" + + typedef struct Configuration + { + char header [16]; + WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS]; + Blynk_Credentials Blynk_Creds [NUM_BLYNK_CREDENTIALS]; + int blynk_port; + // YOUR GSM / GPRS RELATED + char apn [32]; + char gprsUser [32]; + char gprsPass [32]; + char gprsPin [12]; // A PIN (Personal Identification Number) is a 4-8 digit passcode + // END OF YOUR GSM / GPRS RELATED + char board_name [24]; + int checkSum; + } Blynk_WF_Configuration; + +*/ + +bool LOAD_DEFAULT_CONFIG_DATA = true; +//bool LOAD_DEFAULT_CONFIG_DATA = false; + +Blynk_WF_Configuration defaultConfig = +{ + //char header[16], dummy, not used + "GSM", + //WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS] + //WiFi_Creds.wifi_ssid and WiFi_Creds.wifi_pw + "SSID1", "password1", + "SSID2", "password2", + // Blynk_Credentials Blynk_Creds [NUM_BLYNK_CREDENTIALS]; + // Blynk_Creds.blynk_server, Blynk_Creds.wifi_blynk_token and Blynk_Creds.gsm_blynk_token + "account.ddns.net", "wifi_token", "gsm_token", + "account.duckdns.org", "wifi_token1", "gsm_token1", + //int blynk_port; + 8080, + // YOUR GSM / GPRS RELATED + //char apn [32]; + "rogers-core-appl1.apn", + //char gprsUser [32]; + "wapuser1", + //char gprsPass [32]; + "wap", + //char gprsPin [12]; // A PIN (Personal Identification Number) is a 4-8 digit passcode + "1245678", + // END OF YOUR GSM / GPRS RELATED + //char board_name [24]; + "8266-GSM-WiFi", + //int checkSum, dummy, not used + 0 +}; + +/////////// End Default Config Data ///////////// + + +#endif //Credentials_h diff --git a/examples/ESP8266_GSM/ESP8266_GSM.ino b/examples/ESP8266_GSM/ESP8266_GSM.ino index b7a678d..b85d669 100644 --- a/examples/ESP8266_GSM/ESP8266_GSM.ino +++ b/examples/ESP8266_GSM/ESP8266_GSM.ino @@ -1,12 +1,12 @@ /**************************************************************************************************************************** - ESP8266_GSM.ino_Config.ino - For ESP32 TTGO-TCALL boards to run GSM/GPRS and WiFi simultaneously, using config portal feature + ESP8266_GSM.ino + For ESP8266 boards to run GSM/GPRS and WiFi simultaneously, using config portal feature Library to enable GSM/GPRS and WiFi running simultaneously , with WiFi config portal. Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager Licensed under MIT license - Version: 1.0.8 + Version: 1.0.9 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -19,173 +19,14 @@ 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM *****************************************************************************************************************************/ -#ifndef ESP8266 -#error This code is intended to run on the ESP8266 platform! Please check your Tools->Board setting. -#endif - -#define BLYNK_PRINT Serial -#define BLYNK_HEARTBEAT 60 - -#define MODEM_RST D0 // Pin D0 mapped to pin GPIO16/USER/WAKE of ESP8266. This pin is also used for Onboard-Blue LED. -#define MODEM_PWKEY D5 // Pin D5 mapped to pin GPIO14/HSCLK of ESP8266 -#define MODEM_POWER_ON D6 // Pin D6 mapped to pin GPIO12/HMISO of ESP8266 - -#define MODEM_TX D8 // Pin D8 mapped to pin GPIO15/TXD2/HCS of ESP8266 -#define MODEM_RX D7 // Pin D7 mapped to pin GPIO13/RXD2/HMOSI of ESP8266 - -#define I2C_SDA D2 // Pin D2 mapped to pin GPIO4/SDA of ESP8266 -#define I2C_SCL D1 // Pin D1 mapped to pin GPIO5/SCL of ESP8266 - -// Select your modem: -#define TINY_GSM_MODEM_SIM800 -//#define TINY_GSM_MODEM_SIM808 -//#define TINY_GSM_MODEM_SIM868 -//#define TINY_GSM_MODEM_SIM900 -//#define TINY_GSM_MODEM_SIM5300 -//#define TINY_GSM_MODEM_SIM5320 -//#define TINY_GSM_MODEM_SIM5360 -//#define TINY_GSM_MODEM_SIM7000 -//#define TINY_GSM_MODEM_SIM7100 -//#define TINY_GSM_MODEM_SIM7500 -//#define TINY_GSM_MODEM_SIM7600 -//#define TINY_GSM_MODEM_SIM7800 -//#define TINY_GSM_MODEM_UBLOX -//#define TINY_GSM_MODEM_SARAR4 -//#define TINY_GSM_MODEM_M95 -//#define TINY_GSM_MODEM_BG96 -//#define TINY_GSM_MODEM_A6 -//#define TINY_GSM_MODEM_A7 -//#define TINY_GSM_MODEM_M590 -//#define TINY_GSM_MODEM_MC60 -//#define TINY_GSM_MODEM_MC60E -//#define TINY_GSM_MODEM_XBEE -//#define TINY_GSM_MODEM_SEQUANS_MONARCH - -// Increase RX buffer if needed -#define TINY_GSM_RX_BUFFER 1024 - -#include - -#define USE_SPIFFS true -//#define USE_SPIFFS false - -#define EEPROM_SIZE 2048 -#define EEPROM_START 512 - -//#define USE_BLYNK_WM false -#define USE_BLYNK_WM true - -#include - -#if USE_BLYNK_WM -#include - -#define USE_DYNAMIC_PARAMETERS true - -/////////////// Start dynamic Credentials /////////////// - -//Defined in -/************************************** - #define MAX_ID_LEN 5 - #define MAX_DISPLAY_NAME_LEN 16 - - typedef struct - { - char id [MAX_ID_LEN + 1]; - char displayName [MAX_DISPLAY_NAME_LEN + 1]; - char *pdata; - uint8_t maxlen; - } MenuItem; -**************************************/ - -#if USE_DYNAMIC_PARAMETERS - -#define MAX_MQTT_SERVER_LEN 34 -char MQTT_Server [MAX_MQTT_SERVER_LEN + 1] = ""; - -#define MAX_MQTT_PORT_LEN 6 -char MQTT_Port [MAX_MQTT_PORT_LEN + 1] = ""; - -#define MAX_MQTT_USERNAME_LEN 34 -char MQTT_UserName [MAX_MQTT_USERNAME_LEN + 1] = ""; +#include "defines.h" +#include "Credentials.h" +#include "dynamicParams.h" -#define MAX_MQTT_PW_LEN 34 -char MQTT_PW [MAX_MQTT_PW_LEN + 1] = ""; - -#define MAX_MQTT_SUBS_TOPIC_LEN 34 -char MQTT_SubsTopic [MAX_MQTT_SUBS_TOPIC_LEN + 1] = ""; - -#define MAX_MQTT_PUB_TOPIC_LEN 34 -char MQTT_PubTopic [MAX_MQTT_PUB_TOPIC_LEN + 1] = ""; - -MenuItem myMenuItems [] = -{ - { "mqtt", "MQTT Server", MQTT_Server, MAX_MQTT_SERVER_LEN }, - { "mqpt", "Port", MQTT_Port, MAX_MQTT_PORT_LEN }, - { "user", "MQTT UserName", MQTT_UserName, MAX_MQTT_USERNAME_LEN }, - { "mqpw", "MQTT PWD", MQTT_PW, MAX_MQTT_PW_LEN }, - { "subs", "Subs Topics", MQTT_SubsTopic, MAX_MQTT_SUBS_TOPIC_LEN }, - { "pubs", "Pubs Topics", MQTT_PubTopic, MAX_MQTT_PUB_TOPIC_LEN }, -}; - -uint16_t NUM_MENU_ITEMS = sizeof(myMenuItems) / sizeof(MenuItem); //MenuItemSize; - -#else - -MenuItem myMenuItems [] = {}; - -uint16_t NUM_MENU_ITEMS = 0; -#endif - - -/////// // End dynamic Credentials /////////// - -#else -#include - -// Your WiFi credentials. -#define ssid "****" -#define pass "****" - -#define USE_LOCAL_SERVER true -//#define USE_LOCAL_SERVER false - -#if USE_LOCAL_SERVER -#define wifi_blynk_tok "****" -#define gsm_blynk_tok "****" -//#define blynk_server "account.duckdns.org" -#define blynk_server "xxx.xxx.xxx.xxx" -#else -#define wifi_blynk_tok "****" -#define gsm_blynk_tok "****" -#define blynk_server "blynk-cloud.com" -#endif - -#define apn "rogers-core-appl1.apn" -#define gprsUser "" //"wapuser1" -#define gprsPass "" //"wap" -#endif - -#define BLYNK_HARDWARE_PORT 8080 - -// Set serial for debug console (to the Serial Monitor, default speed 115200) -#define SerialMon Serial - -#include -SoftwareSerial SerialAT(MODEM_RX, MODEM_TX); // RX, TX - -// Uncomment this if you want to see all AT commands -#define DUMP_AT_COMMANDS false - -#if DUMP_AT_COMMANDS -#include -StreamDebugger debugger(SerialAT, SerialMon); -TinyGsm modem(debugger); -#else -TinyGsm modem(SerialAT); -#endif void heartBeatPrint(void) { @@ -244,9 +85,10 @@ void setup() // Set console baud rate SerialMon.begin(115200); while (!SerialMon); - - SerialMon.println(F("\nStart ESP8266-WIFI-GSM")); + SerialMon.print(F("\nStart ESP8266-WIFI-GSM using ")); + SerialMon.println(CurrentFileFS); + // Set-up modem reset, enable, power pins pinMode(MODEM_PWKEY, OUTPUT); pinMode(MODEM_RST, OUTPUT); @@ -290,7 +132,7 @@ void setup() Serial.print(F("gprs apn = ")); Serial.println(localBlynkGSM_ESP8266_config.apn); - if (String(localBlynkGSM_ESP8266_config.apn) == String("nothing")) + if (String(localBlynkGSM_ESP8266_config.apn) == NO_CONFIG) { Serial.println(F("No valid stored apn. Must run WiFi to Open config portal")); valid_apn = false; @@ -299,12 +141,20 @@ void setup() { valid_apn = true; - Blynk_GSM.config(modem, localBlynkGSM_ESP8266_config.gsm_blynk_tok, localBlynkGSM_ESP8266_config.blynk_server, BLYNK_HARDWARE_PORT); - GSM_CONNECT_OK = Blynk_GSM.connectNetwork(localBlynkGSM_ESP8266_config.apn, localBlynkGSM_ESP8266_config.gprsUser, - localBlynkGSM_ESP8266_config.gprsPass); + for (int index = 0; index < NUM_BLYNK_CREDENTIALS; index++) + { + Blynk_GSM.config(modem, localBlynkGSM_ESP8266_config.Blynk_Creds[index].gsm_blynk_token, + localBlynkGSM_ESP8266_config.Blynk_Creds[index].blynk_server, localBlynkGSM_ESP8266_config.blynk_port); - if (GSM_CONNECT_OK) - Blynk_GSM.connect(); + GSM_CONNECT_OK = Blynk_GSM.connectNetwork(localBlynkGSM_ESP8266_config.apn, localBlynkGSM_ESP8266_config.gprsUser, + localBlynkGSM_ESP8266_config.gprsPass); + + if (GSM_CONNECT_OK) + { + if ( Blynk_GSM.connect() == true ) + break; + } + } } #endif } @@ -312,7 +162,7 @@ void setup() #if (USE_BLYNK_WM && USE_DYNAMIC_PARAMETERS) void displayCredentials(void) { - Serial.println("Your stored Credentials :"); + Serial.println("\nYour stored Credentials :"); for (int i = 0; i < NUM_MENU_ITEMS; i++) { @@ -334,7 +184,7 @@ void loop() } check_status(); - + #if (USE_BLYNK_WM && USE_DYNAMIC_PARAMETERS) static bool displayedCredentials = false; diff --git a/examples/ESP8266_GSM/defines.h b/examples/ESP8266_GSM/defines.h new file mode 100644 index 0000000..fa9fb1d --- /dev/null +++ b/examples/ESP8266_GSM/defines.h @@ -0,0 +1,174 @@ +/**************************************************************************************************************************** + defines.h for ESP8266_GSM.ino + For ESP8266 boards to run GSM/GPRS and WiFi simultaneously, using config portal feature + + Library to enable GSM/GPRS and WiFi running simultaneously , with WiFi config portal. + Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases + Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager + Licensed under MIT license + Version: 1.0.9 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 17/01/2020 Initial coding. Add config portal similar to Blynk_WM library. + 1.0.1 K Hoang 27/01/2020 Change Synch XMLHttpRequest to Async (https://xhr.spec.whatwg.org/). Reduce code size + 1.0.2 K Hoang 08/02/2020 Enable GSM/GPRS and WiFi running simultaneously + 1.0.3 K Hoang 18/02/2020 Add checksum. Add clearConfigData() + 1.0.4 K Hoang 14/03/2020 Enhance Config Portal GUI. Reduce code size. + 1.0.5 K Hoang 20/03/2020 Add more modem supports. See the list in README.md + 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch + 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. + 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM + *****************************************************************************************************************************/ + +#ifndef defines_h +#define defines_h + + +#ifndef ESP8266 + #error This code is intended to run on the ESP8266 platform! Please check your Tools->Board setting. +#endif + +#define BLYNK_PRINT Serial +#define BLYNK_HEARTBEAT 60 + +#define DOUBLERESETDETECTOR_DEBUG true //false +#define BLYNK_WM_DEBUG 1 + +// #define USE_SPIFFS and USE_LITTLEFS false => using EEPROM for configuration data in WiFiManager +// #define USE_LITTLEFS true => using LITTLEFS for configuration data in WiFiManager +// #define USE_LITTLEFS false and USE_SPIFFS true => using SPIFFS for configuration data in WiFiManager +// Be sure to define USE_LITTLEFS and USE_SPIFFS before #include +// From ESP8266 core 2.7.1, SPIFFS will be deprecated and to be replaced by LittleFS +// Select USE_LITTLEFS (higher priority) or USE_SPIFFS + +//#define USE_LITTLEFS true +//#define USE_LITTLEFS false +//#define USE_SPIFFS false +//#define USE_SPIFFS true + +#if USE_LITTLEFS + //LittleFS has higher priority + #define CurrentFileFS F("LittleFS") + #ifdef USE_SPIFFS + #undef USE_SPIFFS + #endif + #define USE_SPIFFS false +#elif USE_SPIFFS + #define CurrentFileFS F("SPIFFS") +#else + #define CurrentFileFS F("EEPROM") + // EEPROM_SIZE must be <= 4096 and >= CONFIG_DATA_SIZE (currently 172 bytes) + #define EEPROM_SIZE (4 * 1024) + // EEPROM_START + CONFIG_DATA_SIZE must be <= EEPROM_SIZE + #define EEPROM_START 0 +#endif + +// Force some params in Blynk, only valid for library version 1.0.1 and later +#define TIMEOUT_RECONNECT_WIFI 10000L +#define RESET_IF_CONFIG_TIMEOUT true +#define CONFIG_TIMEOUT_RETRYTIMES_BEFORE_RESET 5 +// Those above #define's must be placed before #include + + +#define MODEM_RST D0 // Pin D0 mapped to pin GPIO16/USER/WAKE of ESP8266. This pin is also used for Onboard-Blue LED. +#define MODEM_PWKEY D5 // Pin D5 mapped to pin GPIO14/HSCLK of ESP8266 +#define MODEM_POWER_ON D6 // Pin D6 mapped to pin GPIO12/HMISO of ESP8266 + +#define MODEM_TX D8 // Pin D8 mapped to pin GPIO15/TXD2/HCS of ESP8266 +#define MODEM_RX D7 // Pin D7 mapped to pin GPIO13/RXD2/HMOSI of ESP8266 + +#define I2C_SDA D2 // Pin D2 mapped to pin GPIO4/SDA of ESP8266 +#define I2C_SCL D1 // Pin D1 mapped to pin GPIO5/SCL of ESP8266 + +// Select your modem: +#define TINY_GSM_MODEM_SIM800 +//#define TINY_GSM_MODEM_SIM808 +//#define TINY_GSM_MODEM_SIM868 +//#define TINY_GSM_MODEM_SIM900 +//#define TINY_GSM_MODEM_SIM5300 +//#define TINY_GSM_MODEM_SIM5320 +//#define TINY_GSM_MODEM_SIM5360 +//#define TINY_GSM_MODEM_SIM7000 +//#define TINY_GSM_MODEM_SIM7100 +//#define TINY_GSM_MODEM_SIM7500 +//#define TINY_GSM_MODEM_SIM7600 +//#define TINY_GSM_MODEM_SIM7800 +//#define TINY_GSM_MODEM_UBLOX +//#define TINY_GSM_MODEM_SARAR4 +//#define TINY_GSM_MODEM_M95 +//#define TINY_GSM_MODEM_BG96 +//#define TINY_GSM_MODEM_A6 +//#define TINY_GSM_MODEM_A7 +//#define TINY_GSM_MODEM_M590 +//#define TINY_GSM_MODEM_MC60 +//#define TINY_GSM_MODEM_MC60E +//#define TINY_GSM_MODEM_XBEE +//#define TINY_GSM_MODEM_SEQUANS_MONARCH + +// Increase RX buffer if needed +#define TINY_GSM_RX_BUFFER 1024 + +#include + +//#define USE_BLYNK_WM false +#define USE_BLYNK_WM true + +#include + +#if USE_BLYNK_WM + + #include + +#else + #include + + // Your WiFi credentials. + #define ssid "****" + #define pass "****" + + #define USE_LOCAL_SERVER true + //#define USE_LOCAL_SERVER false + + #if USE_LOCAL_SERVER + #define wifi_blynk_tok "****" + #define gsm_blynk_tok "****" + //#define blynk_server "account.duckdns.org" + // Use direct IPAddress in case GPRS can't use DDNS fast enough and can't connect + #define blynk_server "xxx.xxx.xxx.xxx" + #else + #define wifi_blynk_tok "****" + #define gsm_blynk_tok "****" + #define blynk_server "blynk-cloud.com" + #endif + + #define apn "rogers-core-appl1.apn" + #define gprsUser "" //"wapuser1" + #define gprsPass "" //"wap" + +#endif //USE_BLYNK_WM + +#define BLYNK_HARDWARE_PORT 8080 + +// Set serial for debug console (to the Serial Monitor, default speed 115200) +#define SerialMon Serial + +#include +SoftwareSerial SerialAT(MODEM_RX, MODEM_TX); // RX, TX + +// Uncomment this if you want to see all AT commands +#define DUMP_AT_COMMANDS false + +#if DUMP_AT_COMMANDS + #include + StreamDebugger debugger(SerialAT, SerialMon); + TinyGsm modem(debugger); +#else + TinyGsm modem(SerialAT); +#endif + +#define HOST_NAME "8266-GSM-WiFi" + +#endif //defines_h diff --git a/examples/ESP8266_GSM/dynamicParams.h b/examples/ESP8266_GSM/dynamicParams.h new file mode 100644 index 0000000..e5adc9c --- /dev/null +++ b/examples/ESP8266_GSM/dynamicParams.h @@ -0,0 +1,89 @@ +/**************************************************************************************************************************** + dynamicParams.h for ESP8266_GSM.ino + For ESP8266 boards to run GSM/GPRS and WiFi simultaneously, using config portal feature + + Library to enable GSM/GPRS and WiFi running simultaneously , with WiFi config portal. + Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases + Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager + Licensed under MIT license + Version: 1.0.9 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 17/01/2020 Initial coding. Add config portal similar to Blynk_WM library. + 1.0.1 K Hoang 27/01/2020 Change Synch XMLHttpRequest to Async (https://xhr.spec.whatwg.org/). Reduce code size + 1.0.2 K Hoang 08/02/2020 Enable GSM/GPRS and WiFi running simultaneously + 1.0.3 K Hoang 18/02/2020 Add checksum. Add clearConfigData() + 1.0.4 K Hoang 14/03/2020 Enhance Config Portal GUI. Reduce code size. + 1.0.5 K Hoang 20/03/2020 Add more modem supports. See the list in README.md + 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch + 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. + 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM + *****************************************************************************************************************************/ + +#ifndef dynamicParams_h +#define dynamicParams_h + +#define USE_DYNAMIC_PARAMETERS true + +/////////////// Start dynamic Credentials /////////////// + +//Defined in +/************************************** + #define MAX_ID_LEN 5 + #define MAX_DISPLAY_NAME_LEN 16 + + typedef struct + { + char id [MAX_ID_LEN + 1]; + char displayName [MAX_DISPLAY_NAME_LEN + 1]; + char *pdata; + uint8_t maxlen; + } MenuItem; +**************************************/ + +#if USE_DYNAMIC_PARAMETERS + +#define MAX_MQTT_SERVER_LEN 34 +char MQTT_Server [MAX_MQTT_SERVER_LEN + 1] = "mqtt.ddns.net"; + +#define MAX_MQTT_PORT_LEN 6 +char MQTT_Port [MAX_MQTT_PORT_LEN + 1] = "1883"; + +#define MAX_MQTT_USERNAME_LEN 34 +char MQTT_UserName [MAX_MQTT_USERNAME_LEN + 1] = "mqtt-user"; + +#define MAX_MQTT_PW_LEN 34 +char MQTT_PW [MAX_MQTT_PW_LEN + 1] = "mqtt-pass"; + +#define MAX_MQTT_SUBS_TOPIC_LEN 34 +char MQTT_SubsTopic [MAX_MQTT_SUBS_TOPIC_LEN + 1] = "SubTopic_ESP8266_GSM"; + +#define MAX_MQTT_PUB_TOPIC_LEN 34 +char MQTT_PubTopic [MAX_MQTT_PUB_TOPIC_LEN + 1] = "PubTopic_ESP8266_GSM"; + +MenuItem myMenuItems [] = +{ + { "mqtt", "MQTT Server", MQTT_Server, MAX_MQTT_SERVER_LEN }, + { "mqpt", "Port", MQTT_Port, MAX_MQTT_PORT_LEN }, + { "user", "MQTT UserName", MQTT_UserName, MAX_MQTT_USERNAME_LEN }, + { "mqpw", "MQTT PWD", MQTT_PW, MAX_MQTT_PW_LEN }, + { "subs", "Subs Topics", MQTT_SubsTopic, MAX_MQTT_SUBS_TOPIC_LEN }, + { "pubs", "Pubs Topics", MQTT_PubTopic, MAX_MQTT_PUB_TOPIC_LEN }, +}; + +uint16_t NUM_MENU_ITEMS = sizeof(myMenuItems) / sizeof(MenuItem); //MenuItemSize; + +#else + +MenuItem myMenuItems [] = {}; + +uint16_t NUM_MENU_ITEMS = 0; +#endif + + +/////// // End dynamic Credentials /////////// + +#endif //dynamicParams_h diff --git a/examples/TTGO_TCALL_GSM/Credentials.h b/examples/TTGO_TCALL_GSM/Credentials.h new file mode 100644 index 0000000..4ecf34e --- /dev/null +++ b/examples/TTGO_TCALL_GSM/Credentials.h @@ -0,0 +1,116 @@ +/**************************************************************************************************************************** + Credentials.h for TTGO-TCALL.ino + For ESP32 TTGO-TCALL boards to run GSM/GPRS and WiFi simultaneously, using config portal feature + + + Library to enable GSM/GPRS and WiFi running simultaneously , with WiFi config portal. + Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases + Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager + Licensed under MIT license + Version: 1.0.9 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 17/01/2020 Initial coding. Add config portal similar to Blynk_WM library. + 1.0.1 K Hoang 27/01/2020 Change Synch XMLHttpRequest to Async (https://xhr.spec.whatwg.org/). Reduce code size + 1.0.2 K Hoang 08/02/2020 Enable GSM/GPRS and WiFi running simultaneously + 1.0.3 K Hoang 18/02/2020 Add checksum. Add clearConfigData() + 1.0.4 K Hoang 14/03/2020 Enhance Config Portal GUI. Reduce code size. + 1.0.5 K Hoang 20/03/2020 Add more modem supports. See the list in README.md + 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch + 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. + 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM + *****************************************************************************************************************************/ + +#ifndef Credentials_h +#define Credentials_h + +/// Start Default Config Data ////////////////// + +/* + // Defined in + + #define SSID_MAX_LEN 32 + //From v1.0.10, WPA2 passwords can be up to 63 characters long. + #define PASS_MAX_LEN 64 + + typedef struct + { + char wifi_ssid[SSID_MAX_LEN]; + char wifi_pw [PASS_MAX_LEN]; + } WiFi_Credentials; + + #define BLYNK_SERVER_MAX_LEN 32 + #define BLYNK_TOKEN_MAX_LEN 36 + + typedef struct + { + char blynk_server [BLYNK_SERVER_MAX_LEN]; + char wifi_blynk_token [BLYNK_TOKEN_MAX_LEN]; + char gsm_blynk_token [BLYNK_TOKEN_MAX_LEN]; + } Blynk_Credentials; + + #define NUM_WIFI_CREDENTIALS 2 + #define NUM_BLYNK_CREDENTIALS 2 + + // Configurable items besides fixed Header + #define NUM_CONFIGURABLE_ITEMS ( 6 + (2 * NUM_WIFI_CREDENTIALS) + (3 * NUM_BLYNK_CREDENTIALS) ) + #define DEFAULT_GPRS_PIN "1234" + + typedef struct Configuration + { + char header [16]; + WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS]; + Blynk_Credentials Blynk_Creds [NUM_BLYNK_CREDENTIALS]; + int blynk_port; + // YOUR GSM / GPRS RELATED + char apn [32]; + char gprsUser [32]; + char gprsPass [32]; + char gprsPin [12]; // A PIN (Personal Identification Number) is a 4-8 digit passcode + // END OF YOUR GSM / GPRS RELATED + char board_name [24]; + int checkSum; + } Blynk_WF_Configuration; + +*/ + +bool LOAD_DEFAULT_CONFIG_DATA = true; +//bool LOAD_DEFAULT_CONFIG_DATA = false; + +Blynk_WF_Configuration defaultConfig = +{ + //char header[16], dummy, not used + "GSM", + //WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS] + //WiFi_Creds.wifi_ssid and WiFi_Creds.wifi_pw + "SSID1", "password1", + "SSID2", "password2", + // Blynk_Credentials Blynk_Creds [NUM_BLYNK_CREDENTIALS]; + // Blynk_Creds.blynk_server, Blynk_Creds.wifi_blynk_token and Blynk_Creds.gsm_blynk_token + "account.ddns.net", "wifi_token", "gsm_token", + "account.duckdns.org", "wifi_token1", "gsm_token1", + //int blynk_port; + 8080, + // YOUR GSM / GPRS RELATED + //char apn [32]; + "rogers-core-appl1.apn", + //char gprsUser [32]; + "wapuser1", + //char gprsPass [32]; + "wap", + //char gprsPin [12]; // A PIN (Personal Identification Number) is a 4-8 digit passcode + "1245678", + // END OF YOUR GSM / GPRS RELATED + //char board_name [24]; + "ESP32-GSM-WiFi", + //int checkSum, dummy, not used + 0 +}; + +/////////// End Default Config Data ///////////// + + +#endif //Credentials_h diff --git a/examples/TTGO_TCALL_GSM/TTGO_TCALL_GSM.ino b/examples/TTGO_TCALL_GSM/TTGO_TCALL_GSM.ino index 5c58190..16d0062 100644 --- a/examples/TTGO_TCALL_GSM/TTGO_TCALL_GSM.ino +++ b/examples/TTGO_TCALL_GSM/TTGO_TCALL_GSM.ino @@ -6,7 +6,7 @@ Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager Licensed under MIT license - Version: 1.0.8 + Version: 1.0.9 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -19,173 +19,13 @@ 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM *****************************************************************************************************************************/ -#ifndef ESP32 -#error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. -#endif - -#define BLYNK_PRINT Serial -#define BLYNK_HEARTBEAT 60 - -// TTGO T-Call pin definitions -#define MODEM_RST 5 // Pin D5 mapped to pin GPIO5/SPISS/VSPI_SS of ESP32 -#define MODEM_PWKEY 4 // Pin D4 mapped to pin GPIO4/ADC10/TOUCH0 of ESP32 -#define MODEM_POWER_ON 23 // Pin D23 mapped to pin GPIO23/VSPI_MOSI of ESP32 -#define MODEM_TX 27 // Pin D27 mapped to pin GPIO27/ADC17/TOUCH7 of ESP32 -#define MODEM_RX 26 // Pin D26 mapped to pin GPIO26/ADC19/DAC2 of ESP32 -#define I2C_SDA 21 // Pin D21 mapped to pin GPIO21/SDA of ESP32 -#define I2C_SCL 22 // Pin D22 mapped to pin GPIO22/SCL of ESP32 - -// Select your modem: -#define TINY_GSM_MODEM_SIM800 -//#define TINY_GSM_MODEM_SIM808 -//#define TINY_GSM_MODEM_SIM868 -//#define TINY_GSM_MODEM_SIM900 -//#define TINY_GSM_MODEM_SIM5300 -//#define TINY_GSM_MODEM_SIM5320 -//#define TINY_GSM_MODEM_SIM5360 -//#define TINY_GSM_MODEM_SIM7000 -//#define TINY_GSM_MODEM_SIM7100 -//#define TINY_GSM_MODEM_SIM7500 -//#define TINY_GSM_MODEM_SIM7600 -//#define TINY_GSM_MODEM_SIM7800 -//#define TINY_GSM_MODEM_UBLOX -//#define TINY_GSM_MODEM_SARAR4 -//#define TINY_GSM_MODEM_M95 -//#define TINY_GSM_MODEM_BG96 -//#define TINY_GSM_MODEM_A6 -//#define TINY_GSM_MODEM_A7 -//#define TINY_GSM_MODEM_M590 -//#define TINY_GSM_MODEM_MC60 -//#define TINY_GSM_MODEM_MC60E -//#define TINY_GSM_MODEM_XBEE -//#define TINY_GSM_MODEM_SEQUANS_MONARCH - -// Increase RX buffer if needed -#define TINY_GSM_RX_BUFFER 1024 - -//#define USE_BLYNK_WM false -#define USE_BLYNK_WM true - -#define USE_SPIFFS false -//#define USE_SPIFFS true - -#define EEPROM_SIZE 2048 -#define EEPROM_START 256 - -#include - -#if USE_BLYNK_WM -#include - -#define USE_DYNAMIC_PARAMETERS true - -/////////////// Start dynamic Credentials /////////////// - -//Defined in -/************************************** - #define MAX_ID_LEN 5 - #define MAX_DISPLAY_NAME_LEN 16 - - typedef struct - { - char id [MAX_ID_LEN + 1]; - char displayName [MAX_DISPLAY_NAME_LEN + 1]; - char *pdata; - uint8_t maxlen; - } MenuItem; -**************************************/ - -#if USE_DYNAMIC_PARAMETERS - -#define MAX_MQTT_SERVER_LEN 34 -char MQTT_Server [MAX_MQTT_SERVER_LEN + 1] = ""; - -#define MAX_MQTT_PORT_LEN 6 -char MQTT_Port [MAX_MQTT_PORT_LEN + 1] = ""; - -#define MAX_MQTT_USERNAME_LEN 34 -char MQTT_UserName [MAX_MQTT_USERNAME_LEN + 1] = ""; - -#define MAX_MQTT_PW_LEN 34 -char MQTT_PW [MAX_MQTT_PW_LEN + 1] = ""; - -#define MAX_MQTT_SUBS_TOPIC_LEN 34 -char MQTT_SubsTopic [MAX_MQTT_SUBS_TOPIC_LEN + 1] = ""; - -#define MAX_MQTT_PUB_TOPIC_LEN 34 -char MQTT_PubTopic [MAX_MQTT_PUB_TOPIC_LEN + 1] = ""; - -MenuItem myMenuItems [] = -{ - { "mqtt", "MQTT Server", MQTT_Server, MAX_MQTT_SERVER_LEN }, - { "mqpt", "Port", MQTT_Port, MAX_MQTT_PORT_LEN }, - { "user", "MQTT UserName", MQTT_UserName, MAX_MQTT_USERNAME_LEN }, - { "mqpw", "MQTT PWD", MQTT_PW, MAX_MQTT_PW_LEN }, - { "subs", "Subs Topics", MQTT_SubsTopic, MAX_MQTT_SUBS_TOPIC_LEN }, - { "pubs", "Pubs Topics", MQTT_PubTopic, MAX_MQTT_PUB_TOPIC_LEN }, -}; - -uint16_t NUM_MENU_ITEMS = sizeof(myMenuItems) / sizeof(MenuItem); //MenuItemSize; - -#else - -MenuItem myMenuItems [] = {}; - -uint16_t NUM_MENU_ITEMS = 0; -#endif - - -/////// // End dynamic Credentials /////////// - -#else -#include - -// Your WiFi credentials. -#define ssid "****" -#define pass "****" - -#define USE_LOCAL_SERVER true -//#define USE_LOCAL_SERVER false - -#if USE_LOCAL_SERVER -#define wifi_blynk_tok "****" -#define gsm_blynk_tok "****" -//#define blynk_server "account.duckdns.org" -// Usedirect IPAddress in case GPRS can't use DDNS fast enough and can't connect -#define blynk_server "xxx.xxx.xxx.xxx" -#else -#define wifi_blynk_tok "****" -#define gsm_blynk_tok "****" -#define blynk_server "blynk-cloud.com" -#endif - -#define apn "rogers-core-appl1.apn" -#define gprsUser "" //"wapuser1" -#define gprsPass "" //"wap" -#endif - -#define BLYNK_HARDWARE_PORT 8080 - -#include - -// Set serial for debug console (to the Serial Monitor, default speed 115200) -#define SerialMon Serial - -// Use ESP32 Serial2 for GSM, Serial1 for TTGO T-Call -#define SerialAT Serial1 - -// Uncomment this if you want to see all AT commands -#define DUMP_AT_COMMANDS false - -#if DUMP_AT_COMMANDS -#include -StreamDebugger debugger(SerialAT, SerialMon); -TinyGsm modem(debugger); -#else -TinyGsm modem(SerialAT); -#endif +#include "defines.h" +#include "Credentials.h" +#include "dynamicParams.h" void heartBeatPrint(void) { @@ -245,7 +85,8 @@ void setup() SerialMon.begin(115200); while (!SerialMon); - SerialMon.println(F("\nStart TTGO-TCALL-GSM")); + SerialMon.print(F("\nStart TTGO-TCALL-GSM using ")); + SerialMon.println(CurrentFileFS); // Set-up modem reset, enable, power pins pinMode(MODEM_PWKEY, OUTPUT); @@ -289,7 +130,7 @@ void setup() Serial.print(F("gprs apn = ")); Serial.println(localBlynkGSM_ESP32_config.apn); - if (String(localBlynkGSM_ESP32_config.apn) == String("nothing")) + if (String(localBlynkGSM_ESP32_config.apn) == NO_CONFIG) { Serial.println(F("No valid stored apn. Must run WiFi to Open config portal")); valid_apn = false; @@ -298,12 +139,20 @@ void setup() { valid_apn = true; - Blynk_GSM.config(modem, localBlynkGSM_ESP32_config.gsm_blynk_tok, localBlynkGSM_ESP32_config.blynk_server, BLYNK_HARDWARE_PORT); - GSM_CONNECT_OK = Blynk_GSM.connectNetwork(localBlynkGSM_ESP32_config.apn, localBlynkGSM_ESP32_config.gprsUser, - localBlynkGSM_ESP32_config.gprsPass); + for (int index = 0; index < NUM_BLYNK_CREDENTIALS; index++) + { + Blynk_GSM.config(modem, localBlynkGSM_ESP32_config.Blynk_Creds[index].gsm_blynk_token, + localBlynkGSM_ESP32_config.Blynk_Creds[index].blynk_server, localBlynkGSM_ESP32_config.blynk_port); + + GSM_CONNECT_OK = Blynk_GSM.connectNetwork(localBlynkGSM_ESP32_config.apn, localBlynkGSM_ESP32_config.gprsUser, + localBlynkGSM_ESP32_config.gprsPass); - if (GSM_CONNECT_OK) - Blynk_GSM.connect(); + if (GSM_CONNECT_OK) + { + if ( Blynk_GSM.connect() == true ) + break; + } + } } #endif } @@ -311,7 +160,7 @@ void setup() #if (USE_BLYNK_WM && USE_DYNAMIC_PARAMETERS) void displayCredentials(void) { - Serial.println("Your stored Credentials :"); + Serial.println("\nYour stored Credentials :"); for (int i = 0; i < NUM_MENU_ITEMS; i++) { diff --git a/examples/TTGO_TCALL_GSM/defines.h b/examples/TTGO_TCALL_GSM/defines.h new file mode 100644 index 0000000..9b885d3 --- /dev/null +++ b/examples/TTGO_TCALL_GSM/defines.h @@ -0,0 +1,161 @@ +/**************************************************************************************************************************** + defines.h for TTGO-TCALL.ino + For ESP32 TTGO-TCALL boards to run GSM/GPRS and WiFi simultaneously, using config portal feature + + + Library to enable GSM/GPRS and WiFi running simultaneously , with WiFi config portal. + Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases + Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager + Licensed under MIT license + Version: 1.0.9 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 17/01/2020 Initial coding. Add config portal similar to Blynk_WM library. + 1.0.1 K Hoang 27/01/2020 Change Synch XMLHttpRequest to Async (https://xhr.spec.whatwg.org/). Reduce code size + 1.0.2 K Hoang 08/02/2020 Enable GSM/GPRS and WiFi running simultaneously + 1.0.3 K Hoang 18/02/2020 Add checksum. Add clearConfigData() + 1.0.4 K Hoang 14/03/2020 Enhance Config Portal GUI. Reduce code size. + 1.0.5 K Hoang 20/03/2020 Add more modem supports. See the list in README.md + 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch + 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. + 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM + *****************************************************************************************************************************/ + +#ifndef defines_h +#define defines_h + +#ifndef ESP32 + #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. +#endif + +#define BLYNK_PRINT Serial +#define BLYNK_HEARTBEAT 60 + +#define DOUBLERESETDETECTOR_DEBUG true //false +#define BLYNK_WM_DEBUG 1 + +//#define USE_SPIFFS false +#define USE_SPIFFS true + +#if USE_SPIFFS + #define CurrentFileFS F("SPIFFS") +#else + #define CurrentFileFS F("EEPROM") +// EEPROM_SIZE must be <= 2048 and >= CONFIG_DATA_SIZE (currently 172 bytes) + #define EEPROM_SIZE (2 * 1024) + // EEPROM_START + CONFIG_DATA_SIZE must be <= EEPROM_SIZE + #define EEPROM_START 0 +#endif + +// Force some params in Blynk, only valid for library version 1.0.1 and later +#define TIMEOUT_RECONNECT_WIFI 10000L +#define RESET_IF_CONFIG_TIMEOUT true +#define CONFIG_TIMEOUT_RETRYTIMES_BEFORE_RESET 5 +// Those above #define's must be placed before #include + +// TTGO T-Call pin definitions +#define MODEM_RST 5 +#define MODEM_PWKEY 4 +#define MODEM_POWER_ON 23 + +#define MODEM_TX 27 +#define MODEM_RX 26 + +#define I2C_SDA 21 +#define I2C_SCL 22 + +// Select your modem: +#define TINY_GSM_MODEM_SIM800 +//#define TINY_GSM_MODEM_SIM808 +//#define TINY_GSM_MODEM_SIM868 +//#define TINY_GSM_MODEM_SIM900 +//#define TINY_GSM_MODEM_SIM5300 +//#define TINY_GSM_MODEM_SIM5320 +//#define TINY_GSM_MODEM_SIM5360 +//#define TINY_GSM_MODEM_SIM7000 +//#define TINY_GSM_MODEM_SIM7100 +//#define TINY_GSM_MODEM_SIM7500 +//#define TINY_GSM_MODEM_SIM7600 +//#define TINY_GSM_MODEM_SIM7800 +//#define TINY_GSM_MODEM_UBLOX +//#define TINY_GSM_MODEM_SARAR4 +//#define TINY_GSM_MODEM_M95 +//#define TINY_GSM_MODEM_BG96 +//#define TINY_GSM_MODEM_A6 +//#define TINY_GSM_MODEM_A7 +//#define TINY_GSM_MODEM_M590 +//#define TINY_GSM_MODEM_MC60 +//#define TINY_GSM_MODEM_MC60E +//#define TINY_GSM_MODEM_XBEE +//#define TINY_GSM_MODEM_SEQUANS_MONARCH + +// Increase RX buffer if needed +#define TINY_GSM_RX_BUFFER 1024 + +#include + +//#define USE_BLYNK_WM false +#define USE_BLYNK_WM true + +#include + +#if USE_BLYNK_WM + + #include + +#else + #include + + // Your WiFi credentials. + #define ssid "****" + #define pass "****" + + #define USE_LOCAL_SERVER true + //#define USE_LOCAL_SERVER false + + #if USE_LOCAL_SERVER + #define wifi_blynk_tok "****" + #define gsm_blynk_tok "****" + //#define blynk_server "account.duckdns.org" + // Use direct IPAddress in case GPRS can't use DDNS fast enough and can't connect + #define blynk_server "xxx.xxx.xxx.xxx" + #else + #define wifi_blynk_tok "****" + #define gsm_blynk_tok "****" + #define blynk_server "blynk-cloud.com" + #endif + + #define apn "rogers-core-appl1.apn" + #define gprsUser "" //"wapuser1" + #define gprsPass "" //"wap" + +#endif //USE_BLYNK_WM + +#define BLYNK_HARDWARE_PORT 8080 + +// Set serial for debug console (to the Serial Monitor, default speed 115200) +#define SerialMon Serial + +#define RXD2 16 +#define TXD2 17 + +// Use ESP32 Serial2 for GSM, Serial1 for TTGO T-Call +#define SerialAT Serial1 + +// Uncomment this if you want to see all AT commands +#define DUMP_AT_COMMANDS false + +#if DUMP_AT_COMMANDS + #include + StreamDebugger debugger(SerialAT, SerialMon); + TinyGsm modem(debugger); +#else + TinyGsm modem(SerialAT); +#endif + +#define HOST_NAME "ESP32-GSM-WiFi" + +#endif //defines_h diff --git a/examples/TTGO_TCALL_GSM/dynamicParams.h b/examples/TTGO_TCALL_GSM/dynamicParams.h new file mode 100644 index 0000000..4d911e1 --- /dev/null +++ b/examples/TTGO_TCALL_GSM/dynamicParams.h @@ -0,0 +1,90 @@ +/**************************************************************************************************************************** + dynamicParams.h for TTGO-TCALL.ino + For ESP32 TTGO-TCALL boards to run GSM/GPRS and WiFi simultaneously, using config portal feature + + + Library to enable GSM/GPRS and WiFi running simultaneously , with WiFi config portal. + Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases + Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager + Licensed under MIT license + Version: 1.0.9 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 17/01/2020 Initial coding. Add config portal similar to Blynk_WM library. + 1.0.1 K Hoang 27/01/2020 Change Synch XMLHttpRequest to Async (https://xhr.spec.whatwg.org/). Reduce code size + 1.0.2 K Hoang 08/02/2020 Enable GSM/GPRS and WiFi running simultaneously + 1.0.3 K Hoang 18/02/2020 Add checksum. Add clearConfigData() + 1.0.4 K Hoang 14/03/2020 Enhance Config Portal GUI. Reduce code size. + 1.0.5 K Hoang 20/03/2020 Add more modem supports. See the list in README.md + 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch + 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. + 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM + *****************************************************************************************************************************/ + +#ifndef dynamicParams_h +#define dynamicParams_h + +#define USE_DYNAMIC_PARAMETERS true + +/////////////// Start dynamic Credentials /////////////// + +//Defined in +/************************************** + #define MAX_ID_LEN 5 + #define MAX_DISPLAY_NAME_LEN 16 + + typedef struct + { + char id [MAX_ID_LEN + 1]; + char displayName [MAX_DISPLAY_NAME_LEN + 1]; + char *pdata; + uint8_t maxlen; + } MenuItem; +**************************************/ + +#if USE_DYNAMIC_PARAMETERS + +#define MAX_MQTT_SERVER_LEN 34 +char MQTT_Server [MAX_MQTT_SERVER_LEN + 1] = "mqtt.ddns.net"; + +#define MAX_MQTT_PORT_LEN 6 +char MQTT_Port [MAX_MQTT_PORT_LEN + 1] = "1883"; + +#define MAX_MQTT_USERNAME_LEN 34 +char MQTT_UserName [MAX_MQTT_USERNAME_LEN + 1] = "mqtt-user"; + +#define MAX_MQTT_PW_LEN 34 +char MQTT_PW [MAX_MQTT_PW_LEN + 1] = "mqtt-pass"; + +#define MAX_MQTT_SUBS_TOPIC_LEN 34 +char MQTT_SubsTopic [MAX_MQTT_SUBS_TOPIC_LEN + 1] = "SubTopic_ESP32_GSM"; + +#define MAX_MQTT_PUB_TOPIC_LEN 34 +char MQTT_PubTopic [MAX_MQTT_PUB_TOPIC_LEN + 1] = "PubTopic_ESP32_GSM"; + +MenuItem myMenuItems [] = +{ + { "mqtt", "MQTT Server", MQTT_Server, MAX_MQTT_SERVER_LEN }, + { "mqpt", "Port", MQTT_Port, MAX_MQTT_PORT_LEN }, + { "user", "MQTT UserName", MQTT_UserName, MAX_MQTT_USERNAME_LEN }, + { "mqpw", "MQTT PWD", MQTT_PW, MAX_MQTT_PW_LEN }, + { "subs", "Subs Topics", MQTT_SubsTopic, MAX_MQTT_SUBS_TOPIC_LEN }, + { "pubs", "Pubs Topics", MQTT_PubTopic, MAX_MQTT_PUB_TOPIC_LEN }, +}; + +uint16_t NUM_MENU_ITEMS = sizeof(myMenuItems) / sizeof(MenuItem); //MenuItemSize; + +#else + +MenuItem myMenuItems [] = {}; + +uint16_t NUM_MENU_ITEMS = 0; +#endif + + +/////// // End dynamic Credentials /////////// + +#endif //dynamicParams_h diff --git a/keywords.txt b/keywords.txt index c185f76..421eee6 100644 --- a/keywords.txt +++ b/keywords.txt @@ -44,13 +44,13 @@ setSTAStaticIPConfig KEYWORD2 getBoardName KEYWORD2 getWiFiSSID KEYWORD2 getWiFiPW KEYWORD2 +getServerName KEYWORD2 getWiFiToken KEYWORD2 +getGSMToken KEYWORD2 getAPN KEYWORD2 getGPRSUser KEYWORD2 getGPRSPass KEYWORD2 getGPRSPIN KEYWORD2 -getServerName KEYWORD2 -getGSMToken KEYWORD2 getHWPort KEYWORD2 setHostname KEYWORD2 getFullConfigData KEYWORD2 diff --git a/library.json b/library.json index 6c0f083..01caff4 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "BlynkGSM_Manager", - "version": "1.0.8", + "version": "1.0.9", "description": "Build a smartphone app for your project in minutes. Blynk allows creating IoT solutions easily. It supports WiFi, BLE, Bluetooth, Ethernet, GSM, USB, Serial. Works with many boards like ESP8266, ESP32, Arduino UNO, Nano, Due, Mega, Zero, MKR100, Yun, Raspberry Pi, Particle, Energia, ARM mbed, Intel Edison/Galileo/Joule, BBC micro:bit, DFRobot, RedBearLab, Microduino, LinkIt ONE ...", "keywords": "sensors, control, device, smartphone, mobile, app, web, cloud, communication, protocol, iot, m2m, wifi, ble, bluetooth, ethernet, usb, serial, gsm, gprs, 3g, data, esp8266, http", "authors": diff --git a/library.properties b/library.properties index 02d9d3b..35d7a50 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=BlynkGSM_Manager -version=1.0.8 +version=1.0.9 author=Khoi Hoang license=MIT maintainer=Khoi Hoang -sentence=Simple GSM shield Credentials Manager for Blynk and ESP32 / ESP8266 boards, with or without SSL, configuration data saved in SPIFFS / EEPROM +sentence=Simple GSM shield Credentials Manager for Blynk and ESP32 / ESP8266 boards, with or without SSL, configuration data saved in LittleFS / SPIFFS / EEPROM paragraph=Library for configuring/auto(re)connecting GSM shields to Internet and Blynk at runtime and enable GSM/GPRS and WiFi running simultaneously. category=Communication url=https://github.com/khoih-prog/BlynkGSM_Manager diff --git a/pics/Selection_2.png b/pics/Selection_2.png index 504104c98fbc7344e5145b440ee2e50c143d32d5..5d1cca86770152602a28b2db732ea8c49ada6ee5 100644 GIT binary patch literal 25917 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 bs_Y4kTepjiqdM9M1pG*ey%Q}JHt_!snjInP diff --git a/pics/Selection_3.png b/pics/Selection_3.png index 323e8acccb1e6b629a20d05ce644c2fc213e6241..61534704746292f2638afd3441c8f282991f6eb9 100644 GIT binary patch literal 28908 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 literal 40675 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 setting. diff --git a/src/Adapters/BlynkGsm_ESP8266M.h b/src/Adapters/BlynkGsm_ESP8266M.h index 420f709..8889ad0 100644 --- a/src/Adapters/BlynkGsm_ESP8266M.h +++ b/src/Adapters/BlynkGsm_ESP8266M.h @@ -6,7 +6,7 @@ Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager Licensed under MIT license - Version: 1.0.8 + Version: 1.0.9 Original Blynk Library author: @file BlynkSimpleESP8266.h @@ -27,6 +27,8 @@ 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM *****************************************************************************************************************************/ #ifndef ESP8266 #error This code is designed to run on ESP8266, not ESP32 nor Arduino AVR platform! Please check your Tools->Board setting. diff --git a/src/BlynkSimpleEsp32_GSM_WF.h b/src/BlynkSimpleEsp32_GSM_WF.h index 43eb9ee..8c4e733 100644 --- a/src/BlynkSimpleEsp32_GSM_WF.h +++ b/src/BlynkSimpleEsp32_GSM_WF.h @@ -6,7 +6,7 @@ Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager Licensed under MIT license - Version: 1.0.8 + Version: 1.0.9 Original Blynk Library author: @file BlynkSimpleESP8266.h @@ -27,6 +27,8 @@ 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM *****************************************************************************************************************************/ #ifndef BlynkSimpleEsp32_GSM_WF_h diff --git a/src/BlynkSimpleEsp32_GSM_WFM.h b/src/BlynkSimpleEsp32_GSM_WFM.h index 5c3500f..27d7d5d 100644 --- a/src/BlynkSimpleEsp32_GSM_WFM.h +++ b/src/BlynkSimpleEsp32_GSM_WFM.h @@ -6,7 +6,7 @@ Forked from Blynk library v0.6.1 https://github.com/blynkkk/blynk-library/releases Built by Khoi Hoang https://github.com/khoih-prog/BlynkGSM_ESPManager Licensed under MIT license - Version: 1.0.8 + Version: 1.0.9 Original Blynk Library author: @file BlynkSimpleESP8266.h @@ -27,6 +27,8 @@ 1.0.6 K Hoang 07/04/2020 Enable adding dynamic custom parameters from sketch 1.0.7 K Hoang 09/04/2020 SSID password maxlen is 63 now. Permit special chars # and % in input data. 1.0.8 K Hoang 14/04/2020 Fix bug. + 1.0.9 K Hoang 31/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+. Add Configurable Config Portal Title, + Default Config Data and DRD. Add MultiWiFi/Blynk features for WiFi and GPRS/GSM *****************************************************************************************************************************/ #ifndef BlynkSimpleEsp32_GSM_WFM_h @@ -51,12 +53,47 @@ //default to use EEPROM, otherwise, use SPIFFS #if USE_SPIFFS -#include -#include "SPIFFS.h" + #include + #include "SPIFFS.h" + #define FileFS SPIFFS #else -#include + #include #endif +///////// NEW for DRD ///////////// +// These defines must be put before #include +// to select where to store DoubleResetDetector's variable. +// For ESP32, You must select one to be true (EEPROM or SPIFFS) +// For ESP8266, You must select one to be true (RTC, EEPROM or SPIFFS) +// Otherwise, library will use default EEPROM storage +#define ESP8266_DRD_USE_RTC false //true + +#if USE_SPIFFS +#define ESP_DRD_USE_EEPROM false +#define ESP_DRD_USE_SPIFFS true +#else +#define ESP_DRD_USE_EEPROM true +#define ESP_DRD_USE_SPIFFS false +#endif + +#ifndef DOUBLERESETDETECTOR_DEBUG +#define DOUBLERESETDETECTOR_DEBUG false +#endif + +#include //https://github.com/khoih-prog/ESP_DoubleResetDetector + +// Number of seconds after reset during which a +// subseqent reset will be considered a double reset. +#define DRD_TIMEOUT 10 + +// RTC Memory Address for the DoubleResetDetector to use +#define DRD_ADDRESS 0 + +//DoubleResetDetector drd(DRD_TIMEOUT, DRD_ADDRESS); +DoubleResetDetector* drd; + +///////// NEW for DRD ///////////// + #include #define ESP_getChipId() ((uint32_t)ESP.getEfuseMac()) @@ -80,32 +117,55 @@ typedef struct extern uint16_t NUM_MENU_ITEMS; extern MenuItem myMenuItems []; +#define SSID_MAX_LEN 32 +//From v1.0.10, WPA2 passwords can be up to 63 characters long. +#define PASS_MAX_LEN 64 + +typedef struct +{ + char wifi_ssid[SSID_MAX_LEN]; + char wifi_pw [PASS_MAX_LEN]; +} WiFi_Credentials; + +#define BLYNK_SERVER_MAX_LEN 32 +#define BLYNK_TOKEN_MAX_LEN 36 + +typedef struct +{ + char blynk_server [BLYNK_SERVER_MAX_LEN]; + char wifi_blynk_token [BLYNK_TOKEN_MAX_LEN]; + char gsm_blynk_token [BLYNK_TOKEN_MAX_LEN]; +} Blynk_Credentials; + +#define NUM_WIFI_CREDENTIALS 2 +#define NUM_BLYNK_CREDENTIALS 2 + // Configurable items besides fixed Header -#define NUM_CONFIGURABLE_ITEMS 11 +#define NUM_CONFIGURABLE_ITEMS ( 6 + (2 * NUM_WIFI_CREDENTIALS) + (3 * NUM_BLYNK_CREDENTIALS) ) #define DEFAULT_GPRS_PIN "1234" + typedef struct Configuration { char header [16]; - // WiFi related - char wifi_ssid [32]; - char wifi_pw [64]; //From v1.0.7, WPA2 passwords can be up to 63 characters long. - char wifi_blynk_tok [36]; + WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS]; + Blynk_Credentials Blynk_Creds [NUM_BLYNK_CREDENTIALS]; + int blynk_port; // YOUR GSM / GPRS RELATED char apn [32]; char gprsUser [32]; char gprsPass [32]; - char gprsPin [12]; // A PIN (Personal Identification Number) is a 4-8 digit passcode - // YOUR GSM / GPRS RELATED - char blynk_server [32]; - int blynk_port; - char gsm_blynk_tok [36]; + char gprsPin [12]; // A PIN (Personal Identification Number) is a 4-8 digit passcode + // END OF YOUR GSM / GPRS RELATED char board_name [24]; int checkSum; } Blynk_WF_Configuration; +// Currently CONFIG_DATA_SIZE = ( 156 + (96 * NUM_WIFI_CREDENTIALS) + (104 * NUM_BLYNK_CREDENTIALS) ) = 556 +uint16_t CONFIG_DATA_SIZE = sizeof(Blynk_WF_Configuration); -// Currently CONFIG_DATA_SIZE = 356 -uint16_t CONFIG_DATA_SIZE = sizeof(struct Configuration); +///New from v1.0.13 +extern bool LOAD_DEFAULT_CONFIG_DATA; +extern Blynk_WF_Configuration defaultConfig; // -- HTML page fragments const char BLYNK_GSM_HTML_HEAD[] /*PROGMEM*/ = "BlynkGSM_ESP32
\
\
\ -
\ +
\ +
\
\
\
\
\
\ -
\ -
\ +
\ +
\ +
\ +
\ +
\ +
\
"; const char BLYNK_GSM_FLDSET_START[] /*PROGMEM*/ = "
"; const char BLYNK_GSM_FLDSET_END[] /*PROGMEM*/ = "
"; @@ -129,11 +194,14 @@ const char BLYNK_GSM_HTML_BUTTON[] /*PROGMEM*/ = "