├── BVB_WebConfig_OTA_V7 ├── custom.h ├── Page_Root.h ├── Page_Admin.h ├── Page_Script.js.h ├── example.h ├── Page_Style.css.h ├── Page_General.h ├── Page_Information.h ├── Page_applSettings.h ├── helpers.h ├── Page_NTPSettings.h ├── NTP.h ├── global.h ├── Page_NetworkConfiguration.h └── BVB_WebConfig_OTA_V7.ino ├── BVB.pdf ├── .gitattributes ├── README.md └── .gitignore /BVB_WebConfig_OTA_V7/custom.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /BVB.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SensorsIot/Internet-of-Things-with-ESP8266/master/BVB.pdf -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Internet-of-Things-with-ESP8266 2 | 3 | This project develops a framework for IoT devices including: 4 | 5 | Simple connections to Web services 6 | 7 | Program an ESP8266 module in a “Thing” 8 | 9 | Simple interface to smartphone 10 | 11 | Programming stable and reactive “Things” 12 | 13 | Automatically recover from a crash 14 | 15 | A sleep mode to extend battery life 16 | 17 | A new software has to be loaded over the air 18 | 19 | A tutorial is on youtube: https://www.youtube.com/playlist?list=PL3XBzmAj53Rl2vNyL9ucv87xnbUHzpSPw 20 | 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /BVB_WebConfig_OTA_V7/Page_Root.h: -------------------------------------------------------------------------------- 1 | 2 | const char PAGE_Root[] PROGMEM = R"=====( 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | It work's! 14 | 15 | 16 | 17 | )====="; 18 | 19 | void sendRootPage() 20 | { 21 | if (server.args() > 0 ) // Are there any POST/GET Fields ? 22 | { 23 | for ( uint8_t i = 0; i < server.args(); i++ ) { // Iterate through the fields 24 | 25 | } 26 | } 27 | server.send ( 200, "text/html", PAGE_Root ); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /BVB_WebConfig_OTA_V7/Page_Admin.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 4 | // HTML PAGE 5 | // 6 | 7 | const char PAGE_AdminMainPage[] PROGMEM = R"=====( 8 | 9 | Administration 10 |
11 | General Configuration
12 | Network Configuration
13 | Network Information
14 | NTP Settings
15 | Application Settings
16 | 17 | 18 | 32 | 33 | )====="; 34 | 35 | 36 | -------------------------------------------------------------------------------- /BVB_WebConfig_OTA_V7/Page_Script.js.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | const char PAGE_microajax_js[] PROGMEM = R"=====( 7 | function microAjax(B,A){this.bindFunction=function(E,D){return function(){return E.apply(D,[D])}};this.stateChange=function(D){if(this.request.readyState==4){this.callbackFunction(this.request.responseText)}};this.getRequest=function(){if(window.ActiveXObject){return new ActiveXObject("Microsoft.XMLHTTP")}else{if(window.XMLHttpRequest){return new XMLHttpRequest()}}return false};this.postBody=(arguments[2]||"");this.callbackFunction=A;this.url=B;this.request=this.getRequest();if(this.request){var C=this.request;C.onreadystatechange=this.bindFunction(this.stateChange,this);if(this.postBody!==""){C.open("POST",B,true);C.setRequestHeader("X-Requested-With","XMLHttpRequest");C.setRequestHeader("Content-type","application/x-www-form-urlencoded");C.setRequestHeader("Connection","close")}else{C.open("GET",B,true)}C.send(this.postBody)}}; 8 | 9 | function setValues(url) 10 | { 11 | microAjax(url, function (res) 12 | { 13 | res.split(String.fromCharCode(10)).forEach(function(entry) { 14 | fields = entry.split("|"); 15 | if(fields[2] == "input") 16 | { 17 | document.getElementById(fields[0]).value = fields[1]; 18 | } 19 | else if(fields[2] == "div") 20 | { 21 | document.getElementById(fields[0]).innerHTML = fields[1]; 22 | } 23 | else if(fields[2] == "chk") 24 | { 25 | document.getElementById(fields[0]).checked = fields[1]; 26 | } 27 | }); 28 | }); 29 | } 30 | 31 | )====="; 32 | -------------------------------------------------------------------------------- /BVB_WebConfig_OTA_V7/example.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGE_EXAMPLE_H 2 | #define PAGE_EXAMPLE_H 3 | // 4 | // The EXAMPLE PAGE 5 | // 6 | const char PAGE_example[] PROGMEM = R"=====( 7 | 8 | 9 |

My Example goes here

10 | 11 |
Here comes the Dynamic Data in
12 | 30 | 31 | )====="; 32 | #endif 33 | 34 | 35 | void filldynamicdata() 36 | { 37 | String values =""; 38 | values += "mydynamicdata|" + (String) + "This is filled by AJAX. Millis since start: " + (String) millis() + "|div\n"; // Build a string, like this: ID|VALUE|TYPE 39 | server.send ( 200, "text/plain", values); 40 | } 41 | 42 | void processExample() 43 | { 44 | if (server.args() > 0 ) // Are there any POST/GET Fields ? 45 | { 46 | for ( uint8_t i = 0; i < server.args(); i++ ) { // Iterate through the fields 47 | if (server.argName(i) == "firstname") 48 | { 49 | // Your processing for the transmitted form-variable 50 | String fName = server.arg(i); 51 | } 52 | } 53 | } 54 | server.send ( 200, "text/html", PAGE_example ); 55 | } 56 | -------------------------------------------------------------------------------- /BVB_WebConfig_OTA_V7/Page_Style.css.h: -------------------------------------------------------------------------------- 1 | 2 | const char PAGE_Style_css[] PROGMEM = R"=====( 3 | body { color: #000000; font-family: avenir, helvetica, arial, sans-serif; letter-spacing: 0.15em;} 4 | hr { background-color: #eee; border: 0 none; color: #eee; height: 1px; } 5 | .btn, .btn:link, .btn:visited { 6 | border-radius: 0.3em; 7 | border-style: solid; 8 | border-width: 1px; 9 | color: #111; 10 | display: inline-block; 11 | font-family: avenir, helvetica, arial, sans-serif; 12 | letter-spacing: 0.15em; 13 | margin-bottom: 0.5em; 14 | padding: 1em 0.75em; 15 | text-decoration: none; 16 | text-transform: uppercase; 17 | -webkit-transition: color 0.4s, background-color 0.4s, border 0.4s; 18 | transition: color 0.4s, background-color 0.4s, border 0.4s; 19 | } 20 | .btn:hover, .btn:focus { 21 | color: #7FDBFF; 22 | border: 1px solid #7FDBFF; 23 | -webkit-transition: background-color 0.3s, color 0.3s, border 0.3s; 24 | transition: background-color 0.3s, color 0.3s, border 0.3s; 25 | } 26 | .btn:active { 27 | color: #0074D9; 28 | border: 1px solid #0074D9; 29 | -webkit-transition: background-color 0.3s, color 0.3s, border 0.3s; 30 | transition: background-color 0.3s, color 0.3s, border 0.3s; 31 | } 32 | .btn--s 33 | { 34 | font-size: 12px; 35 | } 36 | .btn--m { 37 | font-size: 14px; 38 | } 39 | .btn--l { 40 | font-size: 20px; border-radius: 0.25em !important; 41 | } 42 | .btn--full, .btn--full:link { 43 | border-radius: 0.25em; 44 | display: block; 45 | margin-left: auto; 46 | margin-right: auto; 47 | text-align: center; 48 | width: 100%; 49 | } 50 | .btn--blue:link, .btn--blue:visited { 51 | color: #fff; 52 | background-color: #0074D9; 53 | } 54 | .btn--blue:hover, .btn--blue:focus { 55 | color: #fff !important; 56 | background-color: #0063aa; 57 | border-color: #0063aa; 58 | } 59 | .btn--blue:active { 60 | color: #fff; 61 | background-color: #001F3F; border-color: #001F3F; 62 | } 63 | @media screen and (min-width: 32em) { 64 | .btn--full { 65 | max-width: 16em !important; } 66 | } 67 | )====="; 68 | 69 | -------------------------------------------------------------------------------- /BVB_WebConfig_OTA_V7/Page_General.h: -------------------------------------------------------------------------------- 1 | // 2 | // HTML PAGE 3 | // 4 | 5 | const char PAGE_AdminGeneralSettings[] PROGMEM = R"=====( 6 | 7 | 8 | <  General Settings 9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
Name of Device
18 |
19 | 38 | )====="; 39 | 40 | 41 | // Functions for this Page 42 | void send_devicename_value_html() 43 | { 44 | 45 | String values =""; 46 | values += "devicename|" + (String) config.DeviceName + "|div\n"; 47 | server.send ( 200, "text/plain", values); 48 | Serial.println(__FUNCTION__); 49 | 50 | } 51 | 52 | void send_general_html() 53 | { 54 | 55 | if (server.args() > 0 ) // Save Settings 56 | { 57 | String temp = ""; 58 | for ( uint8_t i = 0; i < server.args(); i++ ) { 59 | if (server.argName(i) == "devicename") config.DeviceName = urldecode(server.arg(i)); 60 | } 61 | WriteConfig(); 62 | firstStart = true; 63 | } 64 | server.send ( 200, "text/html", PAGE_AdminGeneralSettings ); 65 | Serial.println(__FUNCTION__); 66 | 67 | 68 | } 69 | 70 | void send_general_configuration_values_html() 71 | { 72 | String values =""; 73 | values += "devicename|" + (String) config.DeviceName + "|input\n"; 74 | 75 | server.send ( 200, "text/plain", values); 76 | Serial.println(__FUNCTION__); 77 | AdminTimeOutCounter=0; 78 | } 79 | -------------------------------------------------------------------------------- /BVB_WebConfig_OTA_V7/Page_Information.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGE_INFOMATION_H 2 | #define PAGE_INFOMATION_H 3 | 4 | 5 | // 6 | // The HTML PAGE 7 | // 8 | const char PAGE_Information[] PROGMEM = R"=====( 9 | 10 | 11 | 12 | 13 | <  Network Information 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
SSID :
IP :
Netmask :
Gateway :
Mac :
Refresh
25 | 47 | )=====" ; 48 | 49 | 50 | // 51 | // FILL WITH INFOMATION 52 | // 53 | 54 | void send_information_values_html () 55 | { 56 | 57 | String values =""; 58 | 59 | values += "x_ssid|" + (String)WiFi.SSID() + "|div\n"; 60 | values += "x_ip|" + (String) WiFi.localIP()[0] + "." + (String) WiFi.localIP()[1] + "." + (String) WiFi.localIP()[2] + "." + (String) WiFi.localIP()[3] + "|div\n"; 61 | values += "x_gateway|" + (String) WiFi.gatewayIP()[0] + "." + (String) WiFi.gatewayIP()[1] + "." + (String) WiFi.gatewayIP()[2] + "." + (String) WiFi.gatewayIP()[3] + "|div\n"; 62 | values += "x_netmask|" + (String) WiFi.subnetMask()[0] + "." + (String) WiFi.subnetMask()[1] + "." + (String) WiFi.subnetMask()[2] + "." + (String) WiFi.subnetMask()[3] + "|div\n"; 63 | values += "x_mac|" + GetMacAddress() + "|div\n"; 64 | server.send ( 200, "text/plain", values); 65 | Serial.println(__FUNCTION__); 66 | AdminTimeOutCounter=0; 67 | } 68 | 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /BVB_WebConfig_OTA_V7/Page_applSettings.h: -------------------------------------------------------------------------------- 1 | // 2 | // HTML PAGE 3 | // 4 | const char PAGE_ApplicationConfiguration[] PROGMEM = R"=====( 5 | 6 | 7 | <  Application Configuration 8 |
9 | Connect to Router with these settings:
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
Home:
Left:
Right:
Way to station (min):
Warning starts (min):
20 |
21 | 22 | 44 | 45 | 46 | )====="; 47 | 48 | // 49 | // SEND HTML PAGE OR IF A FORM SUMBITTED VALUES, PROCESS THESE VALUES 50 | // 51 | 52 | void send_application_configuration_html() 53 | { 54 | if (server.args() > 0 ) // Save Settings 55 | { 56 | for ( uint8_t i = 0; i < server.args(); i++ ) { 57 | if (server.argName(i) == "base") config.base = urldecode(server.arg(i)); 58 | if (server.argName(i) == "left") config.left = urldecode(server.arg(i)); 59 | if (server.argName(i) == "right") config.right = urldecode(server.arg(i)); 60 | if (server.argName(i) == "wayToStation") config.wayToStation = urldecode(server.arg(i)).toInt(); 61 | if (server.argName(i) == "warningBegin") config.warningBegin = urldecode(server.arg(i)).toInt(); 62 | } 63 | if (config.wayToStation>20) config.wayToStation=20; 64 | if (config.wayToStation<0) config.wayToStation=0; 65 | 66 | if (config.warningBegin>10) config.warningBegin=10; 67 | if (config.warningBegin<0) config.warningBegin=0; 68 | 69 | WriteConfig(); 70 | } 71 | server.send ( 200, "text/html", PAGE_ApplicationConfiguration ); 72 | Serial.println(__FUNCTION__); 73 | } 74 | 75 | 76 | 77 | // 78 | // FILL THE PAGE WITH VALUES 79 | // 80 | 81 | void send_application_configuration_values_html() 82 | { 83 | 84 | String values =""; 85 | 86 | values += "base|" + (String) config.base + "|input\n"; 87 | values += "left|" + (String) config.left + "|input\n"; 88 | values += "right|" + (String) config.right + "|input\n"; 89 | values += "wayToStation|" + (String) config.wayToStation + "|input\n"; 90 | values += "warningBegin|" + (String) config.warningBegin + "|input\n"; 91 | 92 | server.send ( 200, "text/plain", values); 93 | Serial.print("1 "); 94 | Serial.println(__FUNCTION__); 95 | AdminTimeOutCounter=0; 96 | 97 | } 98 | -------------------------------------------------------------------------------- /BVB_WebConfig_OTA_V7/helpers.h: -------------------------------------------------------------------------------- 1 | #ifndef HELPERS_H 2 | #define HELPERS_H 3 | // #define ESP_12 4 | 5 | 6 | // 7 | // Check the Values is between 0-255 8 | // 9 | boolean checkRange(String Value) 10 | { 11 | if (Value.toInt() < 0 || Value.toInt() > 255) 12 | { 13 | return false; 14 | } 15 | else 16 | { 17 | return true; 18 | } 19 | } 20 | 21 | 22 | 23 | void WriteStringToEEPROM(int beginaddress, String string) 24 | { 25 | char charBuf[string.length() + 1]; 26 | string.toCharArray(charBuf, string.length() + 1); 27 | for (int t = 0; t < sizeof(charBuf); t++) 28 | { 29 | EEPROM.write(beginaddress + t, charBuf[t]); 30 | } 31 | } 32 | String ReadStringFromEEPROM(int beginaddress) 33 | { 34 | volatile byte counter = 0; 35 | char rChar; 36 | String retString = ""; 37 | while (1) 38 | { 39 | rChar = EEPROM.read(beginaddress + counter); 40 | if (rChar == 0) break; 41 | if (counter > 31) break; 42 | counter++; 43 | retString.concat(rChar); 44 | 45 | } 46 | return retString; 47 | } 48 | void EEPROMWritelong(int address, long value) 49 | { 50 | byte four = (value & 0xFF); 51 | byte three = ((value >> 8) & 0xFF); 52 | byte two = ((value >> 16) & 0xFF); 53 | byte one = ((value >> 24) & 0xFF); 54 | 55 | //Write the 4 bytes into the eeprom memory. 56 | EEPROM.write(address, four); 57 | EEPROM.write(address + 1, three); 58 | EEPROM.write(address + 2, two); 59 | EEPROM.write(address + 3, one); 60 | } 61 | long EEPROMReadlong(long address) 62 | { 63 | //Read the 4 bytes from the eeprom memory. 64 | long four = EEPROM.read(address); 65 | long three = EEPROM.read(address + 1); 66 | long two = EEPROM.read(address + 2); 67 | long one = EEPROM.read(address + 3); 68 | 69 | //Return the recomposed long by using bitshift. 70 | return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF); 71 | } 72 | 73 | 74 | 75 | 76 | 77 | String GetMacAddress() 78 | { 79 | uint8_t mac[6]; 80 | char macStr[18] = {0}; 81 | WiFi.macAddress(mac); 82 | sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 83 | return String(macStr); 84 | } 85 | 86 | // convert a single hex digit character to its integer value (from https://code.google.com/p/avr-netino/) 87 | unsigned char h2int(char c) 88 | { 89 | if (c >= '0' && c <= '9') { 90 | return ((unsigned char)c - '0'); 91 | } 92 | if (c >= 'a' && c <= 'f') { 93 | return ((unsigned char)c - 'a' + 10); 94 | } 95 | if (c >= 'A' && c <= 'F') { 96 | return ((unsigned char)c - 'A' + 10); 97 | } 98 | return (0); 99 | } 100 | 101 | String urldecode(String input) // (based on https://code.google.com/p/avr-netino/) 102 | { 103 | char c; 104 | String ret = ""; 105 | 106 | for (byte t = 0; t < input.length(); t++) 107 | { 108 | c = input[t]; 109 | if (c == '+') c = ' '; 110 | if (c == '%') { 111 | 112 | 113 | t++; 114 | c = input[t]; 115 | t++; 116 | c = (h2int(c) << 4) | h2int(input[t]); 117 | } 118 | 119 | ret.concat(c); 120 | } 121 | return ret; 122 | 123 | } 124 | 125 | 126 | //---------------- Custom ------------------------ 127 | 128 | #ifdef ESP_12 129 | //ESP12E 130 | #define LED_RED 13 131 | #define LED_GREEN 12 132 | #else 133 | // NodeMCU 134 | #define LED_RED D7 135 | #define LED_GREEN D6 136 | #endif 137 | 138 | 139 | #define OFF 0 140 | 141 | enum ledColor { 142 | off, 143 | green, 144 | red, 145 | both 146 | }; 147 | 148 | void led(ledColor color) { 149 | 150 | switch (color) { 151 | case green: 152 | digitalWrite(LED_GREEN, LOW); 153 | digitalWrite(LED_RED, HIGH); 154 | break; 155 | case red: 156 | digitalWrite(LED_GREEN, HIGH); 157 | digitalWrite(LED_RED, LOW); 158 | break; 159 | case both: 160 | digitalWrite(LED_GREEN, LOW); 161 | digitalWrite(LED_RED, LOW); 162 | break; 163 | case off: 164 | digitalWrite(LED_GREEN, HIGH); 165 | digitalWrite(LED_RED, HIGH); 166 | break; 167 | default: 168 | break; 169 | } 170 | } 171 | 172 | 173 | #endif 174 | -------------------------------------------------------------------------------- /BVB_WebConfig_OTA_V7/Page_NTPSettings.h: -------------------------------------------------------------------------------- 1 | 2 | const char PAGE_NTPConfiguration[] PROGMEM = R"=====( 3 | 4 | 5 | <  NTP Settings 6 |
7 |
8 | 9 | 10 | 11 | 49 | 50 | 51 |
NTP Server:
Update: minutes (0=disable)
Timezone 12 | 48 |
Daylight saving:
52 |
53 | 71 | )====="; 72 | 73 | 74 | void send_NTP_configuration_html() 75 | { 76 | if (server.args() > 0 ) // Save Settings 77 | { 78 | config.isDayLightSaving = false; 79 | String temp = ""; 80 | for ( uint8_t i = 0; i < server.args(); i++ ) { 81 | if (server.argName(i) == "ntpserver") config.ntpServerName = urldecode( server.arg(i)); 82 | if (server.argName(i) == "update") config.Update_Time_Via_NTP_Every = server.arg(i).toInt(); 83 | if (server.argName(i) == "tz") config.timeZone = server.arg(i).toInt(); 84 | if (server.argName(i) == "dst") config.isDayLightSaving = true; 85 | } 86 | WriteConfig(); 87 | 88 | firstStart = true; 89 | } 90 | server.send ( 200, "text/html", PAGE_NTPConfiguration ); 91 | Serial.println(__FUNCTION__); 92 | 93 | } 94 | 95 | 96 | 97 | void send_NTP_configuration_values_html() 98 | { 99 | 100 | String values =""; 101 | values += "ntpserver|" + (String) config.ntpServerName + "|input\n"; 102 | values += "update|" + (String) config.Update_Time_Via_NTP_Every + "|input\n"; 103 | values += "tz|" + (String) config.timeZone + "|input\n"; 104 | values += "dst|" + (String) (config.isDayLightSaving ? "checked" : "") + "|chk\n"; 105 | server.send ( 200, "text/plain", values); 106 | Serial.println(__FUNCTION__); 107 | AdminTimeOutCounter=0; 108 | } 109 | -------------------------------------------------------------------------------- /BVB_WebConfig_OTA_V7/NTP.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | ** 4 | ** NTP 5 | ** 6 | */ 7 | 8 | 9 | static const uint8_t monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 10 | #define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) ) 11 | 12 | 13 | struct strDateTime 14 | { 15 | byte hour; 16 | byte minute; 17 | byte second; 18 | int year; 19 | byte month; 20 | byte day; 21 | byte wday; 22 | } ; 23 | 24 | strDateTime DateTime; // Global DateTime structure, will be refreshed every Second 25 | const int NTP_PACKET_SIZE = 48; 26 | byte packetBuffer[ NTP_PACKET_SIZE]; 27 | 28 | 29 | 30 | void storeNTPtime() 31 | { 32 | unsigned long _unixTime = 0; 33 | 34 | if (WiFi.status() == WL_CONNECTED) 35 | { 36 | UDPNTPClient.begin(2390); // Port for NTP receive 37 | IPAddress timeServerIP; 38 | WiFi.hostByName(config.ntpServerName.c_str(), timeServerIP); 39 | 40 | //Serial.println("sending NTP packet..."); 41 | memset(packetBuffer, 0, NTP_PACKET_SIZE); 42 | packetBuffer[0] = 0b11100011; // LI, Version, Mode 43 | packetBuffer[1] = 0; // Stratum, or type of clock 44 | packetBuffer[2] = 6; // Polling Interval 45 | packetBuffer[3] = 0xEC; // Peer Clock Precision 46 | packetBuffer[12] = 49; 47 | packetBuffer[13] = 0x4E; 48 | packetBuffer[14] = 49; 49 | packetBuffer[15] = 52; 50 | UDPNTPClient.beginPacket(timeServerIP, 123); 51 | UDPNTPClient.write(packetBuffer, NTP_PACKET_SIZE); 52 | UDPNTPClient.endPacket(); 53 | 54 | delay(100); 55 | 56 | int cb = UDPNTPClient.parsePacket(); 57 | if (cb == 0) { 58 | Serial.println("No NTP packet yet"); 59 | } 60 | else 61 | { 62 | Serial.print("NTP packet received, length="); 63 | Serial.println(cb); 64 | UDPNTPClient.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer 65 | unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); 66 | unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); 67 | unsigned long secsSince1900 = highWord << 16 | lowWord; 68 | const unsigned long seventyYears = 2208988800UL; 69 | _unixTime = secsSince1900 - seventyYears; 70 | 71 | } 72 | } else { 73 | Serial.println("Internet yet not connected"); 74 | delay(500); 75 | } 76 | yield(); 77 | if (_unixTime > 0) UnixTimestamp = _unixTime; // store universally available time stamp 78 | } 79 | 80 | 81 | strDateTime ConvertUnixTimeStamp( unsigned long _tempTimeStamp) { 82 | strDateTime _tempDateTime; 83 | uint8_t year; 84 | uint8_t month, monthLength; 85 | uint32_t time; 86 | unsigned long days; 87 | 88 | time = (uint32_t)_tempTimeStamp; 89 | _tempDateTime.second = time % 60; 90 | time /= 60; // now it is minutes 91 | _tempDateTime.minute = time % 60; 92 | time /= 60; // now it is hours 93 | _tempDateTime.hour = time % 24; 94 | time /= 24; // now it is days 95 | _tempDateTime.wday = ((time + 4) % 7) + 1; // Sunday is day 1 96 | 97 | year = 0; 98 | days = 0; 99 | while ((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) { 100 | year++; 101 | } 102 | _tempDateTime.year = year; // year is offset from 1970 103 | 104 | days -= LEAP_YEAR(year) ? 366 : 365; 105 | time -= days; // now it is days in this year, starting at 0 106 | 107 | days = 0; 108 | month = 0; 109 | monthLength = 0; 110 | for (month = 0; month < 12; month++) { 111 | if (month == 1) { // february 112 | if (LEAP_YEAR(year)) { 113 | monthLength = 29; 114 | } else { 115 | monthLength = 28; 116 | } 117 | } else { 118 | monthLength = monthDays[month]; 119 | } 120 | 121 | if (time >= monthLength) { 122 | time -= monthLength; 123 | } else { 124 | break; 125 | } 126 | } 127 | _tempDateTime.month = month + 1; // jan is month 1 128 | _tempDateTime.day = time + 1; // day of month 129 | _tempDateTime.year += 1970; 130 | 131 | return _tempDateTime; 132 | } 133 | 134 | 135 | // 136 | // Summertime calculates the daylight saving time for middle Europe. Input: Unixtime in UTC 137 | // 138 | boolean summerTime(unsigned long _timeStamp ) { 139 | strDateTime _tempDateTime = ConvertUnixTimeStamp(_timeStamp); 140 | // printTime("Innerhalb ", _tempDateTime); 141 | 142 | if (_tempDateTime.month < 3 || _tempDateTime.month > 10) return false; // keine Sommerzeit in Jan, Feb, Nov, Dez 143 | if (_tempDateTime.month > 3 && _tempDateTime.month < 10) return true; // Sommerzeit in Apr, Mai, Jun, Jul, Aug, Sep 144 | if (_tempDateTime.month == 3 && (_tempDateTime.hour + 24 * _tempDateTime.day) >= (3 + 24 * (31 - (5 * _tempDateTime.year / 4 + 4) % 7)) || _tempDateTime.month == 10 && (_tempDateTime.hour + 24 * _tempDateTime.day) < (3 + 24 * (31 - (5 * _tempDateTime.year / 4 + 1) % 7))) 145 | return true; 146 | else 147 | return false; 148 | } 149 | 150 | unsigned long adjustTimeZone(unsigned long _timeStamp, int _timeZone, bool _isDayLightSavingSaving) { 151 | strDateTime _tempDateTime; 152 | _timeStamp += _timeZone * 360; // adjust timezone 153 | // printTime("Innerhalb adjustTimeZone ", ConvertUnixTimeStamp(_timeStamp)); 154 | if (_isDayLightSavingSaving && summerTime(_timeStamp)) _timeStamp += 3600; // Sommerzeit beachten 155 | return _timeStamp; 156 | } 157 | 158 | 159 | void ISRsecondTick() 160 | { 161 | strDateTime _tempDateTime; 162 | AdminTimeOutCounter++; 163 | cNTP_Update++; 164 | UnixTimestamp++; 165 | absoluteActualTime = adjustTimeZone(UnixTimestamp, config.timeZone, config.isDayLightSaving); 166 | DateTime = ConvertUnixTimeStamp(absoluteActualTime); // convert to DateTime format 167 | actualTime = 3600 * DateTime.hour + 60 * DateTime.minute + DateTime.second; 168 | if (millis() - customWatchdog > 30000){ 169 | Serial.println("CustomWatchdog bites. Bye"); 170 | ESP.reset(); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /BVB_WebConfig_OTA_V7/global.h: -------------------------------------------------------------------------------- 1 | #ifndef GLOBAL_H 2 | #define GLOBAL_H 3 | 4 | 5 | ESP8266WebServer server(80); // The Webserver 6 | boolean firstStart = true; // On firststart = true, NTP will try to get a valid time 7 | int AdminTimeOutCounter = 0; // Counter for Disabling the AdminMode 8 | WiFiUDP UDPNTPClient; // NTP Client 9 | volatile unsigned long UnixTimestamp = 0; // GLOBALTIME ( Will be set by NTP) 10 | boolean Refresh = false; // For Main Loop, to refresh things like GPIO / WS2812 11 | int cNTP_Update = 0; // Counter for Updating the time via NTP 12 | Ticker tkSecond; // Second - Timer for Updating Datetime Structure 13 | boolean AdminEnabled = true; // Enable Admin Mode for a given Time 14 | 15 | #define ACCESS_POINT_NAME "ESP" 16 | //#define ACCESS_POINT_PASSWORD "12345678" 17 | #define AdminTimeOut 1 // Defines the Time in Seconds, when the Admin-Mode will be diabled 18 | 19 | #define MAX_CONNECTIONS 3 20 | 21 | 22 | 23 | 24 | //custom declarations 25 | int freq = -1; // signal off 26 | 27 | #ifdef ESP_12 28 | //ESP-12E 29 | #define BEEPPIN 4 30 | #define LEFTPIN 16 31 | #define RIGHTPIN 14 32 | #else 33 | //NodeMCU 34 | #define BEEPPIN D8 35 | #define LEFTPIN D5 36 | #define RIGHTPIN D4 37 | #endif 38 | 39 | int counter = 0; 40 | 41 | #define LOOP_FAST 60 * 1000 42 | #define LOOP_SLOW 120 * 1000 43 | #define BEEPTICKER 100 44 | char serverTransport[] = "transport.opendata.ch"; 45 | String url; 46 | const int httpPort = 80; 47 | const int intensity[] = {1, 4, 10, 20, 20, 40, 40, 80, 80, 160, 160, 160}; 48 | unsigned long waitLoopEntry, loopTime = LOOP_SLOW, waitJSONLoopEntry; 49 | bool okNTPvalue = false; // NTP signal ok 50 | bool requestOK = false; 51 | int minTillDep = -999, secTillDep, lastMinute; 52 | ledColor ledColor; 53 | boolean ledState = false; 54 | unsigned long ledCounter; 55 | char str[80]; 56 | long departureTime, absoluteActualTime, actualTime; 57 | String JSONline; 58 | long departureTimeStamp, lastDepartureTimeStamp, customWatchdog; 59 | 60 | 61 | 62 | int beepOffTimer, beepOnTimer, beepOffTime, beepOnTime ; 63 | 64 | enum defDirection { 65 | none, 66 | left, 67 | right 68 | }; 69 | 70 | enum defBeeper { 71 | beeperOn, 72 | beeperOff, 73 | beeperIdle 74 | }; 75 | 76 | volatile defBeeper beeperStatus = beeperIdle; 77 | 78 | enum defStatus { 79 | admin, 80 | idle, 81 | requestLeft, 82 | requestRight, 83 | recovery 84 | }; 85 | 86 | defStatus status, lastStatus; 87 | 88 | 89 | struct strConfig { 90 | String ssid; 91 | String password; 92 | byte IP[4]; 93 | byte Netmask[4]; 94 | byte Gateway[4]; 95 | boolean dhcp; 96 | String ntpServerName; 97 | long Update_Time_Via_NTP_Every; 98 | long timeZone; 99 | boolean isDayLightSaving; 100 | String DeviceName; 101 | byte wayToStation; 102 | byte warningBegin; 103 | String base; 104 | String right; 105 | String left; 106 | } config; 107 | 108 | byte currentDirection; 109 | defStatus _lastStatus; 110 | 111 | 112 | 113 | /* 114 | ** 115 | ** CONFIGURATION HANDLING 116 | ** 117 | */ 118 | void ConfigureWifi() 119 | { 120 | Serial.println("Configuring Wifi"); 121 | 122 | WiFi.begin ("WLAN", "password"); 123 | 124 | WiFi.begin (config.ssid.c_str(), config.password.c_str()); 125 | 126 | while (WiFi.status() != WL_CONNECTED) { 127 | Serial.println("WiFi not connected"); 128 | led(red); 129 | delay(500); 130 | } 131 | if (!config.dhcp) 132 | { 133 | WiFi.config(IPAddress(config.IP[0], config.IP[1], config.IP[2], config.IP[3] ), IPAddress(config.Gateway[0], config.Gateway[1], config.Gateway[2], config.Gateway[3] ) , IPAddress(config.Netmask[0], config.Netmask[1], config.Netmask[2], config.Netmask[3] )); 134 | } 135 | } 136 | 137 | 138 | void WriteConfig() 139 | { 140 | 141 | Serial.println("Writing Config"); 142 | EEPROM.write(0, 'C'); 143 | EEPROM.write(1, 'F'); 144 | EEPROM.write(2, 'G'); 145 | 146 | EEPROM.write(16, config.dhcp); 147 | EEPROM.write(17, config.isDayLightSaving); 148 | 149 | EEPROMWritelong(18, config.Update_Time_Via_NTP_Every); // 4 Byte 150 | EEPROMWritelong(22, config.timeZone); // 4 Byte 151 | 152 | EEPROM.write(32, config.IP[0]); 153 | EEPROM.write(33, config.IP[1]); 154 | EEPROM.write(34, config.IP[2]); 155 | EEPROM.write(35, config.IP[3]); 156 | 157 | EEPROM.write(36, config.Netmask[0]); 158 | EEPROM.write(37, config.Netmask[1]); 159 | EEPROM.write(38, config.Netmask[2]); 160 | EEPROM.write(39, config.Netmask[3]); 161 | 162 | EEPROM.write(40, config.Gateway[0]); 163 | EEPROM.write(41, config.Gateway[1]); 164 | EEPROM.write(42, config.Gateway[2]); 165 | EEPROM.write(43, config.Gateway[3]); 166 | 167 | WriteStringToEEPROM(64, config.ssid); 168 | WriteStringToEEPROM(96, config.password); 169 | WriteStringToEEPROM(128, config.ntpServerName); 170 | 171 | // Application Settings 172 | WriteStringToEEPROM(160, config.base); 173 | WriteStringToEEPROM(192, config.left); 174 | WriteStringToEEPROM(224, config.right); 175 | EEPROM.write(256, config.warningBegin); 176 | EEPROM.write(257, config.wayToStation); 177 | 178 | WriteStringToEEPROM(258, config.DeviceName); 179 | 180 | EEPROM.commit(); 181 | } 182 | boolean ReadConfig() 183 | { 184 | Serial.println("Reading Configuration"); 185 | if (EEPROM.read(0) == 'C' && EEPROM.read(1) == 'F' && EEPROM.read(2) == 'G' ) 186 | { 187 | Serial.println("Configurarion Found!"); 188 | config.dhcp = EEPROM.read(16); 189 | 190 | config.isDayLightSaving = EEPROM.read(17); 191 | 192 | config.Update_Time_Via_NTP_Every = EEPROMReadlong(18); // 4 Byte 193 | 194 | config.timeZone = EEPROMReadlong(22); // 4 Byte 195 | 196 | config.IP[0] = EEPROM.read(32); 197 | config.IP[1] = EEPROM.read(33); 198 | config.IP[2] = EEPROM.read(34); 199 | config.IP[3] = EEPROM.read(35); 200 | config.Netmask[0] = EEPROM.read(36); 201 | config.Netmask[1] = EEPROM.read(37); 202 | config.Netmask[2] = EEPROM.read(38); 203 | config.Netmask[3] = EEPROM.read(39); 204 | config.Gateway[0] = EEPROM.read(40); 205 | config.Gateway[1] = EEPROM.read(41); 206 | config.Gateway[2] = EEPROM.read(42); 207 | config.Gateway[3] = EEPROM.read(43); 208 | config.ssid = ReadStringFromEEPROM(64); 209 | config.password = ReadStringFromEEPROM(96); 210 | config.ntpServerName = ReadStringFromEEPROM(128); 211 | 212 | 213 | // Application parameters 214 | config.base = ReadStringFromEEPROM(160); 215 | config.left = ReadStringFromEEPROM(192); 216 | config.right = ReadStringFromEEPROM(224); 217 | config.warningBegin = EEPROM.read(256); 218 | config.wayToStation = EEPROM.read(257); 219 | 220 | config.DeviceName = ReadStringFromEEPROM(258); 221 | return true; 222 | 223 | } 224 | else 225 | { 226 | Serial.println("Configurarion NOT FOUND!!!!"); 227 | return false; 228 | } 229 | } 230 | 231 | 232 | 233 | #endif 234 | -------------------------------------------------------------------------------- /BVB_WebConfig_OTA_V7/Page_NetworkConfiguration.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 4 | // HTML PAGE 5 | // 6 | const char PAGE_NetworkConfiguration[] PROGMEM = R"=====( 7 | 8 | 9 | <  Network Configuration 10 |
11 | Connect to Router with these settings:
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
SSID:
Password:
DHCP:
IP: ...
Netmask:...
Gateway:...
22 |
23 |
24 | Connection State:
N/A
25 |
26 | Networks:
27 | 28 | 29 | 30 |
Scanning...
Refresh
31 | 32 | 33 | 62 | 63 | 64 | )====="; 65 | 66 | const char PAGE_WaitAndReload[] PROGMEM = R"=====( 67 | 68 | Please Wait....Configuring and Restarting. 69 | )====="; 70 | 71 | 72 | // 73 | // SEND HTML PAGE OR IF A FORM SUMBITTED VALUES, PROCESS THESE VALUES 74 | // 75 | 76 | void send_network_configuration_html() 77 | { 78 | 79 | if (server.args() > 0 ) // Save Settings 80 | { 81 | String temp = ""; 82 | config.dhcp = false; 83 | for ( uint8_t i = 0; i < server.args(); i++ ) { 84 | if (server.argName(i) == "ssid") config.ssid = urldecode(server.arg(i)); 85 | if (server.argName(i) == "password") config.password = urldecode(server.arg(i)); 86 | if (server.argName(i) == "ip_0") if (checkRange(server.arg(i))) config.IP[0] = server.arg(i).toInt(); 87 | if (server.argName(i) == "ip_1") if (checkRange(server.arg(i))) config.IP[1] = server.arg(i).toInt(); 88 | if (server.argName(i) == "ip_2") if (checkRange(server.arg(i))) config.IP[2] = server.arg(i).toInt(); 89 | if (server.argName(i) == "ip_3") if (checkRange(server.arg(i))) config.IP[3] = server.arg(i).toInt(); 90 | if (server.argName(i) == "nm_0") if (checkRange(server.arg(i))) config.Netmask[0] = server.arg(i).toInt(); 91 | if (server.argName(i) == "nm_1") if (checkRange(server.arg(i))) config.Netmask[1] = server.arg(i).toInt(); 92 | if (server.argName(i) == "nm_2") if (checkRange(server.arg(i))) config.Netmask[2] = server.arg(i).toInt(); 93 | if (server.argName(i) == "nm_3") if (checkRange(server.arg(i))) config.Netmask[3] = server.arg(i).toInt(); 94 | if (server.argName(i) == "gw_0") if (checkRange(server.arg(i))) config.Gateway[0] = server.arg(i).toInt(); 95 | if (server.argName(i) == "gw_1") if (checkRange(server.arg(i))) config.Gateway[1] = server.arg(i).toInt(); 96 | if (server.argName(i) == "gw_2") if (checkRange(server.arg(i))) config.Gateway[2] = server.arg(i).toInt(); 97 | if (server.argName(i) == "gw_3") if (checkRange(server.arg(i))) config.Gateway[3] = server.arg(i).toInt(); 98 | if (server.argName(i) == "dhcp") config.dhcp = true; 99 | } 100 | server.send ( 200, "text/html", PAGE_WaitAndReload ); 101 | WriteConfig(); 102 | ConfigureWifi(); 103 | } 104 | else 105 | { 106 | server.send ( 200, "text/html", PAGE_NetworkConfiguration ); 107 | } 108 | Serial.println(__FUNCTION__); 109 | } 110 | 111 | 112 | 113 | // 114 | // FILL THE PAGE WITH VALUES 115 | // 116 | 117 | void send_network_configuration_values_html() 118 | { 119 | 120 | String values =""; 121 | 122 | values += "ssid|" + (String) config.ssid + "|input\n"; 123 | values += "password|" + (String) config.password + "|input\n"; 124 | values += "ip_0|" + (String) config.IP[0] + "|input\n"; 125 | values += "ip_1|" + (String) config.IP[1] + "|input\n"; 126 | values += "ip_2|" + (String) config.IP[2] + "|input\n"; 127 | values += "ip_3|" + (String) config.IP[3] + "|input\n"; 128 | values += "nm_0|" + (String) config.Netmask[0] + "|input\n"; 129 | values += "nm_1|" + (String) config.Netmask[1] + "|input\n"; 130 | values += "nm_2|" + (String) config.Netmask[2] + "|input\n"; 131 | values += "nm_3|" + (String) config.Netmask[3] + "|input\n"; 132 | values += "gw_0|" + (String) config.Gateway[0] + "|input\n"; 133 | values += "gw_1|" + (String) config.Gateway[1] + "|input\n"; 134 | values += "gw_2|" + (String) config.Gateway[2] + "|input\n"; 135 | values += "gw_3|" + (String) config.Gateway[3] + "|input\n"; 136 | values += "dhcp|" + (String) (config.dhcp ? "checked" : "") + "|chk\n"; 137 | server.send ( 200, "text/plain", values); 138 | Serial.println(__FUNCTION__); 139 | 140 | } 141 | 142 | 143 | // 144 | // FILL THE PAGE WITH NETWORKSTATE & NETWORKS 145 | // 146 | 147 | void send_connection_state_values_html() 148 | { 149 | 150 | String state = "N/A"; 151 | String Networks = ""; 152 | if (WiFi.status() == 0) state = "Idle"; 153 | else if (WiFi.status() == 1) state = "NO SSID AVAILBLE"; 154 | else if (WiFi.status() == 2) state = "SCAN COMPLETED"; 155 | else if (WiFi.status() == 3) state = "CONNECTED"; 156 | else if (WiFi.status() == 4) state = "CONNECT FAILED"; 157 | else if (WiFi.status() == 5) state = "CONNECTION LOST"; 158 | else if (WiFi.status() == 6) state = "DISCONNECTED"; 159 | 160 | 161 | 162 | int n = WiFi.scanNetworks(); 163 | 164 | if (n == 0) 165 | { 166 | Networks = "No networks found!"; 167 | } 168 | else 169 | { 170 | 171 | 172 | Networks = "Found " +String(n) + " Networks
"; 173 | Networks += ""; 174 | Networks += ""; 175 | for (int i = 0; i < n; ++i) 176 | { 177 | int quality=0; 178 | if(WiFi.RSSI(i) <= -100) 179 | { 180 | quality = 0; 181 | } 182 | else if(WiFi.RSSI(i) >= -50) 183 | { 184 | quality = 100; 185 | } 186 | else 187 | { 188 | quality = 2 * (WiFi.RSSI(i) + 100); 189 | } 190 | 191 | 192 | Networks += ""; 193 | } 194 | Networks += "
NameQualityEnc
" + String(WiFi.SSID(i)) + "" + String(quality) + "%" + String((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":"*") + "
"; 195 | } 196 | 197 | String values =""; 198 | values += "connectionstate|" + state + "|div\n"; 199 | values += "networks|" + Networks + "|div\n"; 200 | server.send ( 200, "text/plain", values); 201 | Serial.println(__FUNCTION__); 202 | AdminTimeOutCounter=0; 203 | } 204 | 205 | 206 | -------------------------------------------------------------------------------- /BVB_WebConfig_OTA_V7/BVB_WebConfig_OTA_V7.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ESP_WebConfig 3 | 4 | Copyright (c) 2015 John Lassen. All rights reserved. 5 | This is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | This software is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | Lesser General Public License for more details. 13 | You should have received a copy of the GNU Lesser General Public 14 | License along with this library; if not, write to the Free Software 15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 16 | 17 | Latest version: 1.1.3 - 2015-07-20 18 | Changed the loading of the Javascript and CCS Files, so that they will successively loaded and that only one request goes to the ESP. 19 | 20 | The rest of the coding was done by Andreas Spiess 17.11.15 21 | 22 | 23 | First initial version to the public 24 | 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | /* credentials.h has to be in your libary folder. its content is: 37 | 38 | #define mySSID "yourSSID" 39 | #define myPASSWORD "yourPassword" 40 | 41 | If you do not want this file, hard-code your credentials in this sketch 42 | 43 | */ 44 | 45 | #include 46 | 47 | #include "helpers.h" 48 | #include "global.h" 49 | #include "NTP.h" 50 | 51 | 52 | // Include the HTML, STYLE and Script "Pages" 53 | 54 | #include "Page_Root.h" 55 | #include "Page_Admin.h" 56 | #include "Page_Script.js.h" 57 | #include "Page_Style.css.h" 58 | #include "Page_NTPsettings.h" 59 | #include "Page_Information.h" 60 | #include "Page_General.h" 61 | #include "Page_applSettings.h" 62 | #include "PAGE_NetworkConfiguration.h" 63 | #include "example.h" 64 | 65 | extern "C" { 66 | #include "user_interface.h" 67 | } 68 | WiFiClient client; 69 | Ticker ticker; 70 | 71 | os_timer_t myTimer; 72 | 73 | 74 | //OTA 75 | const char* host = "esp8266-ota"; 76 | const uint16_t aport = 8266; 77 | bool otaFlag = false; 78 | WiFiServer TelnetServer(aport); 79 | WiFiClient Telnet; 80 | WiFiUDP OTA; 81 | 82 | void setup ( void ) { 83 | EEPROM.begin(512); 84 | Serial.begin(115200); 85 | Serial.println(""); 86 | Serial.println("Starting ESP8266"); 87 | 88 | os_timer_setfn(&myTimer, ISRbeepTicker, NULL); 89 | os_timer_arm(&myTimer, BEEPTICKER, true); 90 | 91 | // Custom 92 | pinMode(BEEPPIN, OUTPUT); 93 | pinMode(LED_RED, OUTPUT); 94 | pinMode(LED_GREEN, OUTPUT); 95 | pinMode(LEFTPIN, INPUT_PULLUP); 96 | pinMode(RIGHTPIN, INPUT_PULLUP); 97 | ledColor = off; 98 | beep(3); 99 | delay(2000); 100 | if (!ReadConfig()) 101 | { 102 | // DEFAULT CONFIG 103 | Serial.println("Setting default parameters"); 104 | // please define the credentials either in the file credentials.h or here 105 | config.ssid = mySSID; // SSID of access point 106 | config.password = mypassword; // password of access point 107 | config.dhcp = true; 108 | config.IP[0] = 192; config.IP[1] = 168; config.IP[2] = 1; config.IP[3] = 100; 109 | config.Netmask[0] = 255; config.Netmask[1] = 255; config.Netmask[2] = 255; config.Netmask[3] = 0; 110 | config.Gateway[0] = 192; config.Gateway[1] = 168; config.Gateway[2] = 1; config.Gateway[3] = 1; 111 | config.ntpServerName = "0.ch.pool.ntp.org"; 112 | config.Update_Time_Via_NTP_Every = 5; 113 | config.timeZone = 1; 114 | config.isDayLightSaving = true; 115 | config.DeviceName = "Not Named"; 116 | config.wayToStation = 3; 117 | config.warningBegin = 5; 118 | config.base = "lausen stutz"; 119 | config.right = "lausen"; 120 | config.left = "lausen"; 121 | WriteConfig(); 122 | } 123 | if (!(digitalRead(LEFTPIN) || digitalRead(RIGHTPIN))) { // OTA Mode? 124 | Serial.println("OTA READY"); 125 | otaFlag = true; 126 | otaInit(); 127 | for (int i = 0; i < 10; i++) { 128 | ledColor = both; 129 | delay(200); 130 | ledColor = off; 131 | delay(200); 132 | } 133 | } else { 134 | // normal operation 135 | status = admin; 136 | tkSecond.attach(1, ISRsecondTick); 137 | 138 | currentDirection = EEPROM.read(300); 139 | Serial.printf("Current Direction %d \n", currentDirection); 140 | if ((currentDirection == left || currentDirection == right) && digitalRead(LEFTPIN)) { 141 | // ---------------- RECOVERY ----------------------- 142 | status = recovery; 143 | } else { 144 | 145 | // normal operation 146 | WiFi.mode(WIFI_STA); 147 | WiFi.softAP( "ESP"); 148 | 149 | // Admin page 150 | server.on ( "/", []() { 151 | Serial.println("admin.html"); 152 | server.send ( 200, "text/html", PAGE_AdminMainPage ); // const char top of page 153 | } ); 154 | 155 | server.on ( "/favicon.ico", []() { 156 | Serial.println("favicon.ico"); 157 | server.send ( 200, "text/html", "" ); 158 | } ); 159 | 160 | // Network config 161 | server.on ( "/config.html", send_network_configuration_html ); 162 | // Info Page 163 | server.on ( "/info.html", []() { 164 | Serial.println("info.html"); 165 | server.send ( 200, "text/html", PAGE_Information ); 166 | } ); 167 | server.on ( "/ntp.html", send_NTP_configuration_html ); 168 | 169 | server.on ( "/appl.html", send_application_configuration_html ); 170 | server.on ( "/general.html", send_general_html ); 171 | // server.on ( "/example.html", []() { server.send ( 200, "text/html", PAGE_EXAMPLE ); } ); 172 | server.on ( "/style.css", []() { 173 | Serial.println("style.css"); 174 | server.send ( 200, "text/plain", PAGE_Style_css ); 175 | } ); 176 | server.on ( "/microajax.js", []() { 177 | Serial.println("microajax.js"); 178 | server.send ( 200, "text/plain", PAGE_microajax_js ); 179 | } ); 180 | server.on ( "/admin/values", send_network_configuration_values_html ); 181 | server.on ( "/admin/connectionstate", send_connection_state_values_html ); 182 | server.on ( "/admin/infovalues", send_information_values_html ); 183 | server.on ( "/admin/ntpvalues", send_NTP_configuration_values_html ); 184 | server.on ( "/admin/applvalues", send_application_configuration_values_html ); 185 | server.on ( "/admin/generalvalues", send_general_configuration_values_html); 186 | server.on ( "/admin/devicename", send_devicename_value_html); 187 | 188 | 189 | server.onNotFound ( []() { 190 | Serial.println("Page Not Found"); 191 | server.send ( 400, "text/html", "Page not Found" ); 192 | } ); 193 | server.begin(); 194 | Serial.println( "HTTP server started" ); 195 | 196 | AdminTimeOutCounter = 0; 197 | waitLoopEntry = millis(); 198 | } 199 | } 200 | } 201 | 202 | void loop(void ) { 203 | yield(); // For ESP8266 to not dump 204 | 205 | if (otaFlag) { 206 | otaReceive(); 207 | } 208 | else { 209 | customLoop(); 210 | } 211 | } 212 | 213 | //-------------------------------------- CUSTOM ---------------------------------------- 214 | 215 | void customLoop() { 216 | defDirection _dir; 217 | String _line; 218 | 219 | // Non blocking code !!! 220 | switch (status) { 221 | case admin: 222 | ledColor = both; 223 | server.handleClient(); 224 | 225 | // exit 226 | if (AdminTimeOutCounter > AdminTimeOut) { 227 | Serial.println("Admin Mode disabled!"); 228 | ledColor = red; 229 | for (int hi = 0; hi < 3; hi++) beep(2); 230 | WiFi.mode(WIFI_AP); 231 | ConfigureWifi(); 232 | ledColor = green; 233 | 234 | 235 | // exit 236 | waitJSONLoopEntry = 0; 237 | cNTP_Update = 999; 238 | status = idle; 239 | lastStatus = idle; 240 | } 241 | break; 242 | 243 | case idle: 244 | if (lastStatus != idle) Serial.println("Status idle"); 245 | ledColor = off; 246 | storeDirToEEPROM(none); 247 | freq = -1; // no signal 248 | url = ""; 249 | JSONline = ""; 250 | 251 | // exit 252 | _dir = readButton(); 253 | if (_dir == left) status = requestLeft; 254 | if (_dir == right) status = requestRight; 255 | lastStatus = idle; 256 | break; 257 | 258 | 259 | 260 | case requestLeft: 261 | if (lastStatus != requestLeft) Serial.println("Status requestLeft"); 262 | storeDirToEEPROM(left); 263 | url = "/v1/connections?from=" + config.base + "&to=" + config.left + "&fields[]=connections/from/departure&fields[]=connections/from/prognosis/departure&fields[]=connections/from/departureTimestamp&limit=" + MAX_CONNECTIONS; 264 | if (lastStatus != requestLeft) storeDepartureString(); // if valid url 265 | if (JSONline.length() > 1) { 266 | processRequest(); 267 | 268 | // exit 269 | if (lastDepartureTimeStamp != departureTimeStamp && (lastStatus == requestRight || lastStatus == requestLeft)) status = idle; // next departure time choosen 270 | // Serial.printf("lastDepartureTimeStamp %d departureTimeStamp %d lastStatus %d \n", lastDepartureTimeStamp , departureTimeStamp, lastStatus); 271 | lastDepartureTimeStamp = departureTimeStamp; 272 | lastStatus = requestLeft; 273 | } 274 | _dir = readButton(); 275 | if (_dir == right) { //change direction 276 | Serial.println("Change to right"); 277 | _dir = right; 278 | status = requestRight; 279 | lastStatus = requestLeft; 280 | } 281 | break; 282 | 283 | 284 | 285 | case requestRight: 286 | if (lastStatus != requestRight) Serial.println("Status requestRight"); 287 | storeDirToEEPROM(right); 288 | url = "/v1/connections?from=" + config.base + "&to=" + config.right + "&fields[]=connections/from/departure&fields[]=connections/from/prognosis/departure&fields[]=connections/from/departureTimestamp&limit=" + MAX_CONNECTIONS; 289 | if (lastStatus != requestRight) storeDepartureString(); // if valid url 290 | if (JSONline.length() > 1) { 291 | processRequest(); 292 | 293 | // exit 294 | if (lastDepartureTimeStamp != departureTimeStamp && (lastStatus == requestRight || lastStatus == requestRight)) status = idle; // next departure time choosen 295 | // Serial.printf("lastDepartureTimeStamp %d departureTimeStamp %d lastStatus %d \n", lastDepartureTimeStamp , departureTimeStamp, lastStatus); 296 | lastDepartureTimeStamp = departureTimeStamp; 297 | lastStatus = requestRight; 298 | } 299 | _dir = readButton(); 300 | if (_dir == left) { //change direction 301 | _dir = left; 302 | Serial.println("Change to left"); 303 | status = requestLeft; 304 | lastStatus = requestRight; 305 | } 306 | break; 307 | 308 | 309 | case recovery: 310 | Serial.println("------------ Recovery --------------"); 311 | Serial.println(""); 312 | WiFi.mode(WIFI_AP); 313 | ConfigureWifi(); 314 | ledColor = off; 315 | Serial.println(currentDirection); 316 | 317 | // exit 318 | switch (currentDirection) { 319 | case left: 320 | status = requestLeft; 321 | lastStatus = recovery; 322 | Serial.println("Recovery left"); 323 | break; 324 | 325 | case right: 326 | status = requestRight; 327 | lastStatus = recovery; 328 | Serial.println("Recovery right"); 329 | break; 330 | 331 | default: 332 | status = idle; 333 | lastStatus = recovery; 334 | break; 335 | } 336 | cNTP_Update = 999; // trigger NTP immediately 337 | minTillDep = -999; 338 | break; 339 | 340 | default: 341 | break; 342 | } 343 | 344 | // store NTP time 345 | if ( cNTP_Update > (config.Update_Time_Via_NTP_Every * 60 )) { 346 | storeNTPtime(); 347 | if (DateTime.year > 1970) cNTP_Update = 0; // trigger loop till date is valid 348 | } 349 | 350 | // store departure time String from openTransport 351 | if (millis() - waitJSONLoopEntry > loopTime) { 352 | if (minTillDep > 1 || minTillDep < 0) { // no updates in the last minute 353 | if (url.length() > 1) storeDepartureString(); // if valid url 354 | if (JSONline != "") waitJSONLoopEntry = millis(); 355 | } 356 | } 357 | 358 | // Display LED 359 | if (millis() - ledCounter > 1000 ) { 360 | ledCounter = millis(); 361 | ledState = !ledState; 362 | } 363 | 364 | if (ledState) led(ledColor); 365 | else led(off); 366 | 367 | // send Signal (Beep) 368 | if (freq < 0) setSignal(0, 0); // off 369 | else setSignal(1, freq); 370 | 371 | if (_lastStatus != status || millis() - waitLoopEntry > 10000) { 372 | displayStatus(); 373 | waitLoopEntry = millis(); 374 | _lastStatus = status; 375 | } 376 | customWatchdog = millis(); 377 | } 378 | 379 | 380 | //------------------------- END LOOP ------------------------------------- 381 | 382 | 383 | void processRequest() { 384 | long _diffSec, _diffMin; 385 | 386 | int _positionDeparture = 1; 387 | do { 388 | decodeDepartureTime(_positionDeparture); 389 | if (departureTime != -999) { // valid time 390 | _diffSec = departureTime - actualTime; 391 | if (_diffSec < -10000) _diffSec += 24 * 3600; // correct if time is before midnight and departure is after midnight 392 | _diffMin = (_diffSec / 60) - config.wayToStation; 393 | } else _diffMin = -999; 394 | _positionDeparture++; 395 | } while (_diffMin < 0 && _positionDeparture <= MAX_CONNECTIONS + 1); // next departure if first not reachable 396 | 397 | minTillDep = (_positionDeparture <= MAX_CONNECTIONS) ? _diffMin : -999; // no connection found 398 | 399 | if (minTillDep != -999) { // valid result 400 | freq = (minTillDep >= 0 && minTillDep < 10) ? intensity[minTillDep] : freq = -1; //set frequency if minTillDep between 10 and zero minutes 401 | loopTime = getLoopTime(minTillDep); 402 | } 403 | } 404 | 405 | 406 | boolean getStatus() { 407 | bool stat; 408 | String _line; 409 | 410 | _line = client.readStringUntil('\n'); 411 | // Serial.print(" statusline "); 412 | // Serial.println(line); 413 | 414 | int separatorPosition = _line.indexOf("HTTP/1.1"); 415 | 416 | // Serial.print(" separatorPosition "); 417 | // Serial.println(separatorPosition); 418 | // Serial.print("Line "); 419 | // Serial.print(line); 420 | if (separatorPosition >= 0) { 421 | 422 | if (_line.substring(9, 12) == "200") stat = true; 423 | else stat = false; 424 | // Serial.print("Status "); 425 | // Serial.println(stat); 426 | return stat; 427 | } 428 | } 429 | 430 | 431 | void storeDepartureString() { 432 | bool ok = false; 433 | String _line; 434 | unsigned long serviceTime = millis(); 435 | 436 | ledColor = red; 437 | 438 | url.replace(" ", "%20"); 439 | 440 | if (!client.connect("transport.opendata.ch", 80)) { 441 | Serial.println("connection to ... failed"); 442 | 443 | } else { 444 | client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host:" + serverTransport + "\r\n" + "Connection: keep-alive\r\n\r\n"); 445 | // Wait for answer of webservice 446 | Serial.println(url.substring(1, url.indexOf("&fields"))); 447 | while (!client.available()) { 448 | // Serial.println("waiting"); 449 | } 450 | Serial.printf("Client connect. Service time %d \n", millis() - serviceTime); 451 | delay(200); 452 | } 453 | // Service answered 454 | ok = getStatus(); 455 | Serial.printf("Got Status. Service time %d \n", millis() - serviceTime); 456 | 457 | if (ok) { // JSON packet is avablable 458 | while (client.available()) { 459 | yield(); 460 | ledColor = green; 461 | _line = client.readStringUntil('\n'); 462 | // Serial.println(_line); 463 | 464 | if (_line.indexOf("connections") > 1) { 465 | JSONline = _line; // JSON string detected 466 | Serial.printf("JSONline stored. Service time %d \n", millis() - serviceTime); 467 | } 468 | } 469 | } else Serial.println("-- No data from Service --"); 470 | } 471 | 472 | 473 | 474 | 475 | 476 | 477 | int findJSONkeyword(String keyword0, String keyword1, String keyword2, int pos ) { 478 | int hi = pos, i; 479 | String keyword[3]; 480 | 481 | keyword[0] = keyword0; 482 | keyword[1] = keyword1; 483 | keyword[2] = keyword2; 484 | i = 0; 485 | while (keyword[i] != "" && i < 3) { 486 | hi = JSONline.indexOf(keyword[i], hi + 1); 487 | 488 | i++; 489 | } 490 | if (hi > JSONline.length()) hi = 0; 491 | return hi; 492 | } 493 | 494 | 495 | 496 | 497 | 498 | void decodeDepartureTime(int pos) { 499 | int hour; 500 | int minute; 501 | int second; 502 | int i = 0; 503 | long h1, h2, hh; 504 | int separatorPosition = 1; 505 | String keyword[3]; 506 | 507 | while (i < pos) { 508 | separatorPosition = JSONline.indexOf("from", separatorPosition + 1); 509 | i++; 510 | } 511 | // separatorPosition stands at the line requested by calling function 512 | for (int i = 0; i < 3; i++) keyword[i] = ""; 513 | hh = findJSONkeyword("departure", "", "", separatorPosition); 514 | h1 = parseJSONDate(hh); 515 | 516 | // Serial.println(JSONline); 517 | 518 | hh = findJSONkeyword("prognosis", "departure", "" , separatorPosition); 519 | h2 = parseJSONDate(hh); 520 | 521 | hh = findJSONkeyword("departureTimestamp", "", "" , separatorPosition); // find unique identifier of connection 522 | departureTimeStamp = parseJSONnumber(hh); 523 | departureTime = ( h2 > 0) ? h2 : h1; 524 | } 525 | 526 | 527 | int getTimeStamp(int pos) { 528 | 529 | int hh = findJSONkeyword("departureTimestamp", "", "", pos ); 530 | return JSONline.substring(pos, pos + 4).toInt(); 531 | } 532 | 533 | 534 | 535 | 536 | 537 | long parseJSONDate(int pos) { 538 | int hi; 539 | pos = pos + 11; // adjust for beginning of text 540 | if (JSONline.substring(pos, pos + 4) != "null" ) { 541 | pos = pos + 12; // overread date; 542 | 543 | int hour = JSONline.substring(pos, pos + 2).toInt(); 544 | int minute = JSONline.substring(pos + 3, pos + 5).toInt(); 545 | int second = JSONline.substring(pos + 6, pos + 8).toInt(); 546 | 547 | // ----------------------- Spieldaten ------------------------------ 548 | // hour = 10; 549 | // minute = 28; 550 | // second = 0; 551 | 552 | // ----------------------- Spieldaten ------------------------------ 553 | 554 | hi = second + 60 * minute + 3600 * hour; 555 | } else hi = -999; 556 | return hi; 557 | } 558 | 559 | 560 | 561 | int parseJSONnumber(int pos) { 562 | pos = pos + 20; 563 | return JSONline.substring(pos, pos + 10).toInt(); 564 | } 565 | 566 | 567 | 568 | defDirection readButton() { 569 | defDirection dir = none; 570 | if (!digitalRead(LEFTPIN)) dir = left; 571 | if (!digitalRead(RIGHTPIN)) dir = right; 572 | 573 | if (dir != none) beep(3); 574 | return dir; 575 | } 576 | 577 | 578 | void beep(int _dura) { 579 | beepOnTime = _dura; 580 | beepOffTime = 2; 581 | delay(BEEPTICKER + 10); // wait for next beepTicker 582 | while (beeperStatus != beeperIdle) yield(); 583 | beepOnTime = 0; 584 | 585 | } 586 | 587 | void setSignal (int _onTime, int _offTime) { 588 | if (beeperStatus == beeperIdle) { 589 | beepOnTime = _onTime; 590 | beepOffTime = _offTime; 591 | } 592 | } 593 | 594 | // define loop time based on time till departure 595 | int getLoopTime(int _timeTillDeparture) { 596 | 597 | int _loopTime = LOOP_FAST; 598 | if (_timeTillDeparture > 5) _loopTime = LOOP_SLOW; 599 | if (_timeTillDeparture == -999) _loopTime = 0; // no valid info, immediate update required 600 | return _loopTime; 601 | } 602 | 603 | void storeDirToEEPROM(defDirection dir) { 604 | 605 | if (EEPROM.read(300) != dir) { 606 | Serial.printf("EEPROM direction before %d and after %d \n", EEPROM.read(300), dir); 607 | Serial.println(dir); 608 | EEPROM.write(300, dir); 609 | EEPROM.commit(); 610 | } 611 | } 612 | 613 | 614 | void printTime(String purpose, long _tim) { 615 | 616 | int hours = _tim / 3600; 617 | int res = _tim - hours * 3600; 618 | int minutes = res / 60; 619 | res = res - (minutes * 60); 620 | int seconds = res; 621 | Serial.print(" "); 622 | Serial.print(purpose); 623 | Serial.print(" "); 624 | Serial.print(hours); 625 | Serial.print(" H "); 626 | Serial.print(minutes); 627 | Serial.print(" M "); 628 | Serial.print(seconds); 629 | } 630 | 631 | void displayStatus() { 632 | printTime("Tim", actualTime); 633 | printTime("Dep", departureTime); 634 | Serial.print(" Status "); 635 | Serial.print(status); 636 | Serial.print(" lastStatus "); 637 | Serial.print(lastStatus); 638 | Serial.print(" minTillDep "); 639 | Serial.print(minTillDep); 640 | Serial.print(" loopTime "); 641 | Serial.print(loopTime); 642 | Serial.print(" freq "); 643 | Serial.println(freq); 644 | } 645 | 646 | 647 | void ISRbeepTicker(void *pArg) { 648 | 649 | switch (beeperStatus) { 650 | case beeperIdle: 651 | beepOnTimer = beepOnTime; 652 | beepOffTimer = beepOffTime; 653 | 654 | // exit 655 | if (beepOnTime > 0) beeperStatus = beeperOn; 656 | break; 657 | 658 | case beeperOff: 659 | digitalWrite(BEEPPIN, LOW); // always off 660 | beepOffTimer--; 661 | // exit 662 | if (beepOffTimer <= 0) { 663 | beeperStatus = beeperIdle; 664 | } 665 | break; 666 | 667 | case beeperOn: 668 | if (beepOffTimer > 0) beepOnTimer--; 669 | digitalWrite(BEEPPIN, HIGH); 670 | 671 | // exit 672 | if (beepOnTimer <= 0) { 673 | beeperStatus = beeperOff; 674 | } 675 | break; 676 | 677 | default: 678 | break; 679 | } 680 | } 681 | 682 | 683 | 684 | //------------------- OTA --------------------------------------- 685 | void otaInit() { 686 | 687 | led(red); 688 | 689 | for (int i = 0; i < 3; i++) beep(3); 690 | WiFi.mode(WIFI_AP); 691 | ConfigureWifi(); 692 | MDNS.begin(host); 693 | MDNS.addService("arduino", "tcp", aport); 694 | OTA.begin(aport); 695 | TelnetServer.begin(); 696 | TelnetServer.setNoDelay(true); 697 | Serial.print("IP address: "); 698 | led(green); 699 | Serial.println(WiFi.localIP()); 700 | Serial.println("OTA settings applied"); 701 | } 702 | 703 | void otaReceive() { 704 | if (OTA.parsePacket()) { 705 | IPAddress remote = OTA.remoteIP(); 706 | int cmd = OTA.parseInt(); 707 | int port = OTA.parseInt(); 708 | int size = OTA.parseInt(); 709 | 710 | Serial.print("Update Start: ip:"); 711 | Serial.print(remote); 712 | Serial.printf(", port:%d, size:%d\n", port, size); 713 | uint32_t startTime = millis(); 714 | 715 | WiFiUDP::stopAll(); 716 | 717 | if (!Update.begin(size)) { 718 | Serial.println("Update Begin Error"); 719 | return; 720 | } 721 | 722 | WiFiClient client; 723 | if (client.connect(remote, port)) { 724 | 725 | uint32_t written; 726 | while (!Update.isFinished()) { 727 | written = Update.write(client); 728 | if (written > 0) client.print(written, DEC); 729 | } 730 | Serial.setDebugOutput(false); 731 | 732 | if (Update.end()) { 733 | client.println("OK"); 734 | Serial.printf("Update Success: %u\nRebooting...\n", millis() - startTime); 735 | ESP.restart(); 736 | } else { 737 | Update.printError(client); 738 | Update.printError(Serial); 739 | } 740 | } else { 741 | Serial.printf("Connect Failed: %u\n", millis() - startTime); 742 | } 743 | } 744 | //IDE Monitor (connected to Serial) 745 | if (TelnetServer.hasClient()) { 746 | if (!Telnet || !Telnet.connected()) { 747 | if (Telnet) Telnet.stop(); 748 | Telnet = TelnetServer.available(); 749 | } else { 750 | WiFiClient toKill = TelnetServer.available(); 751 | toKill.stop(); 752 | } 753 | } 754 | if (Telnet && Telnet.connected() && Telnet.available()) { 755 | while (Telnet.available()) 756 | Serial.write(Telnet.read()); 757 | } 758 | if (Serial.available()) { 759 | size_t len = Serial.available(); 760 | uint8_t * sbuf = (uint8_t *)malloc(len); 761 | Serial.readBytes(sbuf, len); 762 | if (Telnet && Telnet.connected()) { 763 | Telnet.write((uint8_t *)sbuf, len); 764 | yield(); 765 | } 766 | free(sbuf); 767 | } 768 | } 769 | 770 | 771 | --------------------------------------------------------------------------------