Websocket reconnect after sleep mode

I'm working on a greenhouse monitor, currently I have a BME280 module connected to a ESP32 Dev board, this unit will be battery powered. In the house I have another ESP32 Dev board with a 3.5" TFT display. I connect both to my house WiFi router.

I have the monitor connect to the internet to get NTP time as well as I can send the BME values to ThinkSpeak. In order to keep that connection I used a websocket to also send the values to the base unit. This is my first websocket project, I found a good tutorial with the codes on YouTube, where else? The monitor is set as the server and the base the client. Although I would think that the base should be the server so I could have multiple monitors, clients, sending values to the base but that's another conversation.

Using the examples, I have gotten it all to work fine with one problem. To save battery life I put the monitor to sleep, so I lose the websocket connection. I have seen online people suggest heartbeats and adding java script to reconnect, that's beyond my knowledge, I have not done any Java script but something I obviously need to learn.

I came up with a crude and not very efficient way to reconnect. The monitor goes to sleep for 5 minutes, will be longer when I get this working, I wake it up for 1 minute. In the base's loop function I added a timer to keep trying to reconnect every 30 seconds. That works although after an hour or so the base gets hung up, again not the best way to do this. It does show me that is what I need to do, detect that I am no longer connected then try to reconnect.

Looking at the code below at the beginning of the loop function is my timer, I have commented out the if statement I was trying different parameters in websocket libraries, I'm also not very good at determining what I can use in the libraries.

So that's the question, what is the best way to reconnect to the server after it has gone to sleep and then woke back up?

Thanks for all comments and suggestions
John

Here's the code


// Changed controller to ESP32
// Added websocket client

#include <SPI.h>
#include <TFT_eSPI.h> // Hardware-specific library
#include <WebServer.h>
#include <WebSocketsClient.h>
#include <ArduinoJson.h>

TFT_eSPI tft = TFT_eSPI();       // Invoke custom library

//==== Defining Variables

unsigned char text;
//char text[6] = "";
String inTemp, inHum, outTemp = "", outHum;
extern uint8_t SmallFont[];
extern uint8_t BigFont[];
extern uint8_t SevenSegNumFont[];

int x, y;

int currentPage = 0; //, selectedUnit;
int selectedUnit = 0;

char grnHouseTemp[20] = " "; // Greenhouse Temperature number
char grnHouseHum[20] = " "; // Greenhouse Humidity number
long int start_time = millis();
long int read_time = 4800; // time between reading Greenhouse data
int grnHouseRead = 0; // bit that system is reading Greenhouse status
float  grnHouse_Temp = 0; // Greenhouse temp
float  grnHouse_Hum = 0; // Greenhouse humdity
float  grnHouse_Press = 0; // Greenhouse pressure
boolean newData = false;

    // temporary array for use when parsing
const byte numChars = 64;
char receivedChars[numChars];
char tempChars[numChars];   

  // For Greenhouse
char temp_str1[25];
char humd_str1[25];
char prss_str1[25];
char Hour_strl[25];
char Min_strl[25];
char Batt_strl[25];

// Incoming data
struct incomingData {
 float In_Temp;
 float In_Prss;
 float In_Humd;
 int   In_Hour;
 int   In_Min;
 float Batt_Lvl;
}myincomingDataStructure;

// data from Greenhouse
struct dataStruct1 {
 float H1_Temp;
 float H1_Prss;
 float H1_Humd;
 float H1_Batt;
}myDataStructure1;

long int touchTime = 0; // time sceen was last touched
long int scrnSavTime = 60000; // Screen saver display time
int scrnSavOn = 0; //In screen saver mode
const int backLite = 53;

// WiFi
const char* ssid = "mySSID"; // Wifi SSID
const char* password = "MyPassword"; //Wi-FI Password
WebSocketsClient webSocket; // websocket client class instance
StaticJsonDocument<120> doc; // Allocate a static JSON document
long int socket_Time = millis();
long int socket_Delay = 30000;

