From c78fd2b81dc597bc1552414970438fcb5bab75c5 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 17 Jul 2024 18:36:10 +0200
Subject: [PATCH 1/5] Unifying codestyle

---
 libraries/WiFiS3/src/Modem.cpp | 101 +++++++++++--------------
 libraries/WiFiS3/src/Modem.h   |   6 +-
 libraries/WiFiS3/src/WiFi.cpp  | 131 ++++++++++++++++-----------------
 3 files changed, 109 insertions(+), 129 deletions(-)

diff --git a/libraries/WiFiS3/src/Modem.cpp b/libraries/WiFiS3/src/Modem.cpp
index 884ae688b..91e8c0f03 100644
--- a/libraries/WiFiS3/src/Modem.cpp
+++ b/libraries/WiFiS3/src/Modem.cpp
@@ -9,96 +9,77 @@ using namespace std;
 /* -------------------------------------------------------------------------- */
 ModemClass::ModemClass(int tx, int rx) : beginned(false), delete_serial(false), _timeout(MODEM_TIMEOUT), trim_results(true), read_by_size(false) {
 /* -------------------------------------------------------------------------- */
-  _serial = new UART(tx,rx);
+   _serial = new UART(tx,rx);
 }
 
 /* -------------------------------------------------------------------------- */
 ModemClass::ModemClass(UART * serial) : beginned(false) , delete_serial(true) , _serial(serial), _timeout(MODEM_TIMEOUT), trim_results(true), read_by_size(false) {
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 }
 
 /* -------------------------------------------------------------------------- */
 ModemClass::~ModemClass() {
-/* -------------------------------------------------------------------------- */   
-  if(_serial != nullptr &&  !delete_serial){
+/* -------------------------------------------------------------------------- */
+   if(_serial != nullptr &&  !delete_serial){
       delete _serial;
       _serial = nullptr;
-  }
+   }
 }
 
 /* -------------------------------------------------------------------------- */
 void ModemClass::begin(int badurate){
-/* -------------------------------------------------------------------------- */   
-  if(_serial != nullptr && !beginned) {
+/* -------------------------------------------------------------------------- */
+   if(_serial != nullptr && !beginned) {
       _serial->begin(badurate);
       beginned = true;
       string res = "";
       _serial->flush();
       modem.write(string(PROMPT(_SOFTRESETWIFI)),res, "%s" , CMD(_SOFTRESETWIFI));
-  }
+   }
 }
 
 /* -------------------------------------------------------------------------- */
 void ModemClass::end(){
-/* -------------------------------------------------------------------------- */   
- _serial->end();
+/* -------------------------------------------------------------------------- */
+   _serial->end();
 }
 
 /* -------------------------------------------------------------------------- */
 bool ModemClass::passthrough(const uint8_t *data, size_t size) {
-/* -------------------------------------------------------------------------- */   
+/* -------------------------------------------------------------------------- */
    _serial->write(data,size);
-   bool res = false;
-   bool found = false;
-   string data_res = "";
-   
-   unsigned long start_time = millis();
-   while(millis() - start_time < _timeout && !found){
-      while(_serial->available()){
-         char c = _serial->read();
-         data_res += c;
-         
-         if(string::npos != data_res.rfind(RESULT_OK)){
-            found = true;
-            res = true;
-            break;
-         } 
-         else if (string::npos != data_res.rfind(RESULT_ERROR)) {
-            found = true;
-            res = false;
-            break;
-         }
-      }
-   }  
-   
-   if(_serial_debug && _debug_level >= 2) {       
-      _serial_debug->print("   ANSWER (passthrough): ");
-      _serial_debug->println(data_res.c_str());
-      if(res) {
-         _serial_debug->println("   Result: OK");
-      }
-      else {
-         _serial_debug->println("   Result: FAILED");
-      }  
-   }     
-   
+
+   std::string tmp, data_res; // FIXME
+   bool res = buf_read(tmp, data_res);
+
+   // if(_serial_debug && _debug_level >= 2) {
+   //    _serial_debug->print("   ANSWER (passthrough): ");
+   //    _serial_debug->println(data_res.c_str());
+   //    if(res) {
+   //       _serial_debug->println("   Result: OK");
+   //    }
+   //    else {
+   //       _serial_debug->println("   Result: FAILED");
+   //    }
+   // }
+
    return res;
 }
 
 /* -------------------------------------------------------------------------- */
 void ModemClass::write_nowait(const string &cmd, string &str, const char * fmt, ...) {
-/* -------------------------------------------------------------------------- */   
+/* -------------------------------------------------------------------------- */
    va_list va;
    va_start (va, fmt);
    vsnprintf((char *)tx_buff, MAX_BUFF_SIZE, fmt, va);
    va_end (va);
-   
-   if(_serial_debug && _debug_level >= 2) { 
+
+   if(_serial_debug && _debug_level >= 2) {
       _serial_debug->print("REQUEST (passthrough): ");
       _serial_debug->write(tx_buff,strlen((char *)tx_buff));
       _serial_debug->println();
    }
-   
+
    _serial->write(tx_buff,strlen((char *)tx_buff));
    return;
 }
@@ -106,13 +87,13 @@ void ModemClass::write_nowait(const string &cmd, string &str, const char * fmt,
 
 /* -------------------------------------------------------------------------- */
 bool ModemClass::write(const string &prompt, string &data_res, const char * fmt, ...){
-/* -------------------------------------------------------------------------- */  
+/* -------------------------------------------------------------------------- */
    data_res.clear();
    va_list va;
    va_start (va, fmt);
    vsnprintf((char *)tx_buff, MAX_BUFF_SIZE, fmt, va);
    va_end (va);
-  
+
    if(_serial_debug) {
       _serial_debug->println();
       _serial_debug->print("REQUEST: ");
@@ -121,7 +102,7 @@ bool ModemClass::write(const string &prompt, string &data_res, const char * fmt,
    }
 
    _serial->write(tx_buff,strlen((char *)tx_buff));
-   return buf_read(prompt,data_res);;
+   return buf_read(prompt, data_res);;
 }
 
 
@@ -134,7 +115,7 @@ typedef enum {
 
 /* -------------------------------------------------------------------------- */
 bool ModemClass::read_by_size_finished(string &rx) {
-/* -------------------------------------------------------------------------- */   
+/* -------------------------------------------------------------------------- */
    bool rv = false;
    static bool first_call = true;
    static ReadBySizeSt_t st = IDLE;
@@ -194,7 +175,7 @@ bool ModemClass::read_by_size_finished(string &rx) {
 
 /* -------------------------------------------------------------------------- */
 bool ModemClass::buf_read(const string &prompt, string &data_res) {
-/* -------------------------------------------------------------------------- */   
+/* -------------------------------------------------------------------------- */
    bool res = false;
    bool found = false;
    
@@ -262,6 +243,7 @@ bool ModemClass::buf_read(const string &prompt, string &data_res) {
          }
       }
    }
+
    if(trim_results) {
       trim(data_res);
    }
@@ -273,7 +255,7 @@ bool ModemClass::buf_read(const string &prompt, string &data_res) {
       _serial_debug->println();
    }
 
-   if(_serial_debug) {       
+   if(_serial_debug) {
       _serial_debug->print("   ANSWER: ");
       _serial_debug->println(data_res.c_str());
       if(res) {
@@ -281,15 +263,14 @@ bool ModemClass::buf_read(const string &prompt, string &data_res) {
       }
       else {
          _serial_debug->println("   Result: FAILED");
-      }  
-   }      
-
+      }
+   }
 
    return res;
 }
 
 #ifdef ARDUINO_UNOWIFIR4
-  ModemClass modem = ModemClass(&Serial2);
+   ModemClass modem = ModemClass(&Serial2);
 #else
-  ModemClass modem = ModemClass(D24,D25);
+   ModemClass modem = ModemClass(D24,D25);
 #endif
diff --git a/libraries/WiFiS3/src/Modem.h b/libraries/WiFiS3/src/Modem.h
index 78539e7e3..fc6fb09f6 100644
--- a/libraries/WiFiS3/src/Modem.h
+++ b/libraries/WiFiS3/src/Modem.h
@@ -36,18 +36,18 @@ class ModemClass {
 
   void read_using_size() {
     read_by_size = true;
-  } 
+  }
   bool beginned;
 
   /* calling this function with no argument will enable debug message to be printed
      on Serial
-     use first parameter UART *u to redirect debug output to a different serial 
+     use first parameter UART *u to redirect debug output to a different serial
 
      level from 0 defaul to 2 (maximum) */
 
   void debug(Stream  &u, uint8_t level = 0) {
     _serial_debug = &u;
-    
+
     if(level > 2) {
       level = 2;
     }
diff --git a/libraries/WiFiS3/src/WiFi.cpp b/libraries/WiFiS3/src/WiFi.cpp
index f99fd6db6..09d0f5398 100644
--- a/libraries/WiFiS3/src/WiFi.cpp
+++ b/libraries/WiFiS3/src/WiFi.cpp
@@ -20,7 +20,6 @@ const char* CWifi::firmwareVersion() {
       return fw_version;
    }
    return "99.99.99";
-   
 }
 
 
@@ -54,7 +53,7 @@ int CWifi::begin(const char* ssid, const char *passphrase) {
          return WL_CONNECTED;
       }
    }
-  return WL_CONNECT_FAILED;
+   return WL_CONNECT_FAILED;
 }
 
 /* passphrase is needed so a default one will be set */
@@ -78,7 +77,7 @@ uint8_t CWifi::beginAP(const char *ssid, const char* passphrase) {
 
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::beginAP(const char *ssid, const char* passphrase, uint8_t channel) {
-/* -------------------------------------------------------------------------- */   
+/* -------------------------------------------------------------------------- */
    string res = "";
    modem.begin();
    modem.write(string(PROMPT(_MODE)),res, "%s%d\r\n" , CMD_WRITE(_MODE), 2);
@@ -97,10 +96,10 @@ uint8_t CWifi::beginAP(const char *ssid, const char* passphrase, uint8_t channel
 /* -------------------------------------------------------------------------- */
 void CWifi::config(IPAddress local_ip) {
 /* -------------------------------------------------------------------------- */
-  IPAddress _gw(local_ip[0],local_ip[1], local_ip[2], 1);
-  IPAddress _sm(255,255,255,0);
-  IPAddress dns(0,0,0,0);
-  return _config(local_ip, _gw, _sm, _gw, dns);
+   IPAddress _gw(local_ip[0],local_ip[1], local_ip[2], 1);
+   IPAddress _sm(255,255,255,0);
+   IPAddress dns(0,0,0,0);
+   return _config(local_ip, _gw, _sm, _gw, dns);
 }
 
 /* -------------------------------------------------------------------------- */
@@ -116,7 +115,7 @@ void CWifi::_config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPA
    string gw  = to_string(gateway[0]) + ".";
           gw += to_string(gateway[1]) + ".";
           gw += to_string(gateway[2]) + ".";
-          gw += to_string(gateway[3]);       
+          gw += to_string(gateway[3]);
 
    string nm  = to_string(subnet[0]) + ".";
           nm += to_string(subnet[1]) + ".";
@@ -127,18 +126,18 @@ void CWifi::_config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPA
           _dns1 += to_string(dns1[1]) + ".";
           _dns1 += to_string(dns1[2]) + ".";
           _dns1 += to_string(dns1[3]);
-   
+
    string _dns2  = to_string(dns2[0]) + ".";
           _dns2 += to_string(dns2[1]) + ".";
           _dns2 += to_string(dns2[2]) + ".";
-          _dns2 += to_string(dns2[3]);              
+          _dns2 += to_string(dns2[3]);
 
    ip_ap = local_ip;
    gw_ap = gateway;
    nm_ap = subnet;
 
    modem.write(PROMPT(_SOFTAPCONFIG),res, "%s%s,%s,%s\r\n" , CMD_WRITE(_SOFTAPCONFIG), ip.c_str(), ip.c_str(), nm.c_str());
-   modem.write(string(PROMPT(_SETIP)),res, "%s%s,%s,%s,%s,%s\r\n" , CMD_WRITE(_SETIP), ip.c_str(), gw.c_str(), nm.c_str(),_dns1.c_str(),_dns2.c_str());   
+   modem.write(string(PROMPT(_SETIP)),res, "%s%s,%s,%s,%s,%s\r\n" , CMD_WRITE(_SETIP), ip.c_str(), gw.c_str(), nm.c_str(),_dns1.c_str(),_dns2.c_str());
 }
 
 /* -------------------------------------------------------------------------- */
@@ -152,32 +151,32 @@ void CWifi::config(IPAddress local_ip, IPAddress dns_server) {
 
 /* -------------------------------------------------------------------------- */
 void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway) {
-/* -------------------------------------------------------------------------- */  
-   
+/* -------------------------------------------------------------------------- */
+
    IPAddress _sm(255,255,255,0);
    IPAddress dns(0,0,0,0);
-   return _config(local_ip, gateway, _sm,dns_server,dns); 
+   return _config(local_ip, gateway, _sm,dns_server,dns);
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) {
 /* -------------------------------------------------------------------------- */
-   
+
    IPAddress dns(0,0,0,0);
-   return _config(local_ip, gateway, subnet,dns_server,dns); 
+   return _config(local_ip, gateway, subnet,dns_server,dns);
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::setDNS(IPAddress dns_server1) {
 /* -------------------------------------------------------------------------- */
    IPAddress dns(0,0,0,0);
-   return _config(localIP(), gatewayIP(), subnetMask(),dns_server1,dns);  
+   return _config(localIP(), gatewayIP(), subnetMask(),dns_server1,dns);
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::setDNS(IPAddress dns_server1, IPAddress dns_server2) {
 /* -------------------------------------------------------------------------- */
-   return _config(localIP(), gatewayIP(), subnetMask(),dns_server1,dns_server2); 
+   return _config(localIP(), gatewayIP(), subnetMask(),dns_server1,dns_server2);
 }
 
 /* -------------------------------------------------------------------------- */
@@ -189,20 +188,20 @@ void CWifi::setHostname(const char* name) {
 
 /* -------------------------------------------------------------------------- */
 int CWifi::disconnect() {
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
    string res = "";
    modem.begin();
 
    if(modem.write(string(PROMPT(_DISCONNECT)),res,CMD(_DISCONNECT))) {
       return 1;
-   }      
+   }
    return 0;
 
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::end(void) {
-/* -------------------------------------------------------------------------- */   
+/* -------------------------------------------------------------------------- */
    string res = "";
    modem.begin();
 
@@ -211,7 +210,7 @@ void CWifi::end(void) {
 
 
 static bool macStr2macArray(uint8_t *mac_out, const char *mac_in) {
-   if(mac_in[2] != ':' || 
+   if(mac_in[2] != ':' ||
       mac_in[5] != ':' ||
       mac_in[8] != ':' ||
       mac_in[11] != ':' ||
@@ -230,10 +229,10 @@ static bool macStr2macArray(uint8_t *mac_out, const char *mac_in) {
 
 /* -------------------------------------------------------------------------- */
 uint8_t* CWifi::macAddress(uint8_t* _mac) {
-/* -------------------------------------------------------------------------- */   
-  string res = "";
-  modem.begin();
-  if(modem.write(string(PROMPT(_MODE)),res, "%s" , CMD_READ(_MODE)))  {
+/* -------------------------------------------------------------------------- */
+   string res = "";
+   modem.begin();
+   if(modem.write(string(PROMPT(_MODE)),res, "%s" , CMD_READ(_MODE)))  {
       if(atoi(res.c_str()) == 1) {
          if(modem.write(string(PROMPT(_MACSTA)),res, "%s" , CMD_READ(_MACSTA)))  {
             macStr2macArray(_mac, res.c_str());
@@ -280,7 +279,7 @@ int8_t CWifi::scanNetworks() {
             ap.encryption_mode = tokens[4];
             access_points.push_back(ap);
          }
-      } 
+      }
    }
 
    return (int8_t)access_points.size();
@@ -317,7 +316,7 @@ IPAddress CWifi::dnsIP(int n) {
          return dns_IP;
       }
    }
-   return IPAddress(0,0,0,0);  
+   return IPAddress(0,0,0,0);
 }
 
 
@@ -334,14 +333,14 @@ IPAddress CWifi::localIP() {
       if(modem.write(string(PROMPT(_MODE)),res, "%s" , CMD_READ(_MODE)))  {
          if(atoi(res.c_str()) == 1) {
             if(modem.write(string(PROMPT(_IPSTA)),res, "%s%d\r\n" , CMD_WRITE(_IPSTA), IP_ADDR)) {
-               
+
                local_IP.fromString(res.c_str());
-               
+
             }
          }
          else if(atoi(res.c_str()) == 2) {
             if(modem.write(string(PROMPT(_IPSOFTAP)),res,  CMD(_IPSOFTAP))) {
-               
+
                local_IP.fromString(res.c_str());
             }
          }
@@ -376,27 +375,27 @@ IPAddress CWifi::gatewayIP() {
       gateway_IP.fromString(res.c_str());
       return gateway_IP;
    }
-  return IPAddress(0,0,0,0);
+   return IPAddress(0,0,0,0);
 }
 
 /* -------------------------------------------------------------------------- */
 const char* CWifi::SSID(uint8_t networkItem) {
 /* -------------------------------------------------------------------------- */
-  if(networkItem < access_points.size()) {
-      return access_points[networkItem].ssid.c_str();   
-  }
-  return nullptr;
+   if(networkItem < access_points.size()) {
+      return access_points[networkItem].ssid.c_str();
+   }
+   return nullptr;
 }
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
 int32_t CWifi::RSSI(uint8_t networkItem) {
-  if(networkItem < access_points.size()) {
-      return atoi(access_points[networkItem].rssi.c_str());   
-  }
-  return -1000;
+   if(networkItem < access_points.size()) {
+      return atoi(access_points[networkItem].rssi.c_str());
+   }
+   return -1000;
 }
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 
 static uint8_t Encr2wl_enc(string e) {
    if (e == string("open")) {
@@ -418,17 +417,17 @@ static uint8_t Encr2wl_enc(string e) {
    } else {
       return ENC_TYPE_UNKNOWN;
    }
- }
+}
 
 
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::encryptionType() {
-/* -------------------------------------------------------------------------- */   
+/* -------------------------------------------------------------------------- */
    scanNetworks();
    string myssid(SSID());
    for(unsigned int i = 0; i < access_points.size(); i++) {
       if(myssid ==  access_points[i].ssid) {
-         return Encr2wl_enc(access_points[i].encryption_mode); 
+         return Encr2wl_enc(access_points[i].encryption_mode);
       }
    }
    return ENC_TYPE_UNKNOWN;
@@ -437,12 +436,12 @@ uint8_t CWifi::encryptionType() {
 
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::encryptionType(uint8_t networkItem) {
-  if(networkItem < access_points.size()) {
-      return Encr2wl_enc(access_points[networkItem].encryption_mode);   
-  }
-  return 0;
+   if(networkItem < access_points.size()) {
+      return Encr2wl_enc(access_points[networkItem].encryption_mode);
+   }
+   return 0;
 }
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
 uint8_t* CWifi::BSSID(uint8_t networkItem, uint8_t* bssid) {
@@ -450,22 +449,22 @@ uint8_t* CWifi::BSSID(uint8_t networkItem, uint8_t* bssid) {
       for(int i = 0; i < 6; i++) {
          *(bssid + i) = access_points[networkItem].uint_bssid[i];
       }
-      return bssid;   
+      return bssid;
    }
-  return nullptr;
+   return nullptr;
 }
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
-uint8_t CWifi::channel(uint8_t networkItem) { 
+uint8_t CWifi::channel(uint8_t networkItem) {
    if(networkItem < access_points.size()) {
-      return atoi(access_points[networkItem].channel.c_str());   
+      return atoi(access_points[networkItem].channel.c_str());
    }
    return 0;
 }
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 const char* CWifi::SSID() {
 /* -------------------------------------------------------------------------- */
    string res = "";
@@ -486,9 +485,9 @@ const char* CWifi::SSID() {
    return "";
 }
 
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 uint8_t* CWifi::BSSID(uint8_t* bssid) {
-/* -------------------------------------------------------------------------- */    
+/* -------------------------------------------------------------------------- */
    string res = "";
    if(modem.write(string(PROMPT(_GETBSSID)), res, CMD_READ(_GETBSSID))) {
       macStr2macArray(bssid, res.c_str());
@@ -497,7 +496,7 @@ uint8_t* CWifi::BSSID(uint8_t* bssid) {
    return nullptr;
 }
 
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 int32_t CWifi::RSSI() {
 /* -------------------------------------------------------------------------- */
    string res = "";
@@ -507,9 +506,9 @@ int32_t CWifi::RSSI() {
    return 0;
 }
 
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 const char* CWifi::softAPSSID() {
-/* -------------------------------------------------------------------------- */    
+/* -------------------------------------------------------------------------- */
    string res = "";
    if(modem.write(string(PROMPT(_GETSOFTAPSSID)), res, CMD_READ(_GETSOFTAPSSID))) {
       apssid = res;
@@ -520,7 +519,7 @@ const char* CWifi::softAPSSID() {
 
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::status() {
-/* -------------------------------------------------------------------------- */   
+/* -------------------------------------------------------------------------- */
    modem.begin();
    string res = "";
    if(modem.write(string(PROMPT(_GETSTATUS)), res, CMD_READ(_GETSTATUS))) {
@@ -532,7 +531,7 @@ uint8_t CWifi::status() {
 
 /* -------------------------------------------------------------------------- */
 int CWifi::hostByName(const char* aHostname, IPAddress& aResult) {
-/* -------------------------------------------------------------------------- */   
+/* -------------------------------------------------------------------------- */
    modem.begin();
    string res = "";
    if(modem.write(string(PROMPT(_GETHOSTBYNAME)),res, "%s%s\r\n" , CMD_WRITE(_GETHOSTBYNAME), aHostname)) {
@@ -545,11 +544,11 @@ int CWifi::hostByName(const char* aHostname, IPAddress& aResult) {
 
 
 uint8_t CWifi::reasonCode() {
-  return 0;
+   return 0;
 }
 
 unsigned long CWifi::getTime() {
-  return 0;
+   return 0;
 }
 
 

From 6f0102d783b2ed6876435bf746611ab4c5391710 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 17 Jul 2024 18:37:03 +0200
Subject: [PATCH 2/5] Improving parse function in modem class

This commit aims to change the logic of the parse function from
repeatedly calling a find on the buffer of incoming data to an approach
based on FSM for parsing a stream of chars
---
 libraries/WiFiS3/src/Modem.cpp | 437 ++++++++++++++++++++++-----------
 libraries/WiFiS3/src/Modem.h   |   9 +-
 2 files changed, 295 insertions(+), 151 deletions(-)

diff --git a/libraries/WiFiS3/src/Modem.cpp b/libraries/WiFiS3/src/Modem.cpp
index 91e8c0f03..aee3a57f5 100644
--- a/libraries/WiFiS3/src/Modem.cpp
+++ b/libraries/WiFiS3/src/Modem.cpp
@@ -1,8 +1,11 @@
 #include "Modem.h"
 
-#define RESULT_OK "OK\r\n"
-#define RESULT_ERROR "ERROR\r\n"
-#define RESULT_DATA "DATA\r\n"
+#define OK_TOKEN        "OK"
+#define ERROR_TOKEN     "ERROR"
+#define TERM_TOKEN      "\r\n"
+#define RESULT_OK       OK_TOKEN TERM_TOKEN
+#define RESULT_ERROR    OK_TOKEN TERM_TOKEN
+#define RESULT_DATA     "DATA" TERM_TOKEN
 
 using namespace std;
 
@@ -49,21 +52,24 @@ bool ModemClass::passthrough(const uint8_t *data, size_t size) {
 /* -------------------------------------------------------------------------- */
    _serial->write(data,size);
 
-   std::string tmp, data_res; // FIXME
-   bool res = buf_read(tmp, data_res);
+   std::string prompt = DO_NOT_CHECK_CMD, data_res;
+   auto res = buf_read(prompt, data_res);
 
-   // if(_serial_debug && _debug_level >= 2) {
-   //    _serial_debug->print("   ANSWER (passthrough): ");
-   //    _serial_debug->println(data_res.c_str());
-   //    if(res) {
-   //       _serial_debug->println("   Result: OK");
-   //    }
-   //    else {
-   //       _serial_debug->println("   Result: FAILED");
-   //    }
-   // }
+   if(_serial_debug && _debug_level >= 2) {
+      _serial_debug->print("   ANSWER (passthrough): ");
+      _serial_debug->println(data_res.c_str());
+      if(res == Ok) {
+         _serial_debug->println("   Result: OK");
+      } else if(res == Error) {
+         _serial_debug->println("   Result: ERROR");
+      } else if(res == Timeout) {
+         _serial_debug->println("   Result: TIMEOUT");
+      } else {
+         _serial_debug->println("   Result: ParseError");
+      }
+   }
 
-   return res;
+   return res == Ok;
 }
 
 /* -------------------------------------------------------------------------- */
@@ -102,170 +108,301 @@ bool ModemClass::write(const string &prompt, string &data_res, const char * fmt,
    }
 
    _serial->write(tx_buff,strlen((char *)tx_buff));
-   return buf_read(prompt, data_res);;
-}
+   auto res = buf_read(prompt, data_res);
 
+   if(_serial_debug) {
+      _serial_debug->print("   ANSWER: ");
+      _serial_debug->println(data_res.c_str());
+      if(res == Ok) {
+         _serial_debug->println("   Result: OK");
+      } else if(res == Error) {
+         _serial_debug->println("   Result: ERROR");
+      } else if(res == Timeout) {
+         _serial_debug->println("   Result: TIMEOUT");
+      } else {
+         _serial_debug->println("   Result: ParseError");
+      }
+   }
 
-typedef enum {
-   IDLE,
-   WAIT_FOR_SIZE,
-   WAIT_FOR_DATA
-} ReadBySizeSt_t;
+   return res == Ok;
+}
 
 
 /* -------------------------------------------------------------------------- */
-bool ModemClass::read_by_size_finished(string &rx) {
+ModemClass::ParseResult ModemClass::buf_read(const string &prompt, string &data_res) {
 /* -------------------------------------------------------------------------- */
-   bool rv = false;
-   static bool first_call = true;
-   static ReadBySizeSt_t st = IDLE;
-   static int data_to_be_received = 0;
-   static int data_received = 0;
-   if(first_call) {
-      first_call = false;
-      st = WAIT_FOR_SIZE;
+   /*
+    * This function implements as FSM that parses basic AT command responses
+    * The expected syntax should match the following regex
+    * - (?:\+(\w+)[:=][ ]?(\w*))?(?:\r\n)?(ERROR\r\n|OK\r\n)
+    *   + "ERROR<CR><LF>" "OK<CR><LF>"
+    *   + "+COMMAND: <CR><LF>OK<CR><LF>"
+    *   + "+COMMAND: <CR><LF>ERROR<CR><LF>"
+    *   + "+COMMAND: 1231231<CR><LF>OK<CR><LF>" (NOTE only one parameter supported)
+    *   + "+COMMAND: 1231231<CR><LF>ERROR<CR><LF>" (NOTE only one parameter supported)
+    * - custom sized response:
+    *   + "+COMMAND: 4| 123OK<CR><LF>"
+    */
+   enum class at_parse_state_t {
+      Begin       = 0,
+      Cmd         = 1,
+      Data        = 2,
+      Sized       = 3,
+      ResWaitLF   = 4,
+      Res         = 5,
+      Error       = 6,
+      ParseError  = 7,
+      Ok          = 8,
+      Completed   = 9,
+   };
+
+   at_parse_state_t state = at_parse_state_t::Begin;
+   std::string commandName;
+
+   ModemClass::ParseResult res = Error;
+   unsigned int sized_read_size = 0;
+   unsigned int sized_read_count = 0;
+   unsigned int result_parse = 0;
+   bool restart = false,
+         consume_char = true; // This flag is used to indicate to consume another character from the stream
+
+   char c;
+
+   // I expect the answer to be in this form: "ERROR<CR><LF>" "OK<CR><LF>"
+   // if prompt == DO_NOT_CHECK_CMD
+   const bool check_prompt = (prompt != DO_NOT_CHECK_CMD);
+
+   if(_serial_debug  && _debug_level >= 1) {
+      _serial_debug->print("RAW: ");
    }
 
-   switch(st) {
-      case IDLE:
-         
-      break;
-      case WAIT_FOR_SIZE: {
-         int pos = rx.find("|");
-         int pos_space = rx.find(" ");
-         if(pos != string::npos && pos_space != string::npos) {
-            string n = rx.substr(pos_space,pos);
-            int to_be_rx = atoi(n.c_str());
-            if(to_be_rx <= 0) {
-               while( _serial->available() ){
-                  _serial->read();
-               }
-               rv = true;
-               first_call = true;
-               st = IDLE;
-            }
-            else {
-               /* add 4 because OK\r\n is always added at the end of data */
-               data_to_be_received = to_be_rx + 4;
-               data_received = 0;
-               st = WAIT_FOR_DATA;
-            } 
-            rx.clear();     
+   unsigned long start_time = millis();
+   while(state != at_parse_state_t::Completed) {
+
+      if(millis() - start_time > _timeout) {
+         res = Timeout;
+         break;
+      }
+
+      if(consume_char && !_serial->available()) {
+         // if there is nothing available, go to the beginning of the cycle
+         continue;
+      } else if(consume_char) { // available is true
+         c = _serial->read();
+      } else if(!consume_char) {
+         // reset consume_char to true
+         consume_char = true;
+      }
+
+      if(_serial_debug  && _debug_level >= 1 && consume_char) {
+         if(c == '\n') {
+            _serial_debug->print("<LF>");
+         } else if (c == '\r') {
+            _serial_debug->print("<CR>");
+         } else if (c == ' ') {
+            _serial_debug->print("<SP>");
+         } else if(c < ' ') {
+            _serial_debug->print("<");
+            _serial_debug->print((unsigned int)c);
+            _serial_debug->print(">");
+         } else {
+            _serial_debug->print(c);
          }
       }
-      break;
-
-      case WAIT_FOR_DATA:
-         data_received++;
-         if(data_received == data_to_be_received) {
-            rv = true;
-            first_call = true;
-            st = IDLE;
+      if(_serial_debug  && _debug_level >= 3) {
+         _serial_debug->print(" State ");
+         _serial_debug->println((int)state);
+      }
+
+      switch(state) {
+      case at_parse_state_t::Begin:
+         /*
+            * In this state we wait for a '+' character, which will mark the beginning of a response
+            * or the status response code "ERROR<CR><LF>" or "OK<CR><LF>"
+            * we need to consume the available buffer if it doesn't match the expected response,
+            * in order to avoiding dealing with previous responses which were not parsed successfully
+            */
+
+         if(c == '+') {
+            // the answer doesn't match the expected form, we need to restart
+            restart = !check_prompt;
+
+            commandName += c; // prompt includes also '+'
+            state = at_parse_state_t::Cmd;
+         } else if(c == RESULT_OK[result_parse]) {
+            // the answer doesn't match the expected form, we need to restart
+            restart = check_prompt;
+
+            state = at_parse_state_t::Ok;
+            result_parse++;
+         } else if(c == RESULT_ERROR[result_parse]) {
+            // the answer doesn't match the expected form, we need to restart
+            restart = check_prompt;
+
+            state = at_parse_state_t::Error;
+            result_parse++;
          }
-      break;
+         // if we uncomment this we can force strict response matching
+         // else {
+         //    state = at_parse_state_t::ParseError;
+         // }
+
+         break;
+      case at_parse_state_t::Cmd:
+         /*
+            * In this state we parse the command prompt and wait for either ':' or '=' characters
+            * in order to go the next state
+            */
+
+         if(c == ':' || c == '=') {
+            commandName += c; // prompt includes also ':'
+
+            if (check_prompt && commandName != prompt) {
+               // the response we got is not the one we were expecting, parse the wrong response till the end
+               // and start the parse of the next response
+               restart = true;
+               commandName = "";
+            }
+            state = at_parse_state_t::Data;
 
-      default:
-         st = IDLE;
-      break;
-   }
-   return rv;
-}
+            data_res = "";
+            // state = at_parse_state_t::Data;
+         } else { // no space should be present in the prompt response
+            commandName += c;
+         }
 
+         break;
+      case at_parse_state_t::Data:
+         /*
+            * In this state we parse response parameters and push them into data_res
+            * in case multiple parameters separated by ',' are sent, they will be present in data_res
+            * - if we encounter <CR> we need to wait for <LF>
+            * - if we encounter <LF> we need to parse the response status
+            * - if we encounter '|', the next token will contain binary sized data, the current value in
+            *   in data_res contains the length of the next token
+            */
+
+         if(c == '|') { // sized read, the previous parameter is the length
+            state = at_parse_state_t::Sized;
+
+            sized_read_size = atoi(data_res.c_str());
+            data_res.clear();
+         } else if(c == '\r') {
+            state = at_parse_state_t::ResWaitLF;
+         } else if(c == '\n') {
+            state = at_parse_state_t::Res;
+         } else if(trim_results && c != ' ') {
+            data_res += c; // in case trim_result is true, avoid adding spaces
+         } else if(!trim_results) {
+            data_res += c;
+         }
 
+         break;
+      case at_parse_state_t::Sized:
+         /*
+            * In this state we collect exactly sized_read_size characters into data_res
+            * when we consume all of them we go into Result parse state, where we supposedly
+            * wait for 'OK'
+            */
+         data_res += c;
 
-/* -------------------------------------------------------------------------- */
-bool ModemClass::buf_read(const string &prompt, string &data_res) {
-/* -------------------------------------------------------------------------- */
-   bool res = false;
-   bool found = false;
-   
-   if(_serial_debug  && _debug_level >= 1) {
-      _serial_debug->print("RAW: ");
-   }
+         if(++sized_read_count == sized_read_size) {
+            state = at_parse_state_t::Res;
+         }
+         break;
+      case at_parse_state_t::ResWaitLF:
+         if(c == '\n') {
+            state = at_parse_state_t::Res;
+         }
 
-   unsigned long start_time = millis();
-   while((millis() - start_time < _timeout) && !found){
-      while( _serial->available() ){
-         char c = _serial->read();
-         data_res += c;
-         
-         if(_serial_debug  && _debug_level >= 1) {
-            _serial_debug->print(c);
+         /*
+            * break is volountary not present, to cover for cases where the response status is in the
+            * following form: '...<CR>OK<CR><LF>' '<CR>ERROR<CR><LF>'
+            */
+      case at_parse_state_t::Res:
+         /*
+            * In this state we wait for either an 'O' or an 'E', in order to get an 'OK<CR><LF>'
+            * or 'ERROR<CR><LF>'
+            * The first two cases is when there is no parameter in the response, but just the OK and ERROR tokens
+            */
+
+         if(data_res == OK_TOKEN) {
+            res = Ok;
+            state = at_parse_state_t::Completed;
+         } else if(data_res == ERROR_TOKEN) {
+            res = Error;
+            state = at_parse_state_t::Completed;
+         } if(c == RESULT_OK[0]) { // OK response
+            state = at_parse_state_t::Ok;
+            result_parse = 1;
+         } else if(c == RESULT_ERROR[0]) { // Error response
+            state = at_parse_state_t::Error;
+            result_parse = 1;
          }
-         
-            
-         if(read_by_size) {
-            if(read_by_size_finished(data_res)) {
-               found = true;
-               read_by_size = false;
-               res = true;
-               if(data_res.size() > 0) {
-                  data_res = data_res.substr(0, data_res.length() - (sizeof(RESULT_OK) - 1));
-               }
-               else {
-                  break;
-               }
-            }
+         // if we uncomment this we can force strict response matching
+         // else {
+         //    state = at_parse_state_t::ParseError;
+         // }
+         break;
+      case at_parse_state_t::Ok:
+         /*
+            * In this state we want to match the exact 'K<CR><LF>' response
+            */
+
+         if(c != RESULT_OK[result_parse++]) {
+            state = at_parse_state_t::ParseError;
          }
-         else {
-            if(string::npos != data_res.rfind(RESULT_DATA)) {
-               found = true;
-               data_res = data_res.substr(0, data_res.length() - (sizeof(RESULT_DATA) - 1));
-               if(prompt != DO_NOT_CHECK_CMD) {
-                  if(removeAtBegin(data_res, prompt)) {
-                     res = true;
-                  }
-               }
-               else {
-                 res = true;
-               }
-               break;  
-            }
-            else if(string::npos != data_res.rfind(RESULT_OK)){
-               found = true;
-               data_res = data_res.substr(0, data_res.length() - (sizeof(RESULT_OK) - 1) );
-               if(prompt != DO_NOT_CHECK_CMD) {
-                  if(removeAtBegin(data_res, prompt)) {
-                     res = true;
-                  }
-               }
-               else {
-                 res = true;
-               }
-               break;
-            } 
-            else if (string::npos != data_res.rfind(RESULT_ERROR)) {
-               found = true;
-               data_res.substr(0, data_res.length() - (sizeof(RESULT_ERROR) - 1));
-               res = false;
-               break;
-            }
+
+         if(result_parse == strlen(RESULT_OK)) {
+            res = Ok;
+            state = at_parse_state_t::Completed;
+         }
+         break;
+      case at_parse_state_t::Error:
+         /*
+            * In this state we want to match the exact 'RROR<CR><LF>' response
+            */
+
+         if(c != RESULT_ERROR[result_parse++]) {
+            state = at_parse_state_t::ParseError;
+         }
+
+         if(result_parse == strlen(RESULT_ERROR)) {
+            res = Error;
+            state = at_parse_state_t::Completed;
          }
+         break;
+      case at_parse_state_t::ParseError:
+         res = ParseError;
+         // if we get a parseError, we go back from the beginning and try again to parse, unitl the timeout expires
+         state = at_parse_state_t::Begin;
+         restart = false;
+         consume_char = false;
+         break;
+      case at_parse_state_t::Completed:
+         break;
+      }
+
+      if(restart && state == at_parse_state_t::Completed) {
+         state = at_parse_state_t::Begin;
+         restart = false;
       }
    }
 
-   if(trim_results) {
-      trim(data_res);
+   if(_serial_debug  && _debug_level >= 3) {
+      _serial_debug->print("Final State ");
+      _serial_debug->print((int)state);
+      _serial_debug->print(" res ");
+      _serial_debug->println((int)res);
    }
+
    trim_results = true;
-   read_by_size = false;
 
    if(_serial_debug && _debug_level >= 1) {
       _serial_debug->print("<-RAW END");
       _serial_debug->println();
    }
 
-   if(_serial_debug) {
-      _serial_debug->print("   ANSWER: ");
-      _serial_debug->println(data_res.c_str());
-      if(res) {
-         _serial_debug->println("   Result: OK");
-      }
-      else {
-         _serial_debug->println("   Result: FAILED");
-      }
-   }
-
    return res;
 }
 
diff --git a/libraries/WiFiS3/src/Modem.h b/libraries/WiFiS3/src/Modem.h
index fc6fb09f6..2ec55e42f 100644
--- a/libraries/WiFiS3/src/Modem.h
+++ b/libraries/WiFiS3/src/Modem.h
@@ -66,7 +66,14 @@ class ModemClass {
   void timeout(size_t timeout_ms) {_timeout = timeout_ms;}
 
 private:
-  bool buf_read(const std::string &cmd, std::string &data_res);
+  enum ParseResult {
+    Ok,
+    Error,
+    ParseError,
+    Timeout
+  };
+
+  ParseResult buf_read(const std::string &cmd, std::string &data_res);
   bool delete_serial;
   UART * _serial;
   unsigned long _timeout;

From 7f88390cbeb586e5d34cb10f331ee8b89fc2ec0f Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 18 Jul 2024 14:51:29 +0200
Subject: [PATCH 3/5] adding a third level of debug

---
 libraries/WiFiS3/src/Modem.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libraries/WiFiS3/src/Modem.h b/libraries/WiFiS3/src/Modem.h
index 2ec55e42f..d69958850 100644
--- a/libraries/WiFiS3/src/Modem.h
+++ b/libraries/WiFiS3/src/Modem.h
@@ -48,8 +48,8 @@ class ModemClass {
   void debug(Stream  &u, uint8_t level = 0) {
     _serial_debug = &u;
 
-    if(level > 2) {
-      level = 2;
+    if(level > 3) {
+      level = 3;
     }
     _debug_level = level;
   }

From 9ce159f52e4c21a82f30c99a76f58e4128412c5e Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Mon, 22 Jul 2024 10:56:40 +0200
Subject: [PATCH 4/5] removing unused methods

---
 libraries/WiFiS3/src/Modem.cpp | 4 ++--
 libraries/WiFiS3/src/Modem.h   | 4 +---
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/libraries/WiFiS3/src/Modem.cpp b/libraries/WiFiS3/src/Modem.cpp
index aee3a57f5..8c58ec6d7 100644
--- a/libraries/WiFiS3/src/Modem.cpp
+++ b/libraries/WiFiS3/src/Modem.cpp
@@ -10,13 +10,13 @@
 using namespace std;
 
 /* -------------------------------------------------------------------------- */
-ModemClass::ModemClass(int tx, int rx) : beginned(false), delete_serial(false), _timeout(MODEM_TIMEOUT), trim_results(true), read_by_size(false) {
+ModemClass::ModemClass(int tx, int rx) : beginned(false), delete_serial(false), _timeout(MODEM_TIMEOUT), trim_results(true) {
 /* -------------------------------------------------------------------------- */
    _serial = new UART(tx,rx);
 }
 
 /* -------------------------------------------------------------------------- */
-ModemClass::ModemClass(UART * serial) : beginned(false) , delete_serial(true) , _serial(serial), _timeout(MODEM_TIMEOUT), trim_results(true), read_by_size(false) {
+ModemClass::ModemClass(UART * serial) : beginned(false) , delete_serial(true) , _serial(serial), _timeout(MODEM_TIMEOUT), trim_results(true) {
 /* -------------------------------------------------------------------------- */
 }
 
diff --git a/libraries/WiFiS3/src/Modem.h b/libraries/WiFiS3/src/Modem.h
index d69958850..3f785bc9d 100644
--- a/libraries/WiFiS3/src/Modem.h
+++ b/libraries/WiFiS3/src/Modem.h
@@ -35,7 +35,7 @@ class ModemClass {
   }
 
   void read_using_size() {
-    read_by_size = true;
+    // read_by_size = true; // deprecated
   }
   bool beginned;
 
@@ -79,8 +79,6 @@ class ModemClass {
   unsigned long _timeout;
   uint8_t tx_buff[MAX_BUFF_SIZE];
   bool trim_results;
-  bool read_by_size;
-  bool read_by_size_finished(std::string &rx);
   Stream * _serial_debug;
   uint8_t _debug_level = 0;
 };

From 2cdcca569622799655ecbbe84fcc0e2dbb21d41f Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Mon, 29 Jul 2024 14:26:22 +0200
Subject: [PATCH 5/5] [WIP baudrate autodetection]

---
 libraries/WiFiS3/src/Modem.cpp | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/libraries/WiFiS3/src/Modem.cpp b/libraries/WiFiS3/src/Modem.cpp
index 8c58ec6d7..c30737647 100644
--- a/libraries/WiFiS3/src/Modem.cpp
+++ b/libraries/WiFiS3/src/Modem.cpp
@@ -34,6 +34,20 @@ void ModemClass::begin(int badurate){
 /* -------------------------------------------------------------------------- */
    if(_serial != nullptr && !beginned) {
       _serial->begin(badurate);
+
+      if(_serial_debug && _debug_level >= 2) {
+         _serial_debug->println("Baudrate autodetection started");
+      }
+      // auto baudrate detection algorithm
+      do {
+         _serial->write(0x55);
+         // delay(1);
+      } while(!_serial->available() || _serial->read() != 0x55); // TODO put timeout
+
+      if(_serial_debug && _debug_level >= 2) {
+         _serial_debug->print("Baudrate autodetection terminated");
+      }
+
       beginned = true;
       string res = "";
       _serial->flush();