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();