const String pin_stat = doc["PIN_Status"]; // String variable tha holds LED status
float Gh1_t = 1.1; // Float variable that holds temperature
float Gh1_h = 2.2; // Float variable that holds Humidity
float Gh1_p = 3.3; // Float variable that holds Pressure
float Gh1_b = 4.4; // Float variable that holds Battery Voltage
int Gh1_hr = 5; // integer for current hour
int Gh1_mn = 6; // integer for current minute

void setup() {
  Serial.begin(115200);
 // Serial.println("Start Setup");

// TFT setup

  tft.init();
  tft.fillScreen(TFT_BLACK); //clears screen, sets to Black
  tft.setRotation(1);  // rotates screen 180' for landscape mode

  currentPage = 0; // Indicates that we are at Home Screen
  selectedUnit = 0; // Indicates the selected unit for the first example, cms or inches

   // Draw intial screen
     drawHomeScreen();
   //drawGreenhouse1();  // Draws the Greenhouse 1 Status screen
    
  touchTime = millis(); // start touch time

 // Connect to local WiFi
  WiFi.begin(ssid, password);
  Serial.begin(115200);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP()); // Print local IP address

  //address, port, and URL path 
  webSocket.begin("192.168.1.175", 81, "/"); 
  // webSocket event handler
  webSocket.onEvent(webSocketEvent);
  // if connection failed retry every 5s
  webSocket.setReconnectInterval(5000);

} // end void setup


void loop() {

if(millis() > socket_Time + socket_Delay){
//if(!connectFailedCb()){  
   //address, port, and URL path 
  webSocket.begin("192.168.1.175", 81, "/"); 
  // webSocket event handler
  webSocket.onEvent(webSocketEvent);
  // if connection failed retry every 5s
  webSocket.setReconnectInterval(5000);

  socket_Time = millis();
} // end if(millis() > socket_Time + socket_Delay)
   webSocket.loop(); // Keep the socket alive
  
  // To get raw touch screen coordinates
 /* 
      uint16_t x, y;
      tft.getTouchRaw(&x, &y);
      Serial.printf("x: %i     ", x);
      Serial.printf("y: %i     ", y);
      Serial.printf("z: %i \n", tft.getTouchRawZ());
      delay(250);
*/

   display_cntrl(); // display screen controls


} // end void loop


// drawHomeScreen - Menu page

 // drawHomeScreen() {
void drawHomeScreen() {
  // Draws Home Screen
   tft.fillScreen(TFT_BLACK); //clears screen, sets to Black
 
// Prints the title on the screen
  tft.setCursor(80, 70);
  tft.setTextColor(TFT_WHITE);
  tft.setTextSize(3);
  tft.print("Greenhouse Monitor"); 

  // Draws the red line under the title
  tft.drawFastHLine(60, 100, 350, TFT_RED);


  // Button - Greenhouse page
  
  tft.fillRoundRect(140, 150, 210, 40, 25, TFT_BLUE);
  tft.drawRoundRect(140, 150, 210, 40, 25, TFT_WHITE);
  tft.setCursor(170, 160);
  tft.setTextColor(TFT_WHITE);
  tft.setTextSize(2);
  tft.print("Greenhouse"); 

 } // end void drawHomeScreen

