├── 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 |
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 | | SSID : | |
17 | | IP : | |
18 | | Netmask : | |
19 | | Gateway : | |
20 | | Mac : | |
21 |
22 |
23 | | Refresh |
24 |
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 |
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 |
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 |
23 |
24 | Connection State:N/A
25 |
26 | Networks:
27 |
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 += "| Name | Quality | Enc |
";
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 += "
| " + String(WiFi.SSID(i)) + " | " + String(quality) + "% | " + String((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":"*") + " |
";
193 | }
194 | Networks += "
";
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 |
--------------------------------------------------------------------------------