Files
MeshCore/src/helpers/wifi/SerialWifiInterface.h
T

169 lines
5.7 KiB
C++

#pragma once
#include "../BaseSerialInterface.h"
#include "../UsbLogging.h"
#include <WiFi.h>
class SerialWifiInterface : public BaseSerialInterface {
public:
enum class State : uint8_t { Off, WaitingIp, Retrying, Listening };
enum class StartError : uint8_t { None, InvalidPort, StartFailed };
enum class DisconnectReason : uint8_t {
None, PeerClosed, FrameTimeout, InvalidHeader, ShortHeader, ShortPayload,
Replaced, Disabled, Ended, TxError, TxTimeout
};
struct Status {
State state;
bool enabled; // Logical enable intent, including a listener awaiting retry.
bool requested;
bool addressReady;
bool listening; // Actual SDK listener state, not merely an assigned IP.
bool clientConnected;
uint16_t port;
uint32_t beginAttempts;
uint32_t beginFailures;
uint32_t retryInMs;
StartError lastStartError;
int lastErrno;
uint32_t acceptedClients;
uint32_t validFrames;
uint8_t lastOpcode;
uint32_t txFrames;
uint8_t lastTxOpcode;
uint32_t disconnects;
DisconnectReason lastDisconnectReason;
};
private:
bool deviceConnected;
// Keep enable intent separate from actual protocol availability. A failed
// cold listener needs loop service; a healthy accepted socket remains usable.
bool _isEnabled;
unsigned long _last_write;
// Arduino's listener conversion and client health accessor are not const.
mutable WiFiServer server;
mutable WiFiClient client;
bool listener_requested = false;
bool listener_was_healthy = false;
bool listener_attempted = false;
uint16_t listener_port = 0;
uint8_t consecutive_start_failures = 0;
uint32_t listener_last_attempt = 0;
uint32_t listener_retry_ms = 0;
uint32_t listener_begin_attempts = 0;
uint32_t listener_begin_failures = 0;
StartError listener_last_error = StartError::None;
int listener_last_errno = 0;
uint32_t accepted_clients = 0;
uint32_t valid_frames = 0;
uint8_t last_opcode = 0;
uint32_t tx_frames = 0;
uint8_t last_tx_opcode = 0;
bool tx_waiting = false;
uint32_t tx_last_progress = 0;
uint32_t disconnect_count = 0;
DisconnectReason last_disconnect_reason = DisconnectReason::None;
struct FrameHeader {
uint8_t type;
uint16_t length;
};
struct Frame {
uint8_t len;
uint8_t buf[MAX_FRAME_SIZE];
};
FrameHeader received_frame_header;
#define FRAME_QUEUE_SIZE 4
// Reuse the former (unused) receive queue for one displaced client's replies.
// No heap allocation or additional frame buffer is needed on reconnect.
int held_queue_len;
Frame held_queue[FRAME_QUEUE_SIZE];
int send_queue_len;
Frame send_queue[FRAME_QUEUE_SIZE];
IPAddress queue_ip;
IPAddress held_ip;
bool queue_has_ip = false;
uint32_t held_since = 0;
size_t send_offset = 0;
uint32_t receive_started = 0;
bool receive_pending = false;
bool receive_capacity_paused = false;
uint32_t receive_capacity_paused_at = 0;
void (*session_changed)(void*) = nullptr;
void* session_context = nullptr;
void clearBuffers();
void expireHeldQueue(uint32_t now);
void selectClient(const IPAddress& ip, uint32_t now);
void disconnectClient(bool cancel_session, DisconnectReason reason);
bool addressReady();
bool serviceListener(uint32_t now);
void stopListener(DisconnectReason reason);
bool responseCapacityAvailable() const;
void updateReceiveCapacityPause(uint32_t now);
protected:
public:
SerialWifiInterface() : server(WiFiServer()), client(WiFiClient()) {
deviceConnected = false;
_isEnabled = false;
_last_write = 0;
clearBuffers();
received_frame_header.type = 0;
received_frame_header.length = 0;
}
// Explicitly request a listener; does not enable the protocol interface.
// Failure or an unready network is retried by loop() only while enabled.
bool begin(int port);
void end();
Status getStatus();
static const char* stateName(State state);
static const char* startErrorName(StartError error);
static const char* disconnectReasonName(DisconnectReason reason);
// Called synchronously while disconnected, on teardown or before a different
// IP can send commands/receive replies. Same-IP live replacement preserves
// the existing owner; actual disconnects cancel its pending operations.
void setSessionChangedCallback(void (*callback)(void*), void* context) {
session_changed = callback;
session_context = context;
}
// BaseSerialInterface methods
void enable() override;
void disable() override;
bool isEnabled() const override {
// Losing the accept listener must not cancel a stream whose separately
// accepted socket is still healthy. Failed cold starts remain unavailable.
return _isEnabled && listener_requested
&& (static_cast<bool>(server) || (deviceConnected && client.connected()));
}
void loop() override;
bool isConnected() const override;
bool isReadBusy() const override;
bool isWriteBusy() const override;
// A parked backlog is not runnable work and must not keep the MCU awake.
bool hasPendingIO() const override { return _isEnabled && deviceConnected && send_queue_len > 0; }
size_t writeFrame(const uint8_t src[], size_t len) override;
size_t checkRecvFrame(uint8_t dest[]) override;
bool hasReceivedFrameHeader();
void resetReceivedFrameHeader();
};
#if WIFI_DEBUG_LOGGING && ARDUINO
#include <Arduino.h>
#define WIFI_DEBUG_PRINT(F, ...) do { if (mesh::isUsbDebugLoggingEnabled()) { mesh::usbLoggingPort().printf("WiFi: " F, ##__VA_ARGS__); } } while(0)
#define WIFI_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbDebugLoggingEnabled()) { mesh::usbLoggingPort().printf("WiFi: " F "\n", ##__VA_ARGS__); } } while(0)
#else
#define WIFI_DEBUG_PRINT(...) {}
#define WIFI_DEBUG_PRINTLN(...) {}
#endif