void drawGreenhouse1() {
  // Draws Report Setup screen
  yield();
//  Serial.println("In drawGreenhouse1");
  // Sets the background color of the screen to black
  tft.fillScreen(TFT_BLACK); 

 // Back to Home button
  
  tft.fillRoundRect(30, 20, 50, 30, 10, TFT_BLUE);
  tft.drawRoundRect(30, 20, 50, 30, 10, TFT_WHITE);
  tft.setCursor(40, 27);
  tft.setTextColor(TFT_WHITE);
  tft.setTextSize(2);
  tft.print("<-"); 

  tft.setCursor(100, 30);
  tft.setTextColor(TFT_WHITE);
  tft.setTextSize(1);
  tft.print("Back to Main Menu"); 

  // Prints the title on the screen
  tft.setCursor(80, 70);
  tft.setTextColor(TFT_WHITE);
  tft.setTextSize(3);
  tft.print("Greenhouse Status"); 

  // Draws the red line under the title
  tft.drawFastHLine(90, 100, 320, TFT_RED);

 // Label - Temperature
  
  tft.fillRect(20, 120, 90, 40, TFT_CYAN);
  tft.drawRect(20, 120, 90, 40, TFT_WHITE);
 // tft.fillRect(0, 0, 90, 40, TFT_CYAN);
 // tft.drawRect(5, 3, 90, 40, TFT_WHITE);
  tft.setCursor(32, 130); 
  tft.setTextColor(TFT_BLACK);
  tft.setTextSize(2);
  tft.print("Temp = "); 
  //tft.drawCentreString("Temp = ",120, 70, 4);

 // Label Humidity #
  
  tft.fillRect(20, 190, 90, 40, TFT_CYAN);
  tft.drawRect(20, 190, 90, 40, TFT_WHITE);
  tft.setCursor(35, 200); 
  tft.setTextColor(TFT_BLACK);
  tft.setTextSize(2);
  tft.print("Hum = "); 

 // Label Pressure
  
  tft.fillRect(240, 120, 100, 40, TFT_CYAN);
  tft.drawRect(240, 120, 100, 40, TFT_WHITE);
  tft.setCursor(250, 130); 
  tft.setTextColor(TFT_BLACK);
  tft.setTextSize(2);
  tft.print("Press = "); 

  // Label Update Time
  
  tft.fillRect(240, 190, 100, 40, TFT_CYAN);
  tft.drawRect(240, 190, 100, 40, TFT_WHITE);
  tft.setCursor(250, 200); 
  tft.setTextColor(TFT_BLACK);
  tft.setTextSize(2);
  tft.print("Time = "); 

  // Label Battery Level
  
  tft.fillRect(20, 260, 135, 40, TFT_CYAN);
  tft.drawRect(20, 260, 135, 40, TFT_WHITE);
  tft.setCursor(35, 270); 
  tft.setTextColor(TFT_BLACK);
  tft.setTextSize(2);
  tft.print("Battery = "); 

} // end of drawGreenhouse1

void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
  //Serial.println("webSocketEvent");
  
  if (type == WStype_TEXT)
  {
    DeserializationError error = deserializeJson(doc, payload); // deserialize incoming Json String
    if (error) { // Print erro msg if incomig String is not JSON formated
      Serial.print(F("deserializeJson() failed: "));
      Serial.println(error.c_str());
      return;
    }
    
    const float t = doc["Temp"]; // Float variable that holds temperature
    Gh1_t = t;
    const float h = doc["Hum"]; // Float variable that holds Humidity
    Gh1_h = h;
    const float p = doc["Press"]; // Float variable that holds pressure
    Gh1_p = p;
    const float b = doc["Batt"]; // Float variable that holds battery voltage
    Gh1_b = b;
    const int hr = doc["Hour"]; // Float variable that holds current hour
    Gh1_hr = hr;
    const int mn = doc["Min"]; // Float variable that holds current minute
    Gh1_mn = mn;
    
    // Print the received data for debugging
    //Serial.print(String(pin_stat));
    //Serial.print(String(t));
    //Serial.println(String(h));
    // Send acknowledgement
    // webSocket.sendTXT("OK");
       
  //  Serial.print("LED = ");
  //  Serial.print("\t");
  //  Serial.print(pin_stat);
  //  Serial.print("\t");
    
    Serial.print("Temp = ");
    Serial.print(t);
    Serial.print("\t");

    Serial.print("Pressure = ");
    Serial.print(p);
    Serial.print("\t");
   
    Serial.print("Humidity = ");
    Serial.println(h);

    Serial.print("Time = ");
    Serial.print(hr);
    Serial.print(":");
    Serial.print(mn);
    Serial.print("\t");

    Serial.print("Battery = ");
    Serial.println(b);

    Serial.println();
   
  } // end if (type == WStype_TEXT)
} // end void webSocketEvent

Do I understand that correctly you use a websocket to transfer data from one ESP32 to the other? This makes the connection overly complex and introduces sources of error not necessary.

I strongly recommend to use either simple TCP connections or a simple Web connection (client to server). Then you don't have the problem to establish a new web socket after sleeping as you use a new connection for every request anyway.

Excuse me but that's bullshit. Javascript is either used on the client (browser) but that won't go to sleep (hopefully) or on the server (node.js) but a server must not sleep by definition.

If you go to sleep your always disconnected you don't have to test that.

You might have to post the code of the other ESP32 too.

@pylon thanks for your feedback. Although I've done a bunch or Arduino projects they've mainly been control a local unit, line follower, solar garden watering type of projects. Originally this project used NRF24L01 to communicate a Arduino Nano to Mega. This is my first project with ESP32's and wanted to use the onboard Wifi, I've never done anything with that kind of WiFi so this is all new to me.

I was able to get the greenhouse monitor, it is the server and the one that goes to sleep, to connect to my WiFi router, I get NTP time and can send the BME280 values to ThinkSpeak. I looked into ESP-Now as a way to also send those values to my in house base unit, the client. But I did find some references that I could not have the server connected to the router as well as use ESP-now, that may also be BS. Doing some more research I found Websockets and that seemed to be a way to have the server connected to both the internet and my base unit, as you point out that may not be the best way.

I will look into both TCP and client to server messaging. Again without really knowing but I'll ask the question anyways. Do I just keep sending request for the values which obviously wont be returned while the server is sleeping but when it wakes up it will? So I would have to set up some kind of overlapping like request the response every 30 seconds and keep the server awake for a minute.

I guess I need to do some homework, thanks for your help
John

Here is the code for the server


// ESP32 with BME/BMP280
// Added Wifi to Thingspeak
// Added Nrfl24l01
// _2 added RTC
// _3 added NTP time
// _4 removed RTC
// Loaded into Weather Station 3.0
// Added Websocket Server

#include <Wire.h>
#include <SPI.h>
#include <WiFi.h>
#include <ThingSpeak.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24Network.h>
#include <time.h>
#include <Mapf.h>
#include <WebServer.h> // Include WebSwever Library for ESP32
#include <ArduinoJson.h> // Include ArduinoJson Library
#include <WebSocketsServer.h>  // Include Websocket Library

Adafruit_BME280 bme; // I2C 
#define SEALEVELPRESSURE_HPA (1013.25)

float Temp = 0;
float Prss = 0;
float Humd = 0;
float c_Temp = 0;

struct dataStruct {
 float H1_Temp;
 float H1_Prss;
 float H1_Humd;
 int16_t   H1_Hour;
 int16_t   H1_Min;
 float Batt_Lvl;
}myDataStructure;

// long int start_delay = 0; // start of delay time
// int baseUpdate = 0; // base data has been updated
// long int write_time = 20000; // time between writes to base
long int lastTime = 0;
long int updateTime = 10000;
long int sleepDelay = 60000;
bool Reset = false;

#define BME280Pwr 25 // BME280 Power signal to GPIO25
//#define NRF24Pwr 33 // NRF24L01 power signal to GPIO33

// Wifi
#define WIFI_NETWORK "my_SSID" //  your network SSID (name)
#define WIFI_PASSWORD "my_Password"    // your network password (use for WPA, or use as key for WEP)
#define WIFI_TIMEOUT_MS 20000
WiFiClient client;

// ThingSpeak
unsigned long myChannelNumber = 1562213; 
const char * myWriteAPIKey = "OTIKE0VFZJ122V2H";

// NRF24L01
RF24 radio(17, 16); // CE, CSN
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 01;   // Address of this node in Octal format ( 04,031, etc)
const uint16_t base_node = 00;   // Address of base node

// for NTP time
const char* NTP_SERVER = "ch.pool.ntp.org";
const char* TZ_INFO    = "EST5EDT,M3.2.0,M11.1.0"; //
tm timeinfo;
time_t now;
long unsigned lastNTPtime;
unsigned long lastEntryTime;

// for deep sleep using ESP32's RTC
#include <Arduino.h>
#define LED_PIN 2

// Only RTC GPIOs can be used as a source for external wake-up.
// They are: 0,2,4,12-15,25-27,32-39. Note pins 34-39 are INPUT only (not PULLUP).
#define BTN_PIN 26

// Define a few time periods
// #define SECONDS_TO_HANG_ABOUT 10
#define SECONDS_TO_SLEEP 300 // duration of sleep
#define uS_TO_S_FACTOR 1000000

// Place all variables you want to retain across sleep periods in the 
// RTC SRAM. 8kB SRAM on the RTC for all variables.
RTC_DATA_ATTR int bootCount = 0;

// for battery monitoring
#define Batt_In 33 //32
 double value=333;// adc reading
  uint8_t bit_resolution=10;// for example 10 bit adc 
  double output_min=0;// 0v
  double output_max=5; //5v
  //double Batt_Lvl;

int interval = 1000; // virtual delay
unsigned long previousMillis = 0; // Tracks the time since last event fired

// web page
String web =  "<!DOCTYPE html><html><head> <title>Websocket</title> <meta name='viewport' content='width=device-width, initial-scale=1.0' /> <meta charset='UTF-8'> <style> body { background-color: #F7F9FD; text-align: center; } </style></head><body> <h1>Temperature: <span id='temp'>-</span></h1> <h1>Humidity: <span id='hum'>-</span></h1> <h1>Received message: <span id='message'>-</span></h1><button type='button' id='btnA'> <h1>ON</h1> </button><button type='button' id='btnB'> <h1>OFF</h1> </button></body><script> var Socket; document.getElementById('btnA').addEventListener('click', buttonApressed); document.getElementById('btnB').addEventListener('click', buttonBpressed); function init() { Socket = new WebSocket('ws://' + window.location.hostname + ':81/'); Socket.onmessage = function(event) { processReceivedCommand(event); }; } function processReceivedCommand(event) { var obj = JSON.parse(event.data); document.getElementById('message').innerHTML = obj.PIN_Status; document.getElementById('temp').innerHTML = obj.Temp; document.getElementById('hum').innerHTML = obj.Hum; console.log(obj.PIN_Status); console.log(obj.Temp); console.log(obj.Hum); } function buttonApressed() { Socket.send('1'); } function buttonBpressed() { Socket.send('0'); } window.onload = function(event) { init(); }</script></html>";
String jsonString; // Temporary storage for the JSON String
String pin_status = ""; // Holds the status of the pin
float t; // holds the temperature value
float h;// holds the Humidity value
float p; // holds pressure value
float b; // holds battery value
int16_t   Gh1_hr;
int16_t   Gh1_mn;

WebServer server(80);  // create instance for web server on port "80"
WebSocketsServer webSocket = WebSocketsServer(81);  //create instance for webSocket server on port"81"



void setup() {
 Serial.begin(115200);

 // Pin Modes
  pinMode(LED_PIN, OUTPUT);
  pinMode(BTN_PIN, INPUT_PULLUP);
  pinMode(BME280Pwr, OUTPUT);
 // pinMode(NRF24Pwr, OUTPUT);
  pinMode(Batt_In,INPUT);

  digitalWrite(BME280Pwr, HIGH); // turn BME280 Power on
  //digitalWrite(NRF24Pwr, HIGH); // turn BME280 Power on
  //digitalWrite(LED_PIN, HIGH); // turn on LED
  delay(500);
  
 // Why are we awake?
  getWakeUpReason();

 // Start BME280
 
if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    //while (1);
    //digitalWrite(LED_PIN, HIGH); // turn on LED
  } // end if (!bmp.begin())

// Start Wifi
  //digitalWrite(LED_PIN, HIGH); // turn on LED
  connectToWiFi();
  ThingSpeak.begin(client);


// NRF24L01
  delay(1000);
  radio.begin();
  radio.setDataRate(RF24_1MBPS);
  radio.setChannel(124);
  radio.setPALevel(RF24_PA_MAX); // SetPALevel = MIN, LOW, HIGH, MAX
  radio.stopListening();
  network.begin(124,this_node);  //(channel, node address)
//digitalWrite(LED_PIN, HIGH); // turn on LED


// NTP time

 Serial.println("\n\nNTP Time Test\n");

 configTime(0, 0, NTP_SERVER);
  // See https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv for Timezone codes for your region
  setenv("TZ", TZ_INFO, 1);

  if (getNTPtime(10)) {  // wait up to 10sec to sync
  } else {
    Serial.println("Time not set");
    //digitalWrite(LED_PIN, HIGH); // turn on LED
    ESP.restart();
  }
  showTime(timeinfo);
  lastNTPtime = time(&now);
  lastEntryTime = millis();
 
lastTime = millis(); // for update of readings 

   // Initialize a web server on the default IP address. and send the webpage as a response.
  server.on("/", []() {
    server.send(200, "text\html", web);
  });
  server.begin(); // init the server
  webSocket.begin();  // init the Websocketserver
  webSocket.onEvent(webSocketEvent);  // init the webSocketEvent function when a websocket event occurs 
  
} // end setup

void loop() {
  //  digitalWrite(LED_PIN, HIGH); // turn on LED

  server.handleClient();  // webserver methode that handles all Client
  webSocket.loop(); // websocket server methode that handles all Client
  unsigned long currentMillis = millis(); // call millis  and Get snapshot of time



  if((millis() > (lastTime + updateTime)) && Reset == false){

  Reset = true;
  
  // Update NTP time
  configTime(0, 0, NTP_SERVER);
  setenv("TZ", TZ_INFO, 1);

  if (getNTPtime(10)) {  // wait up to 10sec to sync
  } else {
    Serial.println("Time not set");
    ESP.restart();
  }
  showTime(timeinfo);
  lastNTPtime = time(&now);
  lastEntryTime = millis();

// Read Battery
    myDataStructure.Batt_Lvl = mapf_ADC(analogRead(Batt_In),0,4.2,12); // example for 12 bit ADC 0 to 3.3 voltage 
    b = myDataStructure.Batt_Lvl;
  
// Read BME280
    c_Temp = bme.readTemperature(); // read temp from BME280
    myDataStructure.H1_Temp = (c_Temp * 9/5) + 32; // calculate F'  
    myDataStructure.H1_Prss = (bme.readPressure() / 3389.39);
    myDataStructure.H1_Humd = bme.readHumidity();
   // myDataStructure.H1_Hour = localTime.tm_hour;
   // myDataStructure.H1_Min =  localTime.tm_min;

// Write to NRF24L01
    RF24NetworkHeader header(base_node);     // (Address where the data is going)
    network.write(header, &myDataStructure, sizeof(myDataStructure)); // send string to base
 
// Update ThingSpeak data
    ThingSpeak.setField(1,myDataStructure.H1_Temp);
    ThingSpeak.setField(2,myDataStructure.H1_Humd);
    ThingSpeak.setField(3,myDataStructure.H1_Prss);
    ThingSpeak.setField(4,myDataStructure.Batt_Lvl);
    ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);

// Send to Serial Monitor
    Serial.print("Temp = ");
    Serial.print(myDataStructure.H1_Temp);
    Serial.print("\t");
    Serial.print("Pressure = ");
    Serial.print(myDataStructure.H1_Prss);
    Serial.print("\t");
    Serial.print("Humdity = ");
    Serial.println(myDataStructure.H1_Humd);
   // Serial.print("\t");
    Serial.print("Time = ");
    Serial.print(myDataStructure.H1_Hour);
    Serial.print(":");
    Serial.print(myDataStructure.H1_Min);
    Serial.print("\t");
    Serial.print("Battery = ");
    Serial.println(myDataStructure.Batt_Lvl);

    Serial.println();
    
    

   // Update Webserver data
  if ((unsigned long)(currentMillis - previousMillis) >= interval) { // How much time has passed, accounting for rollover with subtraction!
    update_temp_hum(); // update temperature data.
    update_webpage(); // Update Humidity Data
    previousMillis = currentMillis;   // Use the snapshot to set track time until next event
  } // end  if ((unsigned long)(currentMillis - previousMillis) >= interval)
  
 //lastTime = millis(); // reset last time data updated
    
 } // end if(millis() > (lastTime + updateTime)

 if(millis() > (lastTime + sleepDelay)){
  // Put ESP to sleep
   Reset = false;
   lastTime = millis(); // reset last time data updated
   gotoSleep();
 } // end if(millis() > (lastTime + sleepDelay)
   
} // end loop

void connectToWiFi(){
  
  //digitalWrite(LED_PIN, HIGH); // turn on LED
  Serial.print("Connecting to Wifi");
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_NETWORK, WIFI_PASSWORD);

  unsigned long startAttemptTime = millis();

  while(WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < WIFI_TIMEOUT_MS){
    Serial.print(".");
    delay(100);
  }

  if(WiFi.status() != WL_CONNECTED){
    
     Serial.println("  Failed  ");
     // take some action here because of failed connection
     
  }else{
    Serial.print("  Connected!  ");
    //digitalWrite(LED_PIN, HIGH); // turn on LED
    Serial.println(WiFi.localIP());
  }

  //digitalWrite(LED_PIN, HIGH); // turn on LED

} // end void connectTo WiFi


bool getNTPtime(int sec) {

  {
    uint32_t start = millis();
    do {
      time(&now);
      localtime_r(&now, &timeinfo);
      Serial.print(".");
      delay(10);
    } while (((millis() - start) <= (1000 * sec)) && (timeinfo.tm_year < (2016 - 1900)));
    if (timeinfo.tm_year <= (2016 - 1900)) return false;  // the NTP call was not successful
    Serial.print("now ");  Serial.println(now);
    char time_output[30];
    strftime(time_output, 30, "%a  %d-%m-%y %T", localtime(&now));
    Serial.println(time_output);
    Serial.println();
  }
  return true;
}


void showTime(tm localTime) {
  Serial.print(localTime.tm_mday);
  Serial.print('/');
  Serial.print(localTime.tm_mon + 1);
  Serial.print('/');
  Serial.print(localTime.tm_year - 100);
  Serial.print('-');
  Serial.print(localTime.tm_hour);
  Serial.print(':');
  Serial.print(localTime.tm_min);
  Serial.print(':');
  Serial.print(localTime.tm_sec);
  Serial.print(" Day of Week ");

  myDataStructure.H1_Hour = localTime.tm_hour;
  Gh1_hr = myDataStructure.H1_Hour;
  myDataStructure.H1_Min =  localTime.tm_min;
  Gh1_mn = myDataStructure.H1_Min;
    
  if (localTime.tm_wday == 0)   Serial.println(7);
  else Serial.println(localTime.tm_wday);
}


/*
 // Shorter way of displaying the time
  void showTime(tm localTime) {
  Serial.printf(
    "%04d-%02d-%02d %02d:%02d:%02d, day %d, %s time\n",
    localTime.tm_year + 1900,
    localTime.tm_mon + 1,
    localTime.tm_mday,
    localTime.tm_hour,
    localTime.tm_min,
    localTime.tm_sec,
    (localTime.tm_wday > 0 ? localTime.tm_wday : 7 ),
    (localTime.tm_isdst == 1 ? "summer" : "standard")
  );
  }

*/

//Put the ESP32 device to sleep, woken by the RTC and/or external switch
void gotoSleep() {

  Serial.println("Going to sleep");
  
  // Allow wake up if Button Switch is pressed
  esp_sleep_enable_ext0_wakeup((gpio_num_t)BTN_PIN, LOW);  // could use: GPIO_NUM_26

  // Allow RTC to wake up this device after X seconds
  esp_sleep_enable_timer_wakeup(SECONDS_TO_SLEEP * uS_TO_S_FACTOR);

  // Sleep starts
  esp_deep_sleep_start();
}

void getWakeUpReason() {
  Serial.print("This is reboot number ");
  Serial.print("\t");
  Serial.println(bootCount);

  esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
  switch (wakeup_reason) {
    case ESP_SLEEP_WAKEUP_EXT0:
      Serial.println("Wakeup caused by external signal using RTC_IO");
      break;
    case ESP_SLEEP_WAKEUP_EXT1:
      Serial.println("Wakeup caused by external signal using RTC_CNTL");
      break;
    case ESP_SLEEP_WAKEUP_TIMER:
      Serial.println("Wakeup caused by RTC timer");
      break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD:
      Serial.println("Wakeup caused by touchpad");
      break;
    case ESP_SLEEP_WAKEUP_ULP:
      Serial.println("Wakeup caused by ULP program");
      break;
    default:
      Serial.println("Wakeup was not caused by deep sleep:");
  }
}

// This function gets a call when a WebSocket event occurs
void webSocketEvent(byte num, WStype_t type, uint8_t * payload, size_t length) {
  switch (type) {
    case WStype_DISCONNECTED: // enum that read status this is used for debugging.
      Serial.print("WS Type ");
      Serial.print(type);
      Serial.println(": DISCONNECTED");
      break;
    case WStype_CONNECTED:  // Check if a WebSocket client is connected or not
      Serial.print("WS Type ");
      Serial.print(type);
      Serial.println(": CONNECTED");
      if (digitalRead(LED_PIN) == HIGH) {  //check if pin 2 is high or low
        pin_status = "ON";
        update_webpage(); // update the webpage accordingly
      }
      else {                          
        pin_status = "OFF"; //check if pin 2 is high or low
        update_webpage();// update the webpage accordingly
      }
      break;
    case WStype_TEXT: // check responce from client
      Serial.println(); // the payload variable stores teh status internally
      Serial.println(payload[0]);
      if (payload[0] == '1') { 
        pin_status = "ON";
        digitalWrite(LED_PIN, HIGH);               
      }
      if (payload[0] == '0') {
        pin_status = "OFF";
        digitalWrite(LED_PIN, LOW);             
      }
      break;
  }
} // end void webSocketEvent

void update_temp_hum(){
  h = bme.readHumidity(); // Read temperature as Celsius (the default)
  t = ((bme.readTemperature() * 9/5) + 32); // Read temperature as Fahrenheit (isFahrenheit = true
  p = (bme.readPressure() / 3389.39);
} // end void update_temp_hum

void update_webpage()
{
  StaticJsonDocument<120> doc;
  // create an object
  JsonObject object = doc.to<JsonObject>();
  object["PIN_Status"] = pin_status ;
  object["Temp"] = t ;
  object["Hum"] = h ;
  object["Press"] = p ;
  object["Batt"] = b ;
  object["Hour"] = Gh1_hr;
  object ["Min"] = Gh1_mn;
  
  serializeJson(doc, jsonString); // serialize the object and save teh result to teh string variable.
 // Serial.println( jsonString ); // print the string for debugging.
  webSocket.broadcastTXT(jsonString); // send the JSON object through the websocket
  jsonString = ""; // clear the String.
}

@pylon I removed the code for the websockets and found a way to create a access point network. I then send HTTP get commands from the client to the server. Works fine and updates as soon as the server wakes up. I got it all working this morning, I have some things to clean up in the code on both ends and lots to learn about using this method.

Thanks for you suggestion and pointing me in this direction
John

As it's your server that goes to sleep (which actually makes "server" a misnomer) I would swap roles and make the other board the TCP server and let the sleeping board connect to that one.

If it "works for you", then OK... As pointed out previously, you have the client/server roles reversed... semantics for a 2-node network, but not too expandable.

Another approach, very expandable, is to have the greenhouse become a real client and post the data to a server when awake. If the server is a bit more mature than an ESP32, then things get interesting: say a Raspberry Pi running NodeRed. In this rPi/NodeRed scenario, the rPi is running a real web server and NodeRed gives you graphical tools and many options for data manipulation.

You can keep the ESP32 as the greenhouse node, but I would replace WiFi with LoRa for the wireless link. Avoids a neighbor trying to connect to your AccessPoint and bringing the system down!

Forget the current setup, envision LoRa providing the serial link, and think of the possibilities:

My NodeRED serial project

With my limited knowledge of WiFi as soon as I saw this example I said "This is backwards" The server should be the base and the client the greenhouse monitor. The project started out as a bee hive monitor, so if I go back to that I had 2 hive monitors, to be expanded to more, sending data back to the base, In that case the hive monitors should be the clients, going to sleep, and the base the server, staying a wake to get data when the hives wake up. Right now it does work for what I want it to do but I've already started working on reversing the client and server logic.

Thanks for confirming my thoughts

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.