Remote.h
  1  // Copyright (C) 2024, Mark Qvist
  2  
  3  // This program is free software: you can redistribute it and/or modify
  4  // it under the terms of the GNU General Public License as published by
  5  // the Free Software Foundation, either version 3 of the License, or
  6  // (at your option) any later version.
  7  
  8  // This program is distributed in the hope that it will be useful,
  9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
 10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 11  // GNU General Public License for more details.
 12  
 13  // You should have received a copy of the GNU General Public License
 14  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 15  
 16  #include <WiFi.h>
 17  
 18  #if CONFIG_IDF_TARGET_ESP32
 19    #include "esp32/rom/rtc.h"
 20  #elif CONFIG_IDF_TARGET_ESP32S2
 21    #include "esp32s2/rom/rtc.h"
 22  #elif CONFIG_IDF_TARGET_ESP32C3
 23    #include "esp32c3/rom/rtc.h"
 24  #elif CONFIG_IDF_TARGET_ESP32S3
 25    #include "esp32s3/rom/rtc.h"
 26  #else 
 27    #error Target CONFIG_IDF_TARGET is not supported
 28  #endif
 29  
 30  #define WIFI_UPDATE_INTERVAL_MS 500
 31  #define WR_SOCKET_TIMEOUT 6
 32  #define WR_READ_TIMEOUT_MS 86400000 /* 24 hours - hosts often only receive, don't disconnect */
 33  #define WR_RECONNECT_INTERVAL_MS 10000
 34  
 35  uint32_t wifi_update_interval_ms = WIFI_UPDATE_INTERVAL_MS;
 36  uint32_t last_wifi_update = 0;
 37  uint32_t wr_last_connect_try = 0;
 38  uint32_t wr_last_read = 0;
 39  
 40  WiFiClient connection;
 41  WiFiServer remote_listener(7633, 1);
 42  IPAddress ap_ip(10, 0, 0, 1);
 43  IPAddress ap_nm(255, 255, 255, 0);
 44  IPAddress wr_device_ip;
 45  char wr_hostname[10];
 46  wl_status_t wr_wifi_status = WL_IDLE_STATUS;
 47  
 48  uint8_t wifi_mode = WIFI_OFF;
 49  bool wifi_init_ran = false;
 50  bool wifi_initialized = false;
 51  
 52  char wr_ssid[33];
 53  char wr_psk[33];
 54  
 55  extern void host_disconnected();
 56  
 57  void wifi_dbg(String msg) { Serial.print("[WiFi] "); Serial.println(msg); }
 58  
 59  uint8_t wifi_remote_mode() { return wifi_mode; }
 60  
 61  bool wifi_is_connected() { return (wr_wifi_status == WL_CONNECTED); }
 62  bool wifi_host_is_connected() { if (connection) { return true; } else { return false; } }
 63  
 64  void wifi_remote_start_ap() {
 65    WiFi.mode(WIFI_AP);
 66    if (wr_ssid[0] != 0x00) {
 67      if (wr_psk[0] != 0x00) { WiFi.softAP(wr_ssid, wr_psk, wr_channel); }
 68      else                   { WiFi.softAP(wr_ssid, NULL, wr_channel); }
 69    } else {
 70      if (wr_psk[0] != 0x00) { WiFi.softAP(bt_devname, wr_psk, wr_channel); }
 71      else                   { WiFi.softAP(bt_devname, NULL, wr_channel); }
 72    }
 73    delay(150);
 74    WiFi.softAPConfig(ap_ip, ap_ip, ap_nm);
 75    wifi_initialized = true;
 76  }
 77  
 78  void wifi_remote_start_sta() {
 79    WiFi.mode(WIFI_STA);
 80  
 81    uint8_t ip[4]; bool ip_ok = true;
 82    for (uint8_t i = 0; i < 4; i++) { ip[i]  = EEPROM.read(config_addr(ADDR_CONF_IP+i)); }
 83    if (ip[0]==0x00 && ip[1]==0x00 && ip[2]==0x00 && ip[3]==0x00) { ip_ok = false; }
 84    if (ip[0]==0xFF && ip[1]==0xFF && ip[2]==0xFF && ip[3]==0xFF) { ip_ok = false; }
 85  
 86    uint8_t nm[4]; bool nm_ok = true;
 87    for (uint8_t i = 0; i < 4; i++) { nm[i]  = EEPROM.read(config_addr(ADDR_CONF_NM+i)); }
 88    if (nm[0]==0x00 && nm[1]==0x00 && nm[2]==0x00 && nm[3]==0x00) { nm_ok = false; }
 89    if (nm[0]==0xFF && nm[1]==0xFF && nm[2]==0xFF && nm[3]==0xFF) { nm_ok = false; }
 90  
 91    if (ip_ok && nm_ok) {
 92      IPAddress sta_ip(ip[0], ip[1], ip[2], ip[3]);
 93      IPAddress sta_nm(nm[0], nm[1], nm[2], nm[3]);
 94      WiFi.config(sta_ip, sta_ip, sta_nm);
 95    }
 96  
 97    delay(100);
 98    if (wr_ssid[0] != 0x00) {
 99      if (wr_psk[0] != 0x00) { WiFi.begin(wr_ssid, wr_psk); }
100      else                   { WiFi.begin(wr_ssid); }
101    }
102    
103    delay(500);
104    wr_wifi_status = WiFi.status(); 
105    wifi_initialized = true;
106    wr_last_connect_try = millis();
107    #if defined(WIFI_PS_MAX_MODEM)
108      WiFi.setSleep(WIFI_PS_MAX_MODEM);
109    #else
110      WiFi.setSleep(true);
111    #endif
112  }
113  
114  void wifi_remote_stop() {
115    WiFi.softAPdisconnect(true);
116    WiFi.disconnect(true, true);
117    WiFi.mode(WIFI_MODE_NULL);
118    wifi_initialized = false;
119  }
120  
121  void wifi_remote_start() {
122    if      (wifi_mode == WR_WIFI_AP)  { wifi_remote_start_ap(); }
123    else if (wifi_mode == WR_WIFI_STA) { wifi_remote_start_sta(); }
124    else                               { wifi_remote_stop(); }
125  
126    if (wifi_initialized == true) {
127      remote_listener.begin();
128      remote_listener.setTimeout(WR_SOCKET_TIMEOUT);
129      wr_state = WR_STATE_ON;
130    } else { remote_listener.end(); wr_state = WR_STATE_OFF; }
131  }
132  
133  void wifi_remote_init() {
134    memcpy(wr_hostname, bt_devname, 5);
135    memcpy(wr_hostname+5, bt_devname+6, 4);
136    wr_hostname[9] = 0x00;
137    WiFi.softAPdisconnect(true);
138    WiFi.disconnect(true, true);
139    WiFi.mode(WIFI_MODE_NULL);
140    WiFi.setHostname(wr_hostname);
141  
142    wr_ssid[32] = 0x00; wr_psk[32] = 0x00;
143    for (uint8_t i = 0; i < 32; i++) { wr_ssid[i] = EEPROM.read(config_addr(ADDR_CONF_SSID+i)); if (wr_ssid[i] == 0xFF) { wr_ssid[i] = 0x00; } }
144    for (uint8_t i = 0; i < 32; i++) { wr_psk[i]  = EEPROM.read(config_addr(ADDR_CONF_PSK+i));  if (wr_psk[i]  == 0xFF) { wr_psk[i]  = 0x00; } }
145    wr_channel = EEPROM.read(eeprom_addr(ADDR_CONF_WCHN)); if (wr_channel < 1 || wr_channel > 14) { wr_channel = WR_CHANNEL_DEFAULT; }
146    wifi_remote_start();
147    wifi_init_ran = true;
148  }
149  
150  void wifi_remote_close_all() {
151    // wifi_dbg("Close all"); // TODO: Remove debug
152    if (connection) { connection.stop(); }
153    WiFiClient client = remote_listener.available();
154    while (client) { client.stop(); client = remote_listener.available(); }
155    wr_state = WR_STATE_ON;
156  }
157  
158  void wifi_remote_check_active() {
159    if (millis()-wr_last_read >= WR_READ_TIMEOUT_MS) {
160      // wifi_dbg("Connection activity timed out"); // TODO: Remove debug
161      if (connection && connection.connected()) {
162        connection.stop();
163        wifi_remote_close_all();
164        host_disconnected();
165      }
166    }
167  }
168  
169  bool wifi_remote_available() {
170    if (connection) {
171      if (connection.connected()) {
172        if (connection.available()) { wr_last_read = millis(); return true; }
173        else                        { wifi_remote_check_active(); return false; }
174      } else {
175        // wifi_dbg("Client disconnected"); // TODO: Remove debug
176        wifi_remote_close_all();
177        return false;
178      }
179    } else {
180      WiFiClient client = remote_listener.available();
181      if (!client) { return false; }
182      else {
183        // wifi_dbg("Client connected"); // TODO: Remove debug
184        connection = client;
185        wr_state = WR_STATE_CONNECTED;
186        wr_last_read = millis();
187        if (connection.available()) { return true; }
188        else                        { return false; }
189      }
190    }
191  }
192  
193  uint8_t wifi_remote_read() {
194    if (connection && connection.available()) { return connection.read(); }
195    else {
196      // wifi_dbg("Error: No data to read from TCP socket"); // TODO: Remove debug
197      if (connection) { wifi_remote_close_all(); }
198      return 0xC0;
199    }
200  }
201  
202  void wifi_remote_write(uint8_t byte) { if (connection) { connection.write(byte); } }
203  void wifi_remote_flush(void) { if (connection) { connection.flush(); } }
204  
205  void wifi_update_status() {
206    wr_wifi_status = WiFi.status();
207    if (wr_wifi_status == WL_CONNECTED) { wr_device_ip = WiFi.localIP(); }
208    if (wifi_mode == WR_WIFI_AP && wifi_initialized) { wr_device_ip = WiFi.softAPIP(); wr_wifi_status = WL_CONNECTED; }
209    if (wifi_init_ran && wifi_mode == WR_WIFI_STA && wr_wifi_status != WL_CONNECTED) {
210      if (millis()-wr_last_connect_try >= WR_RECONNECT_INTERVAL_MS) { wifi_remote_init(); }
211    }
212  }
213  
214  void update_wifi() {
215    if (millis()-last_wifi_update >= wifi_update_interval_ms) {
216      wifi_update_status();
217      last_wifi_update = millis();
218    }
219  }