I received my first ESP32s on Tuesday; started working through Rui’s tutorial, and now, Friday, I’d like to show how I’ve progressed in moving a PICAXE BASIC web application to the ESP32. (NB: I programmed a lot in C, but that was 30+ years ago.)
This was for control of a water system with two saline wells, a holding tank, a reverse osmosis module to remove salt, a drinkable holding tank, and various pumps and sensors.
The base HTML code is in the form of a template stored in a 24LC64 eeprom (4144 bytes written). It contains tags for variables to be inserted in the code, in the form of ~##, e.g., ~01 through ~99. When a client request comes in, the program scans through the html code and replaces ~## with the appropriate value (something read from a sensor). In this program, those are just fixed values–I haven’t connected to the sensors and fed in their values. I also haven’t implemented the inputs from the web page.
Whenever the program connects to the router, it uses sntp to get the current time (in Nova Scotia). That is displayed on the web page.
Issues: usually have to press the “Enable” button to get the eeprom to be read properly.
Many thanks to Rui for the tutorial, which gave me just what I needed to port this web server to the ESP32.
// webwater controls a water delivery system with 2 saline wells, // 2 saline holding tanks, 2 reverse osmosis filters, a drinkable holding tank, // and various sensors and pumps // Load Wi-Fi library #include #include #include #include #include "apps/sntp/sntp.h" // Replace with your network credentials const char* ssid = "mySSID; const char* password = "myPW"; // Set web server port number to 80 WiFiServer server(80); // Variable to store the HTTP request String header; static void obtain_time(void); static void initialize_sntp(void); // Auxiliar variables to store the current output state String output26State = "off"; String output27State = "off"; // Assign output variables to GPIO pins const int output26 = 26; const int output27 = 27; char templt[5000]; // array to hold web page template time_t now; struct tm timeinfo; char strftime_buf[64]; int iVarval[100]; void setup() { Serial.begin(115200); Wire.begin(); delay(1000); eepromGet(); // read web page template from eeprom delay(1000); // Initialize the output variables as outputs /* pinMode(output26, OUTPUT); pinMode(output27, OUTPUT); // Set outputs to LOW digitalWrite(output26, LOW); digitalWrite(output27, LOW); */ // Connect to Wi-Fi network with SSID and password Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } // Print local IP address and start web server Serial.println(""); Serial.println("WiFi connected."); Serial.println("IP address: "); Serial.println(WiFi.localIP()); delay(1000); setup_time(); delay(1000); server.begin(); // set initial variable values--liters pumpte iVarval[3]=27;iVarval[4]=31;iVarval[5]=62;iVarval[6]=407;iVarval[7]=27;iVarval[8]=57;iVarval[9]=388;iVarval[10]=25;iVarval[11]=48;iVarval[12]=320; iVarval[13]=6;iVarval[14]=14;iVarval[15]=87; iVarval[16]=117;iVarval[17]=144;iVarval[18]=111;iVarval[19]=108; // salinity iVarval[20]=999;iVarval[21]=998;iVarval[22]=999;iVarval[23]=999; // water level: 999="OK", 998="Low" iVarval[24]=997;iVarval[25]=996;iVarval[26]=997;iVarval[27]=997;iVarval[28]=997; // pump: 997--off="blue", 996--on="green" } void loop(){ int i, k, iLimit, iVarndx, iVal; char ch; iLimit = 4144; // max characters in template WiFiClient client = server.available(); // Listen for incoming clients if (client) { // If a new client connects, Serial.println("New Client."); // print a message out in the serial port String currentLine = ""; // make a String to hold incoming data from the client while (client.connected()) { // loop while the client's connected if (client.available()) { // if there's bytes to read from the client, char c = client.read(); // read a byte, then Serial.write(c); // print it out the serial monitor header += c; if (c == '\n') { // if the byte is a newline character // if the current line is blank, you got two newline characters in a row. // that's the end of the client HTTP request, so send a response: if (currentLine.length() == 0) { Serial.printf("Header: %s",header); // checking if header is valid // dXNlcjpwYXNz = 'user:pass' (user:pass) base64 encode // Finding the right credential string, then loads web page // if(header.indexOf("bGl6Ynk6bTNkd2F5NDIx") >= 0) { if(true) { // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) // and a content-type so the client knows what's coming, then a blank line: client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println("Connection: close"); client.println(); /* // turns the GPIOs on and off if (header.indexOf("GET /26/on") >= 0) { Serial.println("GPIO 26 on"); output26State = "on"; digitalWrite(output26, HIGH); } else if (header.indexOf("GET /26/off") >= 0) { Serial.println("GPIO 26 off"); output26State = "off"; digitalWrite(output26, LOW); } else if (header.indexOf("GET /27/on") >= 0) { Serial.println("GPIO 27 on"); output27State = "on"; digitalWrite(output27, HIGH); } else if (header.indexOf("GET /27/off") >= 0) { Serial.println("GPIO 27 off"); output27State = "off"; digitalWrite(output27, LOW); } */ // Display the HTML web page client.println(""); i = 0; ch = templt[i++]; do { if (ch >= ' ' && ch <= '~') { // is printable if (ch == '~') { // token needing replacement ch = templt[i++]; iVarndx = ((int)ch-(int)'0') * 10 + (int)templt[i++]-(int)'0'; // Serial.printf("%d \n",iVarndx); switch(iVarndx) { case 1: // date time(&now); localtime_r(&now, &timeinfo); strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); client.printf("%s", strftime_buf); break; case 2: // time--included in "date" above break; case 29: case 30: case 31: // chart--not implemented delay(1); // dummy command break; default: iVal = iVarval[iVarndx]; if (iVal == 999) { client.printf("OK"); } else if (iVal == 998) { client.printf("Low"); } else if (iVal == 997) { client.printf("\"yellow\""); } else if (iVal == 996) { client.printf("\"green\""); } else if (iVal == -1) { // do nothing } else { client.printf("%d",iVal); // print the value } } } else { client.printf("%c",ch); Serial.printf("%c",ch); } } ch = templt[i++]; } while (ch != 0xFE && i < iLimit); // The HTTP response ends with another blank line client.println(); // Break out of the while loop break; } // Wrong user or password, so HTTP request fails... else { client.println("HTTP/1.1 401 Unauthorized"); client.println("WWW-Authenticate: Basic realm=\"Secure\""); client.println("Content-Type: text/html"); client.println(); client.println("Authentication failed"); } } else { // if you got a newline, then clear currentLine currentLine = ""; } } else if (c != '\r') { // if you got anything else but a carriage return character, currentLine += c; // add it to the end of the currentLine } } } // Clear the header variable header = ""; // Close the connection client.stop(); Serial.println("Client disconnected."); Serial.println(""); } } void eepromGet(){ int iCount, iCh, i; char ch, *c, *s; // char templt[5000]; // array to hold web page template iCount = 0; iCh = 0; Wire.beginTransmission(0x50); Wire.write(0); // start at address 0 MSB Wire.write(0); // start at address 0 LSB Serial.println(); do { int err = Wire.requestFrom(0x50,128,false); // request # bytes ("true" releases bus) if(err != 128){ // Serial.printf(" requestFrom Failed, expected 128 bytes, received =%d\nError %d (%s)\n",err,Wire.lastError()),Wire.getErrorText(Wire.lastError))); Serial.printf(" requestFrom Failed, expected 128 bytes, received =%d\n",err); } else { // display it while(Wire.available()){ // Serial.printf("%c",Wire.read()); ch = Wire.read(); templt[iCh++] = ch; } // Serial.printf("%d ",iCount); // Serial.println(); } iCount+= 128; } while (iCount < 4144 && ch != 0xFE); templt[iCh] = 0; // terminate string Wire.requestFrom(0x50,1,true); // request # bytes ("true" releases bus) s = &templt[0]; // get address of string c = strstr(s, "content="); if(c != NULL) { i = c - s + 9; // index into template templt[i++]='9'; templt[i++]='9'; templt[i++]='9'; templt[i++]='"'; // replace refresh time of "15" with "999" } i = 0; ch = templt[i++]; do { Serial.printf("%c",ch); ch = templt[i++]; } while (ch != 0xFE && i < iCh); Wire.endTransmission(true); } static void setup_time(void) { // char strftime_buf[64]; obtain_time(); // update 'now' variable with current time time(&now); // Set timezone to Atlantic Time and print local time setenv("TZ", "AST4ADT,M3.2.0/2,M11.1.0", 1); tzset(); localtime_r(&now, &timeinfo); strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); Serial.printf("The current date/time in Halifax is: %s", strftime_buf); } static void obtain_time(void) { // xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); initialize_sntp(); // wait for time to be set time_t now = 0; struct tm timeinfo = { 0 }; int retry = 0; const int retry_count = 10; while(timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count) { ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count); vTaskDelay(2000 / portTICK_PERIOD_MS); time(&now); localtime_r(&now, &timeinfo); } } static void initialize_sntp(void) { ESP_LOGI(TAG, "Initializing SNTP"); sntp_setoperatingmode(SNTP_OPMODE_POLL); sntp_setservername(0, "pool.ntp.org"); sntp_init(); }
EDIT: Figured out how I had enabled the chart, so added that. Also, I was getting “Brownout” resets; adding a 3300uF capacitor across the power runs on my breadboard fixed that.
Here is the html template code which is stored in the eeprom (the chart in the code is not implemented):
… hmmm. I can’t figure out how to insert the html code literally without its being interpreted.
Hi Lance,
Thanks for sharing your progress and I’m glad you were able to modify/make your project work (I’ll be posting a new tutorial this week on the RNT blog about requesting the current time from an NTP server).
Tip: If your HTML page starts to get too long and you find it difficult to write/edit HTML, you might consider following the approach described in this project: ESP32 Weather Station.
Basically in that project, we store all the HTML formatting in a MicroSD card and when a client requests a web page we load it through the microSD card.
P.S. To avoid problems inserting code, you can post it to https://pastebin.com/ and simply post a link on this Forum.
Thanks. I’m looking at the ESP32 Weather Station blog to see about hooking up a microSD card to my 32s module. It apparently has pins defined (or suggested) for SD–GPIO6-11, marked in green in the diagram. I assume the pin definition is in sd.h. Where is that located?
To Answer my own question, the SD pin definitions are not defined in SD.h, but are apparently defined for the 32s module. They are the same as in the Weather Station SD wiring. The green pins in the image above are for the flash (as marked)–trying to put an SD card on them causes the module to fail to boot.
Correctly wired, SD_Test.ino worked successfully. The wiring for the NodeMCU-32S SD is shown at https://github.com/espressif/arduino-esp32/tree/master/libraries/SD: