/** * Temperature monitor for DS18B20 (Dallas) or similar one (like DS18S20) * supported 1 until 4 sonsors (more is possible by changing code-lines) * target: ESP8266, especially Kit-8 type * written by Peter Rebesky, February 2023 * Copyright (©): 2023 by Peter Rebesky * This code can use in private cases only. Every business or companies using of this codes or codes parts is required an approval of us (me) * Every private using can exchange some parts of code or modify some code-lines. This code is allowed change for private use only. * This software is basicly owned by Peter Rebesky and any comercial using is forbidden without approval of us (me). * Using for connect WIFI by WiFiManager (tzapu) https://github.com/tzapu/WiFiManager * and OLED driver written by http://stefanfrings.de/ */ #include #include #include //ESP8266 Core WiFi Library #include //ESP8266 Core WiFi Library #include #include //Local WebServer #include #include #include // https://github.com/tzapu/WiFiManager #include #include #include #include #include #include "global.h" #include "configFS.h" #include "WebClient.h" #ifdef OLED_CONNECT #include // OLED-Display driver, http://stefanfrings.de OLED display(Wire, 0x3C, 16, 128, 32); #endif WiFiManager wm; // global wm instance WiFiManagerParameter custom_field; // global param ( for non blocking w params ) configFS Config; OneWire oneWire(_oneWireBus); DallasTemperature sensors(&oneWire); WiFiServer server(80); ESP8266WiFiMulti WiFiMulti; // Define NTP Client to get time WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "pool.ntp.org", _UTCOFFSET); //*********************************************************** bool setupWIFI() { WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP delay(1000); Serial.println("\n Starting"); pinMode(_BoardTaster, INPUT); // add a custom input field int customFieldLength = 40; const char* custom_radio_str = "
One
Two
Three"; new (&custom_field) WiFiManagerParameter(custom_radio_str); // custom html input wm.addParameter(&custom_field); wm.setSaveParamsCallback(saveParamCallback); // custom menu via array or vector std::vector menu = {"wifi","info","param","sep","restart","exit"}; wm.setMenu(menu); // set dark theme wm.setClass("invert"); wm.setConfigPortalTimeout(180); // auto close configportal after n seconds checkButton(); // if pressed then reset WIFI config to start new change ssid bool res; res = wm.autoConnect("Temp_WIFI_Setup"); // anonymous ap, first parameter is name of access point if(!res) { Serial.println("Failed to connect or hit timeout"); return false; } else { //if you get here you have connected to the WiFi Serial.println("Connected to saved ssid successfully"); return true; } } //*********************************************************** void checkButton(){ // check for button press if ( digitalRead(_BoardTaster) == LOW ) { // poor mans debounce/press-hold delay(50); if( digitalRead(_BoardTaster) == LOW ){ #ifdef OLED_CONNECT buttonHoldOLED(); #endif Serial.println("Button Pressed"); // still holding button for 3000 ms, reset settings, code is not ideal delay(5000); // reset delay hold if( digitalRead(_BoardTaster) == LOW ){ // Serial.println("Button Held"); // Serial.println("Erasing config now"); // Serial.println("Restarting now!"); wm.resetSettings(); delay(4000); ESP.restart(); } } } } //*********************************************************** String getParam(String name){ //read parameter from server, for customhmtl input String value; if(wm.server->hasArg(name)) { value = wm.server->arg(name); } return value; } //*********************************************************** void saveParamCallback(){ Serial.println("[CALLBACK] saveParamCallback fired"); Serial.println("PARAM customfieldid = " + getParam("customfieldid")); } //*********************************************************** //*************************** function for get time & date as timestamp ******************************** bool syncron_NTP_Time(bool correctOn) { uint32_t timestamp=0; timeClient.begin(); if(timeClient.update()) { timestamp = timeClient.getEpochTime(); // get as GMT if(timestamp !=0){ timestamp += (_MESZ * _UTCOFFSET); // means add 1 hour if(correctOn == true) { _secAdjust +=clockAdjust(timestamp-_timestamp); // negative difference clock is running too fast / positive difference clock is running too slow } _timestamp = timestamp; // syncron timestamp return true; } } return false; } //********* adjust intern clock if time is runnung false ****************// int clockAdjust(int timeDifference){ int adjust=0; if(timeDifference > 60 && _secAdjust > 940){ // positive difference clock is running too slow // if(timeDifference <= 60) adjust--; adjust = timeDifference / -60; } else if(timeDifference < -60 && _secAdjust < 999){ // negative difference clock is running too fast // if(timeDifference >= -60) adjust++; adjust = timeDifference / -60; } if(_secAdjust > 1000){ // if time-correctur get wrong then check if time higher than 1000ms (1s) _secAdjust = 1000; adjust = 0; } return adjust; } //*************************** create a string from values for data and time ******************************** String getTime2String(int hour,int minute,int second){ String act_time; if(hour < 10) act_time += "0"; // if value < 10 then show a zero before act_time += String(hour); //combine string for time act_time += ":"; if(minute < 10) act_time += "0"; act_time += String(minute); act_time += ":"; if(second < 10) act_time += "0"; act_time += String(second); return act_time; } String getDate2String(int year,int month,int day){ String act_date; if (day < 10) act_date += "0"; // if value < 10 then show a zero before act_date += String(day); //combine string for time act_date += "."; if(month < 10) act_date += "0"; act_date += String(month); act_date += "."; act_date += String(year); return act_date; } //**************************** intern clock ******************************* bool internClock(uint32_t currentMillis) { if (currentMillis < _secondMillis) _secondMillis = 0; // handle if currentMillis are overflows, max 32 bits if (currentMillis - _secondMillis >=_secAdjust){ _secondMillis = currentMillis; _act_date_time[_SECOND]++; _timestamp++; if (_act_date_time[_SECOND] >=60){ _act_date_time[_SECOND]=0, _act_date_time[_MINUTE]++;} if (_act_date_time[_MINUTE] >=60){ _act_date_time[_MINUTE]=0, _act_date_time[_HOUR]++;} if (_act_date_time[_HOUR] >=24){ _act_date_time[_HOUR]=0; _act_date_time[_DAY]++;} return true; } else return false; } //*********************************************************** void check4Client() { // Check if a client has connected // String line; if (WiFi.status() == WL_CONNECTED) { WiFiClient client = server.available(); if (!client) { return; } // Wait until the client sends some data // Serial.println("new client"); unsigned long timeout = millis() + 3000; while (!client.available() && millis() < timeout) { delay(1); } if (millis() > timeout) { // Serial.println("timeout"); client.flush(); client.stop(); return; } // Read the first line of the reply from server and print it to Serial and copy it into variable "_argument" if(client.available()){ // String line; String line = client.readStringUntil('\n'); int argBegin = line.indexOf("/"); int argEnd = line.indexOf("HTTP"); int argEQ = line.indexOf("?"); if (argEQ > 0 && argEQ < argEnd) { _argument = line.substring(argBegin+1, argEQ); } else _argument = line.substring(argBegin+1, argEnd-1); // Serial.println(_argument); if (argEQ > 0 && argEQ < argEnd) { _argumentValue = line.substring(argEQ+1, argEnd-1); } else _argumentValue = "none"; // Serial.println(_argumentValue); } // Match the request client.flush(); // Prepare the response String s; if (_argument =="refresh"){ s += getRefreshJSON(); } else if(_argument =="firmware"){ s = HTTP_ANSWER; s += "Your versions number: "+Version+"
"; s += "Check for new firmware now.
"; s += getFilesFromServer(WiFi.localIP()); s += HTTP_TEXT_END; } else if(_argument == "upDate"){ s = HTTP_ANSWER; s += "Update will be start now.
"; s += "Update-file: "+_argumentValue+"
"; s += "Don't turn off power during update!

"; s += "After update, the reconnection only via IP, without arguments behind it!

"; s += "Click here for reconnect after update
"; s += HTTP_TEXT_END; } else if (_argument == "showsetup") { s+=webShowSetup(); } else if(_argument == "setup"){ s += webSetup(); } else if(_argument == "save"){ String postValues = client.readString(); int postBegin = postValues.indexOf("d_data"); // search of the first value for eleminate the header! "d_data" must be the first value of post-data! s+=webSave(postValues.substring(postBegin)); // deliver the short string of post values to save } else if(_argument == "setTime"){ s+=webSetTime(); } else if(_argument =="getJSON"){ s+=valuesAsJSON(); } else { s+=webMain(_transmitResult); } // Send the response to the client client.print(s); delay(1); if(_argument == "upDate"){ delay(1000); //wait one second updateStromLog(_argumentValue); // start update now } // The client will actually be disconnected // when the function returns and 'client' object is detroyed } } //*********** update new software for ESP ***************************/ //*********** get available files ****************************************/ String getFilesFromServer(IPAddress ip){ String files = ""; if (WiFiMulti.run() == WL_CONNECTED) { WiFiClient client; HTTPClient http; String target ="http://"; target += Config.getTargetServer(); target += "/firmware/getFiles.php"; if (http.begin(client,target)){ http.addHeader("Content-Type","application/json;charset=utf-8"); int httpCode = http.POST("{\"userIP\":\""+ip.toString()+"\",\"build\":\""+Version+"\"}"); if(httpCode >= 100){ files = http.getString(); } else files = "Error: no connection."; } http.end(); } return files; } //********** update, input file-name ************************************/ String updateStromLog(String file){ String result = "ERROR: Update failed!"; #ifdef OLED_CONNECT showUpdateOLED(); #endif ESPhttpUpdate.rebootOnUpdate(false); // remove automatic update // digitalWrite(LED,ON); String target = Config.getTargetServer(); WiFiClient client; t_httpUpdate_return ret = ESPhttpUpdate.update(client, target, 80, "/firmware/"+file); switch (ret) { case HTTP_UPDATE_FAILED: // Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); break; case HTTP_UPDATE_NO_UPDATES: break; case HTTP_UPDATE_OK: String result = "Update successful."; delay(1000); // Wait a second and restart ESP.restart(); break; } return result; } //*********** update end *********************************// //**** get temperature from sensor DS18B20 ***************// void getValuesTemperature() { sensors.requestTemperatures(); _temperature1 = sensors.getTempCByIndex(0); if (_numberOfSensors > 1)_temperature2 = sensors.getTempCByIndex(1); if (_numberOfSensors > 2)_temperature3 = sensors.getTempCByIndex(2); if (_numberOfSensors > 3)_temperature4 = sensors.getTempCByIndex(3); } /*********************************************************** * convert a unix-timestamp into single bytes as value * function works up to 1972 until 19.01.2038 ***********************************************************/ void Timestamp2DateTime(uint32_t unixTime){ int dummy = 0; int years=0; uint32_t SecOfYear; uint32_t timeDummy; bool leapYear = false; // default: don't be a leap-year timeDummy = unixTime - 31536000 *2; // sub two years (1970 and 1971) add the years later years= (timeDummy / 126230400) *4; // get the periodes of four years cycle; 3Y+leapY = 126230400 seconds timeDummy = timeDummy % 126230400; // get the rest of timestamp without the cycles like above this line if (timeDummy >= 31622400) { timeDummy -= 31622400; // sub the first leap-year if it's over years++; // add the leapyear years += timeDummy / 31536000; // get the rest of years between 0 until 2 years } years = years + 1972; // add start-year +2: now, we know the actual year and can figure out if is a leap-year _date_time[_YEAR]=years; // if (year % 4 == 0) leapYear = true; // if year divison by four without some rest, then is a leap-year...yeeeh -> it works until 2036 only if ( bitRead(years,0)==0 && bitRead(years,1)==0) { // it's very faster than %-arithmetic leapYear = true; SecOfYear = 31622400; // set the counts of seconds leap-year // Serial.print("LeapYear "); } else SecOfYear = 31536000; // or don't be a leap-year -> regulary seconds _date_time[_DAY] = (timeDummy % SecOfYear) / 86400; // get the days of actual year _date_time[_MONTH] = 0; // set zero for (int i=0; _date_time[_DAY]>=0; i++ ) { // do it until the day(s) are negativ, this loop has be exicute one time -> to set month at ones dummy = _date_time[_DAY]; // store the value befor result is negativ for break, because the last one was the day of actual month _date_time[_DAY] = _date_time[_DAY] - DaysPerMonth[i]; // sub the days of month until it's negativ, according byte-array if (i == 1 && leapYear == true) {_date_time[_DAY] -= 1;} // sub the 29. of february on leapyears ;-) _date_time[_MONTH]++; // add one month on every loop if (i > 11) return; // if this function wild ;-) then break (max. months are 0 - 11) } _date_time[_DAY] = dummy+1; // add one, because the start was on 1. january (start of timstamp = 01.01.1970 and we have to add it) /****** get time ******/ _date_time[_HOUR] = (timeDummy % 86400) / 3600; // the value of _timeDummy contents the rest of hours, minutes and seconds only _date_time[_MINUTE] = (timeDummy % 3600) / 60; // it's shorter to take reduced _timeDummy than the whole timestamp _date_time[_SECOND] = timeDummy % 60; // return values are in array "unsigned int _date_time[6]; //[_YEAR][_MONTH][_DAY][_HOUR][_MINUTE][_SECOND]" } void writeTime(){ _act_date_time[_YEAR] = _date_time[_YEAR]; _act_date_time[_MONTH] = _date_time[_MONTH]; _act_date_time[_DAY] = _date_time[_DAY]; _act_date_time[_HOUR] = _date_time[_HOUR]; _act_date_time[_MINUTE] = _date_time[_MINUTE]; _act_date_time[_SECOND] = _date_time[_SECOND]; } void getTime2Value(){ Timestamp2DateTime(_timestamp); writeTime(); } //*********************************************************** bool send2dbase(String content){ // Serial.println(t1); Serial.println(hum); Serial.println(hPa); Serial.println(heaterOnOff); bool r=false; if (WiFiMulti.run() == WL_CONNECTED) { WiFiClientSecure clientSSL; WiFiClient client; HTTPClient http; bool connect; String getTarget = Config.getTargetURLclean(); String target; if(Config.SendSSL>0){ target = "https://"; target += getTarget; int httpsPort=443; clientSSL.setInsecure(); // magic line without check certificate because memory insufficient clientSSL.connect(target,httpsPort); // set connection by https connect = http.begin(clientSSL,target); }else{ target= "http://"; target += getTarget; connect = http.begin(client,target); } if(connect){ // http.setTimeout(10000); // 10 seconds? if(Config.sendJSON) http.addHeader("Content-Type","application/json;charset=utf-8"); else http.addHeader("Content-Type","application/x-www-form-urlencoded"); int httpCode = 0; _transmitResult = ""; httpCode = http.POST(content); _httpMarker = httpCode; //debug if (httpCode >= 100) { if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_NO_CONTENT){ _transmitResult = http.getString(); if (_transmitResult == "OK
" || httpCode == HTTP_CODE_NO_CONTENT){ _transmitResult = "Werte wurden gesendet: "+getTime2String(_act_date_time[_HOUR],_act_date_time[_MINUTE],_act_date_time[_SECOND]); r = true; // if data saved successful than result=true else false } else { r = false; } } else {_transmitResult ="[HTTP] Unable to connect! "; _transmitResult +=httpCode;} } else {_transmitResult ="[HTTP] missing return-code!"; r = false;} } else {_transmitResult ="[HTTP] Service not connect!"; r = false;} http.end(); } return r; } //**** prepaire send string *******************// String prepaire2Send(bool type){ String ret=""; if(type){ ret=valuesAsJSON(); } else { ret ="&time="; ret += _timestamp; ret+="&name="; ret += Config.getName(); ret+="&usip="; ret += WiFi.localIP().toString(); ret+="&name1="; ret += Config.getSensorName(1); ret+="&temp1="; ret += int(_temperature1*1000); if(_numberOfSensors > 1){ ret+="&name2="; ret += Config.getSensorName(2); ret+="&temp2="; ret += int(_temperature2*1000);} if(_numberOfSensors > 2){ ret+="&name3="; ret += Config.getSensorName(3); ret+="&temp3="; ret += int(_temperature3*1000);} if(_numberOfSensors > 3){ ret+="&name4="; ret += Config.getSensorName(4); ret+="&temp4="; ret += int(_temperature4*1000);} } return ret; } //**** collect values as JSON *****************// String valuesAsJSON(){ String ret=""; ret ="{\"time\":"; ret += _timestamp; ret += ","; ret+="\"name\":\""; ret += Config.getName(); ret+="\","; ret+="\"usip\":\""; ret += WiFi.localIP().toString(); ret+="\","; ret+="\"name1\":\""; ret += Config.getSensorName(1); ret+="\","; ret+="\"temp1\":"; ret += int(_temperature1*1000); ret+=","; if(_numberOfSensors > 1){ ret+="\"name2\":\""; ret += Config.getSensorName(2); ret+="\","; ret+="\"temp2\":"; ret += int(_temperature2*1000); ret+=",";} if(_numberOfSensors > 2){ ret+="\"name3\":\""; ret += Config.getSensorName(3); ret+="\","; ret+="\"temp3\":"; ret += int(_temperature3*1000); ret+=",";} if(_numberOfSensors > 3){ ret+="\"name4\":\""; ret += Config.getSensorName(4); ret+="\","; ret+="\"temp4\":"; ret += int(_temperature4*1000); ret+=",";} ret+="\"end\":0}"; return ret; } #ifdef OLED_CONNECT //**** if OLED connected, shoe values *****// void ShowValuesOnOLED(){ char temp[20]; String s; switch (_showTemperature){ case 0: s= getTime2String(_act_date_time[_HOUR],_act_date_time[_MINUTE],_act_date_time[_SECOND]); break; case 1: s= "1: "; s += String(_temperature1); s += "°C"; break; case 2: s= "2: "; s += String(_temperature2); s += "°C"; break; case 3: s= "3: "; s += String(_temperature3); s += "°C"; break; case 4: s= "4: "; s += String(_temperature4); s += "°C"; break; } if(_showTemperature >= _numberOfSensors)_showTemperature=0; else _showTemperature++; int oledX = random(0,64); int oledY = random(0,24); s.toCharArray(temp,s.length()+1); display.draw_string(oledX,oledY,temp); display.display(); } void buttonHoldOLED(){ display.clear(); display.draw_string_P(12,16,PSTR("Button is pressed!")); display.display(); } void showUpdateOLED(){ display.clear(); display.draw_string_P(0,16,PSTR("Update is in progress!")); display.display(); } //**** intial OLED display *****// void initOLED(){ Wire.begin(); //init I2C display.init(); // initialize OLED-Display display.set_contrast(64); impressumOLED(); display.draw_string_P(10,24,PSTR("Try to connect WLAN")); display.display(); // Serial.println("Init OLED successful"); } void impressumOLED(){ display.draw_string_P(30,2,_impressum); display.draw_string_P(30,12,_typeVersion); } void connectWlanOLED(){ display.clear(); impressumOLED(); display.draw_string_P(10,24,PSTR("WLAN is connected")); display.display(); } //**** show valueson OLED *****/ void displayValuesOnOLED(){ if(_oledDisplayWait > _OLED_TIME_WAIT){ display.clear(); ShowValuesOnOLED(); } else { if(_oledDisplayWait ==0 ){display.clear(); impressumOLED();} _oledDisplayWait++; display.draw_string(10,24,"Start:"); display.draw_string(40+(5*_oledDisplayWait),22,"."); display.display(); } // Serial.println(_oledDisplayWait); } #endif //*********************************************************** void setup() { pinMode(_BoardTaster, INPUT_PULLUP); // set IO0 as input #ifdef OLED_CONNECT initOLED(); #endif Serial.begin(115200); // Serial.setDebugOutput(true); Serial.println("Try to connect WIFI"); wifi_station_set_hostname(wiFiHostname); bool result = setupWIFI(); if (result) { Serial.print("connected to ssid: "); Serial.print(WiFi.SSID()); Serial.print(" with IP:"); Serial.println(WiFi.localIP()); } else { Serial.println("Failed to connect!"); Serial.println("Please take a reset,"); Serial.println("or hold the button for 6s!"); delay(3000); checkButton(); ESP.restart(); } #ifdef OLED_CONNECT connectWlanOLED(); #endif sensors.begin(); Serial.println("Start Server now"); server.begin(); Config.begin(); syncron_NTP_Time(false); getTime2Value(); _numberOfSensors = sensors.getDeviceCount(); // get numbers of connected sensors delay(500); getValuesTemperature(); // get the first values from DS18B20 } //*********************************************************** void loop() { yield(); uint32_t _currentMillis = millis(); checkButton(); // set new SSID (WLAN Setup) if(internClock(_currentMillis)== true) { // every second will run this component if (_act_date_time[_HOUR]==23 && _act_date_time[_MINUTE]==5 && _act_date_time[_SECOND]==10) { // update timestamp every new day at the 30th second if (syncron_NTP_Time(true) == true) getTime2Value(); else if(!WiFi.isConnected()) ESP.restart(); // check if connected yet } if (_timestamp < _TimeOf2021 && _act_date_time[_SECOND] == 50) if (syncron_NTP_Time(false) == true) getTime2Value(); // get timestamp again if sync was failure, perhaps after power down if (_act_date_time[_SECOND] == 1) { // is called every minute at the first second if (_act_date_time[_MINUTE] == 0 || _act_date_time[_MINUTE] == 15 || _act_date_time[_MINUTE] == 30 || _act_date_time[_MINUTE] == 45) { if (Config.sendPUSH){ send2dbase(prepaire2Send(Config.sendJSON)); } else {_transmitResult="Automatisches Senden ist aus.
Pull-Mode";} } } #ifdef OLED_CONNECT displayValuesOnOLED(); // show values on OLED #endif if(_act_date_time[_SECOND] % 5 ==0)getValuesTemperature(); // get data from sensor every 5 seconds } check4Client(); // thats is if with the browser show data on port 80 by html }