Merge branch 'develop' of github.com:meshtastic/firmware into xmodem-proto

This commit is contained in:
Thomas Göttgens
2023-01-12 10:00:04 +01:00
42 changed files with 359 additions and 324 deletions

View File

@@ -210,7 +210,7 @@ Channel &Channels::getByIndex(ChannelIndex chIndex)
Channel &Channels::getByName(const char* chName)
{
for (ChannelIndex i = 0; i < getNumChannels(); i++) {
if (strcasecmp(channelFile.channels[i].settings.name, chName) == 0) {
if (strcasecmp(getGlobalId(i), chName) == 0) {
return channelFile.channels[i];
}
}

View File

@@ -2,9 +2,23 @@
#include "SX126xInterface.cpp"
#include "SX128xInterface.h"
#include "SX128xInterface.cpp"
#include "api/ServerAPI.h"
#include "api/ServerAPI.cpp"
// We need this declaration for proper linking in derived classes
template class SX126xInterface<SX1262>;
template class SX126xInterface<SX1268>;
template class SX126xInterface<LLCC68>;
template class SX128xInterface<SX1280>;
#if HAS_ETHERNET
#include "api/ethServerAPI.h"
template class ServerAPI<EthernetClient>;
template class APIServerPort<ethServerAPI, EthernetServer>;
#endif
#if HAS_WIFI
#include "api/WiFiServerAPI.h"
template class ServerAPI<WiFiClient>;
template class APIServerPort<WiFiServerAPI, WiFiServer>;
#endif

View File

@@ -280,8 +280,7 @@ NodeInfo *MeshService::refreshMyNodeInfo()
node->last_heard =
getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid
// For the time in the position field, only set that if we have a real GPS clock
position.time = getValidTime(RTCQualityGPS);
position.time = getValidTime(RTCQualityFromNet);
updateBatteryLevel(powerStatus->getBatteryChargePercent());
@@ -327,4 +326,4 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
bool MeshService::isToPhoneQueueEmpty()
{
return toPhoneQueue.isEmpty();
}
}

View File

@@ -89,9 +89,6 @@ class PhoneAPI : public Observer<uint32_t> // FIXME, we shouldn't be inheriting
void setInitialState() { state = STATE_SEND_MY_INFO; }
/// emit a debugging log character, FIXME - implement
void debugOut(char c) { }
protected:
/// Our fromradio packet while it is being assembled
FromRadio fromRadioScratch = {};

View File

@@ -138,7 +138,7 @@ void Router::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelI
void Router::abortSendAndNak(Routing_Error err, MeshPacket *p)
{
LOG_ERROR("Error=%d, returning NAK and dropping packet.\n", err);
sendAckNak(Routing_Error_NO_INTERFACE, getFrom(p), p->id, p->channel);
sendAckNak(err, getFrom(p), p->id, p->channel);
packetPool.release(p);
}
@@ -199,15 +199,19 @@ ErrorCode Router::send(MeshPacket *p)
} // should have already been handled by sendLocal
// Abort sending if we are violating the duty cycle
if (!config.lora.override_duty_cycle && myRegion->dutyCycle != 100) {
float hourlyTxPercent = airTime->utilizationTXPercent();
if (hourlyTxPercent > myRegion->dutyCycle) {
uint8_t silentMinutes = airTime->getSilentMinutes(hourlyTxPercent, myRegion->dutyCycle);
LOG_WARN("Duty cycle limit exceeded. Aborting send for now, you can send again in %d minutes.\n", silentMinutes);
Routing_Error err = Routing_Error_DUTY_CYCLE_LIMIT;
abortSendAndNak(err, p);
return err;
}
if (!config.lora.override_duty_cycle && myRegion->dutyCycle < 100) {
float hourlyTxPercent = airTime->utilizationTXPercent();
if (hourlyTxPercent > myRegion->dutyCycle) {
uint8_t silentMinutes = airTime->getSilentMinutes(hourlyTxPercent, myRegion->dutyCycle);
LOG_WARN("Duty cycle limit exceeded. Aborting send for now, you can send again in %d minutes.\n", silentMinutes);
Routing_Error err = Routing_Error_DUTY_CYCLE_LIMIT;
if (getFrom(p) == nodeDB.getNodeNum()) { // only send NAK to API, not to the mesh
abortSendAndNak(err, p);
} else {
packetPool.release(p);
}
return err;
}
}
// PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0;

View File

@@ -70,7 +70,7 @@ bool SX126xInterface<T>::init()
#if defined(SX126X_TXEN) && (SX126X_TXEN != RADIOLIB_NC)
// lora.begin sets Dio2 as RF switch control, which is not true if we are manually controlling RX and TX
if (res == RADIOLIB_ERR_NONE)
res = lora.setDio2AsRfSwitch(false);
res = lora.setDio2AsRfSwitch(true);
#endif
#if 0

View File

@@ -18,9 +18,6 @@ class SinglePortModule : public MeshModule
SinglePortModule(const char *_name, PortNum _ourPortNum) : MeshModule(_name), ourPortNum(_ourPortNum) {}
protected:
uint32_t max_channel_util_percent = 40;
uint32_t polite_channel_util_percent = 25;
/**
* @return true if you want to receive the specified portnum
*/

View File

@@ -0,0 +1,67 @@
#include "ServerAPI.h"
#include "configuration.h"
#include <Arduino.h>
template<typename T>
ServerAPI<T>::ServerAPI(T &_client) : StreamAPI(&client), concurrency::OSThread("ServerAPI"), client(_client)
{
LOG_INFO("Incoming wifi connection\n");
}
template<typename T>
ServerAPI<T>::~ServerAPI()
{
client.stop();
}
template<typename T>
void ServerAPI<T>::close()
{
client.stop(); // drop tcp connection
StreamAPI::close();
}
/// Check the current underlying physical link to see if the client is currently connected
template<typename T>
bool ServerAPI<T>::checkIsConnected()
{
return client.connected();
}
template<class T>
int32_t ServerAPI<T>::runOnce()
{
if (client.connected()) {
return StreamAPI::runOncePart();
} else {
LOG_INFO("Client dropped connection, suspending API service\n");
enabled = false; // we no longer need to run
return 0;
}
}
template<class T, class U>
APIServerPort<T, U>::APIServerPort(int port) : U(port), concurrency::OSThread("ApiServer") {}
template<class T, class U>
void APIServerPort<T, U>::init()
{
U::begin();
}
template<class T, class U>
int32_t APIServerPort<T, U>::runOnce()
{
auto client = U::available();
if (client) {
// Close any previous connection (see FIXME in header file)
if (openAPI) {
LOG_INFO("Force closing previous TCP connection\n");
delete openAPI;
}
openAPI = new T(client);
}
return 100; // only check occasionally for incoming connections
}

View File

@@ -1,21 +1,21 @@
#pragma once
#include "StreamAPI.h"
#include <WiFi.h>
/**
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
*/
class WiFiServerAPI : public StreamAPI, private concurrency::OSThread
template<class T>
class ServerAPI : public StreamAPI, private concurrency::OSThread
{
private:
WiFiClient client;
T client;
public:
explicit WiFiServerAPI(WiFiClient &_client);
explicit ServerAPI(T &_client);
virtual ~WiFiServerAPI();
virtual ~ServerAPI();
/// override close to also shutdown the TCP link
virtual void close();
@@ -34,25 +34,21 @@ class WiFiServerAPI : public StreamAPI, private concurrency::OSThread
/**
* Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
*/
class WiFiServerPort : public WiFiServer, private concurrency::OSThread
template<class T, class U>
class APIServerPort : public U, private concurrency::OSThread
{
/** The currently open port
*
* FIXME: We currently only allow one open TCP connection at a time, because we depend on the loop() call in this class to
* delegate to the worker. Once coroutines are implemented we can relax this restriction.
*/
WiFiServerAPI *openAPI = NULL;
T *openAPI = NULL;
public:
explicit WiFiServerPort(int port);
explicit APIServerPort(int port);
void init();
/// If an api server is running, we try to spit out debug 'serial' characters there
static void debugOut(char c);
protected:
int32_t runOnce() override;
};
void initApiServer(int port=4403);

View File

@@ -0,0 +1,25 @@
#include "configuration.h"
#include <Arduino.h>
#if HAS_WIFI
#include "WiFiServerAPI.h"
static WiFiServerPort *apiPort;
void initApiServer(int port)
{
// Start API server on port 4403
if (!apiPort) {
apiPort = new WiFiServerPort(port);
LOG_INFO("API server listening on TCP port %d\n", port);
apiPort->init();
}
}
WiFiServerAPI::WiFiServerAPI(WiFiClient &_client) : ServerAPI(_client)
{
LOG_INFO("Incoming wifi connection\n");
}
WiFiServerPort::WiFiServerPort(int port) : APIServerPort(port) {}
#endif

View File

@@ -0,0 +1,25 @@
#pragma once
#include "ServerAPI.h"
#include <WiFi.h>
/**
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
*/
class WiFiServerAPI : public ServerAPI<WiFiClient>
{
public:
explicit WiFiServerAPI(WiFiClient &_client);
};
/**
* Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
*/
class WiFiServerPort : public APIServerPort<WiFiServerAPI, WiFiServer>
{
public:
explicit WiFiServerPort(int port);
};
void initApiServer(int port=4403);

View File

@@ -0,0 +1,27 @@
#include "configuration.h"
#include <Arduino.h>
#if HAS_ETHERNET
#include "ethServerAPI.h"
static ethServerPort *apiPort;
void initApiServer(int port)
{
// Start API server on port 4403
if (!apiPort) {
apiPort = new ethServerPort(port);
LOG_INFO("API server listening on TCP port %d\n", port);
apiPort->init();
}
}
ethServerAPI::ethServerAPI(EthernetClient &_client) : ServerAPI(_client)
{
LOG_INFO("Incoming ethernet connection\n");
}
ethServerPort::ethServerPort(int port) : APIServerPort(port) {}
#endif

View File

@@ -0,0 +1,25 @@
#pragma once
#include "ServerAPI.h"
#include <RAK13800_W5100S.h>
/**
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
*/
class ethServerAPI : public ServerAPI<EthernetClient>
{
public:
explicit ethServerAPI(EthernetClient &_client);
};
/**
* Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
*/
class ethServerPort : public APIServerPort<ethServerAPI, EthernetServer>
{
public:
explicit ethServerPort(int port);
};
void initApiServer(int port=4403);

View File

@@ -5,7 +5,7 @@
#include <SPI.h>
#include <RAK13800_W5100S.h>
#include "target_specific.h"
#include "mesh/eth/ethServerAPI.h"
#include "mesh/api/ethServerAPI.h"
#include "mqtt/MQTT.h"
#ifndef DISABLE_NTP

View File

@@ -1,82 +0,0 @@
#include "ethServerAPI.h"
#include "configuration.h"
#include <Arduino.h>
static ethServerPort *apiPort;
void initApiServer(int port)
{
// Start API server on port 4403
if (!apiPort) {
apiPort = new ethServerPort(port);
LOG_INFO("API server listening on TCP port %d\n", port);
apiPort->init();
}
}
ethServerAPI::ethServerAPI(EthernetClient &_client) : StreamAPI(&client), concurrency::OSThread("ethServerAPI"), client(_client)
{
LOG_INFO("Incoming ethernet connection\n");
}
ethServerAPI::~ethServerAPI()
{
client.stop();
// FIXME - delete this if the client dropps the connection!
}
/// override close to also shutdown the TCP link
void ethServerAPI::close()
{
client.stop(); // drop tcp connection
StreamAPI::close();
}
/// Check the current underlying physical link to see if the client is currently connected
bool ethServerAPI::checkIsConnected()
{
return client.connected();
}
int32_t ethServerAPI::runOnce()
{
if (client.connected()) {
return StreamAPI::runOncePart();
} else {
LOG_INFO("Client dropped connection, suspending API service\n");
enabled = false; // we no longer need to run
return 0;
}
}
/// If an api server is running, we try to spit out debug 'serial' characters there
void ethServerPort::debugOut(char c)
{
if (apiPort && apiPort->openAPI)
apiPort->openAPI->debugOut(c);
}
ethServerPort::ethServerPort(int port) : EthernetServer(port), concurrency::OSThread("ApiServer") {}
void ethServerPort::init()
{
begin();
}
int32_t ethServerPort::runOnce()
{
auto client = available();
if (client) {
// Close any previous connection (see FIXME in header file)
if (openAPI) {
LOG_WARN("Force closing previous TCP connection\n");
delete openAPI;
}
openAPI = new ethServerAPI(client);
}
return 100; // only check occasionally for incoming connections
}

View File

@@ -1,58 +0,0 @@
#pragma once
#include "StreamAPI.h"
#include <RAK13800_W5100S.h>
/**
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
*/
class ethServerAPI : public StreamAPI, private concurrency::OSThread
{
private:
EthernetClient client;
public:
explicit ethServerAPI(EthernetClient &_client);
virtual ~ethServerAPI();
/// override close to also shutdown the TCP link
virtual void close();
protected:
/// We override this method to prevent publishing EVENT_SERIAL_CONNECTED/DISCONNECTED for wifi links (we want the board to
/// stay in the POWERED state to prevent disabling wifi)
virtual void onConnectionChanged(bool connected) override {}
virtual int32_t runOnce() override; // Check for dropped client connections
/// Check the current underlying physical link to see if the client is currently connected
virtual bool checkIsConnected() override;
};
/**
* Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
*/
class ethServerPort : public EthernetServer, private concurrency::OSThread
{
/** The currently open port
*
* FIXME: We currently only allow one open TCP connection at a time, because we depend on the loop() call in this class to
* delegate to the worker. Once coroutines are implemented we can relax this restriction.
*/
ethServerAPI *openAPI = NULL;
public:
explicit ethServerPort(int port);
void init();
/// If an api server is running, we try to spit out debug 'serial' characters there
static void debugOut(char c);
protected:
int32_t runOnce() override;
};
void initApiServer(int port=4403);

View File

@@ -157,7 +157,7 @@ extern const pb_msgdesc_t LocalModuleConfig_msg;
/* Maximum encoded size of messages (where known) */
#define LocalConfig_size 391
#define LocalModuleConfig_size 380
#define LocalModuleConfig_size 412
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -85,6 +85,8 @@ typedef enum _HardwareModel {
HardwareModel_HELTEC_V3 = 43,
/* New Heltec Wireless Stick Lite with ESP32-S3 CPU */
HardwareModel_HELTEC_WSL_V3 = 44,
/* New BETAFPV ELRS Micro TX Module 2.4G with ESP32 CPU */
HardwareModel_BETAFPV_2400_TX = 45,
/* Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. */
HardwareModel_PRIVATE_HW = 255
} HardwareModel;
@@ -440,9 +442,10 @@ typedef struct _Waypoint {
bool locked;
/* Name of the waypoint - max 30 chars */
char name[30];
/* *
Description of the waypoint - max 100 chars */
/* Description of the waypoint - max 100 chars */
char description[100];
/* Designator icon for the waypoint in the form of a unicode emoji */
uint32_t icon;
} Waypoint;
typedef PB_BYTES_ARRAY_T(256) MeshPacket_encrypted_t;
@@ -783,7 +786,7 @@ extern "C" {
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define Routing_init_default {0, {RouteDiscovery_init_default}}
#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
#define Waypoint_init_default {0, 0, 0, 0, 0, "", ""}
#define Waypoint_init_default {0, 0, 0, 0, 0, "", "", 0}
#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN}
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0, false, DeviceMetrics_init_default}
#define MyNodeInfo_init_default {0, 0, 0, "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, 0, 0}
@@ -797,7 +800,7 @@ extern "C" {
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define Routing_init_zero {0, {RouteDiscovery_init_zero}}
#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
#define Waypoint_init_zero {0, 0, 0, 0, 0, "", ""}
#define Waypoint_init_zero {0, 0, 0, 0, 0, "", "", 0}
#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN}
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0, false, DeviceMetrics_init_zero}
#define MyNodeInfo_init_zero {0, 0, 0, "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, 0, 0}
@@ -855,6 +858,7 @@ extern "C" {
#define Waypoint_locked_tag 5
#define Waypoint_name_tag 6
#define Waypoint_description_tag 7
#define Waypoint_icon_tag 8
#define MeshPacket_from_tag 1
#define MeshPacket_to_tag 2
#define MeshPacket_channel_tag 3
@@ -987,7 +991,8 @@ X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 3) \
X(a, STATIC, SINGULAR, UINT32, expire, 4) \
X(a, STATIC, SINGULAR, BOOL, locked, 5) \
X(a, STATIC, SINGULAR, STRING, name, 6) \
X(a, STATIC, SINGULAR, STRING, description, 7)
X(a, STATIC, SINGULAR, STRING, description, 7) \
X(a, STATIC, SINGULAR, FIXED32, icon, 8)
#define Waypoint_CALLBACK NULL
#define Waypoint_DEFAULT NULL
@@ -1144,7 +1149,7 @@ extern const pb_msgdesc_t Compressed_msg;
#define Routing_size 42
#define ToRadio_size 324
#define User_size 77
#define Waypoint_size 156
#define Waypoint_size 161
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -80,7 +80,7 @@ typedef struct _ModuleConfig_MQTTConfig {
bool enabled;
/* The server to use for our MQTT global message gateway feature.
If not set, the default server will be used */
char address[32];
char address[64];
/* MQTT username to use (most useful for a custom MQTT server).
If using a custom server, this will be honoured even if empty.
If using the default server, this will only be honoured if set, otherwise the device will use the default username */
@@ -553,13 +553,13 @@ extern const pb_msgdesc_t ModuleConfig_CannedMessageConfig_msg;
#define ModuleConfig_AudioConfig_size 19
#define ModuleConfig_CannedMessageConfig_size 49
#define ModuleConfig_ExternalNotificationConfig_size 40
#define ModuleConfig_MQTTConfig_size 169
#define ModuleConfig_MQTTConfig_size 201
#define ModuleConfig_RangeTestConfig_size 10
#define ModuleConfig_RemoteHardwareConfig_size 2
#define ModuleConfig_SerialConfig_size 26
#define ModuleConfig_StoreForwardConfig_size 22
#define ModuleConfig_TelemetryConfig_size 18
#define ModuleConfig_size 172
#define ModuleConfig_size 204
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -5,7 +5,7 @@
#include "configuration.h"
#include "main.h"
#include "mesh/http/WebServer.h"
#include "mesh/wifi/WiFiServerAPI.h"
#include "mesh/api/WiFiServerAPI.h"
#include "mqtt/MQTT.h"
#include "target_specific.h"
#include <ESPmDNS.h>
@@ -55,11 +55,13 @@ static int32_t reconnectWiFi()
// Make sure we clear old connection credentials
WiFi.disconnect(false, true);
LOG_INFO("Reconnecting to WiFi access point %s\n",wifiName);
WiFi.mode(WIFI_MODE_STA);
WiFi.begin(wifiName, wifiPsw);
delay(5000);
if (!WiFi.isConnected()) {
WiFi.begin(wifiName, wifiPsw);
}
}
#ifndef DISABLE_NTP
@@ -167,7 +169,7 @@ bool initWifi()
WiFi.mode(WIFI_MODE_STA);
WiFi.setHostname(ourHost);
WiFi.onEvent(WiFiEvent);
WiFi.setAutoReconnect(false);
WiFi.setAutoReconnect(true);
WiFi.setSleep(false);
if (config.network.address_mode == Config_NetworkConfig_AddressMode_STATIC && config.network.ipv4_config.ip != 0) {
WiFi.config(config.network.ipv4_config.ip,
@@ -182,7 +184,8 @@ bool initWifi()
WiFi.onEvent(
[](WiFiEvent_t event, WiFiEventInfo_t info) {
LOG_WARN("WiFi lost connection. Reason: %d", info.wifi_sta_disconnected.reason);
LOG_WARN("WiFi lost connection. Reason: %d\n", info.wifi_sta_disconnected.reason);
/*
If we are disconnected from the AP for some reason,

View File

@@ -1,82 +0,0 @@
#include "WiFiServerAPI.h"
#include "configuration.h"
#include <Arduino.h>
static WiFiServerPort *apiPort;
void initApiServer(int port)
{
// Start API server on port 4403
if (!apiPort) {
apiPort = new WiFiServerPort(port);
LOG_INFO("API server listening on TCP port %d\n", port);
apiPort->init();
}
}
WiFiServerAPI::WiFiServerAPI(WiFiClient &_client) : StreamAPI(&client), concurrency::OSThread("WiFiServerAPI"), client(_client)
{
LOG_INFO("Incoming wifi connection\n");
}
WiFiServerAPI::~WiFiServerAPI()
{
client.stop();
// FIXME - delete this if the client dropps the connection!
}
/// override close to also shutdown the TCP link
void WiFiServerAPI::close()
{
client.stop(); // drop tcp connection
StreamAPI::close();
}
/// Check the current underlying physical link to see if the client is currently connected
bool WiFiServerAPI::checkIsConnected()
{
return client.connected();
}
int32_t WiFiServerAPI::runOnce()
{
if (client.connected()) {
return StreamAPI::runOncePart();
} else {
LOG_INFO("Client dropped connection, suspending API service\n");
enabled = false; // we no longer need to run
return 0;
}
}
/// If an api server is running, we try to spit out debug 'serial' characters there
void WiFiServerPort::debugOut(char c)
{
if (apiPort && apiPort->openAPI)
apiPort->openAPI->debugOut(c);
}
WiFiServerPort::WiFiServerPort(int port) : WiFiServer(port), concurrency::OSThread("ApiServer") {}
void WiFiServerPort::init()
{
begin();
}
int32_t WiFiServerPort::runOnce()
{
auto client = available();
if (client) {
// Close any previous connection (see FIXME in header file)
if (openAPI) {
LOG_INFO("Force closing previous TCP connection\n");
delete openAPI;
}
openAPI = new WiFiServerAPI(client);
}
return 100; // only check occasionally for incoming connections
}