[core] Move arduino/ files to cores/
This commit is contained in:
208
cores/beken-72xx/arduino/libraries/LT/LT.cpp
Normal file
208
cores/beken-72xx/arduino/libraries/LT/LT.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
|
||||
|
||||
#include <LibreTuyaAPI.h>
|
||||
#include <libraries/Flash/Flash.h>
|
||||
|
||||
// can't include <flash.h> as it collides with <Flash.h> on Windows -_-
|
||||
#define REG_FLASH_BASE 0x00803000
|
||||
#define REG_FLASH_OPERATE_SW (REG_FLASH_BASE + 0 * 4)
|
||||
#define REG_FLASH_RDID (REG_FLASH_BASE + 4 * 4)
|
||||
#define FLASH_BUSY_SW (0x01UL << 31)
|
||||
#define FLASH_WP_VALUE (0x01UL << 30)
|
||||
#define FLASH_OP_SW (0x01UL << 29)
|
||||
#define FLASH_OP_TYPE_POS 24
|
||||
#define FLASH_OP_RDID 20
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <flash_pub.h>
|
||||
#include <param_config.h>
|
||||
#include <start_type_pub.h>
|
||||
#include <sys_ctrl.h>
|
||||
#include <sys_rtos.h>
|
||||
#include <wdt_pub.h>
|
||||
#include <wlan_ui_pub.h>
|
||||
|
||||
extern uint8_t system_mac[];
|
||||
extern uint32_t wdt_ctrl(uint32_t cmd, void *param);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
void LibreTuya::restart() {
|
||||
bk_reboot();
|
||||
}
|
||||
|
||||
void LibreTuya::restartDownloadMode() {
|
||||
bk_reboot();
|
||||
}
|
||||
|
||||
ResetReason LibreTuya::getResetReason() {
|
||||
switch (bk_misc_get_start_type()) {
|
||||
case RESET_SOURCE_POWERON:
|
||||
return RESET_REASON_POWER;
|
||||
|
||||
case RESET_SOURCE_REBOOT:
|
||||
return RESET_REASON_SOFTWARE;
|
||||
|
||||
case RESET_SOURCE_WATCHDOG:
|
||||
return RESET_REASON_WATCHDOG;
|
||||
|
||||
case RESET_SOURCE_CRASH_XAT0:
|
||||
case RESET_SOURCE_CRASH_UNDEFINED:
|
||||
case RESET_SOURCE_CRASH_PREFETCH_ABORT:
|
||||
case RESET_SOURCE_CRASH_DATA_ABORT:
|
||||
case RESET_SOURCE_CRASH_UNUSED:
|
||||
case RESET_SOURCE_CRASH_PER_XAT0:
|
||||
return RESET_REASON_CRASH;
|
||||
|
||||
case RESET_SOURCE_DEEPPS_GPIO:
|
||||
case RESET_SOURCE_DEEPPS_RTC:
|
||||
return RESET_REASON_SLEEP;
|
||||
}
|
||||
return RESET_REASON_UNKNOWN;
|
||||
}
|
||||
|
||||
/* CPU-related */
|
||||
|
||||
ChipType LibreTuya::getChipType() {
|
||||
uint8_t chipId = *(uint8_t *)(SCTRL_CHIP_ID);
|
||||
return CHIP_TYPE_ENUM(FAMILY, chipId);
|
||||
}
|
||||
|
||||
const char *LibreTuya::getChipModel() {
|
||||
return STRINGIFY_MACRO(MCU);
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getChipId() {
|
||||
uint8_t mac[6];
|
||||
cfg_load_mac(mac); // force loading MAC from TLV (ignore user-set WiFi MAC)
|
||||
return (mac[3]) | (mac[4] << 8) | (mac[5] << 16);
|
||||
}
|
||||
|
||||
uint8_t LibreTuya::getChipCores() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *LibreTuya::getChipCoreType() {
|
||||
return "ARM968E-S";
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getCpuFreq() {
|
||||
return configCPU_CLOCK_HZ;
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getCycleCount() {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Flash memory utilities */
|
||||
|
||||
FlashId LibreTuya::getFlashChipId() {
|
||||
uint32_t data = (FLASH_OP_RDID << FLASH_OP_TYPE_POS) | FLASH_OP_SW | FLASH_WP_VALUE;
|
||||
REG_WRITE(REG_FLASH_OPERATE_SW, data);
|
||||
while (REG_READ(REG_FLASH_OPERATE_SW) & FLASH_BUSY_SW) {}
|
||||
FlashId id = {
|
||||
.manufacturerId = REG_RD8(REG_FLASH_RDID, 2),
|
||||
.chipId = REG_RD8(REG_FLASH_RDID, 1),
|
||||
.chipSizeId = REG_RD8(REG_FLASH_RDID, 0),
|
||||
};
|
||||
return id;
|
||||
}
|
||||
|
||||
/* Memory management */
|
||||
|
||||
uint32_t LibreTuya::getRamSize() {
|
||||
return 256 * 1024;
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getHeapSize() {
|
||||
#if configDYNAMIC_HEAP_SIZE
|
||||
extern unsigned char _empty_ram;
|
||||
#if CFG_SOC_NAME == SOC_BK7231N
|
||||
return (0x00400000 + 192 * 1024) - (uint32_t)(&_empty_ram);
|
||||
#else
|
||||
return (0x00400000 + 256 * 1024) - (uint32_t)(&_empty_ram);
|
||||
#endif
|
||||
#else
|
||||
return configTOTAL_HEAP_SIZE;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getFreeHeap() {
|
||||
return xPortGetFreeHeapSize();
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getMinFreeHeap() {
|
||||
return xPortGetMinimumEverFreeHeapSize();
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getMaxAllocHeap() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* OTA-related */
|
||||
|
||||
static int8_t otaImage2Valid = -1;
|
||||
|
||||
uint8_t LibreTuya::otaGetRunning() {
|
||||
// Beken has bootloader-based OTA, running app is always index 1
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t LibreTuya::otaGetStoredIndex() {
|
||||
return otaHasImage2() ? 2 : 1;
|
||||
}
|
||||
|
||||
bool LibreTuya::otaSupportsDual() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibreTuya::otaHasImage1() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibreTuya::otaHasImage2() {
|
||||
if (otaImage2Valid != -1)
|
||||
return otaImage2Valid;
|
||||
// check download RBL
|
||||
// TODO: maybe check header CRC or even binary hashes
|
||||
uint32_t magic;
|
||||
Flash.readBlock(FLASH_DOWNLOAD_OFFSET, (uint8_t *)&magic, 4);
|
||||
otaImage2Valid = magic == 0x004C4252; // "RBL\0", little-endian
|
||||
return otaImage2Valid;
|
||||
}
|
||||
|
||||
bool LibreTuya::otaSwitch(bool force) {
|
||||
// no need to check otaGetStoredIndex() as it does the same as otaHasImage2()
|
||||
|
||||
// force checking validity again
|
||||
otaImage2Valid = -1;
|
||||
|
||||
if (otaHasImage2() && force) {
|
||||
// "rollback" - abort bootloader upgrade operation by wiping first sector
|
||||
return Flash.eraseSector(FLASH_DOWNLOAD_OFFSET);
|
||||
}
|
||||
|
||||
return otaHasImage2(); // false if second image is not valid
|
||||
}
|
||||
|
||||
/* Watchdog */
|
||||
|
||||
bool LibreTuya::wdtEnable(uint32_t timeout) {
|
||||
wdt_ctrl(WCMD_SET_PERIOD, &timeout);
|
||||
wdt_ctrl(WCMD_POWER_UP, NULL);
|
||||
}
|
||||
|
||||
void LibreTuya::wdtDisable() {
|
||||
wdt_ctrl(WCMD_POWER_DOWN, NULL);
|
||||
}
|
||||
|
||||
void LibreTuya::wdtFeed() {
|
||||
wdt_ctrl(WCMD_RELOAD_PERIOD, NULL);
|
||||
}
|
||||
|
||||
/* Global instance */
|
||||
|
||||
LibreTuya LT;
|
||||
LibreTuya ESP = LT;
|
||||
109
cores/beken-72xx/arduino/libraries/Serial/SerialClass.cpp
Normal file
109
cores/beken-72xx/arduino/libraries/Serial/SerialClass.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-23. */
|
||||
|
||||
#include "SerialClass.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <uart_pub.h>
|
||||
|
||||
extern void bk_send_byte(uint8_t uport, uint8_t data);
|
||||
extern void uart_hw_set_change(uint8_t uport, bk_uart_config_t *uart_config);
|
||||
extern int uart_rx_callback_set(int uport, uart_callback callback, void *param);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#ifdef PIN_SERIAL1_TX
|
||||
SerialClass Serial1(UART1_PORT);
|
||||
#endif
|
||||
#ifdef PIN_SERIAL2_TX
|
||||
SerialClass Serial2(UART2_PORT);
|
||||
#endif
|
||||
|
||||
SerialClass::SerialClass(uint8_t port) {
|
||||
this->port = port;
|
||||
this->buf = NULL;
|
||||
}
|
||||
|
||||
#if LT_AUTO_DOWNLOAD_REBOOT
|
||||
static uint8_t adrState = 0;
|
||||
static const uint8_t adrCmd[] = {0x01, 0xE0, 0xFC, 0x01, 0x00};
|
||||
|
||||
static void adrParse(uint8_t c) {
|
||||
// parse and respond to link check command (CMD_LinkCheck=0)
|
||||
adrState = (adrState + 1) * (c == adrCmd[adrState]);
|
||||
if (adrState == 5) {
|
||||
LT_I("Auto download mode: rebooting");
|
||||
LT.restart();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void callback(int port, void *param) {
|
||||
RingBuffer *buf = (RingBuffer *)param;
|
||||
int ch;
|
||||
while ((ch = uart_read_byte(port)) != -1) {
|
||||
#if LT_AUTO_DOWNLOAD_REBOOT && defined(PIN_SERIAL1_RX)
|
||||
// parse UART protocol commands on UART1
|
||||
if (port == UART1_PORT)
|
||||
adrParse(ch);
|
||||
#endif
|
||||
buf->store_char(ch);
|
||||
}
|
||||
}
|
||||
|
||||
void SerialClass::begin(unsigned long baudrate, uint16_t config) {
|
||||
uint8_t dataWidth = ((config & SERIAL_DATA_MASK) >> 8) - 1; // 0x100..0x400 -> 0..3
|
||||
uint8_t parity = 3 - (config & SERIAL_PARITY_MASK); // 0x3..0x1 -> 0..2
|
||||
uint8_t stopBits = (config & SERIAL_STOP_BIT_MASK) == SERIAL_STOP_BIT_2; // 0x10..0x30 -> 0..1
|
||||
|
||||
bk_uart_config_t cfg = {
|
||||
.baud_rate = baudrate,
|
||||
.data_width = (uart_data_width_t)dataWidth,
|
||||
.parity = (uart_parity_t)parity,
|
||||
.stop_bits = (uart_stop_bits_t)stopBits,
|
||||
.flow_control = FLOW_CTRL_DISABLED,
|
||||
};
|
||||
|
||||
if (this->buf) {
|
||||
this->buf->clear();
|
||||
} else {
|
||||
this->buf = new RingBuffer();
|
||||
}
|
||||
|
||||
uart_hw_set_change(port, &cfg);
|
||||
uart_rx_callback_set(port, callback, this->buf);
|
||||
}
|
||||
|
||||
void SerialClass::end() {
|
||||
uart_rx_callback_set(port, NULL, NULL);
|
||||
switch (port) {
|
||||
case 1:
|
||||
uart1_exit();
|
||||
break;
|
||||
case 2:
|
||||
uart2_exit();
|
||||
break;
|
||||
}
|
||||
delete this->buf;
|
||||
}
|
||||
|
||||
int SerialClass::available() {
|
||||
return buf->available();
|
||||
}
|
||||
|
||||
int SerialClass::peek() {
|
||||
return buf->peek();
|
||||
}
|
||||
|
||||
int SerialClass::read() {
|
||||
return buf->read_char();
|
||||
}
|
||||
|
||||
void SerialClass::flush() {
|
||||
uart_wait_tx_over();
|
||||
}
|
||||
|
||||
size_t SerialClass::write(uint8_t c) {
|
||||
bk_send_byte(port, c);
|
||||
return 1;
|
||||
}
|
||||
38
cores/beken-72xx/arduino/libraries/Serial/SerialClass.h
Normal file
38
cores/beken-72xx/arduino/libraries/Serial/SerialClass.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-23. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <api/HardwareSerial.h>
|
||||
#include <api/RingBuffer.h>
|
||||
|
||||
using namespace arduino;
|
||||
|
||||
class SerialClass : public HardwareSerial {
|
||||
private:
|
||||
uint8_t port;
|
||||
RingBuffer *buf;
|
||||
|
||||
public:
|
||||
SerialClass(uint8_t port);
|
||||
|
||||
inline void begin(unsigned long baudrate) {
|
||||
begin(baudrate, SERIAL_8N1);
|
||||
}
|
||||
|
||||
void begin(unsigned long baudrate, uint16_t config);
|
||||
void end();
|
||||
int available();
|
||||
int peek();
|
||||
int read();
|
||||
void flush();
|
||||
size_t write(uint8_t c);
|
||||
|
||||
operator bool() {
|
||||
return !!buf;
|
||||
}
|
||||
|
||||
using Print::write;
|
||||
};
|
||||
|
||||
#define HAS_SERIAL_CLASS 1
|
||||
81
cores/beken-72xx/arduino/libraries/WiFi/WiFi.cpp
Normal file
81
cores/beken-72xx/arduino/libraries/WiFi/WiFi.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-26. */
|
||||
|
||||
#include "WiFiPriv.h"
|
||||
|
||||
WiFiClass::WiFiClass() {
|
||||
memset(&data, 0x00, sizeof(WiFiData));
|
||||
data.scanSem = xSemaphoreCreateBinary();
|
||||
}
|
||||
|
||||
WiFiClass::~WiFiClass() {
|
||||
vSemaphoreDelete(data.scanSem);
|
||||
}
|
||||
|
||||
void WiFiClass::dataInitialize() {
|
||||
if (data.statusIp)
|
||||
return;
|
||||
LT_DM(WIFI, "Data init");
|
||||
data.configSta = zalloc(sizeof(network_InitTypeDef_st));
|
||||
data.configAp = zalloc(sizeof(network_InitTypeDef_ap_st));
|
||||
data.statusIp = malloc(sizeof(IPStatusTypedef));
|
||||
data.statusLink = malloc(sizeof(LinkStatusTypeDef));
|
||||
STA_CFG->dhcp_mode = DHCP_CLIENT;
|
||||
LT_DM(WIFI, "Data = %p", data.configSta);
|
||||
}
|
||||
|
||||
void WiFiClass::dataFree() {
|
||||
LT_DM(WIFI, "Data free");
|
||||
free(data.configSta);
|
||||
free(data.configAp);
|
||||
free(data.statusIp);
|
||||
free(data.statusLink);
|
||||
data.configSta = NULL;
|
||||
data.configAp = NULL;
|
||||
data.statusIp = NULL;
|
||||
data.statusLink = NULL;
|
||||
}
|
||||
|
||||
WiFiStatus eventTypeToStatus(uint8_t type) {
|
||||
// rw_msg_pub.h:9
|
||||
switch (type) {
|
||||
case RW_EVT_STA_IDLE:
|
||||
return WL_IDLE_STATUS;
|
||||
case RW_EVT_STA_NO_AP_FOUND:
|
||||
return WL_NO_SSID_AVAIL;
|
||||
case RW_EVT_STA_CONNECTING:
|
||||
case RW_EVT_STA_CONNECTED:
|
||||
return WL_SCAN_COMPLETED;
|
||||
case RW_EVT_STA_GOT_IP:
|
||||
return WL_CONNECTED;
|
||||
case RW_EVT_STA_PASSWORD_WRONG:
|
||||
case RW_EVT_STA_ASSOC_FULL:
|
||||
case RW_EVT_STA_CONNECT_FAILED:
|
||||
return WL_CONNECT_FAILED;
|
||||
case RW_EVT_STA_BEACON_LOSE:
|
||||
return WL_CONNECTION_LOST;
|
||||
case RW_EVT_STA_DISCONNECTED:
|
||||
return WL_DISCONNECTED;
|
||||
}
|
||||
}
|
||||
|
||||
WiFiAuthMode securityTypeToAuthMode(uint8_t type) {
|
||||
// wlan_ui_pub.h:62
|
||||
switch (type) {
|
||||
case BK_SECURITY_TYPE_NONE:
|
||||
return WIFI_AUTH_OPEN;
|
||||
case BK_SECURITY_TYPE_WEP:
|
||||
return WIFI_AUTH_WEP;
|
||||
case BK_SECURITY_TYPE_WPA_TKIP:
|
||||
case BK_SECURITY_TYPE_WPA_AES:
|
||||
return WIFI_AUTH_WPA_PSK;
|
||||
case BK_SECURITY_TYPE_WPA2_TKIP:
|
||||
case BK_SECURITY_TYPE_WPA2_AES:
|
||||
case BK_SECURITY_TYPE_WPA2_MIXED:
|
||||
return WIFI_AUTH_WPA2_PSK;
|
||||
case BK_SECURITY_TYPE_WPA3_SAE:
|
||||
return WIFI_AUTH_WPA3_PSK;
|
||||
case BK_SECURITY_TYPE_WPA3_WPA2_MIXED:
|
||||
return WIFI_AUTH_WPA2_WPA3_PSK;
|
||||
}
|
||||
return WIFI_AUTH_INVALID;
|
||||
}
|
||||
127
cores/beken-72xx/arduino/libraries/WiFi/WiFiAP.cpp
Normal file
127
cores/beken-72xx/arduino/libraries/WiFi/WiFiAP.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-07-01. */
|
||||
|
||||
#include "WiFiPriv.h"
|
||||
|
||||
bool WiFiClass::softAP(const char *ssid, const char *passphrase, int channel, bool ssidHidden, int maxClients) {
|
||||
if (!enableAP(true))
|
||||
return WL_CONNECT_FAILED;
|
||||
if (!validate(ssid, passphrase))
|
||||
return WL_CONNECT_FAILED;
|
||||
|
||||
LT_HEAP_I();
|
||||
|
||||
// Beken SDK bug: bk_wlan_ap_init_adv() doesn't null-terminate the passphrase
|
||||
memset(g_ap_param_ptr->key, '\0', 65);
|
||||
|
||||
strcpy(AP_CFG->wifi_ssid, ssid);
|
||||
if (passphrase) {
|
||||
strcpy(AP_CFG->wifi_key, passphrase);
|
||||
AP_CFG->security = BK_SECURITY_TYPE_WPA2_MIXED;
|
||||
} else {
|
||||
AP_CFG->wifi_key[0] = '\0';
|
||||
AP_CFG->security = BK_SECURITY_TYPE_NONE;
|
||||
}
|
||||
|
||||
AP_CFG->channel = channel;
|
||||
AP_CFG->ssid_hidden = ssidHidden;
|
||||
AP_CFG->max_con = maxClients;
|
||||
AP_CFG->dhcp_mode = DHCP_SERVER;
|
||||
AP_CFG->wifi_retry_interval = 100;
|
||||
|
||||
LT_IM(WIFI, "Creating SoftAP %s", ssid);
|
||||
|
||||
if (!AP_CFG->local_ip_addr[0]) {
|
||||
LT_DM(WIFI, "Setting default IP config");
|
||||
softAPConfig((uint32_t)0, (uint32_t)0, (uint32_t)0);
|
||||
}
|
||||
|
||||
LT_DM(WIFI, "Static IP: %s / %s / %s", AP_CFG->local_ip_addr, AP_CFG->net_mask, AP_CFG->gateway_ip_addr);
|
||||
|
||||
__wrap_bk_printf_disable();
|
||||
OSStatus ret = bk_wlan_start_ap_adv(AP_CFG);
|
||||
__wrap_bk_printf_enable();
|
||||
|
||||
if (ret != 0) {
|
||||
LT_EM(WIFI, "SoftAP failed; ret=%d", ret);
|
||||
return false;
|
||||
}
|
||||
LT_DM(WIFI, "AP start OK");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WiFiClass::softAPConfig(IPAddress localIP, IPAddress gateway, IPAddress subnet) {
|
||||
dataInitialize();
|
||||
if (!localIP) {
|
||||
localIP = gateway = IPAddress(192, 168, 43, 1);
|
||||
subnet = IPAddress(255, 255, 255, 0);
|
||||
}
|
||||
sprintf(AP_CFG->local_ip_addr, IP_FMT, localIP[0], localIP[1], localIP[2], localIP[3]);
|
||||
sprintf(AP_CFG->net_mask, IP_FMT, subnet[0], subnet[1], subnet[2], subnet[3]);
|
||||
sprintf(AP_CFG->gateway_ip_addr, IP_FMT, gateway[0], gateway[1], gateway[2], gateway[3]);
|
||||
// from wlan_ui.c:1370
|
||||
if (uap_ip_is_start()) {
|
||||
uap_ip_down();
|
||||
ip_address_set(
|
||||
BK_STATION,
|
||||
AP_CFG->dhcp_mode,
|
||||
AP_CFG->local_ip_addr,
|
||||
AP_CFG->net_mask,
|
||||
AP_CFG->gateway_ip_addr,
|
||||
AP_CFG->dns_server_ip_addr
|
||||
);
|
||||
uap_ip_start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WiFiClass::softAPdisconnect(bool wifiOff) {
|
||||
if (!(getMode() & WIFI_MODE_AP))
|
||||
// do not call SDK methods before even initializing WiFi first
|
||||
return true;
|
||||
return bk_wlan_stop(BK_SOFT_AP) == kNoErr;
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::softAPgetStationNum() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
IPAddress WiFiClass::softAPIP() {
|
||||
AP_GET_IP_STATUS_RETURN((uint32_t)0);
|
||||
IPAddress ip;
|
||||
ip.fromString(IP_STATUS->ip);
|
||||
return ip;
|
||||
}
|
||||
|
||||
IPAddress WiFiClass::softAPSubnetMask() {
|
||||
AP_GET_IP_STATUS_RETURN((uint32_t)0);
|
||||
IPAddress ip;
|
||||
ip.fromString(IP_STATUS->mask);
|
||||
return ip;
|
||||
}
|
||||
|
||||
const char *WiFiClass::softAPgetHostname() {
|
||||
struct netif *ifs = (struct netif *)net_get_uap_handle();
|
||||
return netif_get_hostname(ifs);
|
||||
}
|
||||
|
||||
bool WiFiClass::softAPsetHostname(const char *hostname) {
|
||||
struct netif *ifs = (struct netif *)net_get_uap_handle();
|
||||
netif_set_hostname(ifs, (char *)hostname);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t *WiFiClass::softAPmacAddress(uint8_t *mac) {
|
||||
setMacAddress(mac);
|
||||
return mac;
|
||||
}
|
||||
|
||||
String WiFiClass::softAPmacAddress(void) {
|
||||
uint8_t mac[ETH_ALEN];
|
||||
wifi_get_mac_address((char *)mac, CONFIG_ROLE_AP);
|
||||
return macToString(mac);
|
||||
}
|
||||
|
||||
const String WiFiClass::softAPSSID(void) {
|
||||
AP_GET_LINK_STATUS_RETURN("");
|
||||
return AP_CFG->wifi_ssid;
|
||||
}
|
||||
25
cores/beken-72xx/arduino/libraries/WiFi/WiFiData.h
Normal file
25
cores/beken-72xx/arduino/libraries/WiFi/WiFiData.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-26. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <rw_msg_pub.h>
|
||||
#include <semphr.h>
|
||||
|
||||
} // extern "C"
|
||||
|
||||
typedef struct {
|
||||
void *configSta;
|
||||
void *configAp;
|
||||
unsigned long scannedAt;
|
||||
SemaphoreHandle_t scanSem;
|
||||
void *statusIp;
|
||||
void *statusLink;
|
||||
rw_evt_type lastStaEvent;
|
||||
rw_evt_type lastApEvent;
|
||||
bool apEnabled;
|
||||
} WiFiData;
|
||||
149
cores/beken-72xx/arduino/libraries/WiFi/WiFiEvents.cpp
Normal file
149
cores/beken-72xx/arduino/libraries/WiFi/WiFiEvents.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-07-10. */
|
||||
|
||||
#include "WiFiPriv.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
static xQueueHandle wifiEventQueueHandle = NULL;
|
||||
static xTaskHandle wifiEventTaskHandle = NULL;
|
||||
|
||||
static const rw_evt_type eventConnected = RW_EVT_STA_CONNECTED;
|
||||
|
||||
// callback for bk_wlan_status_register_cb()
|
||||
static void wifiStatusCallback(rw_evt_type *pEvent) {
|
||||
if (wifiEventQueueHandle && wifiEventTaskHandle) {
|
||||
xQueueSend(wifiEventQueueHandle, pEvent, portMAX_DELAY);
|
||||
} else {
|
||||
wifiEventHandler(*pEvent);
|
||||
}
|
||||
}
|
||||
|
||||
static void wifiEventTask(void *arg) {
|
||||
rw_evt_type event = RW_EVT_MAX;
|
||||
for (;;) {
|
||||
if (xQueueReceive(wifiEventQueueHandle, &event, portMAX_DELAY) == pdTRUE) {
|
||||
wifiEventHandler(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wifiEventSendArduino(EventId event) {
|
||||
event = (EventId)(RW_EVT_ARDUINO | event);
|
||||
wifiStatusCallback((rw_evt_type *)&event);
|
||||
}
|
||||
|
||||
void startWifiTask() {
|
||||
if (!wifiEventQueueHandle) {
|
||||
LT_HEAP_I();
|
||||
wifiEventQueueHandle = xQueueCreate(32, sizeof(rw_evt_type));
|
||||
LT_HEAP_I();
|
||||
}
|
||||
if (!wifiEventTaskHandle) {
|
||||
LT_HEAP_I();
|
||||
xTaskCreate(wifiEventTask, "wifievent", 512, NULL, 4, &wifiEventTaskHandle);
|
||||
LT_HEAP_I();
|
||||
}
|
||||
bk_wlan_status_register_cb((FUNC_1PARAM_PTR)wifiStatusCallback);
|
||||
}
|
||||
|
||||
void wifiEventHandler(rw_evt_type event) {
|
||||
if (!pWiFi)
|
||||
return; // failsafe
|
||||
|
||||
LT_DM(WIFI, "BK event %u", event);
|
||||
|
||||
if (event <= RW_EVT_STA_GOT_IP)
|
||||
pWiFi->data.lastStaEvent = event;
|
||||
else
|
||||
pWiFi->data.lastApEvent = event;
|
||||
|
||||
EventId eventId;
|
||||
EventInfo eventInfo;
|
||||
String ssid;
|
||||
|
||||
memset(&eventInfo, 0, sizeof(EventInfo));
|
||||
|
||||
switch (event) {
|
||||
case RW_EVT_STA_IDLE:
|
||||
eventId = ARDUINO_EVENT_WIFI_READY;
|
||||
break;
|
||||
|
||||
case RW_EVT_STA_BEACON_LOSE:
|
||||
case RW_EVT_STA_PASSWORD_WRONG:
|
||||
case RW_EVT_STA_NO_AP_FOUND:
|
||||
case RW_EVT_STA_ASSOC_FULL:
|
||||
case RW_EVT_STA_DISCONNECTED:
|
||||
case RW_EVT_STA_CONNECT_FAILED:
|
||||
eventId = ARDUINO_EVENT_WIFI_STA_DISCONNECTED;
|
||||
eventInfo.wifi_sta_disconnected.ssid_len = 0;
|
||||
switch (event) {
|
||||
case RW_EVT_STA_BEACON_LOSE:
|
||||
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_BEACON_TIMEOUT;
|
||||
break;
|
||||
case RW_EVT_STA_PASSWORD_WRONG:
|
||||
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_AUTH_FAIL;
|
||||
break;
|
||||
case RW_EVT_STA_NO_AP_FOUND:
|
||||
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_NO_AP_FOUND;
|
||||
break;
|
||||
case RW_EVT_STA_ASSOC_FULL:
|
||||
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_ASSOC_TOOMANY;
|
||||
break;
|
||||
case RW_EVT_STA_DISCONNECTED:
|
||||
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_ASSOC_LEAVE;
|
||||
break;
|
||||
case RW_EVT_STA_CONNECT_FAILED:
|
||||
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_CONNECTION_FAIL;
|
||||
break;
|
||||
default:
|
||||
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_UNSPECIFIED;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case RW_EVT_STA_CONNECTED:
|
||||
eventId = ARDUINO_EVENT_WIFI_STA_CONNECTED;
|
||||
ssid = pWiFi->SSID();
|
||||
eventInfo.wifi_sta_connected.ssid_len = ssid.length();
|
||||
eventInfo.wifi_sta_connected.channel = pWiFi->channel();
|
||||
eventInfo.wifi_sta_connected.authmode = pWiFi->getEncryption();
|
||||
memcpy(eventInfo.wifi_sta_connected.ssid, ssid.c_str(), eventInfo.wifi_sta_connected.ssid_len + 1);
|
||||
memcpy(eventInfo.wifi_sta_connected.bssid, pWiFi->BSSID(), 6);
|
||||
break;
|
||||
|
||||
case RW_EVT_STA_GOT_IP:
|
||||
eventId = ARDUINO_EVENT_WIFI_STA_GOT_IP;
|
||||
eventInfo.got_ip.if_index = 0;
|
||||
eventInfo.got_ip.ip_changed = true;
|
||||
eventInfo.got_ip.ip_info.ip.addr = WiFi.localIP();
|
||||
eventInfo.got_ip.ip_info.netmask.addr = WiFi.subnetMask();
|
||||
eventInfo.got_ip.ip_info.gw.addr = WiFi.gatewayIP();
|
||||
break;
|
||||
|
||||
case RW_EVT_AP_CONNECTED:
|
||||
eventId = ARDUINO_EVENT_WIFI_AP_STACONNECTED;
|
||||
// TODO station MAC
|
||||
break;
|
||||
|
||||
case RW_EVT_AP_DISCONNECTED:
|
||||
eventId = ARDUINO_EVENT_WIFI_AP_STADISCONNECTED;
|
||||
// TODO station MAC
|
||||
break;
|
||||
|
||||
case RW_EVT_ARDUINO | ARDUINO_EVENT_WIFI_SCAN_DONE:
|
||||
eventId = ARDUINO_EVENT_WIFI_SCAN_DONE;
|
||||
eventInfo.wifi_scan_done.status = 0;
|
||||
if (pWiFi->scan)
|
||||
eventInfo.wifi_scan_done.number = pWiFi->scan->count;
|
||||
eventInfo.wifi_scan_done.scan_id = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (event < RW_EVT_ARDUINO)
|
||||
return;
|
||||
eventId = (EventId)(event - RW_EVT_ARDUINO);
|
||||
break;
|
||||
}
|
||||
|
||||
pWiFi->postEvent(eventId, eventInfo);
|
||||
}
|
||||
89
cores/beken-72xx/arduino/libraries/WiFi/WiFiGeneric.cpp
Normal file
89
cores/beken-72xx/arduino/libraries/WiFi/WiFiGeneric.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-26. */
|
||||
|
||||
#include "WiFiPriv.h"
|
||||
|
||||
bool WiFiClass::modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap) {
|
||||
__wrap_bk_printf_disable();
|
||||
startWifiTask();
|
||||
|
||||
if (!__bk_rf_is_init) {
|
||||
LT_DM(WIFI, "Initializing func&app");
|
||||
func_init_extended();
|
||||
app_pre_start();
|
||||
// wait for the init_thread to finish its job
|
||||
while (xTaskGetHandle("init_thread")) {
|
||||
LT_VM(WIFI, "Waiting for init_thread");
|
||||
delay(10);
|
||||
}
|
||||
LT_DM(WIFI, "Init OK");
|
||||
__bk_rf_is_init = true;
|
||||
}
|
||||
|
||||
LT_HEAP_I();
|
||||
|
||||
if (mode) {
|
||||
LT_DM(WIFI, "Wakeup RF");
|
||||
uint32_t reg = 1; // this is only checked for being true-ish
|
||||
sddev_control(SCTRL_DEV_NAME, CMD_RF_HOLD_BIT_SET, ®);
|
||||
}
|
||||
|
||||
if (sta == WLMODE_ENABLE) {
|
||||
LT_DM(WIFI, "Enabling STA");
|
||||
bk_wlan_sta_init(NULL);
|
||||
#if CFG_WPA_CTRL_IFACE
|
||||
wlan_sta_enable();
|
||||
#endif
|
||||
wifiEventSendArduino(ARDUINO_EVENT_WIFI_STA_START);
|
||||
} else if (sta == WLMODE_DISABLE) {
|
||||
LT_DM(WIFI, "Disabling STA");
|
||||
bk_wlan_stop(BK_STATION);
|
||||
wifiEventSendArduino(ARDUINO_EVENT_WIFI_STA_STOP);
|
||||
}
|
||||
|
||||
LT_HEAP_I();
|
||||
|
||||
if (ap == WLMODE_ENABLE) {
|
||||
LT_DM(WIFI, "Enabling AP");
|
||||
// fake it - on BK7231, enabling the AP without starting it breaks all connection attempts
|
||||
data.apEnabled = true;
|
||||
wifiEventSendArduino(ARDUINO_EVENT_WIFI_AP_START);
|
||||
} else if (ap == WLMODE_DISABLE) {
|
||||
LT_DM(WIFI, "Disabling AP");
|
||||
bk_wlan_stop(BK_SOFT_AP);
|
||||
data.apEnabled = false;
|
||||
wifiEventSendArduino(ARDUINO_EVENT_WIFI_AP_STOP);
|
||||
}
|
||||
|
||||
// force checking actual mode again
|
||||
mode = getMode();
|
||||
|
||||
if (!mode)
|
||||
dataFree();
|
||||
|
||||
LT_HEAP_I();
|
||||
|
||||
__wrap_bk_printf_enable();
|
||||
return true;
|
||||
}
|
||||
|
||||
WiFiMode WiFiClass::getMode() {
|
||||
uint8_t sta = !!bk_wlan_has_role(VIF_STA) * WIFI_MODE_STA;
|
||||
uint8_t ap = data.apEnabled * WIFI_MODE_AP; // report the faked value
|
||||
return (WiFiMode)(sta | ap);
|
||||
}
|
||||
|
||||
WiFiStatus WiFiClass::status() {
|
||||
rw_evt_type status = data.lastStaEvent;
|
||||
if (status == RW_EVT_STA_CONNECTED && STA_CFG->dhcp_mode == DHCP_DISABLE)
|
||||
status = RW_EVT_STA_GOT_IP;
|
||||
return eventTypeToStatus(status);
|
||||
}
|
||||
|
||||
IPAddress WiFiClass::hostByName(const char *hostname) {
|
||||
ip_addr_t ip;
|
||||
int ret = netconn_gethostbyname(hostname, &ip);
|
||||
if (ret == ERR_OK) {
|
||||
return ip.addr;
|
||||
}
|
||||
return IPAddress();
|
||||
}
|
||||
90
cores/beken-72xx/arduino/libraries/WiFi/WiFiPrivate.h
Normal file
90
cores/beken-72xx/arduino/libraries/WiFi/WiFiPrivate.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-26. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <api/WiFi/WiFi.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <lwip/api.h>
|
||||
#include <lwip/ip_addr.h>
|
||||
#include <lwip/netif.h>
|
||||
// port/net.h
|
||||
#include <net.h>
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <semphr.h>
|
||||
|
||||
#include <common.h>
|
||||
#include <config.h>
|
||||
#include <main_none.h>
|
||||
#include <param_config.h>
|
||||
#include <rw_msg_rx.h>
|
||||
#include <sa_ap.h>
|
||||
#include <sys_ctrl_pub.h>
|
||||
#include <vif_mgmt.h>
|
||||
#include <wlan_ui_pub.h>
|
||||
#include <wpa_supplicant_i.h>
|
||||
|
||||
extern void func_init_extended();
|
||||
extern void app_pre_start();
|
||||
extern void bk_wlan_ap_init(network_InitTypeDef_st *inNetworkInitPara);
|
||||
|
||||
// func/hostapd-2.5/wpa_supplicant/main_supplicant.c
|
||||
extern struct wpa_ssid_value *wpas_connect_ssid;
|
||||
|
||||
// app/param_config.c
|
||||
extern general_param_t *g_wlan_general_param;
|
||||
extern ap_param_t *g_ap_param_ptr;
|
||||
extern sta_param_t *g_sta_param_ptr;
|
||||
extern uint8_t system_mac[6];
|
||||
|
||||
// WiFi.cpp
|
||||
WiFiStatus eventTypeToStatus(uint8_t type);
|
||||
WiFiAuthMode securityTypeToAuthMode(uint8_t type);
|
||||
|
||||
// WiFiEvents.cpp
|
||||
extern void wifiEventSendArduino(EventId event);
|
||||
extern void startWifiTask();
|
||||
extern void wifiEventHandler(rw_evt_type event);
|
||||
|
||||
#define RW_EVT_ARDUINO (1 << 7)
|
||||
|
||||
#define IP_FMT "%u.%u.%u.%u"
|
||||
|
||||
#define STA_CFG ((network_InitTypeDef_st *)data.configSta)
|
||||
#define AP_CFG ((network_InitTypeDef_ap_st *)data.configAp)
|
||||
#define IP_STATUS ((IPStatusTypedef *)data.statusIp)
|
||||
#define LINK_STATUS ((LinkStatusTypeDef *)data.statusLink)
|
||||
|
||||
#define STA_GET_LINK_STATUS_RETURN(ret) \
|
||||
{ \
|
||||
if (!sta_ip_is_start()) \
|
||||
return ret; \
|
||||
memset(LINK_STATUS, 0x00, sizeof(LinkStatusTypeDef)); \
|
||||
bk_wlan_get_link_status(LINK_STATUS); \
|
||||
}
|
||||
|
||||
#define STA_GET_IP_STATUS_RETURN(ret) \
|
||||
{ \
|
||||
if (!sta_ip_is_start()) \
|
||||
return ret; \
|
||||
memset(IP_STATUS, 0x00, sizeof(IPStatusTypedef)); \
|
||||
bk_wlan_get_ip_status(IP_STATUS, BK_STATION); \
|
||||
}
|
||||
|
||||
#define AP_GET_LINK_STATUS_RETURN(ret) \
|
||||
{ \
|
||||
if (!uap_ip_is_start()) \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define AP_GET_IP_STATUS_RETURN(ret) \
|
||||
{ \
|
||||
if (!uap_ip_is_start()) \
|
||||
return ret; \
|
||||
memset(IP_STATUS, 0x00, sizeof(IPStatusTypedef)); \
|
||||
bk_wlan_get_ip_status(IP_STATUS, BK_SOFT_AP); \
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
224
cores/beken-72xx/arduino/libraries/WiFi/WiFiSTA.cpp
Normal file
224
cores/beken-72xx/arduino/libraries/WiFi/WiFiSTA.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-27. */
|
||||
|
||||
#include "WiFiPriv.h"
|
||||
|
||||
WiFiStatus
|
||||
WiFiClass::begin(const char *ssid, const char *passphrase, int32_t channel, const uint8_t *bssid, bool connect) {
|
||||
if (!enableSTA(true))
|
||||
return WL_CONNECT_FAILED;
|
||||
if (!validate(ssid, passphrase))
|
||||
return WL_CONNECT_FAILED;
|
||||
|
||||
LT_HEAP_I();
|
||||
|
||||
disconnect(false);
|
||||
|
||||
strcpy(STA_CFG->wifi_ssid, ssid);
|
||||
if (passphrase) {
|
||||
strcpy(STA_CFG->wifi_key, passphrase);
|
||||
} else {
|
||||
STA_CFG->wifi_bssid[0] = '\0';
|
||||
}
|
||||
|
||||
if (reconnect(bssid))
|
||||
return WL_CONNECTED;
|
||||
|
||||
return WL_CONNECT_FAILED;
|
||||
}
|
||||
|
||||
bool WiFiClass::config(IPAddress localIP, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2) {
|
||||
dataInitialize();
|
||||
|
||||
STA_CFG->dhcp_mode = localIP ? DHCP_DISABLE : DHCP_CLIENT;
|
||||
if (localIP) {
|
||||
sprintf(STA_CFG->local_ip_addr, IP_FMT, localIP[0], localIP[1], localIP[2], localIP[3]);
|
||||
sprintf(STA_CFG->net_mask, IP_FMT, subnet[0], subnet[1], subnet[2], subnet[3]);
|
||||
sprintf(STA_CFG->gateway_ip_addr, IP_FMT, gateway[0], gateway[1], gateway[2], gateway[3]);
|
||||
if (dns1) {
|
||||
sprintf(STA_CFG->dns_server_ip_addr, IP_FMT, dns1[0], dns1[1], dns1[2], dns1[3]);
|
||||
} else {
|
||||
STA_CFG->dns_server_ip_addr[0] = '\0';
|
||||
}
|
||||
} else {
|
||||
STA_CFG->local_ip_addr[0] = '\0';
|
||||
STA_CFG->net_mask[0] = '\0';
|
||||
STA_CFG->gateway_ip_addr[0] = '\0';
|
||||
STA_CFG->dns_server_ip_addr[0] = '\0';
|
||||
}
|
||||
|
||||
// from wlan_ui.c:1370
|
||||
if (sta_ip_is_start()) {
|
||||
sta_ip_down();
|
||||
ip_address_set(
|
||||
BK_STATION,
|
||||
STA_CFG->dhcp_mode,
|
||||
STA_CFG->local_ip_addr,
|
||||
STA_CFG->net_mask,
|
||||
STA_CFG->gateway_ip_addr,
|
||||
STA_CFG->dns_server_ip_addr
|
||||
);
|
||||
sta_ip_start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WiFiClass::reconnect(const uint8_t *bssid) {
|
||||
dataInitialize();
|
||||
if (!bssid && !STA_CFG->wifi_ssid[0]) {
|
||||
LT_EM(WIFI, "(B)SSID not specified");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (bssid) {
|
||||
LT_IM(WIFI, "Connecting to " MACSTR, MAC2STR(bssid));
|
||||
} else {
|
||||
LT_IM(WIFI, "Connecting to %s", STA_CFG->wifi_ssid);
|
||||
}
|
||||
|
||||
LT_DM(WIFI, "Data = %p", data.configSta);
|
||||
|
||||
STA_CFG->wifi_mode = BK_STATION;
|
||||
STA_CFG->wifi_retry_interval = 100;
|
||||
if (bssid)
|
||||
memcpy(STA_CFG->wifi_bssid, bssid, 6);
|
||||
else
|
||||
memset(STA_CFG->wifi_bssid, 0x00, 6);
|
||||
|
||||
if (STA_CFG->dhcp_mode == DHCP_DISABLE) {
|
||||
LT_DM(WIFI, "Static IP: %s / %s / %s", STA_CFG->local_ip_addr, STA_CFG->net_mask, STA_CFG->gateway_ip_addr);
|
||||
LT_DM(WIFI, "Static DNS: %s", STA_CFG->dns_server_ip_addr);
|
||||
} else {
|
||||
LT_DM(WIFI, "Using DHCP");
|
||||
}
|
||||
|
||||
LT_DM(WIFI, "Starting WiFi...");
|
||||
|
||||
__wrap_bk_printf_disable();
|
||||
bk_wlan_start_sta(STA_CFG);
|
||||
__wrap_bk_printf_enable();
|
||||
|
||||
LT_DM(WIFI, "Start OK");
|
||||
return true;
|
||||
|
||||
error:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WiFiClass::disconnect(bool wifiOff) {
|
||||
#if LT_DEBUG_WIFI
|
||||
memset(LINK_STATUS, 0x00, sizeof(LinkStatusTypeDef));
|
||||
bk_wlan_get_link_status(LINK_STATUS);
|
||||
LT_DM(WIFI, "Disconnecting from %s (wifiOff=%d)", LINK_STATUS ? LINK_STATUS->ssid : NULL, wifiOff);
|
||||
#endif
|
||||
bk_wlan_connection_loss();
|
||||
if (wifiOff)
|
||||
enableSTA(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WiFiClass::setAutoReconnect(bool autoReconnect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WiFiClass::getAutoReconnect() {
|
||||
return false;
|
||||
}
|
||||
|
||||
IPAddress WiFiClass::localIP() {
|
||||
STA_GET_IP_STATUS_RETURN((uint32_t)0);
|
||||
IPAddress ip;
|
||||
ip.fromString(IP_STATUS->ip);
|
||||
return ip;
|
||||
}
|
||||
|
||||
IPAddress WiFiClass::subnetMask() {
|
||||
STA_GET_IP_STATUS_RETURN((uint32_t)0);
|
||||
IPAddress ip;
|
||||
ip.fromString(IP_STATUS->mask);
|
||||
return ip;
|
||||
}
|
||||
|
||||
IPAddress WiFiClass::gatewayIP() {
|
||||
STA_GET_IP_STATUS_RETURN((uint32_t)0);
|
||||
IPAddress ip;
|
||||
ip.fromString(IP_STATUS->gate);
|
||||
return ip;
|
||||
}
|
||||
|
||||
IPAddress WiFiClass::dnsIP(uint8_t dns_no) {
|
||||
STA_GET_IP_STATUS_RETURN((uint32_t)0);
|
||||
IPAddress ip;
|
||||
ip.fromString(IP_STATUS->dns);
|
||||
return ip;
|
||||
}
|
||||
|
||||
IPAddress WiFiClass::broadcastIP() {
|
||||
return calculateBroadcast(localIP(), subnetMask());
|
||||
}
|
||||
|
||||
const char *WiFiClass::getHostname() {
|
||||
struct netif *ifs = (struct netif *)net_get_sta_handle();
|
||||
return netif_get_hostname(ifs);
|
||||
}
|
||||
|
||||
bool WiFiClass::setHostname(const char *hostname) {
|
||||
struct netif *ifs = (struct netif *)net_get_sta_handle();
|
||||
netif_set_hostname(ifs, (char *)hostname);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t *WiFiClass::macAddress(uint8_t *mac) {
|
||||
wifi_get_mac_address((char *)mac, CONFIG_ROLE_STA);
|
||||
return mac;
|
||||
}
|
||||
|
||||
bool WiFiClass::setMacAddress(const uint8_t *mac) {
|
||||
if (mac[0] & 0x01) {
|
||||
LT_EM(WIFI, "Invalid MAC address");
|
||||
return false;
|
||||
}
|
||||
// ensure "mac_inited" is true
|
||||
wifi_get_mac_address((char *)system_mac, BK_STATION);
|
||||
// store the MAC globally
|
||||
memcpy(system_mac, mac, 6);
|
||||
WiFiMode previousMode = getMode();
|
||||
if (previousMode) {
|
||||
mode(WIFI_MODE_NULL);
|
||||
mode(previousMode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const String WiFiClass::SSID() {
|
||||
STA_GET_LINK_STATUS_RETURN("");
|
||||
return (char *)LINK_STATUS->ssid;
|
||||
}
|
||||
|
||||
const String WiFiClass::psk() {
|
||||
if (!isConnected())
|
||||
return "";
|
||||
struct wpa_supplicant *wpas = wpa_suppliant_ctrl_get_wpas();
|
||||
if (!wpas || !wpas->conf || !wpas->conf->ssid)
|
||||
return "";
|
||||
return (char *)wpas->conf->ssid->passphrase;
|
||||
}
|
||||
|
||||
uint8_t *WiFiClass::BSSID() {
|
||||
STA_GET_LINK_STATUS_RETURN(NULL);
|
||||
return LINK_STATUS->bssid;
|
||||
}
|
||||
|
||||
int32_t WiFiClass::channel() {
|
||||
STA_GET_LINK_STATUS_RETURN(0);
|
||||
return LINK_STATUS->channel;
|
||||
}
|
||||
|
||||
int8_t WiFiClass::RSSI() {
|
||||
STA_GET_LINK_STATUS_RETURN(0);
|
||||
return LINK_STATUS->wifi_strength;
|
||||
}
|
||||
|
||||
WiFiAuthMode WiFiClass::getEncryption() {
|
||||
STA_GET_LINK_STATUS_RETURN(WIFI_AUTH_INVALID);
|
||||
return securityTypeToAuthMode(LINK_STATUS->security);
|
||||
}
|
||||
95
cores/beken-72xx/arduino/libraries/WiFi/WiFiScan.cpp
Normal file
95
cores/beken-72xx/arduino/libraries/WiFi/WiFiScan.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-27. */
|
||||
|
||||
#include "WiFiPriv.h"
|
||||
|
||||
static void scanHandler(void *ctx, uint8_t param) {
|
||||
LT_HEAP_I();
|
||||
WiFiClass *cls = (WiFiClass *)ctx;
|
||||
if (!cls) {
|
||||
LT_WM(WIFI, "Called without ctx");
|
||||
return;
|
||||
}
|
||||
WiFiScanData *scan = cls->scan;
|
||||
if (!scan) {
|
||||
LT_WM(WIFI, "Called without cls->scan");
|
||||
return;
|
||||
}
|
||||
|
||||
ScanResult_adv result;
|
||||
if (wlan_sta_scan_result(&result)) {
|
||||
LT_EM(WIFI, "Failed to get scan result");
|
||||
goto end;
|
||||
}
|
||||
LT_IM(WIFI, "Found %d APs", result.ApNum);
|
||||
|
||||
cls->scanAlloc(result.ApNum);
|
||||
if (!scan->ap) {
|
||||
LT_WM(WIFI, "scan->ap alloc failed");
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < result.ApNum; i++) {
|
||||
scan->ap[i].ssid = strdup(result.ApList[i].ssid);
|
||||
scan->ap[i].auth = securityTypeToAuthMode(result.ApList[i].security);
|
||||
scan->ap[i].rssi = result.ApList[i].ApPower;
|
||||
scan->ap[i].channel = result.ApList[i].channel;
|
||||
memcpy(scan->ap[i].bssid.addr, result.ApList[i].bssid, 6);
|
||||
}
|
||||
|
||||
cls->data.scannedAt = millis();
|
||||
|
||||
wifiEventSendArduino(ARDUINO_EVENT_WIFI_SCAN_DONE);
|
||||
|
||||
end:
|
||||
scan->timeout = 0;
|
||||
if (scan->running) {
|
||||
// running == false means it was discarded (timeout)
|
||||
scan->running = false;
|
||||
xSemaphoreGive(cls->data.scanSem);
|
||||
}
|
||||
LT_HEAP_I();
|
||||
return;
|
||||
}
|
||||
|
||||
int16_t WiFiClass::scanNetworks(bool async, bool showHidden, bool passive, uint32_t maxMsPerChannel, uint8_t channel) {
|
||||
if (scan && scan->running) {
|
||||
if (scan->timeout && millis() > scan->timeout) {
|
||||
LT_WM(WIFI, "Scan timeout, discarding");
|
||||
scan->running = false;
|
||||
} else {
|
||||
return WIFI_SCAN_RUNNING;
|
||||
}
|
||||
}
|
||||
enableSTA(true);
|
||||
scanDelete();
|
||||
scanInit();
|
||||
|
||||
LT_IM(WIFI, "Starting WiFi scan");
|
||||
|
||||
__wrap_bk_printf_disable();
|
||||
mhdr_scanu_reg_cb(scanHandler, this);
|
||||
bk_wlan_start_scan();
|
||||
|
||||
LT_HEAP_I();
|
||||
|
||||
scan->running = true;
|
||||
scan->timeout = millis() + maxMsPerChannel * 20 + 1000;
|
||||
|
||||
int16_t ret = WIFI_SCAN_RUNNING;
|
||||
if (!async) {
|
||||
LT_IM(WIFI, "Waiting for results");
|
||||
xSemaphoreTake(data.scanSem, 1); // reset the semaphore quickly
|
||||
xSemaphoreTake(data.scanSem, pdMS_TO_TICKS(maxMsPerChannel * 20));
|
||||
if (scan->running) {
|
||||
scanDelete();
|
||||
ret = WIFI_SCAN_FAILED;
|
||||
goto exit;
|
||||
}
|
||||
ret = scan->count;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
__wrap_bk_printf_enable();
|
||||
return ret;
|
||||
}
|
||||
32
cores/beken-72xx/arduino/src/ArduinoFamily.h
Normal file
32
cores/beken-72xx/arduino/src/ArduinoFamily.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-14. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "WCharacterFixup.h"
|
||||
#endif
|
||||
|
||||
#define delay delayMilliseconds // change delay()'s signature - it's defined as static inline in WVariant.h
|
||||
#include <api/ArduinoAPI.h>
|
||||
#include <core/LibreTuyaAPI.h>
|
||||
#undef delay
|
||||
|
||||
// Include family-specific code
|
||||
#include "WVariant.h"
|
||||
// Include board variant
|
||||
#include "variant.h"
|
||||
|
||||
// Choose the main UART output port
|
||||
#ifndef LT_UART_DEFAULT_PORT
|
||||
#if defined(PIN_SERIAL2_TX)
|
||||
#define LT_UART_DEFAULT_PORT 2
|
||||
#else
|
||||
#define LT_UART_DEFAULT_PORT 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Define available serial ports
|
||||
#ifdef __cplusplus
|
||||
#include "SerialClass.h"
|
||||
#include <core/SerialExtern.h>
|
||||
#endif
|
||||
5
cores/beken-72xx/arduino/src/MD5Impl.h
Normal file
5
cores/beken-72xx/arduino/src/MD5Impl.h
Normal file
@@ -0,0 +1,5 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-07-11. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define LT_MD5_USE_HOSTAPD 1
|
||||
22
cores/beken-72xx/arduino/src/WVariant.h
Normal file
22
cores/beken-72xx/arduino/src/WVariant.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-18. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "sdk_extern.h"
|
||||
#include "sdk_mem.h"
|
||||
|
||||
// define an inline delay() which overrides BDK's delay()
|
||||
static inline __attribute__((always_inline)) void delay(unsigned long ms) {
|
||||
delayMilliseconds(ms);
|
||||
}
|
||||
|
||||
// from fixups/arch_main.c
|
||||
extern unsigned char __bk_rf_is_init;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
38
cores/beken-72xx/arduino/src/main.cpp
Normal file
38
cores/beken-72xx/arduino/src/main.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <rtos_pub.h>
|
||||
#include <sys_rtos.h>
|
||||
|
||||
extern int uart_print_port;
|
||||
|
||||
} // extern "C"
|
||||
|
||||
beken_thread_t mainThread;
|
||||
|
||||
void initArduino() {
|
||||
// set default UART output port
|
||||
uart_print_port = LT_UART_DEFAULT_PORT - 1;
|
||||
#if LT_AUTO_DOWNLOAD_REBOOT && defined(PIN_SERIAL1_RX) && defined(PIN_SERIAL1_TX)
|
||||
// initialize auto-download-reboot parser
|
||||
Serial1.begin(115200);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool startMainTask() {
|
||||
OSStatus ret = rtos_create_thread(
|
||||
&mainThread,
|
||||
THD_APPLICATION_PRIORITY,
|
||||
"main",
|
||||
(beken_thread_function_t)mainTask,
|
||||
8192,
|
||||
NULL
|
||||
);
|
||||
if (ret != kNoErr)
|
||||
return false;
|
||||
vTaskStartScheduler();
|
||||
return true;
|
||||
}
|
||||
109
cores/beken-72xx/arduino/src/wiring.c
Normal file
109
cores/beken-72xx/arduino/src/wiring.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <arm_arch.h>
|
||||
#include <bk_timer.h>
|
||||
#include <bk_timer_pub.h>
|
||||
#include <rtos_pub.h>
|
||||
#include <sys_rtos.h>
|
||||
|
||||
#define TICKS_PER_US (CFG_XTAL_FREQUENCE / 1000 / 1000)
|
||||
#define US_PER_OVERFLOW (portTICK_PERIOD_MS * 1000)
|
||||
#define TICKS_PER_OVERFLOW (TICKS_PER_US * US_PER_OVERFLOW)
|
||||
|
||||
void delayMilliseconds(unsigned long ms) {
|
||||
rtos_delay_milliseconds(ms);
|
||||
}
|
||||
|
||||
static uint32_t getTicksCount() {
|
||||
// copied from bk_timer_ctrl(), for speeds
|
||||
uint32_t timeout = 0;
|
||||
REG_WRITE(TIMER0_2_READ_CTL, (BKTIMER0 << 2) | 1);
|
||||
while (REG_READ(TIMER0_2_READ_CTL) & 1) {
|
||||
timeout++;
|
||||
if (timeout > (120 * 1000))
|
||||
return 0;
|
||||
}
|
||||
return REG_READ(TIMER0_2_READ_VALUE);
|
||||
}
|
||||
|
||||
void delayMicroseconds(unsigned int us) {
|
||||
#if LT_MICROS_HIGH_RES
|
||||
if (us == 0)
|
||||
return;
|
||||
us--;
|
||||
uint32_t startTick = getTicksCount();
|
||||
/* startTick2 accounts for the case where the timer counter overflows */
|
||||
uint32_t startTick2 = startTick - TICKS_PER_OVERFLOW;
|
||||
uint32_t delayTicks = TICKS_PER_US * us;
|
||||
while (delayTicks > TICKS_PER_OVERFLOW) {
|
||||
// The delay is longer than what the timer can count.
|
||||
// Let it overflow until only a fraction of TICKS_PER_OVERFLOW remain.
|
||||
while (getTicksCount() > startTick) {}
|
||||
while (getTicksCount() < startTick) {}
|
||||
delayTicks -= TICKS_PER_OVERFLOW;
|
||||
}
|
||||
while ((getTicksCount() - startTick < delayTicks) || // normal case
|
||||
(getTicksCount() - startTick2 < delayTicks) // counter overflow case
|
||||
) {}
|
||||
#else
|
||||
volatile uint32_t i, j;
|
||||
for (i = 0; i < us; i++) {
|
||||
for (j = 0; j < 6; j++) {}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned long millis() {
|
||||
return xTaskGetTickCount() * portTICK_PERIOD_MS;
|
||||
}
|
||||
|
||||
unsigned long micros() {
|
||||
#if (CFG_SOC_NAME == SOC_BK7231)
|
||||
#error "Not implemented"
|
||||
#endif
|
||||
|
||||
#if LT_MICROS_HIGH_RES
|
||||
static uint32_t lastMillis = 0;
|
||||
static uint32_t correctedMillis = 0;
|
||||
static uint32_t lastTicks = 0;
|
||||
uint32_t nowMillis = millis();
|
||||
uint32_t nowTicks = getTicksCount();
|
||||
bool tickOverflow = nowTicks < lastTicks;
|
||||
bool millisUpdated = nowMillis != lastMillis;
|
||||
if (millisUpdated) {
|
||||
/* reset artificially corrected millis */
|
||||
correctedMillis = nowMillis;
|
||||
} else if (tickOverflow) {
|
||||
/*
|
||||
This can happen if micros is called from within a interruptLock block (interrupts disabled).
|
||||
In this case, if the tick counter rolls over, millis() won't be updated, and micros will
|
||||
lag by US_PER_OVERFLOW milliseconds (one rollover).
|
||||
The workaround only works as long as micros() calls happen within 2ms of eachother.
|
||||
WARNING: if interrupts are disabled for more than 2ms, micros() and millis() will temporarily get out of sync.
|
||||
*/
|
||||
correctedMillis += portTICK_PERIOD_MS;
|
||||
}
|
||||
lastMillis = nowMillis;
|
||||
lastTicks = nowTicks;
|
||||
return correctedMillis * 1000 + nowTicks / (CFG_XTAL_FREQUENCE / 1000 / 1000);
|
||||
#else
|
||||
#if 0
|
||||
uint32_t timeout = 0;
|
||||
REG_WRITE(TIMER3_5_READ_CTL, (BKTIMER3 << 2) | 1);
|
||||
while (REG_READ(TIMER3_5_READ_CTL) & 1) {
|
||||
timeout++;
|
||||
if (timeout > (120 * 1000))
|
||||
return 0;
|
||||
}
|
||||
return millis() * 1000 + REG_READ(TIMER3_5_READ_VALUE) / 32;
|
||||
#endif
|
||||
return millis() * 1000;
|
||||
#endif
|
||||
}
|
||||
|
||||
void yield() {
|
||||
runPeriodicTasks();
|
||||
vTaskDelay(1);
|
||||
taskYIELD();
|
||||
}
|
||||
142
cores/beken-72xx/arduino/src/wiring_analog.c
Normal file
142
cores/beken-72xx/arduino/src/wiring_analog.c
Normal file
@@ -0,0 +1,142 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-20. */
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <pwm_pub.h>
|
||||
#include <saradc_pub.h>
|
||||
|
||||
static GPIO_INDEX pwmToGpio[] = {
|
||||
GPIO6, // PWM0
|
||||
GPIO7, // PWM1
|
||||
GPIO8, // PWM2
|
||||
GPIO9, // PWM3
|
||||
GPIO24, // PWM4
|
||||
GPIO26, // PWM5
|
||||
};
|
||||
|
||||
#if CFG_SOC_NAME == SOC_BK7231N
|
||||
static GPIO_INDEX adcToGpio[] = {
|
||||
-1, // ADC0 - VBAT
|
||||
GPIONUM, // ADC1
|
||||
GPIONUM, // ADC2
|
||||
GPIO23, // ADC3
|
||||
GPIONUM, // ADC4
|
||||
GPIONUM, // ADC5
|
||||
GPIONUM, // ADC6
|
||||
GPIONUM, // ADC7
|
||||
};
|
||||
#else
|
||||
static GPIO_INDEX adcToGpio[] = {
|
||||
-1, // ADC0 - VBAT
|
||||
GPIO4, // ADC1
|
||||
GPIO5, // ADC2
|
||||
GPIO23, // ADC3
|
||||
GPIO2, // ADC4
|
||||
GPIO3, // ADC5
|
||||
GPIO12, // ADC6
|
||||
GPIO13, // ADC7
|
||||
};
|
||||
#endif
|
||||
|
||||
static uint8_t gpioToPwm(GPIO_INDEX gpio) {
|
||||
for (uint8_t i = 0; i < sizeof(pwmToGpio) / sizeof(GPIO_INDEX); i++) {
|
||||
if (pwmToGpio[i] == gpio)
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t gpioToAdc(GPIO_INDEX gpio) {
|
||||
for (uint8_t i = 0; i < sizeof(adcToGpio) / sizeof(GPIO_INDEX); i++) {
|
||||
if (adcToGpio[i] == gpio)
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pwm_param_t pwm;
|
||||
static uint16_t adcData[1];
|
||||
|
||||
uint16_t analogReadVoltage(pin_size_t pinNumber) {
|
||||
PinInfo *pin = pinInfo(pinNumber);
|
||||
if (!pin)
|
||||
return 0;
|
||||
if (!pinSupported(pin, PIN_ADC))
|
||||
return 0;
|
||||
|
||||
UINT32 status;
|
||||
saradc_desc_t adc;
|
||||
DD_HANDLE handle;
|
||||
saradc_config_param_init(&adc);
|
||||
adc.channel = gpioToAdc(pin->gpio);
|
||||
adc.mode = ADC_CONFIG_MODE_CONTINUE;
|
||||
adc.pData = adcData;
|
||||
adc.data_buff_size = 1;
|
||||
handle = ddev_open(SARADC_DEV_NAME, &status, (uint32_t)&adc);
|
||||
if (status)
|
||||
return 0;
|
||||
// wait for data
|
||||
while (!adc.has_data || adc.current_sample_data_cnt < 1) {
|
||||
delay(1);
|
||||
}
|
||||
ddev_control(handle, SARADC_CMD_RUN_OR_STOP_ADC, (void *)false);
|
||||
ddev_close(handle);
|
||||
return adcData[0];
|
||||
}
|
||||
|
||||
uint16_t analogReadMaxVoltage(pin_size_t pinNumber) {
|
||||
return 3300;
|
||||
}
|
||||
|
||||
void analogWrite(pin_size_t pinNumber, int value) {
|
||||
PinInfo *pin = pinInfo(pinNumber);
|
||||
if (!pin)
|
||||
return;
|
||||
if (!pinSupported(pin, PIN_PWM))
|
||||
return;
|
||||
|
||||
float percent = value * 1.0 / ((1 << _analogWriteResolution) - 1);
|
||||
uint32_t frequency = 26 * _analogWritePeriod - 1;
|
||||
uint32_t dutyCycle = percent * frequency;
|
||||
pwm.channel = gpioToPwm(pin->gpio);
|
||||
#if CFG_SOC_NAME != SOC_BK7231N
|
||||
pwm.duty_cycle = dutyCycle;
|
||||
#else
|
||||
pwm.duty_cycle1 = dutyCycle;
|
||||
pwm.duty_cycle2 = 0;
|
||||
pwm.duty_cycle3 = 0;
|
||||
#endif
|
||||
|
||||
if (dutyCycle) {
|
||||
if (!pinEnabled(pin, PIN_PWM)) {
|
||||
// enable PWM and set its value
|
||||
pwm.cfg.bits.en = PWM_ENABLE;
|
||||
pwm.cfg.bits.int_en = PWM_INT_DIS;
|
||||
pwm.cfg.bits.mode = PWM_PWM_MODE;
|
||||
pwm.cfg.bits.clk = PWM_CLK_26M;
|
||||
pwm.end_value = frequency;
|
||||
pwm.p_Int_Handler = NULL;
|
||||
__wrap_bk_printf_disable();
|
||||
sddev_control(PWM_DEV_NAME, CMD_PWM_INIT_PARAM, &pwm);
|
||||
sddev_control(PWM_DEV_NAME, CMD_PWM_INIT_LEVL_SET_HIGH, &pwm.channel);
|
||||
sddev_control(PWM_DEV_NAME, CMD_PWM_UNIT_ENABLE, &pwm.channel);
|
||||
__wrap_bk_printf_enable();
|
||||
pin->enabled &= ~PIN_GPIO;
|
||||
pin->enabled |= PIN_PWM;
|
||||
} else {
|
||||
// update duty cycle
|
||||
sddev_control(PWM_DEV_NAME, CMD_PWM_SET_DUTY_CYCLE, &pwm);
|
||||
}
|
||||
} else {
|
||||
if (pinEnabled(pin, PIN_PWM)) {
|
||||
// disable PWM
|
||||
pwm.cfg.bits.en = PWM_DISABLE;
|
||||
__wrap_bk_printf_disable();
|
||||
sddev_control(PWM_DEV_NAME, CMD_PWM_DEINIT_PARAM, &pwm);
|
||||
__wrap_bk_printf_enable();
|
||||
pin->enabled &= ~PIN_PWM;
|
||||
}
|
||||
// force level as LOW
|
||||
pinMode(pinNumber, OUTPUT);
|
||||
digitalWrite(pinNumber, LOW);
|
||||
}
|
||||
}
|
||||
62
cores/beken-72xx/arduino/src/wiring_digital.c
Normal file
62
cores/beken-72xx/arduino/src/wiring_digital.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-20. */
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void pinMode(pin_size_t pinNumber, PinMode pinMode) {
|
||||
PinInfo *pin = pinInfo(pinNumber);
|
||||
if (!pin)
|
||||
return;
|
||||
if (!pinSupported(pin, PIN_GPIO))
|
||||
return;
|
||||
if (pinEnabled(pin, PIN_PWM))
|
||||
// disable PWM before using the pin
|
||||
analogWrite(pinNumber, 0);
|
||||
if (pinEnabled(pin, PIN_GPIO) && pin->mode == pinMode)
|
||||
return;
|
||||
switch (pinMode) {
|
||||
case INPUT:
|
||||
gpio_config(pin->gpio, GMODE_INPUT);
|
||||
break;
|
||||
case OUTPUT:
|
||||
gpio_config(pin->gpio, GMODE_OUTPUT);
|
||||
break;
|
||||
case INPUT_PULLUP:
|
||||
gpio_config(pin->gpio, GMODE_INPUT_PULLUP);
|
||||
break;
|
||||
case INPUT_PULLDOWN:
|
||||
gpio_config(pin->gpio, GMODE_INPUT_PULLDOWN);
|
||||
break;
|
||||
case OUTPUT_OPENDRAIN:
|
||||
gpio_config(pin->gpio, GMODE_SET_HIGH_IMPENDANCE);
|
||||
break;
|
||||
}
|
||||
pin->enabled |= PIN_GPIO;
|
||||
pin->mode = pinMode;
|
||||
}
|
||||
|
||||
void digitalWrite(pin_size_t pinNumber, PinStatus status) {
|
||||
// verify level is 0 or 1
|
||||
if (status > HIGH)
|
||||
return;
|
||||
PinInfo *pin = pinInfo(pinNumber);
|
||||
if (!pin)
|
||||
return;
|
||||
// pin is not GPIO yet or not OUTPUT; enable or disable input pullup
|
||||
if (!pinEnabled(pin, PIN_GPIO) || !pinIsOutput(pin)) {
|
||||
pinMode(pinNumber, status * INPUT_PULLUP);
|
||||
return;
|
||||
}
|
||||
// write the new state
|
||||
gpio_output(pin->gpio, status);
|
||||
}
|
||||
|
||||
PinStatus digitalRead(pin_size_t pinNumber) {
|
||||
PinInfo *pin = pinInfo(pinNumber);
|
||||
if (!pin)
|
||||
return 0;
|
||||
// pin is not GPIO yet or not INPUT; change the mode
|
||||
if (!pinEnabled(pin, PIN_GPIO) || !pinIsInput(pin))
|
||||
pinMode(pinNumber, INPUT);
|
||||
// read the value
|
||||
return gpio_input(pin->gpio);
|
||||
}
|
||||
99
cores/beken-72xx/arduino/src/wiring_irq.c
Normal file
99
cores/beken-72xx/arduino/src/wiring_irq.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-07-31. */
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
static void *irqHandlerList[PINS_COUNT] = {NULL};
|
||||
static void *irqHandlerArgs[PINS_COUNT] = {NULL};
|
||||
static bool irqChangeList[PINS_COUNT];
|
||||
|
||||
static void irqHandler(unsigned char gpio) {
|
||||
int pin = -1;
|
||||
for (pin_size_t i = 0; i < PINS_COUNT; i++) {
|
||||
if (pinTable[i].gpio == gpio) {
|
||||
pin = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pin == -1)
|
||||
return;
|
||||
if (!irqHandlerList[pin])
|
||||
return;
|
||||
if (irqChangeList[pin]) {
|
||||
if (pinTable[pin].mode == INPUT_PULLDOWN) {
|
||||
pinTable[pin].mode = INPUT_PULLUP;
|
||||
gpio_int_enable(pinTable[pin].gpio, GPIO_INT_LEVEL_FALLING, irqHandler);
|
||||
} else if (pinTable[pin].mode == INPUT_PULLUP) {
|
||||
pinTable[pin].mode = INPUT_PULLDOWN;
|
||||
gpio_int_enable(pinTable[pin].gpio, GPIO_INT_LEVEL_RISING, irqHandler);
|
||||
}
|
||||
}
|
||||
if (irqHandlerArgs[pin] == NULL) {
|
||||
((voidFuncPtr)irqHandlerList[pin])();
|
||||
} else {
|
||||
((voidFuncPtrParam)irqHandlerList[pin])(irqHandlerArgs[pin]);
|
||||
}
|
||||
}
|
||||
|
||||
void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callback, PinStatus mode) {
|
||||
attachInterruptParam(interruptNumber, (voidFuncPtrParam)callback, mode, NULL);
|
||||
}
|
||||
|
||||
void attachInterruptParam(pin_size_t interruptNumber, voidFuncPtrParam callback, PinStatus mode, void *param) {
|
||||
PinInfo *pin = pinInfo(interruptNumber);
|
||||
if (!pin)
|
||||
return;
|
||||
if (!pinSupported(pin, PIN_IRQ))
|
||||
return;
|
||||
uint32_t event = 0;
|
||||
PinMode modeNew = 0;
|
||||
bool change = 0;
|
||||
|
||||
switch (mode) {
|
||||
case LOW:
|
||||
event = GPIO_INT_LEVEL_LOW;
|
||||
modeNew = INPUT_PULLUP;
|
||||
change = false;
|
||||
break;
|
||||
case HIGH:
|
||||
event = GPIO_INT_LEVEL_HIGH;
|
||||
modeNew = INPUT_PULLDOWN;
|
||||
change = false;
|
||||
break;
|
||||
case FALLING:
|
||||
event = GPIO_INT_LEVEL_FALLING;
|
||||
modeNew = INPUT_PULLUP;
|
||||
change = false;
|
||||
break;
|
||||
case RISING:
|
||||
event = GPIO_INT_LEVEL_RISING;
|
||||
modeNew = INPUT_PULLDOWN;
|
||||
change = false;
|
||||
break;
|
||||
case CHANGE:
|
||||
event = GPIO_INT_LEVEL_FALLING;
|
||||
modeNew = INPUT_PULLUP;
|
||||
change = true;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
irqHandlerList[interruptNumber] = callback;
|
||||
irqHandlerArgs[interruptNumber] = param;
|
||||
irqChangeList[interruptNumber] = change;
|
||||
gpio_int_enable(pin->gpio, event, irqHandler);
|
||||
pin->enabled |= PIN_IRQ | PIN_GPIO;
|
||||
pin->mode = modeNew;
|
||||
}
|
||||
|
||||
void detachInterrupt(pin_size_t interruptNumber) {
|
||||
PinInfo *pin = pinInfo(interruptNumber);
|
||||
if (!pin)
|
||||
return;
|
||||
if (!pinSupported(pin, PIN_IRQ))
|
||||
return;
|
||||
irqHandlerList[interruptNumber] = NULL;
|
||||
irqHandlerArgs[interruptNumber] = NULL;
|
||||
irqChangeList[interruptNumber] = false;
|
||||
gpio_int_disable(pin->gpio);
|
||||
pin->enabled &= ~PIN_IRQ;
|
||||
}
|
||||
65
cores/beken-72xx/base/port/fal_flash_bk72xx_port.c
Normal file
65
cores/beken-72xx/base/port/fal_flash_bk72xx_port.c
Normal file
@@ -0,0 +1,65 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
|
||||
|
||||
#include <lt_logger.h>
|
||||
#include <sdk_extern.h>
|
||||
|
||||
#include <fal.h>
|
||||
#include <flash_pub.h>
|
||||
|
||||
#define FLASH_ERASE_MIN_SIZE (4 * 1024)
|
||||
|
||||
extern uint32_t flash_ctrl(uint32_t cmd, void *param);
|
||||
extern PROTECT_TYPE get_flash_protect();
|
||||
extern void flash_protection_op(uint8_t mode, PROTECT_TYPE type);
|
||||
|
||||
static void unprotect() {
|
||||
PROTECT_TYPE type = get_flash_protect();
|
||||
if (type != FLASH_PROTECT_NONE) {
|
||||
flash_protection_op(0, FLASH_PROTECT_NONE);
|
||||
#if LT_LOGLEVEL <= LT_LEVEL_DEBUG
|
||||
LT_D("Flash protect: %u -> %u", type, get_flash_protect());
|
||||
uint16_t sr = 0;
|
||||
flash_ctrl(CMD_FLASH_READ_SR, &sr);
|
||||
LT_D("SR = %04x", sr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static int init() {
|
||||
__wrap_bk_printf_disable();
|
||||
flash_init();
|
||||
flash_ctrl(CMD_FLASH_WRITE_ENABLE, NULL);
|
||||
unprotect();
|
||||
__wrap_bk_printf_enable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read(long offset, uint8_t *buf, size_t size) {
|
||||
flash_read((char *)buf, size, offset);
|
||||
return size;
|
||||
}
|
||||
|
||||
static int write(long offset, const uint8_t *buf, size_t size) {
|
||||
unprotect();
|
||||
flash_write((char *)buf, size, offset);
|
||||
return size;
|
||||
}
|
||||
|
||||
static int erase(long offset, size_t size) {
|
||||
unprotect();
|
||||
size = ((size - 1) / FLASH_ERASE_MIN_SIZE) + 1;
|
||||
for (uint16_t i = 0; i < size; i++) {
|
||||
uint32_t addr = offset + i * FLASH_ERASE_MIN_SIZE;
|
||||
flash_ctrl(CMD_FLASH_ERASE_SECTOR, &addr);
|
||||
}
|
||||
return size * FLASH_ERASE_MIN_SIZE;
|
||||
}
|
||||
|
||||
const struct fal_flash_dev flash0 = {
|
||||
.name = FAL_FLASH_DEV_NAME,
|
||||
.addr = 0x0,
|
||||
.len = FLASH_LENGTH,
|
||||
.blk_size = FLASH_ERASE_MIN_SIZE,
|
||||
.ops = {init, read, write, erase},
|
||||
.write_gran = 1,
|
||||
};
|
||||
21
cores/beken-72xx/base/port/printf.c
Normal file
21
cores/beken-72xx/base/port/printf.c
Normal file
@@ -0,0 +1,21 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
|
||||
|
||||
#include <printf_config.h>
|
||||
|
||||
#include <printf/printf.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern void bk_send_byte(uint8_t uport, uint8_t data);
|
||||
extern int uart_print_port;
|
||||
|
||||
void putchar_(char c) {
|
||||
bk_send_byte(uart_print_port, c);
|
||||
}
|
||||
|
||||
void putchar_p(char c, unsigned long port) {
|
||||
bk_send_byte((port & 0xFF) - 1, c);
|
||||
}
|
||||
|
||||
WRAP_PRINTF(bk_printf);
|
||||
7
cores/beken-72xx/base/port/printf_port.h
Normal file
7
cores/beken-72xx/base/port/printf_port.h
Normal file
@@ -0,0 +1,7 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-20. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <printf_config.h>
|
||||
|
||||
WRAP_DISABLE_DEF(bk_printf);
|
||||
20
cores/beken-72xx/base/sdk_mem.h
Normal file
20
cores/beken-72xx/base/sdk_mem.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-18. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
// Beken SDK is actually pretty good, in terms of declaring
|
||||
// stdlib functions properly! So no need for any #define hell.
|
||||
#include <mem_pub.h>
|
||||
|
||||
// All the MemMang functions are in stdlib, just wrapped
|
||||
// during linking.
|
||||
#include <stdlib.h>
|
||||
// for memcpy etc.
|
||||
#include <string.h>
|
||||
|
||||
// ...except zalloc, which is apparently not in the stdlib
|
||||
#define zalloc os_zalloc
|
||||
|
||||
#define LT_HEAP_FUNC xPortGetFreeHeapSize
|
||||
30
cores/beken-72xx/base/sdk_private.h
Normal file
30
cores/beken-72xx/base/sdk_private.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-18. */
|
||||
|
||||
#pragma once
|
||||
|
||||
// for printf() etc (they are wrapped anyway)
|
||||
#include <stdio.h>
|
||||
|
||||
// most stuff is here
|
||||
#include <include.h>
|
||||
// for os_printf
|
||||
#include <uart_pub.h>
|
||||
// for GPIO names
|
||||
#include <gpio_pub.h>
|
||||
|
||||
// conflict with stl_algobase.h
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
// include printf() wrapper disable methods
|
||||
#include <printf_port.h>
|
||||
|
||||
// make non-SDK code call the proper printf()
|
||||
#undef bk_printf
|
||||
#undef os_printf
|
||||
#undef warning_prf
|
||||
#undef fatal_prf
|
||||
#define bk_printf printf
|
||||
#define os_printf printf
|
||||
#define warning_prf printf
|
||||
#define fatal_prf printf
|
||||
148
cores/common/arduino/libraries/api/LT/LT.cpp
Normal file
148
cores/common/arduino/libraries/api/LT/LT.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-06. */
|
||||
|
||||
#include "LibreTuyaClass.h"
|
||||
|
||||
/**
|
||||
* @brief Get LibreTuya version string.
|
||||
*/
|
||||
const char *LibreTuya::getVersion() {
|
||||
return LT_VERSION_STR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get board name.
|
||||
*/
|
||||
const char *LibreTuya::getBoard() {
|
||||
return LT_BOARD_STR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get CPU family ID.
|
||||
*/
|
||||
ChipFamily LibreTuya::getChipFamily() {
|
||||
return FAMILY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get CPU family name as string.
|
||||
*/
|
||||
const char *LibreTuya::getChipFamilyName() {
|
||||
return STRINGIFY_MACRO(FAMILY) + 2;
|
||||
}
|
||||
|
||||
static char *deviceName = NULL;
|
||||
|
||||
/**
|
||||
* @brief Get device friendly name in format "LT-<board>-<chip id>".
|
||||
* Can be used as hostname.
|
||||
*/
|
||||
const char *LibreTuya::getDeviceName() {
|
||||
if (deviceName)
|
||||
return deviceName;
|
||||
uint32_t chipId = getChipId();
|
||||
uint8_t *id = (uint8_t *)&chipId;
|
||||
|
||||
const char *board = getBoard();
|
||||
uint8_t boardLen = strlen(board);
|
||||
deviceName = (char *)malloc(3 + boardLen + 1 + 6 + 1);
|
||||
|
||||
sprintf(deviceName, "LT-%s-%02x%02x%02x", board, id[0], id[1], id[2]);
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a textual representation of a reset reason.
|
||||
*
|
||||
* @param reason value to convert to text, uses getResetReason() by default
|
||||
*/
|
||||
const char *LibreTuya::getResetReasonName(ResetReason reason) {
|
||||
if (reason >= RESET_REASON_MAX)
|
||||
reason = getResetReason();
|
||||
switch (reason) {
|
||||
case RESET_REASON_POWER:
|
||||
return "Power-On";
|
||||
case RESET_REASON_BROWNOUT:
|
||||
return "Brownout";
|
||||
case RESET_REASON_HARDWARE:
|
||||
return "HW Reboot";
|
||||
case RESET_REASON_SOFTWARE:
|
||||
return "SW Reboot";
|
||||
case RESET_REASON_WATCHDOG:
|
||||
return "WDT Reset";
|
||||
case RESET_REASON_CRASH:
|
||||
return "Crash";
|
||||
case RESET_REASON_SLEEP:
|
||||
return "Sleep Wakeup";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get CPU frequency in MHz.
|
||||
*/
|
||||
uint32_t LibreTuya::getCpuFreqMHz() {
|
||||
return getCpuFreq() / 1000000;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get flash chip total size.
|
||||
* The default implementation uses the least significant
|
||||
* byte of the chip ID to determine the size.
|
||||
*/
|
||||
__attribute__((weak)) uint32_t LibreTuya::getFlashChipSize() {
|
||||
FlashId id = getFlashChipId();
|
||||
if (id.chipSizeId >= 0x14 && id.chipSizeId <= 0x19) {
|
||||
return (1 << id.chipSizeId);
|
||||
}
|
||||
#ifdef FLASH_LENGTH
|
||||
return FLASH_LENGTH;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the OTA index for updated firmware.
|
||||
*
|
||||
* Note: returns 1 for chips without dual-OTA.
|
||||
*/
|
||||
uint8_t LibreTuya::otaGetTarget() {
|
||||
if (!otaSupportsDual())
|
||||
return 1;
|
||||
return otaGetRunning() ^ 0b11;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform OTA rollback: switch to the previous image, or abort current
|
||||
* switched OTA update, if not rebooted yet.
|
||||
*
|
||||
* @return false if no second image to run, writing failed or dual-OTA not supported
|
||||
*/
|
||||
bool LibreTuya::otaRollback() {
|
||||
if (!otaCanRollback())
|
||||
return false;
|
||||
if (otaGetRunning() != otaGetStoredIndex())
|
||||
// force switching back to current image
|
||||
return otaSwitch(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if OTA rollback is supported and available (there is another image to run).
|
||||
* @return false if no second image to run or dual-OTA not supported
|
||||
*/
|
||||
bool LibreTuya::otaCanRollback() {
|
||||
if (!otaSupportsDual())
|
||||
return false;
|
||||
if (otaGetRunning() == otaGetStoredIndex())
|
||||
return true;
|
||||
if (otaGetRunning() == 1 && otaHasImage1())
|
||||
return true;
|
||||
if (otaGetRunning() == 2 && otaHasImage2())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void LibreTuya::gpioRecover() {
|
||||
// nop by default
|
||||
}
|
||||
193
cores/common/arduino/libraries/api/LT/LT.h
Normal file
193
cores/common/arduino/libraries/api/LT/LT.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-06. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "LibreTuyaAPI.h"
|
||||
#include <core/ChipType.h>
|
||||
|
||||
typedef enum {
|
||||
RESET_REASON_UNKNOWN = 0,
|
||||
RESET_REASON_POWER = 1,
|
||||
RESET_REASON_BROWNOUT = 2,
|
||||
RESET_REASON_HARDWARE = 3,
|
||||
RESET_REASON_SOFTWARE = 4,
|
||||
RESET_REASON_WATCHDOG = 5,
|
||||
RESET_REASON_CRASH = 6,
|
||||
RESET_REASON_SLEEP = 7,
|
||||
RESET_REASON_MAX = 8,
|
||||
} ResetReason;
|
||||
|
||||
/**
|
||||
* @brief Flash chip ID structure.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t manufacturerId;
|
||||
uint8_t chipId;
|
||||
uint8_t chipSizeId;
|
||||
} FlashId;
|
||||
|
||||
/**
|
||||
* @brief Main LibreTuya API class.
|
||||
*
|
||||
* This class contains all functions common amongst all families.
|
||||
* Implementations of these methods may vary between families.
|
||||
*
|
||||
* The class is accessible using the `LT` global object (defined by the family).
|
||||
*/
|
||||
class LibreTuya {
|
||||
public: /* Common methods - note: these are documented in LibreTuyaAPI.cpp */
|
||||
const char *getVersion();
|
||||
const char *getBoard();
|
||||
ChipFamily getChipFamily();
|
||||
const char *getChipFamilyName();
|
||||
const char *getDeviceName();
|
||||
const char *getResetReasonName(ResetReason reason = RESET_REASON_MAX);
|
||||
uint32_t getCpuFreqMHz();
|
||||
uint32_t getFlashChipSize();
|
||||
uint8_t otaGetTarget();
|
||||
bool otaRollback();
|
||||
bool otaCanRollback();
|
||||
|
||||
public: /* Compatibility methods */
|
||||
/**
|
||||
* @brief Alias of getMaxAllocHeap().
|
||||
*/
|
||||
inline uint32_t getMaxFreeBlockSize() {
|
||||
return getMaxAllocHeap();
|
||||
}
|
||||
|
||||
public: /* Family-defined methods */
|
||||
/**
|
||||
* @brief Reboot the CPU.
|
||||
*/
|
||||
void restart();
|
||||
/**
|
||||
* @brief Reboot the CPU and stay in download mode (if possible).
|
||||
*/
|
||||
void restartDownloadMode();
|
||||
/**
|
||||
* @brief Get the reason of last chip reset.
|
||||
*/
|
||||
ResetReason getResetReason();
|
||||
/**
|
||||
* @brief Reconfigure GPIO pins used for debugging
|
||||
* (SWD/JTAG), so that they can be used as normal I/O.
|
||||
*/
|
||||
void gpioRecover();
|
||||
|
||||
public: /* CPU-related */
|
||||
/**
|
||||
* @brief Get CPU model ID.
|
||||
*/
|
||||
ChipType getChipType();
|
||||
/**
|
||||
* @brief Get CPU model name as string.
|
||||
*/
|
||||
const char *getChipModel();
|
||||
/**
|
||||
* @brief Get CPU unique ID. This may be based on MAC, eFuse, etc.
|
||||
* Note: the number should be 24-bit (with most significant byte being zero).
|
||||
*/
|
||||
uint32_t getChipId();
|
||||
/**
|
||||
* @brief Get CPU core count.
|
||||
*/
|
||||
uint8_t getChipCores();
|
||||
/**
|
||||
* @brief Get CPU core type name as string.
|
||||
*/
|
||||
const char *getChipCoreType();
|
||||
/**
|
||||
* @brief Get CPU frequency in Hz.
|
||||
*/
|
||||
uint32_t getCpuFreq();
|
||||
/**
|
||||
* @brief Get CPU cycle count.
|
||||
*/
|
||||
uint32_t getCycleCount();
|
||||
|
||||
public: /* Flash memory utilities */
|
||||
/**
|
||||
* @brief Read flash chip ID and return a FlashId struct.
|
||||
*/
|
||||
FlashId getFlashChipId();
|
||||
|
||||
public: /* Memory management */
|
||||
/**
|
||||
* @brief Get total RAM size.
|
||||
*/
|
||||
uint32_t getRamSize();
|
||||
/**
|
||||
* @brief Get total heap size.
|
||||
*/
|
||||
uint32_t getHeapSize();
|
||||
/**
|
||||
* @brief Get free heap size.
|
||||
*/
|
||||
uint32_t getFreeHeap();
|
||||
/**
|
||||
* @brief Get lowest level of free heap memory.
|
||||
*/
|
||||
uint32_t getMinFreeHeap();
|
||||
/**
|
||||
* @brief Get largest block of heap that can be allocated at once.
|
||||
*/
|
||||
uint32_t getMaxAllocHeap();
|
||||
|
||||
public: /* OTA-related */
|
||||
/**
|
||||
* @brief Get the currently running firmware OTA index.
|
||||
*/
|
||||
uint8_t otaGetRunning();
|
||||
/**
|
||||
* @brief Read the currently active OTA index, i.e. the one that will boot upon restart.
|
||||
*/
|
||||
uint8_t otaGetStoredIndex();
|
||||
/**
|
||||
* @brief Check if the chip supports dual-OTA (i.e. OTA is flashed to a different partition).
|
||||
*
|
||||
* TODO: make this work for actual dual-OTA chips; remove checking this in otaGetTarget() etc.
|
||||
*/
|
||||
bool otaSupportsDual();
|
||||
/**
|
||||
* @brief Check if OTA1 image is valid.
|
||||
*/
|
||||
bool otaHasImage1();
|
||||
/**
|
||||
* @brief Check if OTA2 image is valid.
|
||||
*/
|
||||
bool otaHasImage2();
|
||||
/**
|
||||
* @brief Try to switch OTA index to the other image.
|
||||
*
|
||||
* Note: should return true for chips without dual-OTA. Should return false if one of two images is not valid.
|
||||
*
|
||||
* @param force switch even if other image already marked as active
|
||||
* @return false if writing failed; true otherwise
|
||||
*/
|
||||
bool otaSwitch(bool force = false);
|
||||
|
||||
public: /* Watchdog */
|
||||
/**
|
||||
* @brief Enable the hardware watchdog.
|
||||
*
|
||||
* @param timeout watchdog timeout, milliseconds (defaults to 10s)
|
||||
* @return whether the chip has a hardware watchdog
|
||||
*/
|
||||
bool wdtEnable(uint32_t timeout = 10000);
|
||||
/**
|
||||
* @brief Disable the hardware watchdog.
|
||||
*/
|
||||
void wdtDisable();
|
||||
/**
|
||||
* @brief Feed/reset the hardware watchdog timer.
|
||||
*/
|
||||
void wdtFeed();
|
||||
};
|
||||
|
||||
extern LibreTuya LT;
|
||||
extern LibreTuya ESP;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-07-03. */
|
||||
|
||||
#include "SoftwareSerial.h"
|
||||
|
||||
#ifdef LT_ARD_HAS_SOFTSERIAL
|
||||
|
||||
SoftwareSerial::SoftwareSerial(pin_size_t receivePin, pin_size_t transmitPin, bool inverted) {
|
||||
data.rx.buf = NULL;
|
||||
data.tx.buf = NULL;
|
||||
data.rx.pin = receivePin;
|
||||
data.tx.pin = transmitPin;
|
||||
data.invert = inverted == true;
|
||||
}
|
||||
|
||||
int SoftwareSerial::available() {
|
||||
return data.rx.buf->available();
|
||||
}
|
||||
|
||||
int SoftwareSerial::peek() {
|
||||
return data.rx.buf->peek();
|
||||
}
|
||||
|
||||
int SoftwareSerial::read() {
|
||||
return data.rx.buf->read_char();
|
||||
}
|
||||
|
||||
void SoftwareSerial::flush() {
|
||||
while (data.rx.buf->available()) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
size_t SoftwareSerial::write(uint8_t c) {
|
||||
while (data.tx.buf->isFull()) {
|
||||
yield();
|
||||
}
|
||||
data.tx.buf->store_char(c);
|
||||
if (data.tx.state == SS_IDLE) {
|
||||
data.tx.state = SS_START;
|
||||
this->startTx();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,76 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-07-03. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef LT_ARD_HAS_SOFTSERIAL
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <api/HardwareSerial.h>
|
||||
#include <api/RingBuffer.h>
|
||||
|
||||
using namespace arduino;
|
||||
|
||||
typedef enum {
|
||||
SS_IDLE = 0,
|
||||
SS_START,
|
||||
SS_DATA0,
|
||||
SS_DATA1,
|
||||
SS_DATA2,
|
||||
SS_DATA3,
|
||||
SS_DATA4,
|
||||
SS_DATA5,
|
||||
SS_DATA6,
|
||||
SS_DATA7,
|
||||
SS_STOP,
|
||||
SS_END,
|
||||
} SoftState;
|
||||
|
||||
typedef struct {
|
||||
SoftState state;
|
||||
RingBuffer *buf;
|
||||
uint8_t byte;
|
||||
pin_size_t pin;
|
||||
void *param;
|
||||
} SoftData;
|
||||
|
||||
typedef struct {
|
||||
SoftData rx;
|
||||
SoftData tx;
|
||||
uint8_t invert;
|
||||
void *param;
|
||||
} SoftSerial;
|
||||
|
||||
class SoftwareSerial : public HardwareSerial {
|
||||
private:
|
||||
SoftSerial data;
|
||||
void *param;
|
||||
|
||||
public:
|
||||
SoftwareSerial(pin_size_t receivePin, pin_size_t transmitPin, bool inverted = false);
|
||||
|
||||
inline void begin(unsigned long baudrate) {
|
||||
begin(baudrate, SERIAL_8N1);
|
||||
}
|
||||
|
||||
int available();
|
||||
int peek();
|
||||
int read();
|
||||
void flush();
|
||||
size_t write(uint8_t c);
|
||||
|
||||
operator bool() {
|
||||
return data.rx.buf || data.tx.buf;
|
||||
}
|
||||
|
||||
public: // Family needs to implement these methods only
|
||||
void begin(unsigned long baudrate, uint16_t config);
|
||||
void end();
|
||||
|
||||
private:
|
||||
void startTx();
|
||||
void endTx();
|
||||
|
||||
using Print::write;
|
||||
};
|
||||
|
||||
#endif
|
||||
77
cores/common/arduino/libraries/api/WiFi/WiFi.cpp
Normal file
77
cores/common/arduino/libraries/api/WiFi/WiFi.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
|
||||
|
||||
#include "WiFi.h"
|
||||
|
||||
void WiFiClass::printDiag(Print &dest) {
|
||||
const char *modes[] = {"NULL", "STA", "AP", "STA+AP"};
|
||||
const char *enc[] = {"Open", "WEP", "WPA PSK", "WPA2 PSK", "WPA/WPA2", "WPA", "WPA2"};
|
||||
|
||||
dest.print("Mode: ");
|
||||
dest.println(modes[getMode()]);
|
||||
|
||||
if (getMode() & WIFI_MODE_STA) {
|
||||
dest.println("-- Station --");
|
||||
dest.print("SSID: ");
|
||||
if (isConnected()) {
|
||||
dest.println(SSID());
|
||||
dest.print("Channel: ");
|
||||
dest.println(channel());
|
||||
dest.print("BSSID: ");
|
||||
dest.println(BSSIDstr());
|
||||
dest.print("RSSI: ");
|
||||
dest.println(RSSI());
|
||||
dest.print("Encryption: ");
|
||||
dest.println(enc[getEncryption()]);
|
||||
dest.print("IP: ");
|
||||
dest.println(localIP());
|
||||
dest.print("MAC: ");
|
||||
dest.println(macAddress());
|
||||
dest.print("Hostname: ");
|
||||
dest.println(getHostname());
|
||||
} else {
|
||||
dest.println("disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
if (getMode() & WIFI_MODE_AP) {
|
||||
dest.println("-- Access Point --");
|
||||
dest.print("SSID: ");
|
||||
if (softAPSSID().length()) {
|
||||
dest.println(softAPSSID());
|
||||
dest.print("IP: ");
|
||||
dest.println(softAPIP());
|
||||
dest.print("MAC: ");
|
||||
dest.println(softAPmacAddress());
|
||||
dest.print("Hostname: ");
|
||||
dest.println(softAPgetHostname());
|
||||
} else {
|
||||
dest.println("disconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WiFiClass::validate(const char *ssid, const char *passphrase) {
|
||||
if (!ssid || *ssid == 0x00 || strlen(ssid) > 32) {
|
||||
LT_WM(WIFI, "SSID not specified or too long");
|
||||
return false;
|
||||
}
|
||||
if (passphrase) {
|
||||
uint16_t length = strlen(passphrase);
|
||||
if (length < 8) {
|
||||
LT_WM(WIFI, "Passphrase too short (%u)", length);
|
||||
return false;
|
||||
}
|
||||
if (length > 63) {
|
||||
LT_WM(WIFI, "Passphrase too long (%u)", length);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void WiFiClass::dataInitialize() {}
|
||||
|
||||
__attribute__((weak)) void WiFiClass::dataFree() {}
|
||||
|
||||
WiFiClass WiFi;
|
||||
WiFiClass *pWiFi = NULL;
|
||||
197
cores/common/arduino/libraries/api/WiFi/WiFi.h
Normal file
197
cores/common/arduino/libraries/api/WiFi/WiFi.h
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
WiFi.h - esp32 Wifi support.
|
||||
Based on WiFi.h from Arduino WiFi shield library.
|
||||
Copyright (c) 2011-2014 Arduino. All right reserved.
|
||||
Modified by Ivan Grokhotkov, December 2014
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <api/Events.h>
|
||||
#include <api/IPAddress.h>
|
||||
#include <api/IPv6Address.h>
|
||||
#include <vector>
|
||||
|
||||
#include "WiFiType.h"
|
||||
|
||||
#ifdef LT_ARD_HAS_WIFI
|
||||
// family's data structure
|
||||
#include <WiFiData.h>
|
||||
#endif
|
||||
|
||||
class WiFiClass {
|
||||
public:
|
||||
#ifdef LT_ARD_HAS_WIFI
|
||||
// must be public for WiFiEvents & WiFiScan static handlers
|
||||
WiFiData data;
|
||||
#endif
|
||||
WiFiScanData *scan = NULL;
|
||||
|
||||
public: /* WiFi.cpp */
|
||||
WiFiClass();
|
||||
~WiFiClass();
|
||||
void printDiag(Print &dest);
|
||||
bool validate(const char *ssid, const char *passphrase);
|
||||
void dataInitialize();
|
||||
void dataFree();
|
||||
|
||||
public: /* WiFiGeneric.cpp */
|
||||
bool mode(WiFiMode mode);
|
||||
bool modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap);
|
||||
WiFiMode getMode();
|
||||
WiFiStatus status();
|
||||
|
||||
bool enableSTA(bool enable);
|
||||
bool enableAP(bool enable);
|
||||
|
||||
bool setSleep(bool enable);
|
||||
bool getSleep();
|
||||
|
||||
bool setTxPower(int power);
|
||||
int getTxPower();
|
||||
|
||||
int hostByName(const char *hostname, IPAddress &aResult);
|
||||
IPAddress hostByName(const char *hostname);
|
||||
|
||||
static IPAddress calculateNetworkID(IPAddress ip, IPAddress subnet);
|
||||
static IPAddress calculateBroadcast(IPAddress ip, IPAddress subnet);
|
||||
static uint8_t calculateSubnetCIDR(IPAddress subnetMask);
|
||||
static String macToString(uint8_t *mac);
|
||||
|
||||
protected: /* WiFiEvents.cpp */
|
||||
static std::vector<EventHandler> handlers;
|
||||
|
||||
public: /* WiFiEvents.cpp */
|
||||
uint16_t onEvent(EventCb callback, EventId eventId = ARDUINO_EVENT_MAX);
|
||||
uint16_t onEvent(EventFuncCb callback, EventId eventId = ARDUINO_EVENT_MAX);
|
||||
uint16_t onEvent(EventSysCb callback, EventId eventId = ARDUINO_EVENT_MAX);
|
||||
void removeEvent(EventCb callback, EventId eventId);
|
||||
void removeEvent(EventSysCb callback, EventId eventId);
|
||||
void removeEvent(uint16_t id);
|
||||
static void postEvent(EventId eventId, EventInfo eventInfo);
|
||||
|
||||
public: /* WiFiSTA.cpp */
|
||||
WiFiStatus begin(
|
||||
const char *ssid,
|
||||
const char *passphrase = NULL,
|
||||
int32_t channel = 0,
|
||||
const uint8_t *bssid = NULL,
|
||||
bool connect = true
|
||||
);
|
||||
WiFiStatus
|
||||
begin(char *ssid, char *passphrase = NULL, int32_t channel = 0, const uint8_t *bssid = NULL, bool connect = true);
|
||||
|
||||
bool config(
|
||||
IPAddress localIP,
|
||||
IPAddress gateway,
|
||||
IPAddress subnet,
|
||||
IPAddress dns1 = (uint32_t)0x00000000,
|
||||
IPAddress dns2 = (uint32_t)0x00000000
|
||||
);
|
||||
|
||||
bool reconnect(const uint8_t *bssid = NULL);
|
||||
bool disconnect(bool wifiOff = false);
|
||||
|
||||
bool isConnected();
|
||||
|
||||
bool setAutoReconnect(bool autoReconnect);
|
||||
bool getAutoReconnect();
|
||||
|
||||
WiFiStatus waitForConnectResult(unsigned long timeout);
|
||||
|
||||
IPAddress localIP();
|
||||
uint8_t *macAddress(uint8_t *mac);
|
||||
String macAddress();
|
||||
IPAddress subnetMask();
|
||||
IPAddress gatewayIP();
|
||||
IPAddress dnsIP(uint8_t dns_no = 0);
|
||||
IPAddress broadcastIP();
|
||||
IPAddress networkID();
|
||||
uint8_t subnetCIDR();
|
||||
bool enableIpV6();
|
||||
IPv6Address localIPv6();
|
||||
const char *getHostname();
|
||||
bool setHostname(const char *hostname);
|
||||
bool setMacAddress(const uint8_t *mac);
|
||||
|
||||
inline bool hostname(const String &aHostname) {
|
||||
return setHostname(aHostname.c_str());
|
||||
}
|
||||
|
||||
const String SSID();
|
||||
const String psk();
|
||||
uint8_t *BSSID();
|
||||
String BSSIDstr();
|
||||
int32_t channel();
|
||||
int8_t RSSI();
|
||||
WiFiAuthMode getEncryption();
|
||||
|
||||
public: /* WiFiScan.cpp */
|
||||
int16_t scanNetworks(
|
||||
bool async = false,
|
||||
bool showHidden = false,
|
||||
bool passive = false,
|
||||
uint32_t maxMsPerChannel = 300,
|
||||
uint8_t channel = 0
|
||||
);
|
||||
bool getNetworkInfo(
|
||||
uint8_t networkItem,
|
||||
String &ssid,
|
||||
WiFiAuthMode &encryptionType,
|
||||
int32_t &RSSI,
|
||||
uint8_t *&BSSID,
|
||||
int32_t &channel
|
||||
);
|
||||
|
||||
int16_t scanComplete();
|
||||
uint8_t scanAlloc(uint8_t count);
|
||||
void scanInit();
|
||||
void scanDelete();
|
||||
|
||||
String SSID(uint8_t networkItem);
|
||||
WiFiAuthMode encryptionType(uint8_t networkItem);
|
||||
int32_t RSSI(uint8_t networkItem);
|
||||
uint8_t *BSSID(uint8_t networkItem);
|
||||
String BSSIDstr(uint8_t networkItem);
|
||||
int32_t channel(uint8_t networkItem);
|
||||
|
||||
public: /* WiFiAP.cpp */
|
||||
bool softAP(
|
||||
const char *ssid, const char *passphrase = NULL, int channel = 1, bool ssidHidden = false, int maxClients = 4
|
||||
);
|
||||
bool softAPConfig(IPAddress localIP, IPAddress gateway, IPAddress subnet);
|
||||
bool softAPdisconnect(bool wifiOff = false);
|
||||
|
||||
uint8_t softAPgetStationNum();
|
||||
|
||||
IPAddress softAPIP();
|
||||
IPAddress softAPBroadcastIP();
|
||||
IPAddress softAPNetworkID();
|
||||
uint8_t softAPSubnetCIDR();
|
||||
IPAddress softAPSubnetMask();
|
||||
bool softAPenableIpV6();
|
||||
IPv6Address softAPIPv6();
|
||||
const char *softAPgetHostname();
|
||||
bool softAPsetHostname(const char *hostname);
|
||||
uint8_t *softAPmacAddress(uint8_t *mac);
|
||||
String softAPmacAddress(void);
|
||||
const String softAPSSID(void);
|
||||
};
|
||||
|
||||
extern WiFiClass WiFi;
|
||||
extern WiFiClass *pWiFi;
|
||||
23
cores/common/arduino/libraries/api/WiFi/WiFiAP.cpp
Normal file
23
cores/common/arduino/libraries/api/WiFi/WiFiAP.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
|
||||
|
||||
#include "WiFi.h"
|
||||
|
||||
IPAddress WiFiClass::softAPBroadcastIP() {
|
||||
return calculateBroadcast(softAPIP(), softAPSubnetMask());
|
||||
}
|
||||
|
||||
IPAddress WiFiClass::softAPNetworkID() {
|
||||
return calculateNetworkID(softAPIP(), softAPSubnetMask());
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::softAPSubnetCIDR() {
|
||||
return calculateSubnetCIDR(softAPSubnetMask());
|
||||
}
|
||||
|
||||
__attribute__((weak)) bool WiFiClass::softAPenableIpV6() {
|
||||
return false;
|
||||
}
|
||||
|
||||
__attribute__((weak)) IPv6Address WiFiClass::softAPIPv6() {
|
||||
return IPv6Address();
|
||||
}
|
||||
84
cores/common/arduino/libraries/api/WiFi/WiFiEvents.cpp
Normal file
84
cores/common/arduino/libraries/api/WiFi/WiFiEvents.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-05-17. */
|
||||
|
||||
#include "WiFi.h"
|
||||
|
||||
std::vector<EventHandler> WiFiClass::handlers;
|
||||
|
||||
uint16_t WiFiClass::onEvent(EventCb callback, EventId eventId) {
|
||||
if (!callback)
|
||||
return 0;
|
||||
EventHandler handler;
|
||||
handler.cb = callback;
|
||||
handler.eventId = eventId;
|
||||
handlers.push_back(handler);
|
||||
return handler.id;
|
||||
}
|
||||
|
||||
uint16_t WiFiClass::onEvent(EventFuncCb callback, EventId eventId) {
|
||||
if (!callback)
|
||||
return 0;
|
||||
EventHandler handler;
|
||||
handler.fcb = callback;
|
||||
handler.eventId = eventId;
|
||||
handlers.push_back(handler);
|
||||
return handler.id;
|
||||
}
|
||||
|
||||
uint16_t WiFiClass::onEvent(EventSysCb callback, EventId eventId) {
|
||||
if (!callback)
|
||||
return 0;
|
||||
EventHandler handler;
|
||||
handler.scb = callback;
|
||||
handler.eventId = eventId;
|
||||
handlers.push_back(handler);
|
||||
return handler.id;
|
||||
}
|
||||
|
||||
void WiFiClass::removeEvent(EventCb callback, EventId eventId) {
|
||||
if (!callback)
|
||||
return;
|
||||
for (uint16_t i = 0; i < handlers.size(); i++) {
|
||||
EventHandler handler = handlers[i];
|
||||
if (handler.cb == callback && handler.eventId == eventId) {
|
||||
handlers.erase(handlers.begin() + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiClass::removeEvent(EventSysCb callback, EventId eventId) {
|
||||
if (!callback)
|
||||
return;
|
||||
for (uint16_t i = 0; i < handlers.size(); i++) {
|
||||
EventHandler handler = handlers[i];
|
||||
if (handler.scb == callback && handler.eventId == eventId) {
|
||||
handlers.erase(handlers.begin() + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiClass::removeEvent(uint16_t id) {
|
||||
for (uint16_t i = 0; i < handlers.size(); i++) {
|
||||
EventHandler handler = handlers[i];
|
||||
if (handler.id == id) {
|
||||
handlers.erase(handlers.begin() + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiClass::postEvent(EventId eventId, EventInfo eventInfo) {
|
||||
for (auto handler : handlers) {
|
||||
if (handler.eventId != ARDUINO_EVENT_MAX && handler.eventId != eventId)
|
||||
continue;
|
||||
if (handler.cb) {
|
||||
handler.cb(eventId);
|
||||
} else if (handler.fcb) {
|
||||
handler.fcb(eventId, eventInfo);
|
||||
} else if (handler.scb) {
|
||||
Event_t event = {
|
||||
.event_id = eventId,
|
||||
.event_info = eventInfo,
|
||||
};
|
||||
handler.scb(&event);
|
||||
}
|
||||
}
|
||||
}
|
||||
173
cores/common/arduino/libraries/api/WiFi/WiFiEvents.h
Normal file
173
cores/common/arduino/libraries/api/WiFi/WiFiEvents.h
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "WiFiType.h"
|
||||
|
||||
/** Argument structure for WIFI_EVENT_SCAN_DONE event */
|
||||
typedef struct {
|
||||
uint32_t status; /**< status of scanning APs: 0 - success, 1 - failure */
|
||||
uint8_t number; /**< number of scan results */
|
||||
uint8_t scan_id; /**< scan sequence number, used for block scan */
|
||||
} wifi_event_sta_scan_done_t;
|
||||
|
||||
/** Argument structure for WIFI_EVENT_STA_CONNECTED event */
|
||||
typedef struct {
|
||||
uint8_t ssid[32]; /**< SSID of connected AP */
|
||||
uint8_t ssid_len; /**< SSID length of connected AP */
|
||||
uint8_t bssid[6]; /**< BSSID of connected AP*/
|
||||
uint8_t channel; /**< channel of connected AP*/
|
||||
wifi_auth_mode_t authmode; /**< authentication mode used by AP*/
|
||||
} wifi_event_sta_connected_t;
|
||||
|
||||
/** Argument structure for WIFI_EVENT_STA_DISCONNECTED event */
|
||||
typedef struct {
|
||||
uint8_t ssid[32]; /**< SSID of disconnected AP */
|
||||
uint8_t ssid_len; /**< SSID length of disconnected AP */
|
||||
uint8_t bssid[6]; /**< BSSID of disconnected AP */
|
||||
uint8_t reason; /**< reason of disconnection */
|
||||
} wifi_event_sta_disconnected_t;
|
||||
|
||||
/** Argument structure for WIFI_EVENT_STA_AUTHMODE_CHANGE event */
|
||||
typedef struct {
|
||||
wifi_auth_mode_t old_mode; /**< the old auth mode of AP */
|
||||
wifi_auth_mode_t new_mode; /**< the new auth mode of AP */
|
||||
} wifi_event_sta_authmode_change_t;
|
||||
|
||||
/** Argument structure for WIFI_EVENT_STA_WPS_ER_PIN event */
|
||||
typedef struct {
|
||||
uint8_t pin_code[8]; /**< PIN code of station in enrollee mode */
|
||||
} wifi_event_sta_wps_er_pin_t;
|
||||
|
||||
/** Argument structure for WIFI_EVENT_STA_WPS_ER_FAILED event */
|
||||
typedef enum {
|
||||
WPS_FAIL_REASON_NORMAL = 0, /**< ESP32 WPS normal fail reason */
|
||||
WPS_FAIL_REASON_RECV_M2D, /**< ESP32 WPS receive M2D frame */
|
||||
WPS_FAIL_REASON_MAX
|
||||
} wifi_event_sta_wps_fail_reason_t;
|
||||
|
||||
#define MAX_SSID_LEN 32
|
||||
#define MAX_PASSPHRASE_LEN 64
|
||||
#define MAX_WPS_AP_CRED 3
|
||||
|
||||
/** Argument structure for WIFI_EVENT_STA_WPS_ER_SUCCESS event */
|
||||
typedef struct {
|
||||
uint8_t ap_cred_cnt; /**< Number of AP credentials received */
|
||||
|
||||
struct {
|
||||
uint8_t ssid[MAX_SSID_LEN]; /**< SSID of AP */
|
||||
uint8_t passphrase[MAX_PASSPHRASE_LEN]; /**< Passphrase for the AP */
|
||||
} ap_cred[MAX_WPS_AP_CRED]; /**< All AP credentials received from WPS handshake */
|
||||
} wifi_event_sta_wps_er_success_t;
|
||||
|
||||
/** Argument structure for WIFI_EVENT_AP_STACONNECTED event */
|
||||
typedef struct {
|
||||
uint8_t mac[6]; /**< MAC address of the station connected to ESP32 soft-AP */
|
||||
uint8_t aid; /**< the aid that ESP32 soft-AP gives to the station connected to */
|
||||
bool is_mesh_child; /**< flag to identify mesh child */
|
||||
} wifi_event_ap_staconnected_t;
|
||||
|
||||
/** Argument structure for WIFI_EVENT_AP_STADISCONNECTED event */
|
||||
typedef struct {
|
||||
uint8_t mac[6]; /**< MAC address of the station disconnects to ESP32 soft-AP */
|
||||
uint8_t aid; /**< the aid that ESP32 soft-AP gave to the station disconnects to */
|
||||
bool is_mesh_child; /**< flag to identify mesh child */
|
||||
} wifi_event_ap_stadisconnected_t;
|
||||
|
||||
/** Argument structure for WIFI_EVENT_AP_PROBEREQRECVED event */
|
||||
typedef struct {
|
||||
int rssi; /**< Received probe request signal strength */
|
||||
uint8_t mac[6]; /**< MAC address of the station which send probe request */
|
||||
} wifi_event_ap_probe_req_rx_t;
|
||||
|
||||
/**
|
||||
* @brief FTM operation status types
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
FTM_STATUS_SUCCESS = 0, /**< FTM exchange is successful */
|
||||
FTM_STATUS_UNSUPPORTED, /**< Peer does not support FTM */
|
||||
FTM_STATUS_CONF_REJECTED, /**< Peer rejected FTM configuration in FTM Request */
|
||||
FTM_STATUS_NO_RESPONSE, /**< Peer did not respond to FTM Requests */
|
||||
FTM_STATUS_FAIL, /**< Unknown error during FTM exchange */
|
||||
} wifi_ftm_status_t;
|
||||
|
||||
/** Argument structure for */
|
||||
typedef struct {
|
||||
uint8_t dlog_token; /**< Dialog Token of the FTM frame */
|
||||
int8_t rssi; /**< RSSI of the FTM frame received */
|
||||
uint32_t rtt; /**< Round Trip Time in pSec with a peer */
|
||||
uint64_t t1; /**< Time of departure of FTM frame from FTM Responder in pSec */
|
||||
uint64_t t2; /**< Time of arrival of FTM frame at FTM Initiator in pSec */
|
||||
uint64_t t3; /**< Time of departure of ACK from FTM Initiator in pSec */
|
||||
uint64_t t4; /**< Time of arrival of ACK at FTM Responder in pSec */
|
||||
} wifi_ftm_report_entry_t;
|
||||
|
||||
/** Argument structure for WIFI_EVENT_FTM_REPORT event */
|
||||
typedef struct {
|
||||
uint8_t peer_mac[6]; /**< MAC address of the FTM Peer */
|
||||
wifi_ftm_status_t status; /**< Status of the FTM operation */
|
||||
uint32_t rtt_raw; /**< Raw average Round-Trip-Time with peer in Nano-Seconds */
|
||||
uint32_t rtt_est; /**< Estimated Round-Trip-Time with peer in Nano-Seconds */
|
||||
uint32_t dist_est; /**< Estimated one-way distance in Centi-Meters */
|
||||
wifi_ftm_report_entry_t
|
||||
*ftm_report_data; /**< Pointer to FTM Report with multiple entries, should be freed after use */
|
||||
uint8_t ftm_report_num_entries; /**< Number of entries in the FTM Report data */
|
||||
} wifi_event_ftm_report_t;
|
||||
|
||||
#define WIFI_STATIS_BUFFER (1 << 0)
|
||||
#define WIFI_STATIS_RXTX (1 << 1)
|
||||
#define WIFI_STATIS_HW (1 << 2)
|
||||
#define WIFI_STATIS_DIAG (1 << 3)
|
||||
#define WIFI_STATIS_PS (1 << 4)
|
||||
#define WIFI_STATIS_ALL (-1)
|
||||
|
||||
/** Argument structure for WIFI_EVENT_ACTION_TX_STATUS event */
|
||||
typedef struct {
|
||||
int ifx; /**< WiFi interface to send request to */
|
||||
uint32_t context; /**< Context to identify the request */
|
||||
uint8_t da[6]; /**< Destination MAC address */
|
||||
uint8_t status; /**< Status of the operation */
|
||||
} wifi_event_action_tx_status_t;
|
||||
|
||||
/** Argument structure for WIFI_EVENT_ROC_DONE event */
|
||||
typedef struct {
|
||||
uint32_t context; /**< Context to identify the request */
|
||||
} wifi_event_roc_done_t;
|
||||
|
||||
/** Event structure for IP_EVENT_STA_GOT_IP, IP_EVENT_ETH_GOT_IP events */
|
||||
typedef struct {
|
||||
esp_ip4_addr_t ip; /**< Interface IPV4 address */
|
||||
esp_ip4_addr_t netmask; /**< Interface IPV4 netmask */
|
||||
esp_ip4_addr_t gw; /**< Interface IPV4 gateway address */
|
||||
} esp_netif_ip_info_t;
|
||||
|
||||
/** @brief IPV6 IP address information
|
||||
*/
|
||||
typedef struct {
|
||||
esp_ip6_addr_t ip; /**< Interface IPV6 address */
|
||||
} esp_netif_ip6_info_t;
|
||||
|
||||
typedef struct {
|
||||
int if_index; /*!< Interface index for which the event is received (left for legacy compilation) */
|
||||
void *esp_netif; /*!< Pointer to corresponding esp-netif object */
|
||||
esp_netif_ip_info_t ip_info; /*!< IP address, netmask, gatway IP address */
|
||||
bool ip_changed; /*!< Whether the assigned IP has changed or not */
|
||||
} ip_event_got_ip_t;
|
||||
|
||||
/** Event structure for IP_EVENT_GOT_IP6 event */
|
||||
typedef struct {
|
||||
int if_index; /*!< Interface index for which the event is received (left for legacy compilation) */
|
||||
void *esp_netif; /*!< Pointer to corresponding esp-netif object */
|
||||
esp_netif_ip6_info_t ip6_info; /*!< IPv6 address of the interface */
|
||||
int ip_index; /*!< IPv6 address index */
|
||||
} ip_event_got_ip6_t;
|
||||
|
||||
/** Event structure for IP_EVENT_AP_STAIPASSIGNED event */
|
||||
typedef struct {
|
||||
esp_ip4_addr_t ip; /*!< IP address which was assigned to the station */
|
||||
} ip_event_ap_staipassigned_t;
|
||||
117
cores/common/arduino/libraries/api/WiFi/WiFiGeneric.cpp
Normal file
117
cores/common/arduino/libraries/api/WiFi/WiFiGeneric.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
|
||||
|
||||
#include "WiFi.h"
|
||||
|
||||
bool WiFiClass::mode(WiFiMode mode) {
|
||||
// store a pointer to WiFi for WiFiEvents.cpp
|
||||
pWiFi = this;
|
||||
|
||||
WiFiMode currentMode = getMode();
|
||||
LT_DM(WIFI, "Mode changing %u -> %u", currentMode, mode);
|
||||
if (mode == currentMode)
|
||||
return true;
|
||||
|
||||
// get mode changes as 0/1
|
||||
WiFiModeAction sta = WiFiModeAction((mode & WIFI_MODE_STA) != (currentMode & WIFI_MODE_STA));
|
||||
WiFiModeAction ap = WiFiModeAction((mode & WIFI_MODE_AP) != (currentMode & WIFI_MODE_AP));
|
||||
// change 0/1 to 1/2
|
||||
sta = WiFiModeAction(sta + sta * !!(mode & WIFI_MODE_STA));
|
||||
ap = WiFiModeAction(ap + ap * !!(mode & WIFI_MODE_AP));
|
||||
// initialize data structures if wifi is enabled
|
||||
if (mode)
|
||||
dataInitialize();
|
||||
// actually change the mode
|
||||
LT_HEAP_I();
|
||||
if (!modePriv(mode, sta, ap))
|
||||
return false;
|
||||
if (getMode() != mode) {
|
||||
LT_WM(WIFI, "Mode changed to %d (requested %d)", getMode(), mode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WiFiClass::enableSTA(bool enable) {
|
||||
WiFiMode currentMode = getMode();
|
||||
if (((currentMode & WIFI_MODE_STA) != 0) != enable) {
|
||||
return mode((WiFiMode)(currentMode ^ WIFI_MODE_STA));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WiFiClass::enableAP(bool enable) {
|
||||
WiFiMode currentMode = getMode();
|
||||
if (((currentMode & WIFI_MODE_AP) != 0) != enable) {
|
||||
return mode((WiFiMode)(currentMode ^ WIFI_MODE_AP));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
__attribute__((weak)) bool WiFiClass::setSleep(bool enable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
__attribute__((weak)) bool WiFiClass::getSleep() {
|
||||
return false;
|
||||
}
|
||||
|
||||
__attribute__((weak)) bool WiFiClass::setTxPower(int power) {
|
||||
return false;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int WiFiClass::getTxPower() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WiFiClass::hostByName(const char *hostname, IPAddress &aResult) {
|
||||
aResult = hostByName(hostname);
|
||||
return true;
|
||||
}
|
||||
|
||||
IPAddress WiFiClass::calculateNetworkID(IPAddress ip, IPAddress subnet) {
|
||||
IPAddress networkID;
|
||||
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
networkID[i] = subnet[i] & ip[i];
|
||||
|
||||
return networkID;
|
||||
}
|
||||
|
||||
IPAddress WiFiClass::calculateBroadcast(IPAddress ip, IPAddress subnet) {
|
||||
IPAddress broadcastIp;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
broadcastIp[i] = ~subnet[i] | ip[i];
|
||||
|
||||
return broadcastIp;
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::calculateSubnetCIDR(IPAddress subnetMask) {
|
||||
uint8_t CIDR = 0;
|
||||
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
if (subnetMask[i] == 0x80) // 128
|
||||
CIDR += 1;
|
||||
else if (subnetMask[i] == 0xC0) // 192
|
||||
CIDR += 2;
|
||||
else if (subnetMask[i] == 0xE0) // 224
|
||||
CIDR += 3;
|
||||
else if (subnetMask[i] == 0xF0) // 242
|
||||
CIDR += 4;
|
||||
else if (subnetMask[i] == 0xF8) // 248
|
||||
CIDR += 5;
|
||||
else if (subnetMask[i] == 0xFC) // 252
|
||||
CIDR += 6;
|
||||
else if (subnetMask[i] == 0xFE) // 254
|
||||
CIDR += 7;
|
||||
else if (subnetMask[i] == 0xFF) // 255
|
||||
CIDR += 8;
|
||||
}
|
||||
|
||||
return CIDR;
|
||||
}
|
||||
|
||||
String WiFiClass::macToString(uint8_t *mac) {
|
||||
char macStr[18]; // 6*2 + 5*':' + '\0'
|
||||
sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
return macStr;
|
||||
}
|
||||
48
cores/common/arduino/libraries/api/WiFi/WiFiSTA.cpp
Normal file
48
cores/common/arduino/libraries/api/WiFi/WiFiSTA.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
|
||||
|
||||
#include "WiFi.h"
|
||||
|
||||
WiFiStatus WiFiClass::begin(char *ssid, char *passphrase, int32_t channel, const uint8_t *bssid, bool connect) {
|
||||
return begin((const char *)ssid, (const char *)passphrase, channel, bssid, connect);
|
||||
}
|
||||
|
||||
bool WiFiClass::isConnected() {
|
||||
return status() == WL_CONNECTED;
|
||||
}
|
||||
|
||||
WiFiStatus WiFiClass::waitForConnectResult(unsigned long timeout) {
|
||||
if ((getMode() & WIFI_MODE_STA) == 0) {
|
||||
return WL_DISCONNECTED;
|
||||
}
|
||||
unsigned long start = millis();
|
||||
while ((!status() || status() >= WL_DISCONNECTED) && (millis() - start) < timeout) {
|
||||
delay(100);
|
||||
}
|
||||
return status();
|
||||
}
|
||||
|
||||
String WiFiClass::macAddress(void) {
|
||||
uint8_t mac[6];
|
||||
macAddress(mac);
|
||||
return macToString(mac);
|
||||
}
|
||||
|
||||
IPAddress WiFiClass::networkID() {
|
||||
return calculateNetworkID(gatewayIP(), subnetMask());
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::subnetCIDR() {
|
||||
return calculateSubnetCIDR(subnetMask());
|
||||
}
|
||||
|
||||
String WiFiClass::BSSIDstr() {
|
||||
return macToString(BSSID());
|
||||
}
|
||||
|
||||
__attribute__((weak)) bool WiFiClass::enableIpV6() {
|
||||
return false;
|
||||
}
|
||||
|
||||
__attribute__((weak)) IPv6Address WiFiClass::localIPv6() {
|
||||
return IPv6Address();
|
||||
}
|
||||
83
cores/common/arduino/libraries/api/WiFi/WiFiScan.cpp
Normal file
83
cores/common/arduino/libraries/api/WiFi/WiFiScan.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
|
||||
|
||||
#include "WiFi.h"
|
||||
|
||||
bool WiFiClass::getNetworkInfo(
|
||||
uint8_t networkItem, String &ssid, WiFiAuthMode &encType, int32_t &rssi, uint8_t *&bssid, int32_t &channel
|
||||
) {
|
||||
ssid = SSID(networkItem);
|
||||
encType = encryptionType(networkItem);
|
||||
rssi = RSSI(networkItem);
|
||||
bssid = BSSID(networkItem);
|
||||
channel = this->channel(networkItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
int16_t WiFiClass::scanComplete() {
|
||||
if (!scan)
|
||||
return 0;
|
||||
if (scan->running)
|
||||
return WIFI_SCAN_RUNNING;
|
||||
return scan->count;
|
||||
}
|
||||
|
||||
void WiFiClass::scanInit() {
|
||||
if (scan)
|
||||
return;
|
||||
scan = (WiFiScanData *)zalloc(sizeof(WiFiScanData));
|
||||
}
|
||||
|
||||
void WiFiClass::scanDelete() {
|
||||
if (!scan)
|
||||
return;
|
||||
for (uint8_t i = 0; i < scan->count; i++) {
|
||||
free(scan->ap[i].ssid);
|
||||
}
|
||||
free(scan->ap);
|
||||
free(scan);
|
||||
scan = NULL;
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::scanAlloc(uint8_t count) {
|
||||
uint8_t last = scan->count;
|
||||
scan->count = count;
|
||||
scan->ap = (WiFiScanAP *)realloc(scan->ap, count * sizeof(WiFiScanAP));
|
||||
if (!scan->ap)
|
||||
return 255;
|
||||
memset(scan->ap + last, 0, sizeof(WiFiScanAP));
|
||||
return last;
|
||||
}
|
||||
|
||||
String WiFiClass::SSID(uint8_t networkItem) {
|
||||
if (!scan || networkItem >= scan->count)
|
||||
return "";
|
||||
return scan->ap[networkItem].ssid;
|
||||
}
|
||||
|
||||
WiFiAuthMode WiFiClass::encryptionType(uint8_t networkItem) {
|
||||
if (!scan || networkItem >= scan->count)
|
||||
return WIFI_AUTH_INVALID;
|
||||
return scan->ap[networkItem].auth;
|
||||
}
|
||||
|
||||
int32_t WiFiClass::RSSI(uint8_t networkItem) {
|
||||
if (!scan || networkItem >= scan->count)
|
||||
return 0;
|
||||
return scan->ap[networkItem].rssi;
|
||||
}
|
||||
|
||||
uint8_t *WiFiClass::BSSID(uint8_t networkItem) {
|
||||
if (!scan || networkItem >= scan->count)
|
||||
return NULL;
|
||||
return scan->ap[networkItem].bssid.addr;
|
||||
}
|
||||
|
||||
String WiFiClass::BSSIDstr(uint8_t networkItem) {
|
||||
return macToString(BSSID(networkItem));
|
||||
}
|
||||
|
||||
int32_t WiFiClass::channel(uint8_t networkItem) {
|
||||
if (!scan || networkItem >= scan->count)
|
||||
return 0;
|
||||
return scan->ap[networkItem].channel;
|
||||
}
|
||||
150
cores/common/arduino/libraries/api/WiFi/WiFiType.h
Normal file
150
cores/common/arduino/libraries/api/WiFi/WiFiType.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
ESP8266WiFiType.h - esp8266 Wifi support.
|
||||
Copyright (c) 2011-2014 Arduino. All right reserved.
|
||||
Modified by Ivan Grokhotkov, December 2014
|
||||
Reworked by Markus Sattler, December 2015
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define WIFI_SCAN_RUNNING (-1)
|
||||
#define WIFI_SCAN_FAILED (-2)
|
||||
|
||||
#define WiFiMode_t wifi_mode_t
|
||||
#define WiFiMode wifi_mode_t
|
||||
#define WiFiStatus wl_status_t
|
||||
#define WiFiAuthMode wifi_auth_mode_t
|
||||
|
||||
#define WIFI_OFF WIFI_MODE_NULL
|
||||
#define WIFI_STA WIFI_MODE_STA
|
||||
#define WIFI_AP WIFI_MODE_AP
|
||||
#define WIFI_AP_STA WIFI_MODE_APSTA
|
||||
|
||||
#define WiFiEvent_t arduino_event_id_t
|
||||
#define WiFiEventInfo_t arduino_event_info_t
|
||||
#define WiFiEventId_t uint16_t
|
||||
|
||||
typedef struct {
|
||||
uint8_t addr[6];
|
||||
} WiFiMacAddr;
|
||||
|
||||
struct esp_ip6_addr {
|
||||
uint32_t addr[4];
|
||||
uint8_t zone;
|
||||
};
|
||||
|
||||
struct esp_ip4_addr {
|
||||
uint32_t addr;
|
||||
};
|
||||
|
||||
typedef struct esp_ip4_addr esp_ip4_addr_t;
|
||||
typedef struct esp_ip6_addr esp_ip6_addr_t;
|
||||
|
||||
typedef enum {
|
||||
WIFI_MODE_NULL = 0, /**< null mode */
|
||||
WIFI_MODE_STA, /**< WiFi station mode */
|
||||
WIFI_MODE_AP, /**< WiFi soft-AP mode */
|
||||
WIFI_MODE_APSTA, /**< WiFi station + soft-AP mode */
|
||||
WIFI_MODE_MAX
|
||||
} wifi_mode_t;
|
||||
|
||||
typedef enum {
|
||||
WL_NO_SHIELD = 255, // for compatibility with WiFi Shield library
|
||||
WL_IDLE_STATUS = 0,
|
||||
WL_NO_SSID_AVAIL = 1,
|
||||
WL_SCAN_COMPLETED = 2,
|
||||
WL_CONNECTED = 3,
|
||||
WL_CONNECT_FAILED = 4,
|
||||
WL_CONNECTION_LOST = 5,
|
||||
WL_DISCONNECTED = 6,
|
||||
} wl_status_t;
|
||||
|
||||
typedef enum {
|
||||
WIFI_AUTH_OPEN = 0, /**< authenticate mode : open */
|
||||
WIFI_AUTH_WEP, /**< authenticate mode : WEP */
|
||||
WIFI_AUTH_WPA_PSK, /**< authenticate mode : WPA_PSK */
|
||||
WIFI_AUTH_WPA2_PSK, /**< authenticate mode : WPA2_PSK */
|
||||
WIFI_AUTH_WPA_WPA2_PSK, /**< authenticate mode : WPA_WPA2_PSK */
|
||||
WIFI_AUTH_WPA2_ENTERPRISE, /**< authenticate mode : WPA2_ENTERPRISE */
|
||||
WIFI_AUTH_WPA3_PSK, /**< authenticate mode : WPA3_PSK */
|
||||
WIFI_AUTH_WPA2_WPA3_PSK, /**< authenticate mode : WPA2_WPA3_PSK */
|
||||
WIFI_AUTH_WAPI_PSK, /**< authenticate mode : WAPI_PSK */
|
||||
WIFI_AUTH_WPA,
|
||||
WIFI_AUTH_WPA2,
|
||||
WIFI_AUTH_AUTO = 200,
|
||||
WIFI_AUTH_INVALID = 255,
|
||||
WIFI_AUTH_MAX
|
||||
} wifi_auth_mode_t;
|
||||
|
||||
typedef enum {
|
||||
WIFI_REASON_UNSPECIFIED = 1,
|
||||
WIFI_REASON_AUTH_EXPIRE = 2,
|
||||
WIFI_REASON_AUTH_LEAVE = 3,
|
||||
WIFI_REASON_ASSOC_EXPIRE = 4,
|
||||
WIFI_REASON_ASSOC_TOOMANY = 5,
|
||||
WIFI_REASON_NOT_AUTHED = 6,
|
||||
WIFI_REASON_NOT_ASSOCED = 7,
|
||||
WIFI_REASON_ASSOC_LEAVE = 8,
|
||||
WIFI_REASON_ASSOC_NOT_AUTHED = 9,
|
||||
WIFI_REASON_DISASSOC_PWRCAP_BAD = 10,
|
||||
WIFI_REASON_DISASSOC_SUPCHAN_BAD = 11,
|
||||
WIFI_REASON_BSS_TRANSITION_DISASSOC = 12,
|
||||
WIFI_REASON_IE_INVALID = 13,
|
||||
WIFI_REASON_MIC_FAILURE = 14,
|
||||
WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT = 15,
|
||||
WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT = 16,
|
||||
WIFI_REASON_IE_IN_4WAY_DIFFERS = 17,
|
||||
WIFI_REASON_GROUP_CIPHER_INVALID = 18,
|
||||
WIFI_REASON_PAIRWISE_CIPHER_INVALID = 19,
|
||||
WIFI_REASON_AKMP_INVALID = 20,
|
||||
WIFI_REASON_UNSUPP_RSN_IE_VERSION = 21,
|
||||
WIFI_REASON_INVALID_RSN_IE_CAP = 22,
|
||||
WIFI_REASON_802_1X_AUTH_FAILED = 23,
|
||||
WIFI_REASON_CIPHER_SUITE_REJECTED = 24,
|
||||
WIFI_REASON_INVALID_PMKID = 53,
|
||||
WIFI_REASON_BEACON_TIMEOUT = 200,
|
||||
WIFI_REASON_NO_AP_FOUND = 201,
|
||||
WIFI_REASON_AUTH_FAIL = 202,
|
||||
WIFI_REASON_ASSOC_FAIL = 203,
|
||||
WIFI_REASON_HANDSHAKE_TIMEOUT = 204,
|
||||
WIFI_REASON_CONNECTION_FAIL = 205,
|
||||
WIFI_REASON_AP_TSF_RESET = 206,
|
||||
WIFI_REASON_ROAMING = 207,
|
||||
} wifi_err_reason_t;
|
||||
|
||||
typedef struct {
|
||||
char *ssid;
|
||||
WiFiAuthMode auth;
|
||||
int32_t rssi;
|
||||
WiFiMacAddr bssid;
|
||||
int32_t channel;
|
||||
} WiFiScanAP;
|
||||
|
||||
typedef struct {
|
||||
bool running = false;
|
||||
unsigned long timeout = 0;
|
||||
uint8_t count = 0;
|
||||
WiFiScanAP *ap = NULL;
|
||||
} WiFiScanData;
|
||||
|
||||
typedef enum {
|
||||
WLMODE_NONE = 0,
|
||||
WLMODE_DISABLE = 1,
|
||||
WLMODE_ENABLE = 2,
|
||||
} WiFiModeAction;
|
||||
228
cores/common/arduino/libraries/common/FS/FS.cpp
Normal file
228
cores/common/arduino/libraries/common/FS/FS.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
FS.cpp - file system wrapper
|
||||
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "FS.h"
|
||||
|
||||
using namespace fs;
|
||||
|
||||
size_t File::write(uint8_t c) {
|
||||
if (!*this) {
|
||||
return 0;
|
||||
}
|
||||
return _p->write(&c, 1);
|
||||
}
|
||||
|
||||
time_t File::getLastWrite() {
|
||||
if (!*this) {
|
||||
return 0;
|
||||
}
|
||||
return _p->getLastWrite();
|
||||
}
|
||||
|
||||
size_t File::write(const uint8_t *buf, size_t size) {
|
||||
if (!*this) {
|
||||
return 0;
|
||||
}
|
||||
return _p->write(buf, size);
|
||||
}
|
||||
|
||||
int File::available() {
|
||||
if (!*this) {
|
||||
return false;
|
||||
}
|
||||
return _p->size() - _p->position();
|
||||
}
|
||||
|
||||
int File::read() {
|
||||
if (!*this) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t result;
|
||||
if (_p->read(&result, 1) != 1) {
|
||||
return -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t File::read(uint8_t *buf, size_t size) {
|
||||
if (!*this) {
|
||||
return -1;
|
||||
}
|
||||
return _p->read(buf, size);
|
||||
}
|
||||
|
||||
int File::peek() {
|
||||
if (!*this) {
|
||||
return -1;
|
||||
}
|
||||
size_t curPos = _p->position();
|
||||
int result = read();
|
||||
seek(curPos, SeekSet);
|
||||
return result;
|
||||
}
|
||||
|
||||
void File::flush() {
|
||||
if (!*this) {
|
||||
return;
|
||||
}
|
||||
_p->flush();
|
||||
}
|
||||
|
||||
bool File::seek(uint32_t pos, SeekMode mode) {
|
||||
if (!*this) {
|
||||
return false;
|
||||
}
|
||||
return _p->seek(pos, mode);
|
||||
}
|
||||
|
||||
size_t File::position() const {
|
||||
if (!*this) {
|
||||
return 0;
|
||||
}
|
||||
return _p->position();
|
||||
}
|
||||
|
||||
size_t File::size() const {
|
||||
if (!*this) {
|
||||
return 0;
|
||||
}
|
||||
return _p->size();
|
||||
}
|
||||
|
||||
bool File::setBufferSize(size_t size) {
|
||||
if (!*this) {
|
||||
return 0;
|
||||
}
|
||||
return _p->setBufferSize(size);
|
||||
}
|
||||
|
||||
void File::close() {
|
||||
if (_p) {
|
||||
_p->close();
|
||||
_p = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
File::operator bool() const {
|
||||
return _p != nullptr && *_p != false;
|
||||
}
|
||||
|
||||
const char *File::path() const {
|
||||
if (!*this) {
|
||||
return nullptr;
|
||||
}
|
||||
return _p->path();
|
||||
}
|
||||
|
||||
const char *File::name() const {
|
||||
if (!*this) {
|
||||
return nullptr;
|
||||
}
|
||||
return _p->name();
|
||||
}
|
||||
|
||||
// to implement
|
||||
boolean File::isDirectory(void) {
|
||||
if (!*this) {
|
||||
return false;
|
||||
}
|
||||
return _p->isDirectory();
|
||||
}
|
||||
|
||||
File File::openNextFile(const char *mode) {
|
||||
if (!*this) {
|
||||
return File();
|
||||
}
|
||||
return _p->openNextFile(mode);
|
||||
}
|
||||
|
||||
void File::rewindDirectory(void) {
|
||||
if (!*this) {
|
||||
return;
|
||||
}
|
||||
_p->rewindDirectory();
|
||||
}
|
||||
|
||||
File FS::open(const String &path, const char *mode, const bool create) {
|
||||
return open(path.c_str(), mode, create);
|
||||
}
|
||||
|
||||
File FS::open(const char *path, const char *mode, const bool create) {
|
||||
if (!_impl) {
|
||||
return File();
|
||||
}
|
||||
|
||||
return File(_impl->open(path, mode, create));
|
||||
}
|
||||
|
||||
bool FS::exists(const char *path) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->exists(path);
|
||||
}
|
||||
|
||||
bool FS::exists(const String &path) {
|
||||
return exists(path.c_str());
|
||||
}
|
||||
|
||||
bool FS::remove(const char *path) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->remove(path);
|
||||
}
|
||||
|
||||
bool FS::remove(const String &path) {
|
||||
return remove(path.c_str());
|
||||
}
|
||||
|
||||
bool FS::rename(const char *pathFrom, const char *pathTo) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->rename(pathFrom, pathTo);
|
||||
}
|
||||
|
||||
bool FS::rename(const String &pathFrom, const String &pathTo) {
|
||||
return rename(pathFrom.c_str(), pathTo.c_str());
|
||||
}
|
||||
|
||||
bool FS::mkdir(const char *path) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->mkdir(path);
|
||||
}
|
||||
|
||||
bool FS::mkdir(const String &path) {
|
||||
return mkdir(path.c_str());
|
||||
}
|
||||
|
||||
bool FS::rmdir(const char *path) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->rmdir(path);
|
||||
}
|
||||
|
||||
bool FS::rmdir(const String &path) {
|
||||
return rmdir(path.c_str());
|
||||
}
|
||||
152
cores/common/arduino/libraries/common/FS/FS.h
Normal file
152
cores/common/arduino/libraries/common/FS/FS.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
FS.h - file system wrapper
|
||||
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <memory>
|
||||
|
||||
namespace fs {
|
||||
|
||||
#define FILE_READ "r"
|
||||
#define FILE_WRITE "w"
|
||||
#define FILE_APPEND "a"
|
||||
|
||||
class File;
|
||||
|
||||
class FileImpl;
|
||||
typedef std::shared_ptr<FileImpl> FileImplPtr;
|
||||
class FSImpl;
|
||||
typedef std::shared_ptr<FSImpl> FSImplPtr;
|
||||
|
||||
enum SeekMode { SeekSet = 0, SeekCur = 1, SeekEnd = 2 };
|
||||
|
||||
class File : public Stream {
|
||||
public:
|
||||
File(FileImplPtr p = FileImplPtr()) : _p(p) {
|
||||
_timeout = 0;
|
||||
}
|
||||
|
||||
size_t write(uint8_t) override;
|
||||
size_t write(const uint8_t *buf, size_t size) override;
|
||||
int available() override;
|
||||
int read() override;
|
||||
int peek() override;
|
||||
void flush() override;
|
||||
size_t read(uint8_t *buf, size_t size);
|
||||
|
||||
size_t readBytes(char *buffer, size_t length) {
|
||||
return read((uint8_t *)buffer, length);
|
||||
}
|
||||
|
||||
bool seek(uint32_t pos, SeekMode mode);
|
||||
|
||||
bool seek(uint32_t pos) {
|
||||
return seek(pos, SeekSet);
|
||||
}
|
||||
|
||||
size_t position() const;
|
||||
size_t size() const;
|
||||
bool setBufferSize(size_t size);
|
||||
void close();
|
||||
operator bool() const;
|
||||
time_t getLastWrite();
|
||||
const char *path() const;
|
||||
const char *name() const;
|
||||
|
||||
boolean isDirectory(void);
|
||||
File openNextFile(const char *mode = FILE_READ);
|
||||
void rewindDirectory(void);
|
||||
|
||||
protected:
|
||||
FileImplPtr _p;
|
||||
};
|
||||
|
||||
class FileImpl {
|
||||
public:
|
||||
virtual ~FileImpl() {}
|
||||
|
||||
virtual size_t write(const uint8_t *buf, size_t size) = 0;
|
||||
virtual size_t read(uint8_t *buf, size_t size) = 0;
|
||||
virtual void flush() = 0;
|
||||
virtual bool seek(uint32_t pos, SeekMode mode) = 0;
|
||||
virtual size_t position() const = 0;
|
||||
virtual size_t size() const = 0;
|
||||
virtual bool setBufferSize(size_t size) = 0;
|
||||
virtual void close() = 0;
|
||||
virtual time_t getLastWrite() = 0;
|
||||
virtual const char *path() const = 0;
|
||||
virtual const char *name() const = 0;
|
||||
virtual boolean isDirectory(void) = 0;
|
||||
virtual FileImplPtr openNextFile(const char *mode) = 0;
|
||||
virtual void rewindDirectory(void) = 0;
|
||||
virtual operator bool() = 0;
|
||||
};
|
||||
|
||||
class FS {
|
||||
public:
|
||||
FS(FSImplPtr impl) : _impl(impl) {}
|
||||
|
||||
File open(const char *path, const char *mode = FILE_READ, const bool create = false);
|
||||
File open(const String &path, const char *mode = FILE_READ, const bool create = false);
|
||||
|
||||
bool exists(const char *path);
|
||||
bool exists(const String &path);
|
||||
|
||||
bool remove(const char *path);
|
||||
bool remove(const String &path);
|
||||
|
||||
bool rename(const char *pathFrom, const char *pathTo);
|
||||
bool rename(const String &pathFrom, const String &pathTo);
|
||||
|
||||
bool mkdir(const char *path);
|
||||
bool mkdir(const String &path);
|
||||
|
||||
bool rmdir(const char *path);
|
||||
bool rmdir(const String &path);
|
||||
|
||||
protected:
|
||||
FSImplPtr _impl;
|
||||
};
|
||||
|
||||
class FSImpl {
|
||||
public:
|
||||
FSImpl() {}
|
||||
|
||||
virtual ~FSImpl() {}
|
||||
|
||||
virtual FileImplPtr open(const char *path, const char *mode, const bool create) = 0;
|
||||
virtual bool exists(const char *path) = 0;
|
||||
virtual bool rename(const char *pathFrom, const char *pathTo) = 0;
|
||||
virtual bool remove(const char *path) = 0;
|
||||
virtual bool mkdir(const char *path) = 0;
|
||||
virtual bool rmdir(const char *path) = 0;
|
||||
};
|
||||
|
||||
} // namespace fs
|
||||
|
||||
#ifndef FS_NO_GLOBALS
|
||||
using fs::File;
|
||||
using fs::FS;
|
||||
using fs::SeekCur;
|
||||
using fs::SeekEnd;
|
||||
using fs::SeekMode;
|
||||
using fs::SeekSet;
|
||||
#endif // FS_NO_GLOBALS
|
||||
30
cores/common/arduino/libraries/common/Flash/Flash.cpp
Normal file
30
cores/common/arduino/libraries/common/Flash/Flash.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */
|
||||
|
||||
#include "Flash.h"
|
||||
|
||||
extern "C" {
|
||||
#include <fal.h>
|
||||
}
|
||||
|
||||
// Global Flash object.
|
||||
FlashClass Flash;
|
||||
|
||||
FlashId FlashClass::getChipId() {
|
||||
return LT.getFlashChipId();
|
||||
}
|
||||
|
||||
uint32_t FlashClass::getSize() {
|
||||
return LT.getFlashChipSize();
|
||||
}
|
||||
|
||||
bool FlashClass::eraseSector(uint32_t offset) {
|
||||
return fal_partition_erase(fal_root_part, offset, 1) >= 0;
|
||||
}
|
||||
|
||||
bool FlashClass::readBlock(uint32_t offset, uint8_t *data, size_t size) {
|
||||
return fal_partition_read(fal_root_part, offset, data, size) >= 0;
|
||||
}
|
||||
|
||||
bool FlashClass::writeBlock(uint32_t offset, uint8_t *data, size_t size) {
|
||||
return fal_partition_write(fal_root_part, offset, data, size) >= 0;
|
||||
}
|
||||
17
cores/common/arduino/libraries/common/Flash/Flash.h
Normal file
17
cores/common/arduino/libraries/common/Flash/Flash.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class FlashClass {
|
||||
public:
|
||||
FlashId getChipId();
|
||||
uint32_t getSize();
|
||||
|
||||
bool eraseSector(uint32_t offset);
|
||||
bool readBlock(uint32_t offset, uint8_t *data, size_t size);
|
||||
bool writeBlock(uint32_t offset, uint8_t *data, size_t size);
|
||||
};
|
||||
|
||||
extern FlashClass Flash;
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
IPv6Address.cpp - Base class that provides IPv6Address
|
||||
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "IPv6Address.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <api/Print.h>
|
||||
|
||||
IPv6Address::IPv6Address() {
|
||||
memset(_address.bytes, 0, sizeof(_address.bytes));
|
||||
}
|
||||
|
||||
IPv6Address::IPv6Address(const uint8_t *address) {
|
||||
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||
}
|
||||
|
||||
IPv6Address::IPv6Address(const uint32_t *address) {
|
||||
memcpy(_address.bytes, (const uint8_t *)address, sizeof(_address.bytes));
|
||||
}
|
||||
|
||||
IPv6Address &IPv6Address::operator=(const uint8_t *address) {
|
||||
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool IPv6Address::operator==(const uint8_t *addr) const {
|
||||
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
|
||||
}
|
||||
|
||||
size_t IPv6Address::printTo(Print &p) const {
|
||||
/* size_t n = 0;
|
||||
for(int i = 0; i < 16; i+=2) {
|
||||
if(i){
|
||||
n += p.print(':');
|
||||
}
|
||||
n += p.printf("%02x", _address.bytes[i]);
|
||||
n += p.printf("%02x", _address.bytes[i+1]);
|
||||
|
||||
}
|
||||
return n; */
|
||||
}
|
||||
|
||||
String IPv6Address::toString() const {
|
||||
char szRet[40];
|
||||
sprintf(
|
||||
szRet,
|
||||
"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
|
||||
_address.bytes[0],
|
||||
_address.bytes[1],
|
||||
_address.bytes[2],
|
||||
_address.bytes[3],
|
||||
_address.bytes[4],
|
||||
_address.bytes[5],
|
||||
_address.bytes[6],
|
||||
_address.bytes[7],
|
||||
_address.bytes[8],
|
||||
_address.bytes[9],
|
||||
_address.bytes[10],
|
||||
_address.bytes[11],
|
||||
_address.bytes[12],
|
||||
_address.bytes[13],
|
||||
_address.bytes[14],
|
||||
_address.bytes[15]
|
||||
);
|
||||
return String(szRet);
|
||||
}
|
||||
|
||||
bool IPv6Address::fromString(const char *address) {
|
||||
// format 0011:2233:4455:6677:8899:aabb:ccdd:eeff
|
||||
if (strlen(address) != 39) {
|
||||
return false;
|
||||
}
|
||||
char *pos = (char *)address;
|
||||
size_t i = 0;
|
||||
for (i = 0; i < 16; i += 2) {
|
||||
if (!sscanf(pos, "%2hhx", &_address.bytes[i]) || !sscanf(pos + 2, "%2hhx", &_address.bytes[i + 1])) {
|
||||
return false;
|
||||
}
|
||||
pos += 5;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
IPv6Address.h - Base class that provides IPv6Address
|
||||
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <api/Print.h>
|
||||
#include <api/String.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// A class to make it easier to handle and pass around IP addresses
|
||||
|
||||
namespace arduino {
|
||||
|
||||
class IPv6Address : public Printable {
|
||||
private:
|
||||
union {
|
||||
uint8_t bytes[16]; // IPv4 address
|
||||
uint32_t dword[4];
|
||||
} _address;
|
||||
|
||||
// Access the raw byte array containing the address. Because this returns a pointer
|
||||
// to the internal structure rather than a copy of the address this function should only
|
||||
// be used when you know that the usage of the returned uint8_t* will be transient and not
|
||||
// stored.
|
||||
uint8_t *raw_address() {
|
||||
return _address.bytes;
|
||||
}
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
IPv6Address();
|
||||
IPv6Address(const uint8_t *address);
|
||||
IPv6Address(const uint32_t *address);
|
||||
|
||||
virtual ~IPv6Address() {}
|
||||
|
||||
bool fromString(const char *address);
|
||||
|
||||
bool fromString(const String &address) {
|
||||
return fromString(address.c_str());
|
||||
}
|
||||
|
||||
operator const uint8_t *() const {
|
||||
return _address.bytes;
|
||||
}
|
||||
|
||||
operator const uint32_t *() const {
|
||||
return _address.dword;
|
||||
}
|
||||
|
||||
bool operator==(const IPv6Address &addr) const {
|
||||
return (_address.dword[0] == addr._address.dword[0]) && (_address.dword[1] == addr._address.dword[1]) &&
|
||||
(_address.dword[2] == addr._address.dword[2]) && (_address.dword[3] == addr._address.dword[3]);
|
||||
}
|
||||
|
||||
bool operator==(const uint8_t *addr) const;
|
||||
|
||||
// Overloaded index operator to allow getting and setting individual octets of the address
|
||||
uint8_t operator[](int index) const {
|
||||
return _address.bytes[index];
|
||||
}
|
||||
|
||||
uint8_t &operator[](int index) {
|
||||
return _address.bytes[index];
|
||||
}
|
||||
|
||||
// Overloaded copy operators to allow initialisation of IPv6Address objects from other types
|
||||
IPv6Address &operator=(const uint8_t *address);
|
||||
|
||||
// TODO implement printTo()
|
||||
virtual size_t printTo(Print &p) const;
|
||||
String toString() const;
|
||||
|
||||
friend class UDP;
|
||||
friend class Client;
|
||||
friend class Server;
|
||||
};
|
||||
|
||||
} // namespace arduino
|
||||
|
||||
using arduino::IPv6Address;
|
||||
@@ -0,0 +1,5 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../IPv6Address.h"
|
||||
37
cores/common/arduino/libraries/common/MD5/MD5.h
Normal file
37
cores/common/arduino/libraries/common/MD5/MD5.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-03. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <MD5Impl.h>
|
||||
|
||||
// available built-in implementations
|
||||
#if LT_MD5_USE_POLARSSL
|
||||
#include "MD5PolarSSLImpl.h"
|
||||
#endif
|
||||
#if LT_MD5_USE_MBEDTLS
|
||||
#include "MD5MbedTLSImpl.h"
|
||||
#endif
|
||||
#if LT_MD5_USE_HOSTAPD
|
||||
#include "MD5HostapdImpl.h"
|
||||
#endif
|
||||
|
||||
// common API
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef LT_MD5_CTX_T
|
||||
#define LT_MD5_CTX_T void
|
||||
#endif
|
||||
|
||||
// for compatibility with ESP8266
|
||||
typedef LT_MD5_CTX_T md5_context_t;
|
||||
|
||||
void MD5Init(LT_MD5_CTX_T *context);
|
||||
void MD5Update(LT_MD5_CTX_T *context, const unsigned char *buf, unsigned len);
|
||||
void MD5Final(unsigned char digest[16], LT_MD5_CTX_T *context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
14
cores/common/arduino/libraries/common/MD5/MD5HostapdImpl.h
Normal file
14
cores/common/arduino/libraries/common/MD5/MD5HostapdImpl.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-07-12. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <crypto/md5_i.h>
|
||||
#define LT_MD5_CTX_T struct MD5Context
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
28
cores/common/arduino/libraries/common/MD5/MD5MbedTLSImpl.cpp
Normal file
28
cores/common/arduino/libraries/common/MD5/MD5MbedTLSImpl.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-07-11. */
|
||||
|
||||
#if LT_ARD_HAS_MD5
|
||||
|
||||
#include "MD5.h"
|
||||
|
||||
#if LT_MD5_USE_MBEDTLS
|
||||
|
||||
extern "C" {
|
||||
|
||||
void MD5Init(LT_MD5_CTX_T *context) {
|
||||
mbedtls_md5_init(context);
|
||||
mbedtls_md5_starts(context);
|
||||
}
|
||||
|
||||
void MD5Update(LT_MD5_CTX_T *context, const unsigned char *buf, unsigned len) {
|
||||
mbedtls_md5_update(context, buf, len);
|
||||
}
|
||||
|
||||
void MD5Final(unsigned char digest[16], LT_MD5_CTX_T *context) {
|
||||
mbedtls_md5_finish(context, digest);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // LT_MD5_USE_MBEDTLS
|
||||
|
||||
#endif // LT_ARD_HAS_MD5
|
||||
14
cores/common/arduino/libraries/common/MD5/MD5MbedTLSImpl.h
Normal file
14
cores/common/arduino/libraries/common/MD5/MD5MbedTLSImpl.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-07-11. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <mbedtls/md5.h>
|
||||
#define LT_MD5_CTX_T mbedtls_md5_context
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-03. */
|
||||
|
||||
#if LT_ARD_HAS_MD5
|
||||
|
||||
#include "MD5.h"
|
||||
|
||||
#if LT_MD5_USE_POLARSSL
|
||||
|
||||
extern "C" {
|
||||
|
||||
void MD5Init(LT_MD5_CTX_T *context) {
|
||||
md5_init(context);
|
||||
md5_starts(context);
|
||||
}
|
||||
|
||||
void MD5Update(LT_MD5_CTX_T *context, const unsigned char *buf, unsigned len) {
|
||||
md5_update(context, buf, len);
|
||||
}
|
||||
|
||||
void MD5Final(unsigned char digest[16], LT_MD5_CTX_T *context) {
|
||||
md5_finish(context, digest);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // LT_MD5_USE_POLARSSL
|
||||
|
||||
#endif // LT_ARD_HAS_MD5
|
||||
14
cores/common/arduino/libraries/common/MD5/MD5PolarSSLImpl.h
Normal file
14
cores/common/arduino/libraries/common/MD5/MD5PolarSSLImpl.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-03. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <polarssl/md5.h>
|
||||
#define LT_MD5_CTX_T md5_context
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <api/String.h>
|
||||
|
||||
typedef enum {
|
||||
PT_I8,
|
||||
PT_U8,
|
||||
PT_I16,
|
||||
PT_U16,
|
||||
PT_I32,
|
||||
PT_U32,
|
||||
PT_I64,
|
||||
PT_U64,
|
||||
PT_STR,
|
||||
PT_BLOB,
|
||||
PT_INVALID,
|
||||
} PreferenceType;
|
||||
|
||||
class IPreferences {
|
||||
public:
|
||||
IPreferences() {}
|
||||
|
||||
~IPreferences() {}
|
||||
|
||||
bool begin(const char *name, bool readOnly = false, const char *partition_label = NULL);
|
||||
void end();
|
||||
|
||||
bool clear();
|
||||
bool remove(const char *key);
|
||||
|
||||
size_t putChar(const char *key, int8_t value);
|
||||
size_t putUChar(const char *key, uint8_t value);
|
||||
size_t putShort(const char *key, int16_t value);
|
||||
size_t putUShort(const char *key, uint16_t value);
|
||||
size_t putInt(const char *key, int32_t value);
|
||||
size_t putUInt(const char *key, uint32_t value);
|
||||
size_t putLong(const char *key, int32_t value);
|
||||
size_t putULong(const char *key, uint32_t value);
|
||||
size_t putLong64(const char *key, int64_t value);
|
||||
size_t putULong64(const char *key, uint64_t value);
|
||||
size_t putFloat(const char *key, float_t value);
|
||||
size_t putDouble(const char *key, double_t value);
|
||||
size_t putBool(const char *key, bool value);
|
||||
size_t putString(const char *key, const char *value);
|
||||
size_t putString(const char *key, String value);
|
||||
size_t putBytes(const char *key, const void *value, size_t len);
|
||||
|
||||
bool isKey(const char *key);
|
||||
PreferenceType getType(const char *key);
|
||||
int8_t getChar(const char *key, int8_t defaultValue = 0);
|
||||
uint8_t getUChar(const char *key, uint8_t defaultValue = 0);
|
||||
int16_t getShort(const char *key, int16_t defaultValue = 0);
|
||||
uint16_t getUShort(const char *key, uint16_t defaultValue = 0);
|
||||
int32_t getInt(const char *key, int32_t defaultValue = 0);
|
||||
uint32_t getUInt(const char *key, uint32_t defaultValue = 0);
|
||||
int32_t getLong(const char *key, int32_t defaultValue = 0);
|
||||
uint32_t getULong(const char *key, uint32_t defaultValue = 0);
|
||||
int64_t getLong64(const char *key, int64_t defaultValue = 0);
|
||||
uint64_t getULong64(const char *key, uint64_t defaultValue = 0);
|
||||
float_t getFloat(const char *key, float_t defaultValue = NAN);
|
||||
double_t getDouble(const char *key, double_t defaultValue = NAN);
|
||||
bool getBool(const char *key, bool defaultValue = false);
|
||||
size_t getString(const char *key, char *value, size_t maxLen);
|
||||
String getString(const char *key, String defaultValue = String());
|
||||
size_t getBytesLength(const char *key);
|
||||
size_t getBytes(const char *key, void *buf, size_t maxLen);
|
||||
size_t freeEntries();
|
||||
};
|
||||
224
cores/common/arduino/libraries/common/Update/Update.cpp
Normal file
224
cores/common/arduino/libraries/common/Update/Update.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-05-29. */
|
||||
|
||||
#include "Update.h"
|
||||
|
||||
UpdateClass::UpdateClass() : ctx(NULL), info(NULL), buf(NULL) {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the update process.
|
||||
*
|
||||
* @param size total UF2 file size
|
||||
* @param command must be U_FLASH
|
||||
* @return false if parameters are invalid or update is running, true otherwise
|
||||
*/
|
||||
bool UpdateClass::begin(size_t size, int command, int unused2, uint8_t unused3, const char *unused4) {
|
||||
if (ctx)
|
||||
return false;
|
||||
cleanup();
|
||||
|
||||
LT_DM(OTA, "begin(%u, ...) / OTA curr: %u, trgt: %u", size, LT.otaGetRunning(), LT.otaGetTarget());
|
||||
|
||||
ctx = uf2_ctx_init(LT.otaGetTarget(), FAMILY);
|
||||
info = uf2_info_init();
|
||||
|
||||
if (!size) {
|
||||
cleanup(UPDATE_ERROR_SIZE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (command != U_FLASH) {
|
||||
cleanup(UPDATE_ERROR_BAD_ARGUMENT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bytesTotal = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finalize the update process. Check for errors and update completion, then activate the new firmware image.
|
||||
*
|
||||
* @param evenIfRemaining no idea
|
||||
* @return false in case of errors or no update running, true otherwise
|
||||
*/
|
||||
bool UpdateClass::end(bool evenIfRemaining) {
|
||||
if (hasError() || !ctx)
|
||||
// false if not running
|
||||
return false;
|
||||
|
||||
if (!isFinished() && !evenIfRemaining) {
|
||||
// abort if not finished
|
||||
cleanup(UPDATE_ERROR_ABORT);
|
||||
return false;
|
||||
}
|
||||
// TODO what is evenIfRemaining for?
|
||||
if (!LT.otaSwitch()) {
|
||||
// try to activate the second OTA
|
||||
cleanup(UPDATE_ERROR_ACTIVATE);
|
||||
return false;
|
||||
}
|
||||
|
||||
cleanup();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a chunk of data to the buffer or flash memory.
|
||||
*
|
||||
* It's advised to write in 512-byte chunks (or its multiples).
|
||||
*
|
||||
* @param data
|
||||
* @param len
|
||||
* @return size_t
|
||||
*/
|
||||
size_t UpdateClass::write(uint8_t *data, size_t len) {
|
||||
size_t written = 0;
|
||||
if (hasError() || !ctx)
|
||||
// 0 if not running
|
||||
return 0;
|
||||
|
||||
LT_VM(OTA, "write(%u) / buf %u/512", len, bufSize());
|
||||
|
||||
/* while (buf == bufPos && len >= UF2_BLOCK_SIZE) {
|
||||
// buffer empty and entire block is in data
|
||||
if (!tryWriteData(data, UF2_BLOCK_SIZE)) {
|
||||
// returns 0 if data contains an invalid block
|
||||
return written;
|
||||
}
|
||||
data += UF2_BLOCK_SIZE;
|
||||
len -= UF2_BLOCK_SIZE;
|
||||
written += UF2_BLOCK_SIZE;
|
||||
} */
|
||||
|
||||
// write until buffer space is available
|
||||
uint16_t toWrite; // 1..512
|
||||
while (len && (toWrite = min(len, bufLeft()))) {
|
||||
tryWriteData(data, toWrite);
|
||||
if (hasError()) {
|
||||
// return on errors
|
||||
printErrorContext2(data, toWrite);
|
||||
return written;
|
||||
}
|
||||
data += toWrite;
|
||||
len -= toWrite;
|
||||
written += toWrite;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
size_t UpdateClass::writeStream(Stream &data) {
|
||||
size_t written = 0;
|
||||
if (hasError() || !ctx)
|
||||
// 0 if not running
|
||||
return 0;
|
||||
|
||||
uint32_t lastData = millis();
|
||||
// loop until the update is complete
|
||||
while (remaining()) {
|
||||
// check stream availability
|
||||
int available = data.available();
|
||||
if (available <= 0) {
|
||||
if (millis() - lastData > UPDATE_TIMEOUT_MS) {
|
||||
// waited for data too long; abort with error
|
||||
cleanup(UPDATE_ERROR_STREAM);
|
||||
return written;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// available > 0
|
||||
lastData = millis();
|
||||
|
||||
// read data to fit in the remaining buffer space
|
||||
bufAlloc();
|
||||
uint16_t read = data.readBytes(bufPos, bufLeft());
|
||||
bufPos += read;
|
||||
written += read;
|
||||
tryWriteData();
|
||||
if (hasError()) {
|
||||
// return on errors
|
||||
printErrorContext2(NULL, read); // buf is not valid anymore
|
||||
return written;
|
||||
}
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Try to use the buffer as a block to write. In case of UF2 errors,
|
||||
* error codes are set, the update is aborted and 0 is returned
|
||||
*
|
||||
* @param data received data to copy to buffer or NULL if already in buffer
|
||||
* @param len received data length - must be at most bufLeft()
|
||||
* @return size_t "used" data size - 0 or 512
|
||||
*/
|
||||
size_t UpdateClass::tryWriteData(uint8_t *data, size_t len) {
|
||||
uf2_block_t *block = NULL;
|
||||
|
||||
LT_VM(OTA, "Writing %u to buffer (%u/512)", len, bufSize());
|
||||
|
||||
if (len == UF2_BLOCK_SIZE) {
|
||||
// data has a complete block
|
||||
block = (uf2_block_t *)data;
|
||||
} else if (data && len) {
|
||||
// data has a part of a block, copy it to buffer
|
||||
bufAlloc();
|
||||
memcpy(bufPos, data, len);
|
||||
bufPos += len;
|
||||
}
|
||||
|
||||
if (!block && bufSize() == UF2_BLOCK_SIZE) {
|
||||
// use buffer as block (only if not found above)
|
||||
block = (uf2_block_t *)buf;
|
||||
}
|
||||
|
||||
// a complete block has been found
|
||||
if (block) {
|
||||
if (checkUf2Error(uf2_check_block(ctx, block)))
|
||||
// block is invalid
|
||||
return 0;
|
||||
|
||||
if (errUf2 == UF2_ERR_IGNORE)
|
||||
// treat ignored blocks as valid
|
||||
return UF2_BLOCK_SIZE;
|
||||
|
||||
if (!bytesWritten) {
|
||||
// parse header block to allow retrieving firmware info
|
||||
if (checkUf2Error(uf2_parse_header(ctx, block, info)))
|
||||
// header is invalid
|
||||
return 0;
|
||||
|
||||
LT_IM(OTA, "%s v%s - LT v%s @ %s", info->fw_name, info->fw_version, info->lt_version, info->board);
|
||||
|
||||
if (bytesTotal == UPDATE_SIZE_UNKNOWN) {
|
||||
// set total update size from block count info
|
||||
bytesTotal = block->block_count * UF2_BLOCK_SIZE;
|
||||
} else if (bytesTotal != block->block_count * UF2_BLOCK_SIZE) {
|
||||
// given update size does not match the block count
|
||||
LT_EM(OTA, "Image size wrong; got %u, calculated %u", bytesTotal, block->block_count * UF2_BLOCK_SIZE);
|
||||
cleanup(UPDATE_ERROR_SIZE);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
// write data blocks normally
|
||||
if (checkUf2Error(uf2_write(ctx, block)))
|
||||
// block writing failed
|
||||
return 0;
|
||||
}
|
||||
|
||||
// increment total writing progress
|
||||
bytesWritten += UF2_BLOCK_SIZE;
|
||||
// call progress callback
|
||||
if (callback)
|
||||
callback(bytesWritten, bytesTotal);
|
||||
// reset the buffer as it's used already
|
||||
if (bufSize() == UF2_BLOCK_SIZE)
|
||||
bufPos = buf;
|
||||
return UF2_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UpdateClass Update;
|
||||
154
cores/common/arduino/libraries/common/Update/Update.h
Normal file
154
cores/common/arduino/libraries/common/Update/Update.h
Normal file
@@ -0,0 +1,154 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
#include <uf2ota/uf2ota.h>
|
||||
|
||||
// No Error
|
||||
#define UPDATE_ERROR_OK (0)
|
||||
// Flash Write Failed
|
||||
#define UPDATE_ERROR_WRITE (1)
|
||||
// Flash Erase Failed
|
||||
#define UPDATE_ERROR_ERASE (2)
|
||||
// Flash Read Failed
|
||||
#define UPDATE_ERROR_READ (3)
|
||||
// Not Enough Space
|
||||
#define UPDATE_ERROR_SPACE (4)
|
||||
// Bad Size Given
|
||||
#define UPDATE_ERROR_SIZE (5)
|
||||
// Stream Read Timeout
|
||||
#define UPDATE_ERROR_STREAM (6)
|
||||
// MD5 Check Failed
|
||||
#define UPDATE_ERROR_MD5 (7)
|
||||
// Wrong Magic Byte
|
||||
#define UPDATE_ERROR_MAGIC_BYTE (8)
|
||||
// Could Not Activate The Firmware
|
||||
#define UPDATE_ERROR_ACTIVATE (9)
|
||||
// Partition Could Not be Found
|
||||
#define UPDATE_ERROR_NO_PARTITION (10)
|
||||
// Bad Argument
|
||||
#define UPDATE_ERROR_BAD_ARGUMENT (11)
|
||||
// Aborted
|
||||
#define UPDATE_ERROR_ABORT (12)
|
||||
|
||||
#define UPDATE_SIZE_UNKNOWN 0xFFFFFFFF
|
||||
|
||||
#define U_FLASH 0
|
||||
#define U_SPIFFS 100
|
||||
#define U_AUTH 200
|
||||
|
||||
#define ENCRYPTED_BLOCK_SIZE 16
|
||||
|
||||
#define UPDATE_TIMEOUT_MS 30 * 1000
|
||||
|
||||
class UpdateClass {
|
||||
public:
|
||||
typedef std::function<void(size_t, size_t)> THandlerFunction_Progress;
|
||||
|
||||
public: /* Update.cpp */
|
||||
UpdateClass();
|
||||
bool begin(
|
||||
size_t size = UPDATE_SIZE_UNKNOWN,
|
||||
int command = U_FLASH,
|
||||
int unused2 = -1,
|
||||
uint8_t unused3 = LOW,
|
||||
const char *unused4 = NULL // this is for SPIFFS
|
||||
);
|
||||
bool end(bool evenIfRemaining = false);
|
||||
size_t write(uint8_t *data, size_t len);
|
||||
size_t writeStream(Stream &data);
|
||||
bool canRollBack();
|
||||
bool rollBack();
|
||||
// bool setMD5(const char *expected_md5);
|
||||
|
||||
private: /* Update.cpp */
|
||||
size_t tryWriteData(uint8_t *data = NULL, size_t len = 0);
|
||||
|
||||
public: /* UpdateUtil.cpp */
|
||||
UpdateClass &onProgress(THandlerFunction_Progress callback);
|
||||
void abort();
|
||||
void printError(Print &out);
|
||||
const char *errorString();
|
||||
const char *getFirmwareName();
|
||||
const char *getFirmwareVersion();
|
||||
const char *getLibreTuyaVersion();
|
||||
const char *getBoardName();
|
||||
|
||||
private: /* UpdateUtil.cpp */
|
||||
void cleanup(uint8_t ardErr = UPDATE_ERROR_OK, uf2_err_t uf2Err = UF2_ERR_OK);
|
||||
bool checkUf2Error(uf2_err_t err);
|
||||
void bufAlloc();
|
||||
void printErrorContext1();
|
||||
void printErrorContext2(const uint8_t *data, size_t len);
|
||||
uint16_t bufLeft();
|
||||
uint16_t bufSize();
|
||||
|
||||
private:
|
||||
// uf2ota context
|
||||
uf2_ota_t *ctx;
|
||||
uf2_info_t *info;
|
||||
// block buffer
|
||||
uint8_t *buf;
|
||||
uint8_t *bufPos;
|
||||
// update progress - multiplies of 512 bytes
|
||||
uint32_t bytesWritten;
|
||||
uint32_t bytesTotal;
|
||||
// errors
|
||||
uf2_err_t errUf2;
|
||||
uint8_t errArd;
|
||||
// progress callback
|
||||
THandlerFunction_Progress callback;
|
||||
// String _target_md5;
|
||||
// MD5Builder _md5;
|
||||
|
||||
public:
|
||||
String md5String(void) {
|
||||
// return _md5.toString();
|
||||
}
|
||||
|
||||
void md5(uint8_t *result) {
|
||||
// return _md5.getBytes(result);
|
||||
}
|
||||
|
||||
uint8_t getError() {
|
||||
return errArd;
|
||||
}
|
||||
|
||||
uf2_err_t getUF2Error() {
|
||||
return errUf2;
|
||||
}
|
||||
|
||||
uint16_t getErrorCode() {
|
||||
return (errArd << 8) | errUf2;
|
||||
}
|
||||
|
||||
void clearError() {
|
||||
cleanup(UPDATE_ERROR_OK);
|
||||
}
|
||||
|
||||
bool hasError() {
|
||||
return errArd != UPDATE_ERROR_OK;
|
||||
}
|
||||
|
||||
bool isRunning() {
|
||||
return ctx != NULL;
|
||||
}
|
||||
|
||||
bool isFinished() {
|
||||
return bytesWritten == bytesTotal;
|
||||
}
|
||||
|
||||
size_t size() {
|
||||
return bytesTotal;
|
||||
}
|
||||
|
||||
size_t progress() {
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
size_t remaining() {
|
||||
return bytesTotal - bytesWritten;
|
||||
}
|
||||
};
|
||||
|
||||
extern UpdateClass Update;
|
||||
190
cores/common/arduino/libraries/common/Update/UpdateUtil.cpp
Normal file
190
cores/common/arduino/libraries/common/Update/UpdateUtil.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-05-30. */
|
||||
|
||||
#include "Update.h"
|
||||
|
||||
static const uint8_t errorMap[] = {
|
||||
UPDATE_ERROR_OK, /* UF2_ERR_OK - no error */
|
||||
UPDATE_ERROR_OK, /* UF2_ERR_IGNORE - block should be ignored */
|
||||
UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_MAGIC - wrong magic numbers */
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_FAMILY - family ID mismatched */
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_NOT_HEADER - block is not a header */
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_OTA_VER - unknown/invalid OTA format version */
|
||||
UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_OTA_WRONG - no data for current OTA index */
|
||||
UPDATE_ERROR_NO_PARTITION, /* UF2_ERR_PART_404 - no partition with that name */
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_ONE - only one partition tag in a block */
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_UNSET - attempted to write without target partition */
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_DATA_TOO_LONG - data too long - tags won't fit */
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_SEQ_MISMATCH - sequence number mismatched */
|
||||
UPDATE_ERROR_ERASE, /* UF2_ERR_ERASE_FAILED - erasing flash failed */
|
||||
UPDATE_ERROR_WRITE, /* UF2_ERR_WRITE_FAILED - writing to flash failed */
|
||||
UPDATE_ERROR_WRITE /* UF2_ERR_WRITE_LENGTH - wrote fewer data than requested */
|
||||
};
|
||||
|
||||
static char errorStr[14];
|
||||
|
||||
/**
|
||||
* @brief Set the callback invoked after writing data to flash.
|
||||
*/
|
||||
UpdateClass &UpdateClass::onProgress(THandlerFunction_Progress callback) {
|
||||
this->callback = callback;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void UpdateClass::cleanup(uint8_t ardErr, uf2_err_t uf2Err) {
|
||||
errUf2 = uf2Err;
|
||||
errArd = ardErr;
|
||||
|
||||
#if LT_DEBUG_OTA
|
||||
if (hasError())
|
||||
printErrorContext1();
|
||||
#endif
|
||||
|
||||
free(ctx); // NULL in constructor
|
||||
ctx = NULL;
|
||||
uf2_info_free(info); // NULL in constructor
|
||||
info = NULL;
|
||||
free(buf); // NULL in constructor
|
||||
buf = bufPos = NULL;
|
||||
|
||||
bytesWritten = 0;
|
||||
bytesTotal = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check for UF2 errors. Set errArd and errUf2 in case of errors.
|
||||
* Ignored blocks are not reported as errors.
|
||||
* Abort the update.
|
||||
* Use like: "if (errorUf2(...)) return false;"
|
||||
* @return true if err is not OK, false otherwise
|
||||
*/
|
||||
bool UpdateClass::checkUf2Error(uf2_err_t err) {
|
||||
if (err <= UF2_ERR_IGNORE)
|
||||
return false;
|
||||
cleanup(errorMap[err], err);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Abort the update with UPDATE_ERROR_ABORT reason.
|
||||
*/
|
||||
void UpdateClass::abort() {
|
||||
LT_DM(OTA, "Aborting update");
|
||||
cleanup(UPDATE_ERROR_ABORT);
|
||||
}
|
||||
|
||||
void UpdateClass::bufAlloc() {
|
||||
if (!buf)
|
||||
buf = bufPos = (uint8_t *)malloc(UF2_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
uint16_t UpdateClass::bufLeft() {
|
||||
return buf + UF2_BLOCK_SIZE - bufPos;
|
||||
}
|
||||
|
||||
uint16_t UpdateClass::bufSize() {
|
||||
return bufPos - buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print string error info to the stream.
|
||||
*/
|
||||
void UpdateClass::printError(Print &out) {
|
||||
out.println(errorString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print details about the error and current OTA state.
|
||||
*/
|
||||
void UpdateClass::printErrorContext1() {
|
||||
#if LT_DEBUG_OTA
|
||||
LT_EM(OTA, "Error: %s", errorString());
|
||||
if (errArd == UPDATE_ERROR_ABORT)
|
||||
return;
|
||||
|
||||
LT_EM(OTA, "- written: %u of %u", bytesWritten, bytesTotal);
|
||||
LT_EM(OTA, "- buf: size=%u, left=%u", bufSize(), bufLeft());
|
||||
hexdump(buf, bufSize());
|
||||
|
||||
if (ctx)
|
||||
LT_EM(
|
||||
OTA,
|
||||
"- ctx: seq=%u, part1=%s, part2=%s",
|
||||
ctx->seq - 1, // print last parsed block seq
|
||||
ctx->part1 ? ctx->part1->name : NULL,
|
||||
ctx->part2 ? ctx->part2->name : NULL
|
||||
);
|
||||
|
||||
uf2_block_t *block = (uf2_block_t *)buf;
|
||||
if (buf)
|
||||
LT_EM(OTA, "- buf: seq=%u/%u, addr=%u, len=%u", block->block_seq, block->block_count, block->addr, block->len);
|
||||
#endif
|
||||
}
|
||||
|
||||
void UpdateClass::printErrorContext2(const uint8_t *data, size_t len) {
|
||||
#if LT_DEBUG_OTA
|
||||
LT_EM(OTA, "- while writing %u bytes", len);
|
||||
if (data)
|
||||
hexdump(data, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get string representation of the error in format
|
||||
* "ard=..,uf2=..". Returns "" if no error.
|
||||
*/
|
||||
const char *UpdateClass::errorString() {
|
||||
if (!errArd && !errUf2)
|
||||
return "";
|
||||
sprintf(errorStr, "ard=%u,uf2=%u", errArd, errUf2);
|
||||
return errorStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get firmware name from UF2 info.
|
||||
*/
|
||||
const char *UpdateClass::getFirmwareName() {
|
||||
if (info)
|
||||
return info->fw_name;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get firmware version from UF2 info.
|
||||
*/
|
||||
const char *UpdateClass::getFirmwareVersion() {
|
||||
if (info)
|
||||
return info->fw_version;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get LibreTuya version from UF2 info.
|
||||
*/
|
||||
const char *UpdateClass::getLibreTuyaVersion() {
|
||||
if (info)
|
||||
return info->lt_version;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get target board name from UF2 info.
|
||||
*/
|
||||
const char *UpdateClass::getBoardName() {
|
||||
if (info)
|
||||
return info->board;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief See LT.otaCanRollback() for more info.
|
||||
*/
|
||||
bool UpdateClass::canRollBack() {
|
||||
return LT.otaCanRollback();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief See LT.otaRollback() for more info.
|
||||
*/
|
||||
bool UpdateClass::rollBack() {
|
||||
return LT.otaRollback();
|
||||
}
|
||||
391
cores/common/arduino/libraries/common/WiFiClient/LwIPClient.cpp
Normal file
391
cores/common/arduino/libraries/common/WiFiClient/LwIPClient.cpp
Normal file
@@ -0,0 +1,391 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
|
||||
|
||||
#if LT_ARD_HAS_WIFI && LT_HAS_LWIP
|
||||
|
||||
#include "LwIPClient.h"
|
||||
|
||||
#define MAX_SOCK_NUM 4
|
||||
#define WIFI_CLIENT_CONNECT_TIMEOUT 3000
|
||||
#define WIFI_CLIENT_READ_TIMEOUT 3000
|
||||
#define WIFI_CLIENT_WRITE_RETRY 10
|
||||
#define WIFI_CLIENT_SELECT_TIMEOUT 1000
|
||||
#define WIFI_CLIENT_FLUSH_BUF_SIZE 1024
|
||||
|
||||
// disable #defines removing lwip_ prefix
|
||||
#undef LWIP_COMPAT_SOCKETS
|
||||
#define LWIP_COMPAT_SOCKETS 0
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <lwip/api.h>
|
||||
#include <lwip/dns.h>
|
||||
#include <lwip/err.h>
|
||||
#include <lwip/sockets.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
} // extern "C"
|
||||
|
||||
class SocketHandle {
|
||||
public:
|
||||
int fd;
|
||||
|
||||
SocketHandle(int fd) : fd(fd) {}
|
||||
|
||||
~SocketHandle() {
|
||||
lwip_close(fd);
|
||||
}
|
||||
};
|
||||
|
||||
LwIPClient::LwIPClient() {
|
||||
LT_VM(CLIENT, "LwIPClient()");
|
||||
_connected = false;
|
||||
_sock = NULL;
|
||||
_rxBuffer = NULL;
|
||||
_timeout = WIFI_CLIENT_CONNECT_TIMEOUT;
|
||||
}
|
||||
|
||||
LwIPClient::LwIPClient(int sock) {
|
||||
LT_VM(CLIENT, "LwIPClient(%d)", sock);
|
||||
_connected = true;
|
||||
_sock = std::make_shared<SocketHandle>(sock);
|
||||
_rxBuffer = std::make_shared<LwIPRxBuffer>(sock);
|
||||
_timeout = WIFI_CLIENT_CONNECT_TIMEOUT;
|
||||
}
|
||||
|
||||
LwIPClient::~LwIPClient() {
|
||||
LT_VM(CLIENT, "~LwIPClient()");
|
||||
stop();
|
||||
}
|
||||
|
||||
LwIPClient &LwIPClient::operator=(const LwIPClient &other) {
|
||||
stop();
|
||||
_connected = other._connected;
|
||||
_sock = other._sock;
|
||||
_rxBuffer = other._rxBuffer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool IWiFiClient::operator==(const IWiFiClient &other) const {
|
||||
return fd() == other.fd() && remoteIP() == other.remoteIP() && remotePort() == other.remotePort();
|
||||
}
|
||||
|
||||
int LwIPClient::connect(IPAddress ip, uint16_t port) {
|
||||
return connect(ip, port, _timeout);
|
||||
}
|
||||
|
||||
int LwIPClient::connect(const char *host, uint16_t port) {
|
||||
return connect(host, port, _timeout);
|
||||
}
|
||||
|
||||
int LwIPClient::connect(const char *host, uint16_t port, int32_t timeout) {
|
||||
IPAddress ip = WiFi.hostByName(host);
|
||||
if (!ip)
|
||||
return 0;
|
||||
return connect(ip, port, timeout);
|
||||
}
|
||||
|
||||
int LwIPClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
|
||||
if (_connected)
|
||||
stop();
|
||||
int sock = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (sock < 0) {
|
||||
LT_DM(CLIENT, "socket failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (timeout <= 0)
|
||||
timeout = _timeout; // use default when -1 passed as timeout
|
||||
|
||||
lwip_fcntl(sock, F_SETFL, lwip_fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
|
||||
|
||||
LT_ERRNO();
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = ip;
|
||||
addr.sin_port = htons(port);
|
||||
fd_set fdset;
|
||||
struct timeval tv;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(sock, &fdset);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = timeout * 1000; // millis -> micros
|
||||
|
||||
int res = lwip_connect(sock, (struct sockaddr *)&addr, sizeof(addr));
|
||||
if (res < 0 && errno != EINPROGRESS) {
|
||||
LT_EM(CLIENT, "Connect failed; errno=%d", errno);
|
||||
lwip_close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = lwip_select(sock + 1, NULL, &fdset, NULL, timeout < 0 ? NULL : &tv);
|
||||
if (res < 0) {
|
||||
LT_EM(CLIENT, "Select failed; errno=%d", errno);
|
||||
lwip_close(sock);
|
||||
return 0;
|
||||
}
|
||||
if (res == 0) {
|
||||
LT_EM(CLIENT, "Select timeout; errno=%d", errno);
|
||||
lwip_close(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sockerr;
|
||||
socklen_t len = (socklen_t)sizeof(sockerr);
|
||||
res = lwip_getsockopt(sock, SOL_SOCKET, SO_ERROR, &sockerr, &len);
|
||||
|
||||
if (res < 0 || sockerr != 0) {
|
||||
LT_EM(CLIENT, "Socket error; res=%d, sockerr=%d", res, sockerr);
|
||||
lwip_close(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enable = 1;
|
||||
lwip_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
lwip_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
|
||||
lwip_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable));
|
||||
lwip_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
|
||||
|
||||
LT_ERRNO();
|
||||
|
||||
lwip_fcntl(sock, F_SETFL, lwip_fcntl(sock, F_GETFL, 0) & ~O_NONBLOCK);
|
||||
|
||||
LT_ERRNO();
|
||||
|
||||
_connected = true;
|
||||
_sock = std::make_shared<SocketHandle>(sock);
|
||||
_rxBuffer = std::make_shared<LwIPRxBuffer>(sock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t LwIPClient::write(uint8_t data) {
|
||||
return write(&data, 1);
|
||||
}
|
||||
|
||||
size_t LwIPClient::write(Stream &stream) {
|
||||
uint8_t *buf = (uint8_t *)malloc(1360);
|
||||
if (!buf) {
|
||||
return 0;
|
||||
}
|
||||
size_t toRead = 0, toWrite = 0, written = 0;
|
||||
size_t available = stream.available();
|
||||
while (available) {
|
||||
toRead = (available > 1360) ? 1360 : available;
|
||||
toWrite = stream.readBytes(buf, toRead);
|
||||
written += write(buf, toWrite);
|
||||
available = stream.available();
|
||||
}
|
||||
free(buf);
|
||||
return written;
|
||||
}
|
||||
|
||||
size_t LwIPClient::write(const uint8_t *buf, size_t size) {
|
||||
if (_sock < 0 || !_connected || !size) {
|
||||
setWriteError();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int retry = WIFI_CLIENT_WRITE_RETRY;
|
||||
int written = 0;
|
||||
while (retry) {
|
||||
fd_set fdset;
|
||||
struct timeval tv;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(fd(), &fdset);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = WIFI_CLIENT_SELECT_TIMEOUT * 1000;
|
||||
retry--;
|
||||
|
||||
if (lwip_select(fd() + 1, NULL, &fdset, NULL, &tv) < 0) {
|
||||
LT_WM(CLIENT, "Select failed; errno=%d", errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (FD_ISSET(fd(), &fdset)) {
|
||||
int res = lwip_send(fd(), buf, size, MSG_DONTWAIT);
|
||||
if (res > 0) {
|
||||
written += res;
|
||||
if (res >= size) {
|
||||
retry = 0;
|
||||
} else {
|
||||
buf += res;
|
||||
size -= res;
|
||||
retry = WIFI_CLIENT_WRITE_RETRY;
|
||||
}
|
||||
} else if (res < 0 && errno != EAGAIN) {
|
||||
LT_WM(CLIENT, "Send failed; errno=%d", errno);
|
||||
setWriteError(res);
|
||||
_connected = false;
|
||||
retry = 0;
|
||||
} else {
|
||||
// Try again
|
||||
}
|
||||
}
|
||||
}
|
||||
LT_DM(CLIENT, "wrote %d bytes", written);
|
||||
return written;
|
||||
}
|
||||
|
||||
int LwIPClient::available() {
|
||||
if (!_connected || !_rxBuffer)
|
||||
return 0;
|
||||
int res = _rxBuffer->available();
|
||||
if (_rxBuffer->failed()) {
|
||||
LT_ERRNO();
|
||||
stop();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int LwIPClient::fd() const {
|
||||
if (!_sock)
|
||||
return -1;
|
||||
return _sock->fd;
|
||||
}
|
||||
|
||||
int LwIPClient::socket() {
|
||||
return fd();
|
||||
}
|
||||
|
||||
int LwIPClient::setTimeout(uint32_t seconds) {
|
||||
Client::setTimeout(seconds * 1000);
|
||||
lwip_setsockopt(fd(), SOL_SOCKET, SO_RCVTIMEO, &_timeout, sizeof(_timeout));
|
||||
return lwip_setsockopt(fd(), SOL_SOCKET, SO_SNDTIMEO, &_timeout, sizeof(_timeout));
|
||||
}
|
||||
|
||||
int LwIPClient::read() {
|
||||
uint8_t data;
|
||||
int res = read(&data, 1);
|
||||
if (res < 0)
|
||||
return res;
|
||||
if (res == 0)
|
||||
return -1;
|
||||
return data;
|
||||
}
|
||||
|
||||
int LwIPClient::read(uint8_t *buf, size_t size) {
|
||||
int res = -1;
|
||||
if (_rxBuffer) {
|
||||
res = _rxBuffer->read(buf, size);
|
||||
if (_rxBuffer->failed()) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int LwIPClient::peek() {
|
||||
int res = -1;
|
||||
if (_rxBuffer) {
|
||||
res = _rxBuffer->peek();
|
||||
if (_rxBuffer->failed()) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void LwIPClient::flush() {
|
||||
int res;
|
||||
size_t len = available();
|
||||
if (!len)
|
||||
return;
|
||||
uint8_t *buf = (uint8_t *)malloc(WIFI_CLIENT_FLUSH_BUF_SIZE);
|
||||
if (!buf)
|
||||
return;
|
||||
while (len) {
|
||||
res = lwip_recv(fd(), buf, LWIP_MIN(len, WIFI_CLIENT_FLUSH_BUF_SIZE), MSG_DONTWAIT);
|
||||
if (res < 0) {
|
||||
stop();
|
||||
break;
|
||||
}
|
||||
len -= res;
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void LwIPClient::stop() {
|
||||
LT_VM(CLIENT, "Stopping TCP");
|
||||
_connected = false;
|
||||
_sock = NULL;
|
||||
_rxBuffer = NULL;
|
||||
}
|
||||
|
||||
uint8_t LwIPClient::connected() {
|
||||
if (_connected) {
|
||||
uint8_t dummy;
|
||||
if (lwip_recv(fd(), &dummy, 0, MSG_DONTWAIT) <= 0) {
|
||||
switch (errno) {
|
||||
case EWOULDBLOCK:
|
||||
case ENOENT: // caused by vfs
|
||||
case 0:
|
||||
_connected = true;
|
||||
break;
|
||||
case ENOTCONN:
|
||||
case EPIPE:
|
||||
case ECONNRESET:
|
||||
case ECONNREFUSED:
|
||||
case ECONNABORTED:
|
||||
LT_IM(CLIENT, "Connection closed; errno=%d", errno);
|
||||
_connected = false;
|
||||
break;
|
||||
default:
|
||||
LT_WM(CLIENT, "Connection status unknown; errno=%d", errno);
|
||||
_connected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _connected;
|
||||
}
|
||||
|
||||
IPAddress __attribute__((noinline)) getaddr(int sock, int (*func)(int, struct sockaddr *, socklen_t *)) {
|
||||
struct sockaddr addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
func(sock, &addr, &len);
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
return IPAddress((uint32_t)(s->sin_addr.s_addr));
|
||||
}
|
||||
|
||||
uint16_t __attribute__((noinline)) getport(int sock, int (*func)(int, struct sockaddr *, socklen_t *)) {
|
||||
struct sockaddr addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
func(sock, &addr, &len);
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
return ntohs(s->sin_port);
|
||||
}
|
||||
|
||||
IPAddress LwIPClient::remoteIP() const {
|
||||
return getaddr(fd(), lwip_getpeername);
|
||||
}
|
||||
|
||||
IPAddress LwIPClient::remoteIP(int fd) const {
|
||||
return getaddr(fd, lwip_getpeername);
|
||||
}
|
||||
|
||||
uint16_t LwIPClient::remotePort() const {
|
||||
return getport(fd(), lwip_getpeername);
|
||||
}
|
||||
|
||||
uint16_t LwIPClient::remotePort(int fd) const {
|
||||
return getport(fd, lwip_getpeername);
|
||||
}
|
||||
|
||||
IPAddress LwIPClient::localIP() const {
|
||||
return getaddr(fd(), lwip_getsockname);
|
||||
}
|
||||
|
||||
IPAddress LwIPClient::localIP(int fd) const {
|
||||
return getaddr(fd, lwip_getsockname);
|
||||
}
|
||||
|
||||
uint16_t LwIPClient::localPort() const {
|
||||
return getport(fd(), lwip_getsockname);
|
||||
}
|
||||
|
||||
uint16_t LwIPClient::localPort(int fd) const {
|
||||
return getport(fd, lwip_getsockname);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <api/WiFi/WiFi.h>
|
||||
#include <api/WiFiClient.h>
|
||||
#include <lwip/LwIPRxBuffer.h>
|
||||
#include <memory>
|
||||
|
||||
class SocketHandle;
|
||||
|
||||
class LwIPClient : public IWiFiClient {
|
||||
private:
|
||||
bool _connected;
|
||||
std::shared_ptr<SocketHandle> _sock;
|
||||
std::shared_ptr<LwIPRxBuffer> _rxBuffer;
|
||||
|
||||
public:
|
||||
LwIPClient();
|
||||
LwIPClient(int sock);
|
||||
~LwIPClient();
|
||||
|
||||
int connect(IPAddress ip, uint16_t port);
|
||||
int connect(const char *host, uint16_t port);
|
||||
int connect(IPAddress ip, uint16_t port, int32_t timeout);
|
||||
int connect(const char *host, uint16_t port, int32_t timeout);
|
||||
|
||||
size_t write(uint8_t data);
|
||||
size_t write(const uint8_t *buf, size_t size);
|
||||
size_t write(Stream &stream);
|
||||
|
||||
int available();
|
||||
int fd() const;
|
||||
int socket();
|
||||
int setTimeout(uint32_t seconds);
|
||||
|
||||
int read();
|
||||
int read(uint8_t *buf, size_t size);
|
||||
int peek();
|
||||
void flush();
|
||||
void stop();
|
||||
uint8_t connected();
|
||||
|
||||
LwIPClient &operator=(const LwIPClient &other);
|
||||
|
||||
IPAddress remoteIP() const;
|
||||
IPAddress remoteIP(int sock) const;
|
||||
uint16_t remotePort() const;
|
||||
uint16_t remotePort(int sock) const;
|
||||
IPAddress localIP() const;
|
||||
IPAddress localIP(int sock) const;
|
||||
uint16_t localPort() const;
|
||||
uint16_t localPort(int sock) const;
|
||||
|
||||
using Print::write;
|
||||
};
|
||||
@@ -0,0 +1,117 @@
|
||||
#if LT_HAS_LWIP
|
||||
|
||||
#include "LwIPRxBuffer.h"
|
||||
|
||||
// disable #defines removing lwip_ prefix
|
||||
#undef LWIP_COMPAT_SOCKETS
|
||||
#define LWIP_COMPAT_SOCKETS 0
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <lwip/sockets.h>
|
||||
|
||||
} // extern "C"
|
||||
|
||||
size_t LwIPRxBuffer::r_available() {
|
||||
if (_sock < 0) {
|
||||
LT_DM(CLIENT, "_sock < 0");
|
||||
return 0;
|
||||
}
|
||||
int count = 0; // must be of same size as in lwip_ioctl()
|
||||
int res = lwip_ioctl(_sock, FIONREAD, &count);
|
||||
if (res < 0) {
|
||||
LT_DM(CLIENT, "lwip_ioctl()=%d, errno=%d", res, errno);
|
||||
_failed = true;
|
||||
return 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t LwIPRxBuffer::fillBuffer() {
|
||||
if (!_buffer) {
|
||||
_buffer = (uint8_t *)malloc(_size);
|
||||
if (!_buffer) {
|
||||
LT_E("buffer alloc failed");
|
||||
_failed = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (_fill && _pos == _fill) {
|
||||
_fill = 0;
|
||||
_pos = 0;
|
||||
}
|
||||
if (!_buffer || _size <= _fill || !r_available()) {
|
||||
return 0;
|
||||
}
|
||||
int res = lwip_recv(_sock, _buffer + _fill, _size - _fill, MSG_DONTWAIT);
|
||||
if (res < 0) {
|
||||
if (errno != EWOULDBLOCK) {
|
||||
LT_ERRNO();
|
||||
_failed = true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
_fill += res;
|
||||
return res;
|
||||
}
|
||||
|
||||
LwIPRxBuffer::LwIPRxBuffer(int sock, size_t size)
|
||||
: _size(size), _buffer(NULL), _pos(0), _fill(0), _sock(sock), _failed(false) {
|
||||
//_buffer = (uint8_t *)malloc(_size);
|
||||
}
|
||||
|
||||
LwIPRxBuffer::~LwIPRxBuffer() {
|
||||
free(_buffer);
|
||||
}
|
||||
|
||||
bool LwIPRxBuffer::failed() {
|
||||
return _failed;
|
||||
}
|
||||
|
||||
int LwIPRxBuffer::read(uint8_t *dst, size_t len) {
|
||||
if (!dst || !len || (_pos == _fill && !fillBuffer())) {
|
||||
return _failed ? -1 : 0;
|
||||
}
|
||||
size_t a = _fill - _pos;
|
||||
if (len <= a || ((len - a) <= (_size - _fill) && fillBuffer() >= (len - a))) {
|
||||
if (len == 1) {
|
||||
*dst = _buffer[_pos];
|
||||
} else {
|
||||
memcpy(dst, _buffer + _pos, len);
|
||||
}
|
||||
_pos += len;
|
||||
return len;
|
||||
}
|
||||
size_t left = len;
|
||||
size_t toRead = a;
|
||||
uint8_t *buf = dst;
|
||||
memcpy(buf, _buffer + _pos, toRead);
|
||||
_pos += toRead;
|
||||
left -= toRead;
|
||||
buf += toRead;
|
||||
while (left) {
|
||||
if (!fillBuffer()) {
|
||||
return len - left;
|
||||
}
|
||||
a = _fill - _pos;
|
||||
toRead = (a > left) ? left : a;
|
||||
memcpy(buf, _buffer + _pos, toRead);
|
||||
_pos += toRead;
|
||||
left -= toRead;
|
||||
buf += toRead;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int LwIPRxBuffer::peek() {
|
||||
if (_pos == _fill && !fillBuffer()) {
|
||||
return -1;
|
||||
}
|
||||
return _buffer[_pos];
|
||||
}
|
||||
|
||||
size_t LwIPRxBuffer::available() {
|
||||
return _fill - _pos + r_available();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
class LwIPRxBuffer {
|
||||
private:
|
||||
size_t _size;
|
||||
uint8_t *_buffer;
|
||||
size_t _pos;
|
||||
size_t _fill;
|
||||
int _sock;
|
||||
bool _failed;
|
||||
size_t r_available();
|
||||
size_t fillBuffer();
|
||||
|
||||
public:
|
||||
LwIPRxBuffer(int sock, size_t size = 1436);
|
||||
~LwIPRxBuffer();
|
||||
bool failed();
|
||||
int read(uint8_t *dst, size_t len);
|
||||
int peek();
|
||||
size_t available();
|
||||
};
|
||||
@@ -0,0 +1,453 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
|
||||
|
||||
#if LT_ARD_HAS_WIFI && LT_HAS_MBEDTLS
|
||||
|
||||
#include "MbedTLSClient.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <mbedtls/debug.h>
|
||||
#include <mbedtls/platform.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <mbedtls/ssl.h>
|
||||
|
||||
} // extern "C"
|
||||
|
||||
MbedTLSClient::MbedTLSClient() : WiFiClient() {
|
||||
init(); // ensure the context is zero filled
|
||||
}
|
||||
|
||||
MbedTLSClient::MbedTLSClient(int sock) : WiFiClient(sock) {
|
||||
init(); // ensure the context is zero filled
|
||||
}
|
||||
|
||||
MbedTLSClient::~MbedTLSClient() {
|
||||
LT_VM(CLIENT, "~MbedTLSClient()");
|
||||
stop();
|
||||
}
|
||||
|
||||
void MbedTLSClient::stop() {
|
||||
LT_VM(SSL, "Stopping SSL");
|
||||
|
||||
if (_sslCfg.ca_chain) {
|
||||
mbedtls_x509_crt_free(&_caCert);
|
||||
}
|
||||
if (_sslCfg.key_cert) {
|
||||
mbedtls_x509_crt_free(&_clientCert);
|
||||
mbedtls_pk_free(&_clientKey);
|
||||
}
|
||||
mbedtls_ssl_free(&_sslCtx);
|
||||
mbedtls_ssl_config_free(&_sslCfg);
|
||||
LT_HEAP_I();
|
||||
}
|
||||
|
||||
void MbedTLSClient::init() {
|
||||
// Realtek AmbZ: init platform here to ensure HW crypto is initialized in ssl_init
|
||||
mbedtls_platform_set_calloc_free(calloc, free);
|
||||
mbedtls_ssl_init(&_sslCtx);
|
||||
mbedtls_ssl_config_init(&_sslCfg);
|
||||
}
|
||||
|
||||
int MbedTLSClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
|
||||
return connect(ipToString(ip).c_str(), port, timeout);
|
||||
}
|
||||
|
||||
int MbedTLSClient::connect(const char *host, uint16_t port, int32_t timeout) {
|
||||
if (_pskIdentStr && _pskStr)
|
||||
return connect(host, port, timeout, NULL, NULL, NULL, _pskIdentStr, _pskStr) == 0;
|
||||
return connect(host, port, timeout, _caCertStr, _clientCertStr, _clientKeyStr, NULL, NULL) == 0;
|
||||
}
|
||||
|
||||
int MbedTLSClient::connect(
|
||||
IPAddress ip, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey
|
||||
) {
|
||||
return connect(ipToString(ip).c_str(), port, 0, rootCABuf, clientCert, clientKey, NULL, NULL) == 0;
|
||||
}
|
||||
|
||||
int MbedTLSClient::connect(
|
||||
const char *host, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey
|
||||
) {
|
||||
return connect(host, port, 0, rootCABuf, clientCert, clientKey, NULL, NULL) == 0;
|
||||
}
|
||||
|
||||
int MbedTLSClient::connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psk) {
|
||||
return connect(ipToString(ip).c_str(), port, 0, NULL, NULL, NULL, pskIdent, psk) == 0;
|
||||
}
|
||||
|
||||
int MbedTLSClient::connect(const char *host, uint16_t port, const char *pskIdent, const char *psk) {
|
||||
return connect(host, port, 0, NULL, NULL, NULL, pskIdent, psk) == 0;
|
||||
}
|
||||
|
||||
static int ssl_random(void *data, unsigned char *output, size_t len) {
|
||||
lt_rand_bytes((uint8_t *)output, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void debug_cb(void *ctx, int level, const char *file, int line, const char *str) {
|
||||
// do not print the trailing \n
|
||||
uint16_t len = strlen(str);
|
||||
char *msg = (char *)str;
|
||||
msg[len - 1] = '\0';
|
||||
LT_IM(SSL, "%04d: |%d| %s", line, level, msg);
|
||||
}
|
||||
|
||||
int MbedTLSClient::connect(
|
||||
const char *host,
|
||||
uint16_t port,
|
||||
int32_t timeout,
|
||||
const char *rootCABuf,
|
||||
const char *clientCert,
|
||||
const char *clientKey,
|
||||
const char *pskIdent,
|
||||
const char *psk
|
||||
) {
|
||||
LT_HEAP_I();
|
||||
|
||||
if (!rootCABuf && !pskIdent && !psk && !_insecure && !_useRootCA)
|
||||
return -1;
|
||||
|
||||
if (timeout <= 0)
|
||||
timeout = _timeout; // use default when -1 passed as timeout
|
||||
|
||||
IPAddress addr = WiFi.hostByName(host);
|
||||
if (!(uint32_t)addr)
|
||||
return -1;
|
||||
|
||||
int ret = WiFiClient::connect(addr, port, timeout);
|
||||
if (ret < 0) {
|
||||
LT_EM(SSL, "SSL socket failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *uid = "lt-ssl"; // TODO
|
||||
|
||||
LT_VM(SSL, "Init SSL");
|
||||
init();
|
||||
LT_HEAP_I();
|
||||
|
||||
// mbedtls_debug_set_threshold(4);
|
||||
// mbedtls_ssl_conf_dbg(&_sslCfg, debug_cb, NULL);
|
||||
|
||||
ret = mbedtls_ssl_config_defaults(
|
||||
&_sslCfg,
|
||||
MBEDTLS_SSL_IS_CLIENT,
|
||||
MBEDTLS_SSL_TRANSPORT_STREAM,
|
||||
MBEDTLS_SSL_PRESET_DEFAULT
|
||||
);
|
||||
LT_RET_NZ(ret);
|
||||
|
||||
#ifdef MBEDTLS_SSL_ALPN
|
||||
if (_alpnProtocols) {
|
||||
ret = mbedtls_ssl_conf_alpn_protocols(&_sslCfg, _alpnProtocols);
|
||||
LT_RET_NZ(ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_insecure) {
|
||||
mbedtls_ssl_conf_authmode(&_sslCfg, MBEDTLS_SSL_VERIFY_NONE);
|
||||
} else if (rootCABuf) {
|
||||
mbedtls_x509_crt_init(&_caCert);
|
||||
mbedtls_ssl_conf_authmode(&_sslCfg, MBEDTLS_SSL_VERIFY_REQUIRED);
|
||||
ret = mbedtls_x509_crt_parse(&_caCert, (const unsigned char *)rootCABuf, strlen(rootCABuf) + 1);
|
||||
mbedtls_ssl_conf_ca_chain(&_sslCfg, &_caCert, NULL);
|
||||
if (ret < 0) {
|
||||
mbedtls_x509_crt_free(&_caCert);
|
||||
LT_RET(ret);
|
||||
}
|
||||
} else if (_useRootCA) {
|
||||
return -1; // not implemented
|
||||
} else if (pskIdent && psk) {
|
||||
#ifdef MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED
|
||||
uint16_t len = strlen(psk);
|
||||
if ((len & 1) != 0 || len > 2 * MBEDTLS_PSK_MAX_LEN) {
|
||||
LT_EM(SSL, "PSK length invalid");
|
||||
return -1;
|
||||
}
|
||||
unsigned char pskBin[MBEDTLS_PSK_MAX_LEN] = {};
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
uint8_t c = psk[i];
|
||||
c |= 0b00100000; // make lowercase
|
||||
c -= '0' * (c >= '0' && c <= '9');
|
||||
c -= ('a' - 10) * (c >= 'a' && c <= 'z');
|
||||
if (c > 0xf)
|
||||
return -1;
|
||||
pskBin[i / 2] |= c << (4 * ((i & 1) ^ 1));
|
||||
}
|
||||
ret = mbedtls_ssl_conf_psk(&_sslCfg, pskBin, len / 2, (const unsigned char *)pskIdent, strlen(pskIdent));
|
||||
LT_RET_NZ(ret);
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!_insecure && clientCert && clientKey) {
|
||||
mbedtls_x509_crt_init(&_clientCert);
|
||||
mbedtls_pk_init(&_clientKey);
|
||||
LT_VM(SSL, "Loading client cert");
|
||||
ret = mbedtls_x509_crt_parse(&_clientCert, (const unsigned char *)clientCert, strlen(clientCert) + 1);
|
||||
if (ret < 0) {
|
||||
mbedtls_x509_crt_free(&_clientCert);
|
||||
LT_RET(ret);
|
||||
}
|
||||
LT_VM(SSL, "Loading private key");
|
||||
ret = mbedtls_pk_parse_key(&_clientKey, (const unsigned char *)clientKey, strlen(clientKey) + 1, NULL, 0);
|
||||
if (ret < 0) {
|
||||
mbedtls_x509_crt_free(&_clientCert);
|
||||
LT_RET(ret);
|
||||
}
|
||||
mbedtls_ssl_conf_own_cert(&_sslCfg, &_clientCert, &_clientKey);
|
||||
}
|
||||
|
||||
LT_VM(SSL, "Setting TLS hostname");
|
||||
ret = mbedtls_ssl_set_hostname(&_sslCtx, host);
|
||||
LT_RET_NZ(ret);
|
||||
|
||||
mbedtls_ssl_conf_rng(&_sslCfg, ssl_random, NULL);
|
||||
ret = mbedtls_ssl_setup(&_sslCtx, &_sslCfg);
|
||||
LT_RET_NZ(ret);
|
||||
|
||||
_sockTls = fd();
|
||||
mbedtls_ssl_set_bio(&_sslCtx, &_sockTls, mbedtls_net_send, mbedtls_net_recv, NULL);
|
||||
mbedtls_net_set_nonblock((mbedtls_net_context *)&_sockTls);
|
||||
|
||||
LT_HEAP_I();
|
||||
|
||||
LT_VM(SSL, "SSL handshake");
|
||||
if (_handshakeTimeout == 0)
|
||||
_handshakeTimeout = timeout;
|
||||
unsigned long start = millis();
|
||||
while (ret = mbedtls_ssl_handshake(&_sslCtx)) {
|
||||
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||||
LT_RET(ret);
|
||||
}
|
||||
if ((millis() - start) > _handshakeTimeout) {
|
||||
LT_EM(SSL, "SSL handshake timeout");
|
||||
return -1;
|
||||
}
|
||||
delay(2);
|
||||
}
|
||||
|
||||
LT_HEAP_I();
|
||||
|
||||
if (clientCert && clientKey) {
|
||||
LT_DM(
|
||||
SSL,
|
||||
"Protocol %s, ciphersuite %s",
|
||||
mbedtls_ssl_get_version(&_sslCtx),
|
||||
mbedtls_ssl_get_ciphersuite(&_sslCtx)
|
||||
);
|
||||
ret = mbedtls_ssl_get_record_expansion(&_sslCtx);
|
||||
if (ret >= 0)
|
||||
LT_DM(SSL, "Record expansion: %d", ret);
|
||||
else {
|
||||
LT_WM(SSL, "Record expansion unknown");
|
||||
}
|
||||
}
|
||||
|
||||
LT_VM(SSL, "Verifying certificate");
|
||||
ret = mbedtls_ssl_get_verify_result(&_sslCtx);
|
||||
if (ret) {
|
||||
char buf[512];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", ret);
|
||||
LT_EM(SSL, "Failed to verify peer certificate! Verification info: %s", buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rootCABuf)
|
||||
mbedtls_x509_crt_free(&_caCert);
|
||||
if (clientCert)
|
||||
mbedtls_x509_crt_free(&_clientCert);
|
||||
if (clientKey != NULL)
|
||||
mbedtls_pk_free(&_clientKey);
|
||||
return 0; // OK
|
||||
}
|
||||
|
||||
size_t MbedTLSClient::write(const uint8_t *buf, size_t size) {
|
||||
int ret = -1;
|
||||
while ((ret = mbedtls_ssl_write(&_sslCtx, buf, size)) <= 0) {
|
||||
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) {
|
||||
LT_RET(ret);
|
||||
}
|
||||
delay(2);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int MbedTLSClient::available() {
|
||||
bool peeked = _peeked >= 0;
|
||||
if (!connected())
|
||||
return peeked;
|
||||
|
||||
int ret = mbedtls_ssl_read(&_sslCtx, NULL, 0);
|
||||
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) {
|
||||
stop();
|
||||
return peeked ? peeked : ret;
|
||||
}
|
||||
return mbedtls_ssl_get_bytes_avail(&_sslCtx) + peeked;
|
||||
}
|
||||
|
||||
int MbedTLSClient::read(uint8_t *buf, size_t size) {
|
||||
bool peeked = false;
|
||||
int toRead = available();
|
||||
if ((!buf && size) || toRead <= 0)
|
||||
return -1;
|
||||
if (!size)
|
||||
return 0;
|
||||
if (_peeked >= 0) {
|
||||
buf[0] = _peeked;
|
||||
_peeked = -1;
|
||||
size--;
|
||||
toRead--;
|
||||
if (!size || !toRead)
|
||||
return 1;
|
||||
buf++;
|
||||
peeked = true;
|
||||
}
|
||||
|
||||
int ret = mbedtls_ssl_read(&_sslCtx, buf, size);
|
||||
if (ret < 0) {
|
||||
stop();
|
||||
return peeked ? peeked : ret;
|
||||
}
|
||||
return ret + peeked;
|
||||
}
|
||||
|
||||
int MbedTLSClient::peek() {
|
||||
if (_peeked >= 0)
|
||||
return _peeked;
|
||||
_peeked = timedRead();
|
||||
return _peeked;
|
||||
}
|
||||
|
||||
void MbedTLSClient::flush() {}
|
||||
|
||||
int MbedTLSClient::lastError(char *buf, const size_t size) {
|
||||
return 0; // TODO (?)
|
||||
}
|
||||
|
||||
void MbedTLSClient::setInsecure() {
|
||||
_caCertStr = NULL;
|
||||
_clientCertStr = NULL;
|
||||
_clientKeyStr = NULL;
|
||||
_pskIdentStr = NULL;
|
||||
_pskStr = NULL;
|
||||
_insecure = true;
|
||||
}
|
||||
|
||||
void MbedTLSClient::setPreSharedKey(const char *pskIdent, const char *psk) {
|
||||
_pskIdentStr = pskIdent;
|
||||
_pskStr = psk;
|
||||
}
|
||||
|
||||
void MbedTLSClient::setCACert(const char *rootCA) {
|
||||
_caCertStr = rootCA;
|
||||
}
|
||||
|
||||
void MbedTLSClient::setCertificate(const char *clientCA) {
|
||||
_clientCertStr = clientCA;
|
||||
}
|
||||
|
||||
void MbedTLSClient::setPrivateKey(const char *privateKey) {
|
||||
_clientKeyStr = privateKey;
|
||||
}
|
||||
|
||||
char *streamToStr(Stream &stream, size_t size) {
|
||||
char *buf = (char *)malloc(size + 1);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
if (size != stream.readBytes(buf, size)) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
buf[size] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool MbedTLSClient::loadCACert(Stream &stream, size_t size) {
|
||||
char *str = streamToStr(stream, size);
|
||||
if (str) {
|
||||
_caCertStr = str;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MbedTLSClient::loadCertificate(Stream &stream, size_t size) {
|
||||
char *str = streamToStr(stream, size);
|
||||
if (str) {
|
||||
_clientCertStr = str;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MbedTLSClient::loadPrivateKey(Stream &stream, size_t size) {
|
||||
char *str = streamToStr(stream, size);
|
||||
if (str) {
|
||||
_clientKeyStr = str;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MbedTLSClient::verify(const char *fingerprint, const char *domainName) {
|
||||
uint8_t fpLocal[32] = {};
|
||||
uint16_t len = strlen(fingerprint);
|
||||
uint8_t byte = 0;
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
uint8_t c = fingerprint[i];
|
||||
while ((c == ' ' || c == ':') && i < len) {
|
||||
c = fingerprint[++i];
|
||||
}
|
||||
c |= 0b00100000; // make lowercase
|
||||
c -= '0' * (c >= '0' && c <= '9');
|
||||
c -= ('a' - 10) * (c >= 'a' && c <= 'z');
|
||||
if (c > 0xf)
|
||||
return -1;
|
||||
fpLocal[byte / 2] |= c << (4 * ((byte & 1) ^ 1));
|
||||
byte++;
|
||||
if (byte >= 64)
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t fpRemote[32];
|
||||
if (!getFingerprintSHA256(fpRemote))
|
||||
return false;
|
||||
|
||||
if (memcmp(fpLocal, fpRemote, 32)) {
|
||||
LT_DM(SSL, "Fingerprints don't match");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!domainName)
|
||||
return true;
|
||||
// TODO domain name verification
|
||||
return true;
|
||||
}
|
||||
|
||||
void MbedTLSClient::setHandshakeTimeout(unsigned long handshakeTimeout) {
|
||||
_handshakeTimeout = handshakeTimeout * 1000;
|
||||
}
|
||||
|
||||
void MbedTLSClient::setAlpnProtocols(const char **alpnProtocols) {
|
||||
_alpnProtocols = alpnProtocols;
|
||||
}
|
||||
|
||||
bool MbedTLSClient::getFingerprintSHA256(uint8_t result[32]) {
|
||||
const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(&_sslCtx);
|
||||
if (!cert) {
|
||||
LT_EM(SSL, "Failed to get peer certificate");
|
||||
return false;
|
||||
}
|
||||
mbedtls_sha256_context shaCtx;
|
||||
mbedtls_sha256_init(&shaCtx);
|
||||
mbedtls_sha256_starts(&shaCtx, false);
|
||||
mbedtls_sha256_update(&shaCtx, cert->raw.p, cert->raw.len);
|
||||
mbedtls_sha256_finish(&shaCtx, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // LT_ARD_HAS_WIFI && LT_HAS_MBEDTLS
|
||||
@@ -0,0 +1,88 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <api/WiFi/WiFi.h>
|
||||
#include <api/WiFiClient.h>
|
||||
#include <api/WiFiClientSecure.h>
|
||||
|
||||
#include <WiFiClient.h> // extend family's WiFiClient impl
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <mbedtls/net.h>
|
||||
|
||||
} // extern "C"
|
||||
|
||||
class MbedTLSClient : public WiFiClient, public IWiFiClientSecure {
|
||||
private:
|
||||
mbedtls_ssl_context _sslCtx;
|
||||
mbedtls_ssl_config _sslCfg;
|
||||
mbedtls_x509_crt _caCert;
|
||||
mbedtls_x509_crt _clientCert;
|
||||
mbedtls_pk_context _clientKey;
|
||||
uint32_t _handshakeTimeout = 0;
|
||||
|
||||
void init();
|
||||
int _sockTls = -1;
|
||||
bool _insecure = false;
|
||||
bool _useRootCA = false;
|
||||
int _peeked = -1;
|
||||
|
||||
const char *_caCertStr;
|
||||
const char *_clientCertStr;
|
||||
const char *_clientKeyStr;
|
||||
const char *_pskIdentStr;
|
||||
const char *_pskStr;
|
||||
const char **_alpnProtocols;
|
||||
|
||||
int connect(
|
||||
const char *host,
|
||||
uint16_t port,
|
||||
int32_t timeout,
|
||||
const char *rootCABuf,
|
||||
const char *clientCert,
|
||||
const char *clientKey,
|
||||
const char *pskIdent,
|
||||
const char *psk
|
||||
);
|
||||
|
||||
public:
|
||||
MbedTLSClient();
|
||||
MbedTLSClient(int sock);
|
||||
~MbedTLSClient();
|
||||
|
||||
int connect(IPAddress ip, uint16_t port, int32_t timeout);
|
||||
int connect(const char *host, uint16_t port, int32_t timeout);
|
||||
|
||||
int connect(IPAddress ip, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey);
|
||||
int connect(const char *host, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey);
|
||||
int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psk);
|
||||
int connect(const char *host, uint16_t port, const char *pskIdent, const char *psk);
|
||||
|
||||
size_t write(const uint8_t *buf, size_t size);
|
||||
|
||||
int available();
|
||||
|
||||
int read(uint8_t *buf, size_t size);
|
||||
int peek();
|
||||
void flush();
|
||||
void stop();
|
||||
|
||||
int lastError(char *buf, const size_t size);
|
||||
void setInsecure(); // Don't validate the chain, just accept whatever is given. VERY INSECURE!
|
||||
void setPreSharedKey(const char *pskIdent, const char *psk); // psk in hex
|
||||
void setCACert(const char *rootCA);
|
||||
void setCertificate(const char *clientCA);
|
||||
void setPrivateKey(const char *privateKey);
|
||||
bool loadCACert(Stream &stream, size_t size);
|
||||
bool loadCertificate(Stream &stream, size_t size);
|
||||
bool loadPrivateKey(Stream &stream, size_t size);
|
||||
bool verify(const char *fingerprint, const char *domainName);
|
||||
void setHandshakeTimeout(unsigned long handshakeTimeout);
|
||||
void setAlpnProtocols(const char **alpnProtocols);
|
||||
bool getFingerprintSHA256(uint8_t result[32]);
|
||||
|
||||
using WiFiClient::connect;
|
||||
using WiFiClient::read;
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Client.h - Base class that provides Client
|
||||
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <api/Client.h>
|
||||
|
||||
class IWiFiClient : public Client {
|
||||
public:
|
||||
IWiFiClient() {}
|
||||
|
||||
IWiFiClient(int sock) {}
|
||||
|
||||
~IWiFiClient() {}
|
||||
|
||||
virtual int connect(IPAddress ip, uint16_t port, int32_t timeout) = 0;
|
||||
virtual int connect(const char *host, uint16_t port, int32_t timeout) = 0;
|
||||
|
||||
virtual size_t write(Stream &stream) = 0;
|
||||
|
||||
size_t write_P(PGM_P buffer, size_t size) {
|
||||
return write((const uint8_t *)buffer, size);
|
||||
}
|
||||
|
||||
virtual int fd() const = 0;
|
||||
virtual int socket() = 0;
|
||||
virtual int setTimeout(uint32_t seconds) = 0;
|
||||
|
||||
bool operator==(const IWiFiClient &other) const;
|
||||
|
||||
operator bool() {
|
||||
return connected();
|
||||
}
|
||||
|
||||
virtual bool operator==(const bool value) {
|
||||
return bool() == value;
|
||||
}
|
||||
|
||||
virtual bool operator!=(const bool value) {
|
||||
return bool() != value;
|
||||
}
|
||||
|
||||
virtual bool operator!=(const IWiFiClient &other) {
|
||||
return !this->operator==(other);
|
||||
};
|
||||
|
||||
virtual IPAddress remoteIP() const = 0;
|
||||
virtual IPAddress remoteIP(int sock) const = 0;
|
||||
virtual uint16_t remotePort() const = 0;
|
||||
virtual uint16_t remotePort(int sock) const = 0;
|
||||
virtual IPAddress localIP() const = 0;
|
||||
virtual IPAddress localIP(int sock) const = 0;
|
||||
virtual uint16_t localPort() const = 0;
|
||||
virtual uint16_t localPort(int sock) const = 0;
|
||||
|
||||
using Print::write;
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
WiFiClientSecure.h - Base class that provides Client SSL to ESP32
|
||||
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||
Additions Copyright (C) 2017 Evandro Luis Copercini.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "WiFiClient.h"
|
||||
|
||||
class IWiFiClientSecure {
|
||||
public:
|
||||
virtual int
|
||||
connect(IPAddress ip, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey) = 0;
|
||||
virtual int
|
||||
connect(const char *host, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey) = 0;
|
||||
virtual int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psk) = 0;
|
||||
virtual int connect(const char *host, uint16_t port, const char *pskIdent, const char *psk) = 0;
|
||||
|
||||
virtual int lastError(char *buf, const size_t size) = 0;
|
||||
virtual void setInsecure() = 0; // Don't validate the chain, just accept whatever is given. VERY INSECURE!
|
||||
virtual void setPreSharedKey(const char *pskIdent, const char *psk) = 0; // psk in hex
|
||||
virtual void setCACert(const char *rootCA) = 0;
|
||||
virtual void setCertificate(const char *clientCA) = 0;
|
||||
virtual void setPrivateKey(const char *privateKey) = 0;
|
||||
virtual bool loadCACert(Stream &stream, size_t size) = 0;
|
||||
virtual bool loadCertificate(Stream &stream, size_t size) = 0;
|
||||
virtual bool loadPrivateKey(Stream &stream, size_t size) = 0;
|
||||
virtual bool verify(const char *fingerprint, const char *domainName) = 0;
|
||||
virtual void setHandshakeTimeout(unsigned long handshakeTimeout) = 0;
|
||||
virtual void setAlpnProtocols(const char **alpnProtocols) = 0;
|
||||
virtual bool getFingerprintSHA256(uint8_t result[32]) = 0;
|
||||
};
|
||||
142
cores/common/arduino/libraries/common/WiFiServer/LwIPServer.cpp
Normal file
142
cores/common/arduino/libraries/common/WiFiServer/LwIPServer.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
|
||||
|
||||
#if LT_ARD_HAS_WIFI && LT_HAS_LWIP
|
||||
|
||||
#include "LwIPServer.h"
|
||||
|
||||
// disable #defines removing lwip_ prefix
|
||||
#undef LWIP_COMPAT_SOCKETS
|
||||
#define LWIP_COMPAT_SOCKETS 0
|
||||
|
||||
extern "C" {
|
||||
#include <lwip/api.h>
|
||||
// #include <lwip/dns.h>
|
||||
#include <lwip/err.h>
|
||||
#include <lwip/sockets.h>
|
||||
#include <sys/time.h>
|
||||
}
|
||||
|
||||
LwIPServer::LwIPServer(uint32_t addr, uint16_t port, uint8_t maxClients)
|
||||
: _sock(-1), _sockAccepted(-1), _addr(addr), _port(port), _maxClients(maxClients), _active(false), _noDelay(false) {
|
||||
}
|
||||
|
||||
LwIPServer::operator bool() {
|
||||
return _active;
|
||||
}
|
||||
|
||||
bool LwIPServer::begin(uint16_t port, bool reuseAddr) {
|
||||
if (_active)
|
||||
return true;
|
||||
if (port)
|
||||
_port = port;
|
||||
|
||||
_sock = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (_sock < 0) {
|
||||
LT_EM(SERVER, "Socket failed; errno=%d", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
int enable = reuseAddr;
|
||||
lwip_setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
||||
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = _addr;
|
||||
addr.sin_port = htons(_port);
|
||||
|
||||
if (lwip_bind(_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
LT_EM(SERVER, "Bind failed; errno=%d", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lwip_listen(_sock, _maxClients) < 0) {
|
||||
LT_EM(SERVER, "Bind failed; errno=%d", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t *addrB = (uint8_t *)&_addr;
|
||||
LT_IM(SERVER, "Server running on %hhu.%hhu.%hhu.%hhu:%hu", addrB[0], addrB[1], addrB[2], addrB[3], _port);
|
||||
|
||||
lwip_fcntl(_sock, F_SETFL, O_NONBLOCK);
|
||||
_active = true;
|
||||
_noDelay = false;
|
||||
_sockAccepted = -1;
|
||||
}
|
||||
|
||||
void LwIPServer::end() {
|
||||
if (_sock == -1)
|
||||
return;
|
||||
lwip_close(_sock);
|
||||
_sock = -1;
|
||||
_active = -1;
|
||||
}
|
||||
|
||||
WiFiClient LwIPServer::accept() {
|
||||
if (!_active)
|
||||
return WiFiClient();
|
||||
|
||||
int sock;
|
||||
if (_sockAccepted >= 0) {
|
||||
sock = _sockAccepted;
|
||||
_sockAccepted = -1;
|
||||
} else {
|
||||
struct sockaddr_in addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
sock = lwip_accept(_sock, (struct sockaddr *)&addr, &len);
|
||||
}
|
||||
|
||||
if (sock >= 0) {
|
||||
int enable = 1;
|
||||
if (lwip_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)) == ERR_OK) {
|
||||
enable = _noDelay;
|
||||
if (lwip_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)) == ERR_OK) {
|
||||
// HOTFIX: allow the TCP thread to receive data
|
||||
// I'm not sure what's happening there, so this should probably be fixed properly.
|
||||
// When a connection arrives, sometimes TCP hasn't received anything yet. This causes
|
||||
// calling WiFiClient::connected() check for lwip_recv(), which returns EWOULDBLOCK
|
||||
// as the client is still connected. The problem is that there's basically an infinite loop
|
||||
// created: nowhere in that code is a yield()/delay() that would allow TCP thread to work
|
||||
// and receive data, so LwIP still sees a connected client that sends nothing. At least
|
||||
// that's what I understand. And any loop that doesn't call delay() seems to block the TCP
|
||||
// stack completely and prevents it from even being pinged.
|
||||
LT_DM(SERVER, "Got client");
|
||||
delay(5);
|
||||
return WiFiClient(sock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return WiFiClient();
|
||||
}
|
||||
|
||||
int LwIPServer::setTimeout(uint32_t seconds) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = seconds;
|
||||
tv.tv_usec = 0;
|
||||
if (lwip_setsockopt(_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
|
||||
return -1;
|
||||
return lwip_setsockopt(_sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
|
||||
}
|
||||
|
||||
void LwIPServer::setNoDelay(bool noDelay) {
|
||||
_noDelay = noDelay;
|
||||
}
|
||||
|
||||
bool LwIPServer::getNoDelay() {
|
||||
return _noDelay;
|
||||
}
|
||||
|
||||
bool LwIPServer::hasClient() {
|
||||
if (_sockAccepted >= 0) {
|
||||
return true;
|
||||
}
|
||||
struct sockaddr_in addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
_sockAccepted = lwip_accept(_sock, (struct sockaddr *)&addr, &len);
|
||||
if (_sockAccepted >= 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <api/WiFi/WiFi.h>
|
||||
#include <api/WiFiServer.h>
|
||||
|
||||
#include <WiFiClient.h>
|
||||
|
||||
class LwIPServer : public IWiFiServer<WiFiClient> {
|
||||
private:
|
||||
int _sock;
|
||||
int _sockAccepted;
|
||||
uint32_t _addr;
|
||||
uint16_t _port;
|
||||
uint8_t _maxClients;
|
||||
bool _active;
|
||||
bool _noDelay = false;
|
||||
|
||||
private:
|
||||
LwIPServer(uint32_t addr, uint16_t port = 80, uint8_t maxClients = 4);
|
||||
|
||||
public:
|
||||
LwIPServer(uint16_t port = 80, uint8_t maxClients = 4) : LwIPServer((uint32_t)0, port, maxClients) {}
|
||||
|
||||
LwIPServer(int port = 80, uint8_t maxClients = 4) : LwIPServer((uint32_t)0, port, maxClients) {}
|
||||
|
||||
LwIPServer(const IPAddress &addr, uint16_t port = 80, uint8_t maxClients = 4)
|
||||
: LwIPServer((uint32_t)addr, port, maxClients) {}
|
||||
|
||||
operator bool();
|
||||
|
||||
bool begin(uint16_t port = 0, bool reuseAddr = true);
|
||||
void end();
|
||||
WiFiClient accept();
|
||||
|
||||
size_t write(const uint8_t *buffer, size_t size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stopAll() {}
|
||||
|
||||
int setTimeout(uint32_t seconds);
|
||||
void setNoDelay(bool noDelay);
|
||||
bool getNoDelay();
|
||||
bool hasClient();
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
Server.h - Server class for Raspberry Pi
|
||||
Copyright (c) 2016 Hristo Gochkov All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <api/Print.h>
|
||||
|
||||
#include "WiFiClient.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
template <typename TWiFiClient, typename = std::enable_if<std::is_base_of<IWiFiClient, TWiFiClient>::value>>
|
||||
|
||||
class IWiFiServer : public Print { // arduino::Server is useless anyway
|
||||
public:
|
||||
void listenOnLocalhost() {}
|
||||
|
||||
IWiFiServer(uint16_t port = 80, uint8_t maxClients = 4) {}
|
||||
|
||||
IWiFiServer(const IPAddress &addr, uint16_t port = 80, uint8_t maxClients = 4) {}
|
||||
|
||||
~IWiFiServer() {
|
||||
stop();
|
||||
}
|
||||
|
||||
TWiFiClient available() {
|
||||
return accept();
|
||||
};
|
||||
|
||||
virtual operator bool() = 0;
|
||||
|
||||
virtual bool begin(uint16_t port = 0, bool reuseAddr = true) = 0;
|
||||
virtual void end() = 0;
|
||||
virtual TWiFiClient accept() = 0;
|
||||
|
||||
void close() {
|
||||
end();
|
||||
}
|
||||
|
||||
void stop() {
|
||||
end();
|
||||
}
|
||||
|
||||
virtual int setTimeout(uint32_t seconds) = 0;
|
||||
virtual void stopAll() = 0;
|
||||
virtual void setNoDelay(bool noDelay) = 0;
|
||||
virtual bool getNoDelay() = 0;
|
||||
virtual bool hasClient() = 0;
|
||||
|
||||
size_t write(uint8_t data) {
|
||||
return write(&data, 1);
|
||||
}
|
||||
|
||||
using Print::write;
|
||||
};
|
||||
286
cores/common/arduino/libraries/common/WiFiUdp/LwIPUdp.cpp
Normal file
286
cores/common/arduino/libraries/common/WiFiUdp/LwIPUdp.cpp
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
Udp.cpp - UDP class for Raspberry Pi
|
||||
Copyright (c) 2016 Hristo Gochkov All right reserved.
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#if LT_ARD_HAS_WIFI && LT_HAS_LWIP
|
||||
|
||||
#include "LwIPUdp.h"
|
||||
#include <errno.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <lwip/netdb.h>
|
||||
#include <lwip/sockets.h>
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#undef write
|
||||
#undef read
|
||||
|
||||
LwIPUDP::LwIPUDP() : udp_server(-1), server_port(0), remote_port(0), tx_buffer(0), tx_buffer_len(0), rx_buffer(0) {}
|
||||
|
||||
LwIPUDP::~LwIPUDP() {
|
||||
stop();
|
||||
}
|
||||
|
||||
uint8_t LwIPUDP::begin(IPAddress address, uint16_t port) {
|
||||
stop();
|
||||
|
||||
server_port = port;
|
||||
|
||||
tx_buffer = new char[1460];
|
||||
if (!tx_buffer) {
|
||||
log_e("could not create tx buffer: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((udp_server = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
|
||||
log_e("could not create socket: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yes = 1;
|
||||
if (setsockopt(udp_server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
|
||||
log_e("could not set socket option: %d", errno);
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset((char *)&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(server_port);
|
||||
addr.sin_addr.s_addr = (in_addr_t)address;
|
||||
if (bind(udp_server, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||
log_e("could not bind socket: %d", errno);
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
fcntl(udp_server, F_SETFL, O_NONBLOCK);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t LwIPUDP::begin(uint16_t p) {
|
||||
return begin(IPAddress(INADDR_ANY), p);
|
||||
}
|
||||
|
||||
uint8_t LwIPUDP::beginMulticast(IPAddress a, uint16_t p) {
|
||||
if (begin(IPAddress(INADDR_ANY), p)) {
|
||||
if ((uint32_t)a != 0) {
|
||||
struct ip_mreq mreq;
|
||||
mreq.imr_multiaddr.s_addr = (in_addr_t)a;
|
||||
mreq.imr_interface.s_addr = INADDR_ANY;
|
||||
if (setsockopt(udp_server, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
|
||||
log_e("could not join igmp: %d", errno);
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
multicast_ip = a;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LwIPUDP::stop() {
|
||||
if (tx_buffer) {
|
||||
delete[] tx_buffer;
|
||||
tx_buffer = NULL;
|
||||
}
|
||||
tx_buffer_len = 0;
|
||||
if (rx_buffer) {
|
||||
cbuf *b = rx_buffer;
|
||||
rx_buffer = NULL;
|
||||
delete b;
|
||||
}
|
||||
if (udp_server == -1)
|
||||
return;
|
||||
if ((uint32_t)multicast_ip != 0) {
|
||||
struct ip_mreq mreq;
|
||||
mreq.imr_multiaddr.s_addr = (in_addr_t)multicast_ip;
|
||||
mreq.imr_interface.s_addr = (in_addr_t)0;
|
||||
setsockopt(udp_server, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
|
||||
multicast_ip = IPAddress(INADDR_ANY);
|
||||
}
|
||||
close(udp_server);
|
||||
udp_server = -1;
|
||||
}
|
||||
|
||||
int LwIPUDP::beginMulticastPacket() {
|
||||
if (!server_port || multicast_ip == IPAddress(INADDR_ANY))
|
||||
return 0;
|
||||
remote_ip = multicast_ip;
|
||||
remote_port = server_port;
|
||||
return beginPacket();
|
||||
}
|
||||
|
||||
int LwIPUDP::beginPacket() {
|
||||
if (!remote_port)
|
||||
return 0;
|
||||
|
||||
// allocate tx_buffer if is necessary
|
||||
if (!tx_buffer) {
|
||||
tx_buffer = new char[1460];
|
||||
if (!tx_buffer) {
|
||||
log_e("could not create tx buffer: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
tx_buffer_len = 0;
|
||||
|
||||
// check whereas socket is already open
|
||||
if (udp_server != -1)
|
||||
return 1;
|
||||
|
||||
if ((udp_server = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
|
||||
log_e("could not create socket: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fcntl(udp_server, F_SETFL, O_NONBLOCK);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LwIPUDP::beginPacket(IPAddress ip, uint16_t port) {
|
||||
remote_ip = ip;
|
||||
remote_port = port;
|
||||
return beginPacket();
|
||||
}
|
||||
|
||||
int LwIPUDP::beginPacket(const char *host, uint16_t port) {
|
||||
struct hostent *server;
|
||||
server = gethostbyname(host);
|
||||
if (server == NULL) {
|
||||
log_e("could not get host from dns: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
return beginPacket(IPAddress((const uint8_t *)(server->h_addr_list[0])), port);
|
||||
}
|
||||
|
||||
int LwIPUDP::endPacket() {
|
||||
struct sockaddr_in recipient;
|
||||
recipient.sin_addr.s_addr = (uint32_t)remote_ip;
|
||||
recipient.sin_family = AF_INET;
|
||||
recipient.sin_port = htons(remote_port);
|
||||
int sent = sendto(udp_server, tx_buffer, tx_buffer_len, 0, (struct sockaddr *)&recipient, sizeof(recipient));
|
||||
if (sent < 0) {
|
||||
log_e("could not send data: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t LwIPUDP::write(uint8_t data) {
|
||||
if (tx_buffer_len == 1460) {
|
||||
endPacket();
|
||||
tx_buffer_len = 0;
|
||||
}
|
||||
tx_buffer[tx_buffer_len++] = data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t LwIPUDP::write(const uint8_t *buffer, size_t size) {
|
||||
size_t i;
|
||||
for (i = 0; i < size; i++)
|
||||
write(buffer[i]);
|
||||
return i;
|
||||
}
|
||||
|
||||
int LwIPUDP::parsePacket() {
|
||||
if (rx_buffer)
|
||||
return 0;
|
||||
struct sockaddr_in si_other;
|
||||
int slen = sizeof(si_other), len;
|
||||
char *buf = new char[1460];
|
||||
if (!buf) {
|
||||
return 0;
|
||||
}
|
||||
if ((len = recvfrom(udp_server, buf, 1460, MSG_DONTWAIT, (struct sockaddr *)&si_other, (socklen_t *)&slen)) == -1) {
|
||||
delete[] buf;
|
||||
if (errno == EWOULDBLOCK) {
|
||||
return 0;
|
||||
}
|
||||
log_e("could not receive data: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
remote_ip = IPAddress(si_other.sin_addr.s_addr);
|
||||
remote_port = ntohs(si_other.sin_port);
|
||||
if (len > 0) {
|
||||
rx_buffer = new cbuf(len);
|
||||
rx_buffer->write(buf, len);
|
||||
}
|
||||
delete[] buf;
|
||||
return len;
|
||||
}
|
||||
|
||||
int LwIPUDP::available() {
|
||||
if (!rx_buffer)
|
||||
return 0;
|
||||
return rx_buffer->available();
|
||||
}
|
||||
|
||||
int LwIPUDP::read() {
|
||||
if (!rx_buffer)
|
||||
return -1;
|
||||
int out = rx_buffer->read();
|
||||
if (!rx_buffer->available()) {
|
||||
cbuf *b = rx_buffer;
|
||||
rx_buffer = 0;
|
||||
delete b;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
int LwIPUDP::read(unsigned char *buffer, size_t len) {
|
||||
return read((char *)buffer, len);
|
||||
}
|
||||
|
||||
int LwIPUDP::read(char *buffer, size_t len) {
|
||||
if (!rx_buffer)
|
||||
return 0;
|
||||
int out = rx_buffer->read(buffer, len);
|
||||
if (!rx_buffer->available()) {
|
||||
cbuf *b = rx_buffer;
|
||||
rx_buffer = 0;
|
||||
delete b;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
int LwIPUDP::peek() {
|
||||
if (!rx_buffer)
|
||||
return -1;
|
||||
return rx_buffer->peek();
|
||||
}
|
||||
|
||||
void LwIPUDP::flush() {
|
||||
if (!rx_buffer)
|
||||
return;
|
||||
cbuf *b = rx_buffer;
|
||||
rx_buffer = 0;
|
||||
delete b;
|
||||
}
|
||||
|
||||
IPAddress LwIPUDP::remoteIP() {
|
||||
return remote_ip;
|
||||
}
|
||||
|
||||
uint16_t LwIPUDP::remotePort() {
|
||||
return remote_port;
|
||||
}
|
||||
|
||||
#endif
|
||||
43
cores/common/arduino/libraries/common/WiFiUdp/LwIPUdp.h
Normal file
43
cores/common/arduino/libraries/common/WiFiUdp/LwIPUdp.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-09-10. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <api/WiFi/WiFi.h>
|
||||
#include <api/WiFiUdp.h>
|
||||
#include <cbuf.h>
|
||||
|
||||
class LwIPUDP : public IWiFiUDP {
|
||||
private:
|
||||
int udp_server;
|
||||
IPAddress multicast_ip;
|
||||
IPAddress remote_ip;
|
||||
uint16_t server_port;
|
||||
uint16_t remote_port;
|
||||
char *tx_buffer;
|
||||
size_t tx_buffer_len;
|
||||
cbuf *rx_buffer;
|
||||
|
||||
public:
|
||||
LwIPUDP();
|
||||
~LwIPUDP();
|
||||
uint8_t begin(IPAddress ip, uint16_t port);
|
||||
uint8_t begin(uint16_t port);
|
||||
uint8_t beginMulticast(IPAddress ip, uint16_t port);
|
||||
void stop();
|
||||
int beginMulticastPacket();
|
||||
int beginPacket();
|
||||
int beginPacket(IPAddress ip, uint16_t port);
|
||||
int beginPacket(const char *host, uint16_t port);
|
||||
int endPacket();
|
||||
size_t write(uint8_t);
|
||||
size_t write(const uint8_t *buffer, size_t size);
|
||||
int parsePacket();
|
||||
int available();
|
||||
int read();
|
||||
int read(unsigned char *buffer, size_t len);
|
||||
int read(char *buffer, size_t len);
|
||||
int peek();
|
||||
void flush();
|
||||
IPAddress remoteIP();
|
||||
uint16_t remotePort();
|
||||
};
|
||||
32
cores/common/arduino/libraries/common/WiFiUdp/WiFiUdp.h
Normal file
32
cores/common/arduino/libraries/common/WiFiUdp/WiFiUdp.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <api/Udp.h>
|
||||
|
||||
class IWiFiUDP : public UDP {
|
||||
public:
|
||||
IWiFiUDP() {}
|
||||
|
||||
~IWiFiUDP() {}
|
||||
|
||||
virtual uint8_t begin(IPAddress ip, uint16_t port) = 0;
|
||||
virtual uint8_t begin(uint16_t port) = 0;
|
||||
virtual uint8_t beginMulticast(IPAddress ip, uint16_t port) = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual int beginMulticastPacket() = 0;
|
||||
virtual int beginPacket() = 0;
|
||||
virtual int beginPacket(IPAddress ip, uint16_t port) = 0;
|
||||
virtual int beginPacket(const char *host, uint16_t port) = 0;
|
||||
virtual int endPacket() = 0;
|
||||
virtual size_t write(uint8_t) = 0;
|
||||
virtual size_t write(const uint8_t *buffer, size_t size) = 0;
|
||||
virtual int parsePacket() = 0;
|
||||
virtual int available() = 0;
|
||||
virtual int read() = 0;
|
||||
virtual int read(unsigned char *buffer, size_t len) = 0;
|
||||
virtual int read(char *buffer, size_t len) = 0;
|
||||
virtual int peek() = 0;
|
||||
virtual void flush() = 0;
|
||||
virtual IPAddress remoteIP() = 0;
|
||||
virtual uint16_t remotePort() = 0;
|
||||
};
|
||||
131
cores/common/arduino/libraries/common/mDNS/LwIPmDNS.cpp
Normal file
131
cores/common/arduino/libraries/common/mDNS/LwIPmDNS.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-05-23. */
|
||||
|
||||
#ifdef LT_HAS_LWIP2
|
||||
|
||||
#include "mDNS.h"
|
||||
#include <vector>
|
||||
|
||||
extern "C" {
|
||||
#include <errno.h>
|
||||
#include <lwip/apps/mdns.h>
|
||||
#include <lwip/igmp.h>
|
||||
#include <lwip/init.h>
|
||||
#include <lwip/netif.h>
|
||||
}
|
||||
|
||||
static std::vector<char *> services;
|
||||
static std::vector<uint8_t> protos;
|
||||
static std::vector<std::vector<char *>> records;
|
||||
|
||||
mDNS::mDNS() {}
|
||||
|
||||
mDNS::~mDNS() {}
|
||||
|
||||
static void mdnsTxtCallback(struct mdns_service *service, void *userdata) {
|
||||
size_t index = (size_t)userdata;
|
||||
if (index >= records.size())
|
||||
return;
|
||||
|
||||
for (const auto record : records[index]) {
|
||||
err_t err = mdns_resp_add_service_txtitem(service, record, strlen(record));
|
||||
if (err != ERR_OK)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void mdnsStatusCallback(struct netif *netif, uint8_t result) {
|
||||
LT_DM(MDNS, "Status: netif %u, status %u", netif->num, result);
|
||||
}
|
||||
|
||||
bool mDNS::begin(const char *hostname) {
|
||||
setInstanceName(hostname);
|
||||
LT_DM(MDNS, "Starting (%s)", hostname);
|
||||
#if LWIP_VERSION_MAJOR >= 2 && LWIP_VERSION_MINOR >= 1
|
||||
mdns_resp_register_name_result_cb(mdnsStatusCallback);
|
||||
#endif
|
||||
mdns_resp_init();
|
||||
uint8_t enabled = 0;
|
||||
|
||||
struct netif *netif;
|
||||
for (netif = netif_list; netif != NULL; netif = netif->next) {
|
||||
if (!netif_is_up(netif))
|
||||
continue;
|
||||
|
||||
LT_DM(MDNS, "Adding netif %u", netif->num);
|
||||
if ((netif->flags & NETIF_FLAG_IGMP) == 0) {
|
||||
LT_DM(MDNS, "Enabling IGMP");
|
||||
netif->flags |= NETIF_FLAG_IGMP;
|
||||
igmp_start(netif);
|
||||
}
|
||||
|
||||
err_t ret = mdns_resp_add_netif(netif, hostname, 255);
|
||||
if (ret == ERR_OK)
|
||||
enabled++;
|
||||
else
|
||||
LT_DM(MDNS, "Cannot add netif %u; ret=%d, errno=%d", netif->num, ret, errno);
|
||||
}
|
||||
return enabled > 0;
|
||||
}
|
||||
|
||||
void mDNS::end() {
|
||||
struct netif *netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
if (netif_is_up(netif))
|
||||
mdns_resp_remove_netif(netif);
|
||||
netif = netif->next;
|
||||
}
|
||||
}
|
||||
|
||||
bool mDNS::addServiceImpl(const char *name, const char *service, uint8_t proto, uint16_t port) {
|
||||
bool added = false;
|
||||
struct netif *netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
if (netif_is_up(netif)) {
|
||||
// register TXT callback;
|
||||
// pass service index as userdata parameter
|
||||
LT_DM(MDNS, "Add service: netif %u / %s / %s / %u / %u", netif->num, name, service, proto, port);
|
||||
mdns_resp_add_service(
|
||||
netif,
|
||||
name,
|
||||
service,
|
||||
(mdns_sd_proto)proto,
|
||||
port,
|
||||
255,
|
||||
mdnsTxtCallback,
|
||||
(void *)services.size() // index of newly added service
|
||||
);
|
||||
added = true;
|
||||
}
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
if (!added)
|
||||
return false;
|
||||
|
||||
// add the service to TXT record arrays
|
||||
services.push_back(strdup(service));
|
||||
protos.push_back(proto);
|
||||
records.emplace_back();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mDNS::addServiceTxtImpl(const char *service, uint8_t proto, const char *item) {
|
||||
int8_t index = -1;
|
||||
for (uint8_t i = 0; i < services.size(); i++) {
|
||||
// find a matching service
|
||||
if (strcmp(services[i], service) == 0 && protos[i] == proto) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index == -1)
|
||||
return false;
|
||||
|
||||
records[index].push_back(strdup(item));
|
||||
return true;
|
||||
}
|
||||
|
||||
MDNSResponder MDNS;
|
||||
|
||||
#endif
|
||||
40
cores/common/arduino/libraries/common/mDNS/mDNS.cpp
Normal file
40
cores/common/arduino/libraries/common/mDNS/mDNS.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-08-26. */
|
||||
|
||||
#include "mDNS.h"
|
||||
|
||||
static char *ensureUnderscore(const char *value) {
|
||||
uint8_t len = strlen(value) + 1;
|
||||
char *result = (char *)malloc(len);
|
||||
result[0] = '_';
|
||||
strcpy(result + 1, value + (value[0] == '_'));
|
||||
return result;
|
||||
}
|
||||
|
||||
void mDNS::setInstanceName(const char *name) {
|
||||
if (instanceName)
|
||||
free(instanceName);
|
||||
instanceName = strdup(name);
|
||||
}
|
||||
|
||||
bool mDNS::addService(char *service, char *proto, uint16_t port) {
|
||||
char *_service = ensureUnderscore(service);
|
||||
uint8_t _proto = strncmp(proto + (proto[0] == '_'), "tcp", 3) == 0 ? MDNS_TCP : MDNS_UDP;
|
||||
|
||||
bool result = addServiceImpl(instanceName ? instanceName : "LT mDNS", _service, _proto, port);
|
||||
free(_service);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool mDNS::addServiceTxt(char *service, char *proto, char *key, char *value) {
|
||||
char *_service = ensureUnderscore(service);
|
||||
uint8_t _proto = strncmp(proto + (proto[0] == '_'), "tcp", 3) == 0 ? MDNS_TCP : MDNS_UDP;
|
||||
|
||||
uint8_t txt_len = strlen(key) + strlen(value) + 1;
|
||||
char *txt = (char *)malloc(txt_len + 1);
|
||||
sprintf(txt, "%s=%s", key, value);
|
||||
|
||||
bool result = addServiceTxtImpl(_service, _proto, txt);
|
||||
free(_service);
|
||||
free(txt);
|
||||
return result;
|
||||
}
|
||||
128
cores/common/arduino/libraries/common/mDNS/mDNS.h
Normal file
128
cores/common/arduino/libraries/common/mDNS/mDNS.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
ESP8266 Multicast DNS (port of CC3000 Multicast DNS library)
|
||||
Version 1.1
|
||||
Copyright (c) 2013 Tony DiCola (tony@tonydicola.com)
|
||||
ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com)
|
||||
MDNS-SD Suport 2015 Hristo Gochkov (hristo@espressif.com)
|
||||
Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com)
|
||||
Rewritten for ESP32 by Hristo Gochkov (hristo@espressif.com)
|
||||
|
||||
This is a simple implementation of multicast DNS query support for an Arduino
|
||||
running on ESP32 chip.
|
||||
|
||||
Usage:
|
||||
- Include the ESP32 Multicast DNS library in the sketch.
|
||||
- Call the begin method in the sketch's setup and provide a domain name (without
|
||||
the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the
|
||||
Adafruit CC3000 class instance. Optionally provide a time to live (in seconds)
|
||||
for the DNS record--the default is 1 hour.
|
||||
- Call the update method in each iteration of the sketch's loop function.
|
||||
|
||||
License (MIT license):
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <api/IPv6Address.h>
|
||||
|
||||
#define MDNS_UDP 0
|
||||
#define MDNS_TCP 1
|
||||
|
||||
class mDNS {
|
||||
private:
|
||||
bool addServiceImpl(const char *name, const char *service, uint8_t proto, uint16_t port);
|
||||
bool addServiceTxtImpl(const char *service, uint8_t proto, const char *item);
|
||||
|
||||
char *instanceName = NULL;
|
||||
|
||||
public:
|
||||
mDNS();
|
||||
~mDNS();
|
||||
|
||||
bool begin(const char *hostname);
|
||||
void end();
|
||||
|
||||
void setInstanceName(const char *name);
|
||||
bool addService(char *service, char *proto, uint16_t port);
|
||||
bool addServiceTxt(char *service, char *proto, char *key, char *value);
|
||||
// void enableArduino(uint16_t port = 3232, bool auth = false);
|
||||
// void disableArduino();
|
||||
// void enableWorkstation(esp_interface_t interface = ESP_IF_WIFI_STA);
|
||||
// void disableWorkstation();
|
||||
|
||||
IPAddress queryHost(char *host, uint32_t timeout = 2000);
|
||||
int queryService(char *service, char *proto);
|
||||
|
||||
String hostname(int idx);
|
||||
IPAddress IP(int idx);
|
||||
IPv6Address IPv6(int idx);
|
||||
uint16_t port(int idx);
|
||||
int numTxt(int idx);
|
||||
bool hasTxt(int idx, const char *key);
|
||||
String txt(int idx, const char *key);
|
||||
String txt(int idx, int txtIdx);
|
||||
String txtKey(int idx, int txtIdx);
|
||||
|
||||
void setInstanceName(String name) {
|
||||
setInstanceName(name.c_str());
|
||||
}
|
||||
|
||||
void setInstanceName(char *name) {
|
||||
setInstanceName((const char *)name);
|
||||
}
|
||||
|
||||
bool addService(const char *service, const char *proto, uint16_t port) {
|
||||
return addService((char *)service, (char *)proto, port);
|
||||
}
|
||||
|
||||
bool addService(String service, String proto, uint16_t port) {
|
||||
return addService(service.c_str(), proto.c_str(), port);
|
||||
}
|
||||
|
||||
void addServiceTxt(const char *service, const char *proto, const char *key, const char *value) {
|
||||
addServiceTxt((char *)service, (char *)proto, (char *)key, (char *)value);
|
||||
}
|
||||
|
||||
void addServiceTxt(String service, String proto, String key, String value) {
|
||||
addServiceTxt(service.c_str(), proto.c_str(), key.c_str(), value.c_str());
|
||||
}
|
||||
|
||||
IPAddress queryHost(const char *host, uint32_t timeout = 2000) {
|
||||
return queryHost((char *)host, timeout);
|
||||
}
|
||||
|
||||
IPAddress queryHost(String host, uint32_t timeout = 2000) {
|
||||
return queryHost(host.c_str(), timeout);
|
||||
}
|
||||
|
||||
int queryService(const char *service, const char *proto) {
|
||||
return queryService((char *)service, (char *)proto);
|
||||
}
|
||||
|
||||
int queryService(String service, String proto) {
|
||||
return queryService(service.c_str(), proto.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
typedef mDNS MDNSResponder;
|
||||
|
||||
extern MDNSResponder MDNS;
|
||||
1629
cores/common/arduino/libraries/ext/HTTPClient/HTTPClient.cpp
Normal file
1629
cores/common/arduino/libraries/ext/HTTPClient/HTTPClient.cpp
Normal file
File diff suppressed because it is too large
Load Diff
305
cores/common/arduino/libraries/ext/HTTPClient/HTTPClient.h
Normal file
305
cores/common/arduino/libraries/ext/HTTPClient/HTTPClient.h
Normal file
@@ -0,0 +1,305 @@
|
||||
/**
|
||||
* HTTPClient.h
|
||||
*
|
||||
* Created on: 02.11.2015
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the HTTPClient for Arduino.
|
||||
* Port to ESP32 by Evandro Luis Copercini (2017),
|
||||
* changed fingerprints to CA verification.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HTTPClient_H_
|
||||
#define HTTPClient_H_
|
||||
|
||||
#ifndef HTTPCLIENT_1_1_COMPATIBLE
|
||||
#define HTTPCLIENT_1_1_COMPATIBLE
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <memory>
|
||||
|
||||
/// Cookie jar support
|
||||
#include <vector>
|
||||
|
||||
#define HTTPCLIENT_DEFAULT_TCP_TIMEOUT (5000)
|
||||
|
||||
/// HTTP client errors
|
||||
#define HTTPC_ERROR_CONNECTION_REFUSED (-1)
|
||||
#define HTTPC_ERROR_SEND_HEADER_FAILED (-2)
|
||||
#define HTTPC_ERROR_SEND_PAYLOAD_FAILED (-3)
|
||||
#define HTTPC_ERROR_NOT_CONNECTED (-4)
|
||||
#define HTTPC_ERROR_CONNECTION_LOST (-5)
|
||||
#define HTTPC_ERROR_NO_STREAM (-6)
|
||||
#define HTTPC_ERROR_NO_HTTP_SERVER (-7)
|
||||
#define HTTPC_ERROR_TOO_LESS_RAM (-8)
|
||||
#define HTTPC_ERROR_ENCODING (-9)
|
||||
#define HTTPC_ERROR_STREAM_WRITE (-10)
|
||||
#define HTTPC_ERROR_READ_TIMEOUT (-11)
|
||||
|
||||
/// size for the stream handling
|
||||
#define HTTP_TCP_BUFFER_SIZE (1460)
|
||||
|
||||
/// HTTP codes see RFC7231
|
||||
typedef enum {
|
||||
HTTP_CODE_CONTINUE = 100,
|
||||
HTTP_CODE_SWITCHING_PROTOCOLS = 101,
|
||||
HTTP_CODE_PROCESSING = 102,
|
||||
HTTP_CODE_OK = 200,
|
||||
HTTP_CODE_CREATED = 201,
|
||||
HTTP_CODE_ACCEPTED = 202,
|
||||
HTTP_CODE_NON_AUTHORITATIVE_INFORMATION = 203,
|
||||
HTTP_CODE_NO_CONTENT = 204,
|
||||
HTTP_CODE_RESET_CONTENT = 205,
|
||||
HTTP_CODE_PARTIAL_CONTENT = 206,
|
||||
HTTP_CODE_MULTI_STATUS = 207,
|
||||
HTTP_CODE_ALREADY_REPORTED = 208,
|
||||
HTTP_CODE_IM_USED = 226,
|
||||
HTTP_CODE_MULTIPLE_CHOICES = 300,
|
||||
HTTP_CODE_MOVED_PERMANENTLY = 301,
|
||||
HTTP_CODE_FOUND = 302,
|
||||
HTTP_CODE_SEE_OTHER = 303,
|
||||
HTTP_CODE_NOT_MODIFIED = 304,
|
||||
HTTP_CODE_USE_PROXY = 305,
|
||||
HTTP_CODE_TEMPORARY_REDIRECT = 307,
|
||||
HTTP_CODE_PERMANENT_REDIRECT = 308,
|
||||
HTTP_CODE_BAD_REQUEST = 400,
|
||||
HTTP_CODE_UNAUTHORIZED = 401,
|
||||
HTTP_CODE_PAYMENT_REQUIRED = 402,
|
||||
HTTP_CODE_FORBIDDEN = 403,
|
||||
HTTP_CODE_NOT_FOUND = 404,
|
||||
HTTP_CODE_METHOD_NOT_ALLOWED = 405,
|
||||
HTTP_CODE_NOT_ACCEPTABLE = 406,
|
||||
HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||
HTTP_CODE_REQUEST_TIMEOUT = 408,
|
||||
HTTP_CODE_CONFLICT = 409,
|
||||
HTTP_CODE_GONE = 410,
|
||||
HTTP_CODE_LENGTH_REQUIRED = 411,
|
||||
HTTP_CODE_PRECONDITION_FAILED = 412,
|
||||
HTTP_CODE_PAYLOAD_TOO_LARGE = 413,
|
||||
HTTP_CODE_URI_TOO_LONG = 414,
|
||||
HTTP_CODE_UNSUPPORTED_MEDIA_TYPE = 415,
|
||||
HTTP_CODE_RANGE_NOT_SATISFIABLE = 416,
|
||||
HTTP_CODE_EXPECTATION_FAILED = 417,
|
||||
HTTP_CODE_MISDIRECTED_REQUEST = 421,
|
||||
HTTP_CODE_UNPROCESSABLE_ENTITY = 422,
|
||||
HTTP_CODE_LOCKED = 423,
|
||||
HTTP_CODE_FAILED_DEPENDENCY = 424,
|
||||
HTTP_CODE_UPGRADE_REQUIRED = 426,
|
||||
HTTP_CODE_PRECONDITION_REQUIRED = 428,
|
||||
HTTP_CODE_TOO_MANY_REQUESTS = 429,
|
||||
HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
||||
HTTP_CODE_INTERNAL_SERVER_ERROR = 500,
|
||||
HTTP_CODE_NOT_IMPLEMENTED = 501,
|
||||
HTTP_CODE_BAD_GATEWAY = 502,
|
||||
HTTP_CODE_SERVICE_UNAVAILABLE = 503,
|
||||
HTTP_CODE_GATEWAY_TIMEOUT = 504,
|
||||
HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||
HTTP_CODE_VARIANT_ALSO_NEGOTIATES = 506,
|
||||
HTTP_CODE_INSUFFICIENT_STORAGE = 507,
|
||||
HTTP_CODE_LOOP_DETECTED = 508,
|
||||
HTTP_CODE_NOT_EXTENDED = 510,
|
||||
HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||
} t_http_codes;
|
||||
|
||||
typedef enum { HTTPC_TE_IDENTITY, HTTPC_TE_CHUNKED } transferEncoding_t;
|
||||
|
||||
/**
|
||||
* redirection follow mode.
|
||||
* + `HTTPC_DISABLE_FOLLOW_REDIRECTS` - no redirection will be followed.
|
||||
* + `HTTPC_STRICT_FOLLOW_REDIRECTS` - strict RFC2616, only requests using
|
||||
* GET or HEAD methods will be redirected (using the same method),
|
||||
* since the RFC requires end-user confirmation in other cases.
|
||||
* + `HTTPC_FORCE_FOLLOW_REDIRECTS` - all redirections will be followed,
|
||||
* regardless of a used method. New request will use the same method,
|
||||
* and they will include the same body data and the same headers.
|
||||
* In the sense of the RFC, it's just like every redirection is confirmed.
|
||||
*/
|
||||
typedef enum {
|
||||
HTTPC_DISABLE_FOLLOW_REDIRECTS,
|
||||
HTTPC_STRICT_FOLLOW_REDIRECTS,
|
||||
HTTPC_FORCE_FOLLOW_REDIRECTS
|
||||
} followRedirects_t;
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
class TransportTraits;
|
||||
typedef std::unique_ptr<TransportTraits> TransportTraitsPtr;
|
||||
#endif
|
||||
|
||||
// cookie jar support
|
||||
typedef struct {
|
||||
String host; // host which tries to set the cookie
|
||||
time_t date; // timestamp of the response that set the cookie
|
||||
String name;
|
||||
String value;
|
||||
String domain;
|
||||
String path = "";
|
||||
|
||||
struct {
|
||||
time_t date = 0;
|
||||
bool valid = false;
|
||||
} expires;
|
||||
|
||||
struct {
|
||||
time_t duration = 0;
|
||||
bool valid = false;
|
||||
} max_age;
|
||||
|
||||
bool http_only = false;
|
||||
bool secure = false;
|
||||
} Cookie;
|
||||
|
||||
typedef std::vector<Cookie> CookieJar;
|
||||
|
||||
class HTTPClient {
|
||||
public:
|
||||
HTTPClient();
|
||||
~HTTPClient();
|
||||
|
||||
/*
|
||||
* Since both begin() functions take a reference to client as a parameter, you need to
|
||||
* ensure the client object lives the entire time of the HTTPClient
|
||||
*/
|
||||
bool begin(WiFiClient &client, String url);
|
||||
bool begin(WiFiClient &client, String host, uint16_t port, String uri = "/", bool https = false);
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
bool begin(String url);
|
||||
bool begin(String url, const char *CAcert);
|
||||
bool begin(String host, uint16_t port, String uri = "/");
|
||||
bool begin(String host, uint16_t port, String uri, const char *CAcert);
|
||||
bool begin(String host, uint16_t port, String uri, const char *CAcert, const char *cli_cert, const char *cli_key);
|
||||
#endif
|
||||
|
||||
void end(void);
|
||||
|
||||
bool connected(void);
|
||||
|
||||
void setReuse(bool reuse); /// keep-alive
|
||||
void setUserAgent(const String &userAgent);
|
||||
void setAuthorization(const char *user, const char *password);
|
||||
void setAuthorization(const char *auth);
|
||||
void setAuthorizationType(const char *authType);
|
||||
void setConnectTimeout(int32_t connectTimeout);
|
||||
void setTimeout(uint16_t timeout);
|
||||
|
||||
// Redirections
|
||||
void setFollowRedirects(followRedirects_t follow);
|
||||
void setRedirectLimit(uint16_t limit); // max redirects to follow for a single request
|
||||
|
||||
bool setURL(const String &url);
|
||||
void useHTTP10(bool usehttp10 = true);
|
||||
|
||||
/// request handling
|
||||
int GET();
|
||||
int PATCH(uint8_t *payload, size_t size);
|
||||
int PATCH(String payload);
|
||||
int POST(uint8_t *payload, size_t size);
|
||||
int POST(String payload);
|
||||
int PUT(uint8_t *payload, size_t size);
|
||||
int PUT(String payload);
|
||||
int sendRequest(const char *type, String payload);
|
||||
int sendRequest(const char *type, uint8_t *payload = NULL, size_t size = 0);
|
||||
int sendRequest(const char *type, Stream *stream, size_t size = 0);
|
||||
|
||||
void addHeader(const String &name, const String &value, bool first = false, bool replace = true);
|
||||
|
||||
/// Response handling
|
||||
void collectHeaders(const char *headerKeys[], const size_t headerKeysCount);
|
||||
String header(const char *name); // get request header value by name
|
||||
String header(size_t i); // get request header value by number
|
||||
String headerName(size_t i); // get request header name by number
|
||||
int headers(); // get header count
|
||||
bool hasHeader(const char *name); // check if header exists
|
||||
|
||||
int getSize(void);
|
||||
const String &getLocation(void);
|
||||
|
||||
WiFiClient &getStream(void);
|
||||
WiFiClient *getStreamPtr(void);
|
||||
int writeToStream(Stream *stream);
|
||||
// String getString(void);
|
||||
|
||||
static String errorToString(int error);
|
||||
|
||||
/// Cookie jar support
|
||||
void setCookieJar(CookieJar *cookieJar);
|
||||
void resetCookieJar();
|
||||
void clearAllCookies();
|
||||
|
||||
protected:
|
||||
struct RequestArgument {
|
||||
String key;
|
||||
String value;
|
||||
};
|
||||
|
||||
bool beginInternal(String url, const char *expectedProtocol);
|
||||
void disconnect(bool preserveClient = false);
|
||||
void clear();
|
||||
int returnError(int error);
|
||||
bool connect(void);
|
||||
bool sendHeader(const char *type);
|
||||
int handleHeaderResponse();
|
||||
int writeToStreamDataBlock(Stream *stream, int len);
|
||||
|
||||
/// Cookie jar support
|
||||
void setCookie(String date, String headerValue);
|
||||
bool generateCookieString(String *cookieString);
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
TransportTraitsPtr _transportTraits;
|
||||
std::unique_ptr<WiFiClient> _tcpDeprecated;
|
||||
#endif
|
||||
|
||||
WiFiClient *_client = nullptr;
|
||||
|
||||
/// request handling
|
||||
String _host;
|
||||
uint16_t _port = 0;
|
||||
int32_t _connectTimeout = -1;
|
||||
bool _reuse = true;
|
||||
uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT;
|
||||
bool _useHTTP10 = false;
|
||||
bool _secure = false;
|
||||
|
||||
String _uri;
|
||||
String _protocol;
|
||||
String _headers;
|
||||
String _userAgent = "ESP32HTTPClient";
|
||||
String _base64Authorization;
|
||||
String _authorizationType = "Basic";
|
||||
|
||||
/// Response handling
|
||||
RequestArgument *_currentHeaders = nullptr;
|
||||
size_t _headerKeysCount = 0;
|
||||
|
||||
int _returnCode = 0;
|
||||
int _size = -1;
|
||||
bool _canReuse = false;
|
||||
followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS;
|
||||
uint16_t _redirectLimit = 10;
|
||||
String _location;
|
||||
transferEncoding_t _transferEncoding = HTTPC_TE_IDENTITY;
|
||||
|
||||
/// Cookie jar support
|
||||
CookieJar *_cookieJar = nullptr;
|
||||
};
|
||||
|
||||
#endif /* HTTPClient_H_ */
|
||||
@@ -0,0 +1,2 @@
|
||||
DisableFormat: true
|
||||
SortIncludes: Never
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
StreamString.cpp
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "StreamString.h"
|
||||
|
||||
size_t StreamString::write(const uint8_t *data, size_t size) {
|
||||
if(size && data) {
|
||||
concat(data, size);
|
||||
return size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t StreamString::write(uint8_t data) {
|
||||
return concat((char) data);
|
||||
}
|
||||
|
||||
int StreamString::available() {
|
||||
return length();
|
||||
}
|
||||
|
||||
int StreamString::read() {
|
||||
if(length()) {
|
||||
char c = charAt(0);
|
||||
remove(0, 1);
|
||||
return c;
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int StreamString::peek() {
|
||||
if(length()) {
|
||||
char c = charAt(0);
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void StreamString::flush() {
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
StreamString.h
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef STREAMSTRING_H_
|
||||
#define STREAMSTRING_H_
|
||||
|
||||
|
||||
class StreamString: public Stream, public String
|
||||
{
|
||||
public:
|
||||
size_t write(const uint8_t *buffer, size_t size) override;
|
||||
size_t write(uint8_t data) override;
|
||||
|
||||
int available() override;
|
||||
int read() override;
|
||||
int peek() override;
|
||||
void flush() override;
|
||||
};
|
||||
|
||||
|
||||
#endif /* STREAMSTRING_H_ */
|
||||
56
cores/common/arduino/libraries/ext/WebServer/HTTP_Method.h
Normal file
56
cores/common/arduino/libraries/ext/WebServer/HTTP_Method.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
/* Request Methods */
|
||||
#define HTTP_METHOD_MAP(XX) \
|
||||
XX(0, DELETE, DELETE) \
|
||||
XX(1, GET, GET) \
|
||||
XX(2, HEAD, HEAD) \
|
||||
XX(3, POST, POST) \
|
||||
XX(4, PUT, PUT) \
|
||||
/* pathological */ \
|
||||
XX(5, CONNECT, CONNECT) \
|
||||
XX(6, OPTIONS, OPTIONS) \
|
||||
XX(7, TRACE, TRACE) \
|
||||
/* WebDAV */ \
|
||||
XX(8, COPY, COPY) \
|
||||
XX(9, LOCK, LOCK) \
|
||||
XX(10, MKCOL, MKCOL) \
|
||||
XX(11, MOVE, MOVE) \
|
||||
XX(12, PROPFIND, PROPFIND) \
|
||||
XX(13, PROPPATCH, PROPPATCH) \
|
||||
XX(14, SEARCH, SEARCH) \
|
||||
XX(15, UNLOCK, UNLOCK) \
|
||||
XX(16, BIND, BIND) \
|
||||
XX(17, REBIND, REBIND) \
|
||||
XX(18, UNBIND, UNBIND) \
|
||||
XX(19, ACL, ACL) \
|
||||
/* subversion */ \
|
||||
XX(20, REPORT, REPORT) \
|
||||
XX(21, MKACTIVITY, MKACTIVITY) \
|
||||
XX(22, CHECKOUT, CHECKOUT) \
|
||||
XX(23, MERGE, MERGE) \
|
||||
/* upnp */ \
|
||||
XX(24, MSEARCH, M - SEARCH) \
|
||||
XX(25, NOTIFY, NOTIFY) \
|
||||
XX(26, SUBSCRIBE, SUBSCRIBE) \
|
||||
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||
/* RFC-5789 */ \
|
||||
XX(28, PATCH, PATCH) \
|
||||
XX(29, PURGE, PURGE) \
|
||||
/* CalDAV */ \
|
||||
XX(30, MKCALENDAR, MKCALENDAR) \
|
||||
/* RFC-2068, section 19.6.1.2 */ \
|
||||
XX(31, LINK, LINK) \
|
||||
XX(32, UNLINK, UNLINK) \
|
||||
/* icecast */ \
|
||||
XX(33, SOURCE, SOURCE)
|
||||
|
||||
enum http_method {
|
||||
|
||||
#define XX(num, name, string) HTTP_##name = num,
|
||||
HTTP_METHOD_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
typedef enum http_method HTTPMethod;
|
||||
#define HTTP_ANY (HTTPMethod)(255)
|
||||
609
cores/common/arduino/libraries/ext/WebServer/Parsing.cpp
Normal file
609
cores/common/arduino/libraries/ext/WebServer/Parsing.cpp
Normal file
@@ -0,0 +1,609 @@
|
||||
/*
|
||||
Parsing.cpp - HTTP request parsing.
|
||||
|
||||
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
|
||||
#if LT_ARD_HAS_WIFI
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "WebServer.h"
|
||||
#include "WiFiClient.h"
|
||||
#include "WiFiServer.h"
|
||||
#include "detail/mimetable.h"
|
||||
|
||||
#ifndef WEBSERVER_MAX_POST_ARGS
|
||||
#define WEBSERVER_MAX_POST_ARGS 32
|
||||
#endif
|
||||
|
||||
#define __STR(a) #a
|
||||
#define _STR(a) __STR(a)
|
||||
const char *_http_method_str[] = {
|
||||
#define XX(num, name, string) _STR(name),
|
||||
HTTP_METHOD_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
static const char Content_Type[] PROGMEM = "Content-Type";
|
||||
static const char filename[] PROGMEM = "filename";
|
||||
|
||||
static char *readBytesWithTimeout(WiFiClient &client, size_t maxLength, size_t &dataLength, int timeout_ms) {
|
||||
char *buf = nullptr;
|
||||
dataLength = 0;
|
||||
while (dataLength < maxLength) {
|
||||
int tries = timeout_ms;
|
||||
size_t newLength;
|
||||
while (!(newLength = client.available()) && tries--)
|
||||
delay(1);
|
||||
if (!newLength) {
|
||||
break;
|
||||
}
|
||||
if (!buf) {
|
||||
buf = (char *)malloc(newLength + 1);
|
||||
if (!buf) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
char *newBuf = (char *)realloc(buf, dataLength + newLength + 1);
|
||||
if (!newBuf) {
|
||||
free(buf);
|
||||
return nullptr;
|
||||
}
|
||||
buf = newBuf;
|
||||
}
|
||||
client.readBytes(buf + dataLength, newLength);
|
||||
dataLength += newLength;
|
||||
buf[dataLength] = '\0';
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool WebServer::_parseRequest(WiFiClient &client) {
|
||||
// Read the first line of HTTP request
|
||||
String req = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
// reset header value
|
||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||
_currentHeaders[i].value = String();
|
||||
}
|
||||
|
||||
// First line of HTTP request looks like "GET /path HTTP/1.1"
|
||||
// Retrieve the "/path" part by finding the spaces
|
||||
int addr_start = req.indexOf(' ');
|
||||
int addr_end = req.indexOf(' ', addr_start + 1);
|
||||
if (addr_start == -1 || addr_end == -1) {
|
||||
log_e("Invalid request: %s", req.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
String methodStr = req.substring(0, addr_start);
|
||||
String url = req.substring(addr_start + 1, addr_end);
|
||||
String versionEnd = req.substring(addr_end + 8);
|
||||
_currentVersion = atoi(versionEnd.c_str());
|
||||
String searchStr = "";
|
||||
int hasSearch = url.indexOf('?');
|
||||
if (hasSearch != -1) {
|
||||
searchStr = url.substring(hasSearch + 1);
|
||||
url = url.substring(0, hasSearch);
|
||||
}
|
||||
_currentUri = url;
|
||||
_chunked = false;
|
||||
|
||||
HTTPMethod method = HTTP_ANY;
|
||||
size_t num_methods = sizeof(_http_method_str) / sizeof(const char *);
|
||||
for (size_t i = 0; i < num_methods; i++) {
|
||||
if (methodStr == _http_method_str[i]) {
|
||||
method = (HTTPMethod)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (method == HTTP_ANY) {
|
||||
log_e("Unknown HTTP Method: %s", methodStr.c_str());
|
||||
return false;
|
||||
}
|
||||
_currentMethod = method;
|
||||
|
||||
log_v("method: %s url: %s search: %s", methodStr.c_str(), url.c_str(), searchStr.c_str());
|
||||
|
||||
// attach handler
|
||||
RequestHandler *handler;
|
||||
for (handler = _firstHandler; handler; handler = handler->next()) {
|
||||
if (handler->canHandle(_currentMethod, _currentUri))
|
||||
break;
|
||||
}
|
||||
_currentHandler = handler;
|
||||
|
||||
String formData;
|
||||
// below is needed only when POST type request
|
||||
if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE) {
|
||||
String boundaryStr;
|
||||
String headerName;
|
||||
String headerValue;
|
||||
bool isForm = false;
|
||||
bool isEncoded = false;
|
||||
uint32_t contentLength = 0;
|
||||
// parse headers
|
||||
while (1) {
|
||||
req = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (req == "")
|
||||
break; // no moar headers
|
||||
int headerDiv = req.indexOf(':');
|
||||
if (headerDiv == -1) {
|
||||
break;
|
||||
}
|
||||
headerName = req.substring(0, headerDiv);
|
||||
headerValue = req.substring(headerDiv + 1);
|
||||
headerValue.trim();
|
||||
_collectHeader(headerName.c_str(), headerValue.c_str());
|
||||
|
||||
log_v("headerName: %s", headerName.c_str());
|
||||
log_v("headerValue: %s", headerValue.c_str());
|
||||
|
||||
if (headerName.equalsIgnoreCase(FPSTR(Content_Type))) {
|
||||
using namespace mime;
|
||||
if (headerValue.startsWith(FPSTR(mimeTable[txt].mimeType))) {
|
||||
isForm = false;
|
||||
} else if (headerValue.startsWith(F("application/x-www-form-urlencoded"))) {
|
||||
isForm = false;
|
||||
isEncoded = true;
|
||||
} else if (headerValue.startsWith(F("multipart/"))) {
|
||||
boundaryStr = headerValue.substring(headerValue.indexOf('=') + 1);
|
||||
boundaryStr.replace("\"", "");
|
||||
isForm = true;
|
||||
}
|
||||
} else if (headerName.equalsIgnoreCase(F("Content-Length"))) {
|
||||
contentLength = headerValue.toInt();
|
||||
} else if (headerName.equalsIgnoreCase(F("Host"))) {
|
||||
_hostHeader = headerValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isForm) {
|
||||
size_t plainLength;
|
||||
char *plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT);
|
||||
if (plainLength < contentLength) {
|
||||
free(plainBuf);
|
||||
return false;
|
||||
}
|
||||
if (contentLength > 0) {
|
||||
if (isEncoded) {
|
||||
// url encoded form
|
||||
if (searchStr != "")
|
||||
searchStr += '&';
|
||||
searchStr += plainBuf;
|
||||
}
|
||||
_parseArguments(searchStr);
|
||||
if (!isEncoded) {
|
||||
// plain post json or other data
|
||||
RequestArgument &arg = _currentArgs[_currentArgCount++];
|
||||
arg.key = F("plain");
|
||||
arg.value = String(plainBuf);
|
||||
}
|
||||
|
||||
log_v("Plain: %s", plainBuf);
|
||||
free(plainBuf);
|
||||
} else {
|
||||
// No content - but we can still have arguments in the URL.
|
||||
_parseArguments(searchStr);
|
||||
}
|
||||
}
|
||||
|
||||
if (isForm) {
|
||||
_parseArguments(searchStr);
|
||||
if (!_parseForm(client, boundaryStr, contentLength)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String headerName;
|
||||
String headerValue;
|
||||
// parse headers
|
||||
while (1) {
|
||||
req = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (req == "")
|
||||
break; // no moar headers
|
||||
int headerDiv = req.indexOf(':');
|
||||
if (headerDiv == -1) {
|
||||
break;
|
||||
}
|
||||
headerName = req.substring(0, headerDiv);
|
||||
headerValue = req.substring(headerDiv + 2);
|
||||
_collectHeader(headerName.c_str(), headerValue.c_str());
|
||||
|
||||
log_v("headerName: %s", headerName.c_str());
|
||||
log_v("headerValue: %s", headerValue.c_str());
|
||||
|
||||
if (headerName.equalsIgnoreCase("Host")) {
|
||||
_hostHeader = headerValue;
|
||||
}
|
||||
}
|
||||
_parseArguments(searchStr);
|
||||
}
|
||||
client.flush();
|
||||
|
||||
log_v("Request: %s", url.c_str());
|
||||
log_v(" Arguments: %s", searchStr.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebServer::_collectHeader(const char *headerName, const char *headerValue) {
|
||||
for (int i = 0; i < _headerKeysCount; i++) {
|
||||
if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) {
|
||||
_currentHeaders[i].value = headerValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WebServer::_parseArguments(String data) {
|
||||
log_v("args: %s", data.c_str());
|
||||
if (_currentArgs)
|
||||
delete[] _currentArgs;
|
||||
_currentArgs = 0;
|
||||
if (data.length() == 0) {
|
||||
_currentArgCount = 0;
|
||||
_currentArgs = new RequestArgument[1];
|
||||
return;
|
||||
}
|
||||
_currentArgCount = 1;
|
||||
|
||||
for (int i = 0; i < (int)data.length();) {
|
||||
i = data.indexOf('&', i);
|
||||
if (i == -1)
|
||||
break;
|
||||
++i;
|
||||
++_currentArgCount;
|
||||
}
|
||||
log_v("args count: %d", _currentArgCount);
|
||||
|
||||
_currentArgs = new RequestArgument[_currentArgCount + 1];
|
||||
int pos = 0;
|
||||
int iarg;
|
||||
for (iarg = 0; iarg < _currentArgCount;) {
|
||||
int equal_sign_index = data.indexOf('=', pos);
|
||||
int next_arg_index = data.indexOf('&', pos);
|
||||
log_v("pos %d =@%d &@%d", pos, equal_sign_index, next_arg_index);
|
||||
if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) {
|
||||
log_e("arg missing value: %d", iarg);
|
||||
if (next_arg_index == -1)
|
||||
break;
|
||||
pos = next_arg_index + 1;
|
||||
continue;
|
||||
}
|
||||
RequestArgument &arg = _currentArgs[iarg];
|
||||
arg.key = urlDecode(data.substring(pos, equal_sign_index));
|
||||
arg.value = urlDecode(data.substring(equal_sign_index + 1, next_arg_index));
|
||||
log_v("arg %d key: %s value: %s", iarg, arg.key.c_str(), arg.value.c_str());
|
||||
++iarg;
|
||||
if (next_arg_index == -1)
|
||||
break;
|
||||
pos = next_arg_index + 1;
|
||||
}
|
||||
_currentArgCount = iarg;
|
||||
log_v("args count: %d", _currentArgCount);
|
||||
}
|
||||
|
||||
void WebServer::_uploadWriteByte(uint8_t b) {
|
||||
if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN) {
|
||||
if (_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
_currentUpload->totalSize += _currentUpload->currentSize;
|
||||
_currentUpload->currentSize = 0;
|
||||
}
|
||||
_currentUpload->buf[_currentUpload->currentSize++] = b;
|
||||
}
|
||||
|
||||
int WebServer::_uploadReadByte(WiFiClient &client) {
|
||||
int res = client.read();
|
||||
if (res < 0) {
|
||||
// keep trying until you either read a valid byte or timeout
|
||||
unsigned long startMillis = millis();
|
||||
long timeoutIntervalMillis = client.getTimeout();
|
||||
boolean timedOut = false;
|
||||
for (;;) {
|
||||
if (!client.connected())
|
||||
return -1;
|
||||
// loosely modeled after blinkWithoutDelay pattern
|
||||
while (!timedOut && !client.available() && client.connected()) {
|
||||
delay(2);
|
||||
timedOut = millis() - startMillis >= timeoutIntervalMillis;
|
||||
}
|
||||
|
||||
res = client.read();
|
||||
if (res >= 0) {
|
||||
return res; // exit on a valid read
|
||||
}
|
||||
// NOTE: it is possible to get here and have all of the following
|
||||
// assertions hold true
|
||||
//
|
||||
// -- client.available() > 0
|
||||
// -- client.connected == true
|
||||
// -- res == -1
|
||||
//
|
||||
// a simple retry strategy overcomes this which is to say the
|
||||
// assertion is not permanent, but the reason that this works
|
||||
// is elusive, and possibly indicative of a more subtle underlying
|
||||
// issue
|
||||
|
||||
timedOut = millis() - startMillis >= timeoutIntervalMillis;
|
||||
if (timedOut) {
|
||||
return res; // exit on a timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool WebServer::_parseForm(WiFiClient &client, String boundary, uint32_t len) {
|
||||
(void)len;
|
||||
log_v("Parse Form: Boundary: %s Length: %d", boundary.c_str(), len);
|
||||
String line;
|
||||
int retry = 0;
|
||||
do {
|
||||
line = client.readStringUntil('\r');
|
||||
++retry;
|
||||
} while (line.length() == 0 && retry < 3);
|
||||
|
||||
client.readStringUntil('\n');
|
||||
// start reading the form
|
||||
if (line == ("--" + boundary)) {
|
||||
if (_postArgs)
|
||||
delete[] _postArgs;
|
||||
_postArgs = new RequestArgument[WEBSERVER_MAX_POST_ARGS];
|
||||
_postArgsLen = 0;
|
||||
while (1) {
|
||||
String argName;
|
||||
String argValue;
|
||||
String argType;
|
||||
String argFilename;
|
||||
bool argIsFile = false;
|
||||
|
||||
line = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))) {
|
||||
int nameStart = line.indexOf('=');
|
||||
if (nameStart != -1) {
|
||||
argName = line.substring(nameStart + 2);
|
||||
nameStart = argName.indexOf('=');
|
||||
if (nameStart == -1) {
|
||||
argName = argName.substring(0, argName.length() - 1);
|
||||
} else {
|
||||
argFilename = argName.substring(nameStart + 2, argName.length() - 1);
|
||||
argName = argName.substring(0, argName.indexOf('"'));
|
||||
argIsFile = true;
|
||||
log_v("PostArg FileName: %s", argFilename.c_str());
|
||||
// use GET to set the filename if uploading using blob
|
||||
if (argFilename == F("blob") && hasArg(FPSTR(filename)))
|
||||
argFilename = arg(FPSTR(filename));
|
||||
}
|
||||
log_v("PostArg Name: %s", argName.c_str());
|
||||
using namespace mime;
|
||||
argType = FPSTR(mimeTable[txt].mimeType);
|
||||
line = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))) {
|
||||
argType = line.substring(line.indexOf(':') + 2);
|
||||
// skip next line
|
||||
client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
}
|
||||
log_v("PostArg Type: %s", argType.c_str());
|
||||
if (!argIsFile) {
|
||||
while (1) {
|
||||
line = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (line.startsWith("--" + boundary))
|
||||
break;
|
||||
if (argValue.length() > 0)
|
||||
argValue += "\n";
|
||||
argValue += line;
|
||||
}
|
||||
log_v("PostArg Value: %s", argValue.c_str());
|
||||
|
||||
RequestArgument &arg = _postArgs[_postArgsLen++];
|
||||
arg.key = argName;
|
||||
arg.value = argValue;
|
||||
|
||||
if (line == ("--" + boundary + "--")) {
|
||||
log_v("Done Parsing POST");
|
||||
break;
|
||||
} else if (_postArgsLen >= WEBSERVER_MAX_POST_ARGS) {
|
||||
log_e("Too many PostArgs (max: %d) in request.", WEBSERVER_MAX_POST_ARGS);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
_currentUpload.reset(new HTTPUpload());
|
||||
_currentUpload->status = UPLOAD_FILE_START;
|
||||
_currentUpload->name = argName;
|
||||
_currentUpload->filename = argFilename;
|
||||
_currentUpload->type = argType;
|
||||
_currentUpload->totalSize = 0;
|
||||
_currentUpload->currentSize = 0;
|
||||
log_v(
|
||||
"Start File: %s Type: %s",
|
||||
_currentUpload->filename.c_str(),
|
||||
_currentUpload->type.c_str()
|
||||
);
|
||||
if (_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
_currentUpload->status = UPLOAD_FILE_WRITE;
|
||||
int argByte = _uploadReadByte(client);
|
||||
readfile:
|
||||
|
||||
while (argByte != 0x0D) {
|
||||
if (argByte < 0)
|
||||
return _parseFormUploadAborted();
|
||||
_uploadWriteByte(argByte);
|
||||
argByte = _uploadReadByte(client);
|
||||
}
|
||||
|
||||
argByte = _uploadReadByte(client);
|
||||
if (argByte < 0)
|
||||
return _parseFormUploadAborted();
|
||||
if (argByte == 0x0A) {
|
||||
argByte = _uploadReadByte(client);
|
||||
if (argByte < 0)
|
||||
return _parseFormUploadAborted();
|
||||
if ((char)argByte != '-') {
|
||||
// continue reading the file
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
goto readfile;
|
||||
} else {
|
||||
argByte = _uploadReadByte(client);
|
||||
if (argByte < 0)
|
||||
return _parseFormUploadAborted();
|
||||
if ((char)argByte != '-') {
|
||||
// continue reading the file
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
goto readfile;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t endBuf[boundary.length()];
|
||||
uint32_t i = 0;
|
||||
while (i < boundary.length()) {
|
||||
argByte = _uploadReadByte(client);
|
||||
if (argByte < 0)
|
||||
return _parseFormUploadAborted();
|
||||
if ((char)argByte == 0x0D) {
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
uint32_t j = 0;
|
||||
while (j < i) {
|
||||
_uploadWriteByte(endBuf[j++]);
|
||||
}
|
||||
goto readfile;
|
||||
}
|
||||
endBuf[i++] = (uint8_t)argByte;
|
||||
}
|
||||
|
||||
if (strstr((const char *)endBuf, boundary.c_str()) != NULL) {
|
||||
if (_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
_currentUpload->totalSize += _currentUpload->currentSize;
|
||||
_currentUpload->status = UPLOAD_FILE_END;
|
||||
if (_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
log_v(
|
||||
"End File: %s Type: %s Size: %d",
|
||||
_currentUpload->filename.c_str(),
|
||||
_currentUpload->type.c_str(),
|
||||
_currentUpload->totalSize
|
||||
);
|
||||
line = client.readStringUntil(0x0D);
|
||||
client.readStringUntil(0x0A);
|
||||
if (line == "--") {
|
||||
log_v("Done Parsing POST");
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
uint32_t i = 0;
|
||||
while (i < boundary.length()) {
|
||||
_uploadWriteByte(endBuf[i++]);
|
||||
}
|
||||
argByte = _uploadReadByte(client);
|
||||
goto readfile;
|
||||
}
|
||||
} else {
|
||||
_uploadWriteByte(0x0D);
|
||||
goto readfile;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int iarg;
|
||||
int totalArgs = ((WEBSERVER_MAX_POST_ARGS - _postArgsLen) < _currentArgCount)
|
||||
? (WEBSERVER_MAX_POST_ARGS - _postArgsLen)
|
||||
: _currentArgCount;
|
||||
for (iarg = 0; iarg < totalArgs; iarg++) {
|
||||
RequestArgument &arg = _postArgs[_postArgsLen++];
|
||||
arg.key = _currentArgs[iarg].key;
|
||||
arg.value = _currentArgs[iarg].value;
|
||||
}
|
||||
if (_currentArgs)
|
||||
delete[] _currentArgs;
|
||||
_currentArgs = new RequestArgument[_postArgsLen];
|
||||
for (iarg = 0; iarg < _postArgsLen; iarg++) {
|
||||
RequestArgument &arg = _currentArgs[iarg];
|
||||
arg.key = _postArgs[iarg].key;
|
||||
arg.value = _postArgs[iarg].value;
|
||||
}
|
||||
_currentArgCount = iarg;
|
||||
if (_postArgs) {
|
||||
delete[] _postArgs;
|
||||
_postArgs = nullptr;
|
||||
_postArgsLen = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
log_e("Error: line: %s", line.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
String WebServer::urlDecode(const String &text) {
|
||||
String decoded = "";
|
||||
char temp[] = "0x00";
|
||||
unsigned int len = text.length();
|
||||
unsigned int i = 0;
|
||||
while (i < len) {
|
||||
char decodedChar;
|
||||
char encodedChar = text.charAt(i++);
|
||||
if ((encodedChar == '%') && (i + 1 < len)) {
|
||||
temp[2] = text.charAt(i++);
|
||||
temp[3] = text.charAt(i++);
|
||||
|
||||
decodedChar = strtol(temp, NULL, 16);
|
||||
} else {
|
||||
if (encodedChar == '+') {
|
||||
decodedChar = ' ';
|
||||
} else {
|
||||
decodedChar = encodedChar; // normal ascii char
|
||||
}
|
||||
}
|
||||
decoded += decodedChar;
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
bool WebServer::_parseFormUploadAborted() {
|
||||
_currentUpload->status = UPLOAD_FILE_ABORTED;
|
||||
if (_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // LT_ARD_HAS_WIFI
|
||||
29
cores/common/arduino/libraries/ext/WebServer/Uri.h
Normal file
29
cores/common/arduino/libraries/ext/WebServer/Uri.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <vector>
|
||||
|
||||
class Uri {
|
||||
|
||||
protected:
|
||||
const String _uri;
|
||||
|
||||
public:
|
||||
Uri(const char *uri) : _uri(uri) {}
|
||||
|
||||
Uri(const String &uri) : _uri(uri) {}
|
||||
|
||||
Uri(const __FlashStringHelper *uri) : _uri(String(uri)) {}
|
||||
|
||||
virtual ~Uri() {}
|
||||
|
||||
virtual Uri *clone() const {
|
||||
return new Uri(_uri);
|
||||
};
|
||||
|
||||
virtual void initPathArgs(__attribute__((unused)) std::vector<String> &pathArgs) {}
|
||||
|
||||
virtual bool canHandle(const String &requestUri, __attribute__((unused)) std::vector<String> &pathArgs) {
|
||||
return _uri == requestUri;
|
||||
}
|
||||
};
|
||||
721
cores/common/arduino/libraries/ext/WebServer/WebServer.cpp
Normal file
721
cores/common/arduino/libraries/ext/WebServer/WebServer.cpp
Normal file
@@ -0,0 +1,721 @@
|
||||
/*
|
||||
WebServer.cpp - Dead simple web-server.
|
||||
Supports only one simultaneous client, knows how to handle GET and POST.
|
||||
|
||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
|
||||
#if LT_ARD_HAS_WIFI
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "FS.h"
|
||||
#include "WebServer.h"
|
||||
#include "WiFiClient.h"
|
||||
#include "WiFiServer.h"
|
||||
#include "detail/RequestHandlersImpl.h"
|
||||
// #include "mbedtls/md5.h"
|
||||
#include <libb64/cencode.h>
|
||||
|
||||
static const char AUTHORIZATION_HEADER[] = "Authorization";
|
||||
static const char qop_auth[] PROGMEM = "qop=auth";
|
||||
static const char qop_auth_quoted[] PROGMEM = "qop=\"auth\"";
|
||||
static const char WWW_Authenticate[] = "WWW-Authenticate";
|
||||
static const char Content_Length[] = "Content-Length";
|
||||
|
||||
WebServer::WebServer(IPAddress addr, int port)
|
||||
: _corsEnabled(false), _server(addr, port), _currentMethod(HTTP_ANY), _currentVersion(0), _currentStatus(HC_NONE),
|
||||
_statusChange(0), _nullDelay(true), _currentHandler(nullptr), _firstHandler(nullptr), _lastHandler(nullptr),
|
||||
_currentArgCount(0), _currentArgs(nullptr), _postArgsLen(0), _postArgs(nullptr), _headerKeysCount(0),
|
||||
_currentHeaders(nullptr), _contentLength(0), _chunked(false) {
|
||||
log_v("WebServer::Webserver(addr=%s, port=%d)", ipToString(addr).c_str(), port);
|
||||
}
|
||||
|
||||
WebServer::WebServer(int port)
|
||||
: _corsEnabled(false), _server(port), _currentMethod(HTTP_ANY), _currentVersion(0), _currentStatus(HC_NONE),
|
||||
_statusChange(0), _nullDelay(true), _currentHandler(nullptr), _firstHandler(nullptr), _lastHandler(nullptr),
|
||||
_currentArgCount(0), _currentArgs(nullptr), _postArgsLen(0), _postArgs(nullptr), _headerKeysCount(0),
|
||||
_currentHeaders(nullptr), _contentLength(0), _chunked(false) {
|
||||
log_v("WebServer::Webserver(port=%d)", port);
|
||||
}
|
||||
|
||||
WebServer::~WebServer() {
|
||||
_server.close();
|
||||
if (_currentHeaders)
|
||||
delete[] _currentHeaders;
|
||||
RequestHandler *handler = _firstHandler;
|
||||
while (handler) {
|
||||
RequestHandler *next = handler->next();
|
||||
delete handler;
|
||||
handler = next;
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::begin() {
|
||||
close();
|
||||
_server.begin();
|
||||
_server.setNoDelay(true);
|
||||
}
|
||||
|
||||
void WebServer::begin(uint16_t port) {
|
||||
close();
|
||||
_server.begin(port);
|
||||
_server.setNoDelay(true);
|
||||
}
|
||||
|
||||
String WebServer::_extractParam(String &authReq, const String ¶m, const char delimit) {
|
||||
int _begin = authReq.indexOf(param);
|
||||
if (_begin == -1)
|
||||
return "";
|
||||
return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length()));
|
||||
}
|
||||
|
||||
static String md5str(String &in) {
|
||||
/* char out[33] = {0};
|
||||
mbedtls_md5_context _ctx;
|
||||
uint8_t i;
|
||||
uint8_t *_buf = (uint8_t *)malloc(16);
|
||||
if (_buf == NULL)
|
||||
return String(out);
|
||||
memset(_buf, 0x00, 16);
|
||||
mbedtls_md5_init(&_ctx);
|
||||
mbedtls_md5_starts_ret(&_ctx);
|
||||
mbedtls_md5_update_ret(&_ctx, (const uint8_t *)in.c_str(), in.length());
|
||||
mbedtls_md5_finish_ret(&_ctx, _buf);
|
||||
for (i = 0; i < 16; i++) {
|
||||
sprintf(out + (i * 2), "%02x", _buf[i]);
|
||||
}
|
||||
out[32] = 0;
|
||||
free(_buf);
|
||||
return String(out); */
|
||||
return "";
|
||||
}
|
||||
|
||||
bool WebServer::authenticate(const char *username, const char *password) {
|
||||
if (hasHeader(FPSTR(AUTHORIZATION_HEADER))) {
|
||||
String authReq = header(FPSTR(AUTHORIZATION_HEADER));
|
||||
if (authReq.startsWith(F("Basic"))) {
|
||||
authReq = authReq.substring(6);
|
||||
authReq.trim();
|
||||
char toencodeLen = strlen(username) + strlen(password) + 1;
|
||||
char *toencode = new char[toencodeLen + 1];
|
||||
if (toencode == NULL) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
char *encoded = new char[base64_encode_expected_len(toencodeLen) + 1];
|
||||
if (encoded == NULL) {
|
||||
authReq = "";
|
||||
delete[] toencode;
|
||||
return false;
|
||||
}
|
||||
sprintf(toencode, "%s:%s", username, password);
|
||||
if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equals(encoded)) {
|
||||
authReq = "";
|
||||
delete[] toencode;
|
||||
delete[] encoded;
|
||||
return true;
|
||||
}
|
||||
delete[] toencode;
|
||||
delete[] encoded;
|
||||
} else if (authReq.startsWith(F("Digest"))) {
|
||||
authReq = authReq.substring(7);
|
||||
log_v("%s", authReq.c_str());
|
||||
String _username = _extractParam(authReq, F("username=\""), '\"');
|
||||
if (!_username.length() || _username != String(username)) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
// extracting required parameters for RFC 2069 simpler Digest
|
||||
String _realm = _extractParam(authReq, F("realm=\""), '\"');
|
||||
String _nonce = _extractParam(authReq, F("nonce=\""), '\"');
|
||||
String _uri = _extractParam(authReq, F("uri=\""), '\"');
|
||||
String _response = _extractParam(authReq, F("response=\""), '\"');
|
||||
String _opaque = _extractParam(authReq, F("opaque=\""), '\"');
|
||||
|
||||
if ((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) ||
|
||||
(!_opaque.length())) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
if ((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm)) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
// parameters for the RFC 2617 newer Digest
|
||||
String _nc, _cnonce;
|
||||
if (authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
|
||||
_nc = _extractParam(authReq, F("nc="), ',');
|
||||
_cnonce = _extractParam(authReq, F("cnonce=\""), '\"');
|
||||
}
|
||||
String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password));
|
||||
log_v("Hash of user:realm:pass=%s", _H1.c_str());
|
||||
String _H2 = "";
|
||||
if (_currentMethod == HTTP_GET) {
|
||||
_H2 = md5str(String(F("GET:")) + _uri);
|
||||
} else if (_currentMethod == HTTP_POST) {
|
||||
_H2 = md5str(String(F("POST:")) + _uri);
|
||||
} else if (_currentMethod == HTTP_PUT) {
|
||||
_H2 = md5str(String(F("PUT:")) + _uri);
|
||||
} else if (_currentMethod == HTTP_DELETE) {
|
||||
_H2 = md5str(String(F("DELETE:")) + _uri);
|
||||
} else {
|
||||
_H2 = md5str(String(F("GET:")) + _uri);
|
||||
}
|
||||
log_v("Hash of GET:uri=%s", _H2.c_str());
|
||||
String _responsecheck = "";
|
||||
if (authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
|
||||
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2);
|
||||
} else {
|
||||
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2);
|
||||
}
|
||||
log_v("The Proper response=%s", _responsecheck.c_str());
|
||||
if (_response == _responsecheck) {
|
||||
authReq = "";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
authReq = "";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String WebServer::_getRandomHexString() {
|
||||
char buffer[33]; // buffer to hold 32 Hex Digit + /0
|
||||
int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
sprintf(buffer + (i * 8), "%08x", rand());
|
||||
}
|
||||
return String(buffer);
|
||||
}
|
||||
|
||||
void WebServer::requestAuthentication(HTTPAuthMethod mode, const char *realm, const String &authFailMsg) {
|
||||
if (realm == NULL) {
|
||||
_srealm = String(F("Login Required"));
|
||||
} else {
|
||||
_srealm = String(realm);
|
||||
}
|
||||
if (mode == BASIC_AUTH) {
|
||||
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String(F("\"")));
|
||||
} else {
|
||||
_snonce = _getRandomHexString();
|
||||
_sopaque = _getRandomHexString();
|
||||
sendHeader(
|
||||
String(FPSTR(WWW_Authenticate)),
|
||||
String(F("Digest realm=\"")) + _srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce +
|
||||
String(F("\", opaque=\"")) + _sopaque + String(F("\""))
|
||||
);
|
||||
}
|
||||
using namespace mime;
|
||||
send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg);
|
||||
}
|
||||
|
||||
void WebServer::on(const Uri &uri, WebServer::THandlerFunction handler) {
|
||||
on(uri, HTTP_ANY, handler);
|
||||
}
|
||||
|
||||
void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn) {
|
||||
on(uri, method, fn, _fileUploadHandler);
|
||||
}
|
||||
|
||||
void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) {
|
||||
_addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method));
|
||||
}
|
||||
|
||||
void WebServer::addHandler(RequestHandler *handler) {
|
||||
_addRequestHandler(handler);
|
||||
}
|
||||
|
||||
void WebServer::_addRequestHandler(RequestHandler *handler) {
|
||||
if (!_lastHandler) {
|
||||
_firstHandler = handler;
|
||||
_lastHandler = handler;
|
||||
} else {
|
||||
_lastHandler->next(handler);
|
||||
_lastHandler = handler;
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::serveStatic(const char *uri, FS &fs, const char *path, const char *cache_header) {
|
||||
_addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
|
||||
}
|
||||
|
||||
void WebServer::handleClient() {
|
||||
if (_currentStatus == HC_NONE) {
|
||||
WiFiClient client = _server.available();
|
||||
if (!client) {
|
||||
if (_nullDelay) {
|
||||
delay(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
log_v("New client: client.localIP()=%s", ipToString(client.localIP()).c_str());
|
||||
|
||||
_currentClient = client;
|
||||
_currentStatus = HC_WAIT_READ;
|
||||
_statusChange = millis();
|
||||
}
|
||||
|
||||
bool keepCurrentClient = false;
|
||||
bool callYield = false;
|
||||
|
||||
if (_currentClient.connected()) {
|
||||
switch (_currentStatus) {
|
||||
case HC_NONE:
|
||||
// No-op to avoid C++ compiler warning
|
||||
break;
|
||||
case HC_WAIT_READ:
|
||||
// Wait for data from client to become available
|
||||
if (_currentClient.available()) {
|
||||
if (_parseRequest(_currentClient)) {
|
||||
// because HTTP_MAX_SEND_WAIT is expressed in milliseconds,
|
||||
// it must be divided by 1000
|
||||
_currentClient.setTimeout(HTTP_MAX_SEND_WAIT / 1000);
|
||||
_contentLength = CONTENT_LENGTH_NOT_SET;
|
||||
_handleRequest();
|
||||
|
||||
// Fix for issue with Chrome based browsers:
|
||||
// https://github.com/espressif/arduino-esp32/issues/3652
|
||||
// if (_currentClient.connected()) {
|
||||
// _currentStatus = HC_WAIT_CLOSE;
|
||||
// _statusChange = millis();
|
||||
// keepCurrentClient = true;
|
||||
// }
|
||||
}
|
||||
} else { // !_currentClient.available()
|
||||
if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) {
|
||||
keepCurrentClient = true;
|
||||
}
|
||||
callYield = true;
|
||||
}
|
||||
break;
|
||||
case HC_WAIT_CLOSE:
|
||||
// Wait for client to close the connection
|
||||
if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) {
|
||||
keepCurrentClient = true;
|
||||
callYield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!keepCurrentClient) {
|
||||
_currentClient = WiFiClient();
|
||||
_currentStatus = HC_NONE;
|
||||
_currentUpload.reset();
|
||||
}
|
||||
|
||||
if (callYield) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::close() {
|
||||
_server.close();
|
||||
_currentStatus = HC_NONE;
|
||||
if (!_headerKeysCount)
|
||||
collectHeaders(0, 0);
|
||||
}
|
||||
|
||||
void WebServer::stop() {
|
||||
close();
|
||||
}
|
||||
|
||||
void WebServer::sendHeader(const String &name, const String &value, bool first) {
|
||||
String headerLine = name;
|
||||
headerLine += F(": ");
|
||||
headerLine += value;
|
||||
headerLine += "\r\n";
|
||||
|
||||
if (first) {
|
||||
_responseHeaders = headerLine + _responseHeaders;
|
||||
} else {
|
||||
_responseHeaders += headerLine;
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::setContentLength(const size_t contentLength) {
|
||||
_contentLength = contentLength;
|
||||
}
|
||||
|
||||
void WebServer::enableDelay(boolean value) {
|
||||
_nullDelay = value;
|
||||
}
|
||||
|
||||
void WebServer::enableCORS(boolean value) {
|
||||
_corsEnabled = value;
|
||||
}
|
||||
|
||||
void WebServer::enableCrossOrigin(boolean value) {
|
||||
enableCORS(value);
|
||||
}
|
||||
|
||||
void WebServer::_prepareHeader(String &response, int code, const char *content_type, size_t contentLength) {
|
||||
response = String(F("HTTP/1.")) + String(_currentVersion) + ' ';
|
||||
response += String(code);
|
||||
response += ' ';
|
||||
response += _responseCodeToString(code);
|
||||
response += "\r\n";
|
||||
|
||||
using namespace mime;
|
||||
if (!content_type)
|
||||
content_type = mimeTable[html].mimeType;
|
||||
|
||||
sendHeader(String(F("Content-Type")), String(FPSTR(content_type)), true);
|
||||
if (_contentLength == CONTENT_LENGTH_NOT_SET) {
|
||||
sendHeader(String(FPSTR(Content_Length)), String(contentLength));
|
||||
} else if (_contentLength != CONTENT_LENGTH_UNKNOWN) {
|
||||
sendHeader(String(FPSTR(Content_Length)), String(_contentLength));
|
||||
} else if (_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion) { // HTTP/1.1 or above client
|
||||
// let's do chunked
|
||||
_chunked = true;
|
||||
sendHeader(String(F("Accept-Ranges")), String(F("none")));
|
||||
sendHeader(String(F("Transfer-Encoding")), String(F("chunked")));
|
||||
}
|
||||
if (_corsEnabled) {
|
||||
sendHeader(String(FPSTR("Access-Control-Allow-Origin")), String("*"));
|
||||
sendHeader(String(FPSTR("Access-Control-Allow-Methods")), String("*"));
|
||||
sendHeader(String(FPSTR("Access-Control-Allow-Headers")), String("*"));
|
||||
}
|
||||
sendHeader(String(F("Connection")), String(F("close")));
|
||||
|
||||
response += _responseHeaders;
|
||||
response += "\r\n";
|
||||
_responseHeaders = "";
|
||||
}
|
||||
|
||||
void WebServer::send(int code, const char *content_type, const String &content) {
|
||||
String header;
|
||||
// Can we asume the following?
|
||||
// if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET)
|
||||
// _contentLength = CONTENT_LENGTH_UNKNOWN;
|
||||
_prepareHeader(header, code, content_type, content.length());
|
||||
_currentClientWrite(header.c_str(), header.length());
|
||||
if (content.length())
|
||||
sendContent(content);
|
||||
}
|
||||
|
||||
void WebServer::send_P(int code, PGM_P content_type, PGM_P content) {
|
||||
size_t contentLength = 0;
|
||||
|
||||
if (content != NULL) {
|
||||
contentLength = strlen_P(content);
|
||||
}
|
||||
|
||||
String header;
|
||||
char type[64];
|
||||
strncpy_P(type, (PGM_P)content_type, sizeof(type));
|
||||
_prepareHeader(header, code, (const char *)type, contentLength);
|
||||
_currentClientWrite(header.c_str(), header.length());
|
||||
sendContent_P(content);
|
||||
}
|
||||
|
||||
void WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) {
|
||||
String header;
|
||||
char type[64];
|
||||
strncpy_P(type, (PGM_P)content_type, sizeof(type));
|
||||
_prepareHeader(header, code, (const char *)type, contentLength);
|
||||
sendContent(header);
|
||||
sendContent_P(content, contentLength);
|
||||
}
|
||||
|
||||
void WebServer::send(int code, char *content_type, const String &content) {
|
||||
send(code, (const char *)content_type, content);
|
||||
}
|
||||
|
||||
void WebServer::send(int code, const String &content_type, const String &content) {
|
||||
send(code, (const char *)content_type.c_str(), content);
|
||||
}
|
||||
|
||||
void WebServer::sendContent(const String &content) {
|
||||
sendContent(content.c_str(), content.length());
|
||||
}
|
||||
|
||||
void WebServer::sendContent(const char *content, size_t contentLength) {
|
||||
const char *footer = "\r\n";
|
||||
if (_chunked) {
|
||||
char *chunkSize = (char *)malloc(11);
|
||||
if (chunkSize) {
|
||||
sprintf(chunkSize, "%x%s", contentLength, footer);
|
||||
_currentClientWrite(chunkSize, strlen(chunkSize));
|
||||
free(chunkSize);
|
||||
}
|
||||
}
|
||||
_currentClientWrite(content, contentLength);
|
||||
if (_chunked) {
|
||||
_currentClient.write(footer, 2);
|
||||
if (contentLength == 0) {
|
||||
_chunked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::sendContent_P(PGM_P content) {
|
||||
sendContent_P(content, strlen_P(content));
|
||||
}
|
||||
|
||||
void WebServer::sendContent_P(PGM_P content, size_t size) {
|
||||
const char *footer = "\r\n";
|
||||
if (_chunked) {
|
||||
char *chunkSize = (char *)malloc(11);
|
||||
if (chunkSize) {
|
||||
sprintf(chunkSize, "%x%s", size, footer);
|
||||
_currentClientWrite(chunkSize, strlen(chunkSize));
|
||||
free(chunkSize);
|
||||
}
|
||||
}
|
||||
_currentClientWrite_P(content, size);
|
||||
if (_chunked) {
|
||||
_currentClient.write(footer, 2);
|
||||
if (size == 0) {
|
||||
_chunked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::_streamFileCore(const size_t fileSize, const String &fileName, const String &contentType) {
|
||||
using namespace mime;
|
||||
setContentLength(fileSize);
|
||||
if (fileName.endsWith(String(FPSTR(mimeTable[gz].endsWith))) &&
|
||||
contentType != String(FPSTR(mimeTable[gz].mimeType)) &&
|
||||
contentType != String(FPSTR(mimeTable[none].mimeType))) {
|
||||
sendHeader(F("Content-Encoding"), F("gzip"));
|
||||
}
|
||||
send(200, contentType, "");
|
||||
}
|
||||
|
||||
String WebServer::pathArg(unsigned int i) {
|
||||
if (_currentHandler != nullptr)
|
||||
return _currentHandler->pathArg(i);
|
||||
return "";
|
||||
}
|
||||
|
||||
String WebServer::arg(String name) {
|
||||
for (int j = 0; j < _postArgsLen; ++j) {
|
||||
if (_postArgs[j].key == name)
|
||||
return _postArgs[j].value;
|
||||
}
|
||||
for (int i = 0; i < _currentArgCount; ++i) {
|
||||
if (_currentArgs[i].key == name)
|
||||
return _currentArgs[i].value;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
String WebServer::arg(int i) {
|
||||
if (i < _currentArgCount)
|
||||
return _currentArgs[i].value;
|
||||
return "";
|
||||
}
|
||||
|
||||
String WebServer::argName(int i) {
|
||||
if (i < _currentArgCount)
|
||||
return _currentArgs[i].key;
|
||||
return "";
|
||||
}
|
||||
|
||||
int WebServer::args() {
|
||||
return _currentArgCount;
|
||||
}
|
||||
|
||||
bool WebServer::hasArg(String name) {
|
||||
for (int j = 0; j < _postArgsLen; ++j) {
|
||||
if (_postArgs[j].key == name)
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < _currentArgCount; ++i) {
|
||||
if (_currentArgs[i].key == name)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String WebServer::header(String name) {
|
||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||
if (_currentHeaders[i].key.equalsIgnoreCase(name))
|
||||
return _currentHeaders[i].value;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void WebServer::collectHeaders(const char *headerKeys[], const size_t headerKeysCount) {
|
||||
_headerKeysCount = headerKeysCount + 1;
|
||||
if (_currentHeaders)
|
||||
delete[] _currentHeaders;
|
||||
_currentHeaders = new RequestArgument[_headerKeysCount];
|
||||
_currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER);
|
||||
for (int i = 1; i < _headerKeysCount; i++) {
|
||||
_currentHeaders[i].key = headerKeys[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
String WebServer::header(int i) {
|
||||
if (i < _headerKeysCount)
|
||||
return _currentHeaders[i].value;
|
||||
return "";
|
||||
}
|
||||
|
||||
String WebServer::headerName(int i) {
|
||||
if (i < _headerKeysCount)
|
||||
return _currentHeaders[i].key;
|
||||
return "";
|
||||
}
|
||||
|
||||
int WebServer::headers() {
|
||||
return _headerKeysCount;
|
||||
}
|
||||
|
||||
bool WebServer::hasHeader(String name) {
|
||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||
if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String WebServer::hostHeader() {
|
||||
return _hostHeader;
|
||||
}
|
||||
|
||||
void WebServer::onFileUpload(THandlerFunction fn) {
|
||||
_fileUploadHandler = fn;
|
||||
}
|
||||
|
||||
void WebServer::onNotFound(THandlerFunction fn) {
|
||||
_notFoundHandler = fn;
|
||||
}
|
||||
|
||||
void WebServer::_handleRequest() {
|
||||
bool handled = false;
|
||||
if (!_currentHandler) {
|
||||
log_e("request handler not found");
|
||||
} else {
|
||||
handled = _currentHandler->handle(*this, _currentMethod, _currentUri);
|
||||
if (!handled) {
|
||||
log_e("request handler failed to handle request");
|
||||
}
|
||||
}
|
||||
if (!handled && _notFoundHandler) {
|
||||
_notFoundHandler();
|
||||
handled = true;
|
||||
}
|
||||
if (!handled) {
|
||||
using namespace mime;
|
||||
send(404, String(FPSTR(mimeTable[html].mimeType)), String(F("Not found: ")) + _currentUri);
|
||||
handled = true;
|
||||
}
|
||||
if (handled) {
|
||||
_finalizeResponse();
|
||||
}
|
||||
_currentUri = "";
|
||||
}
|
||||
|
||||
void WebServer::_finalizeResponse() {
|
||||
if (_chunked) {
|
||||
sendContent("");
|
||||
}
|
||||
}
|
||||
|
||||
String WebServer::_responseCodeToString(int code) {
|
||||
switch (code) {
|
||||
case 100:
|
||||
return F("Continue");
|
||||
case 101:
|
||||
return F("Switching Protocols");
|
||||
case 200:
|
||||
return F("OK");
|
||||
case 201:
|
||||
return F("Created");
|
||||
case 202:
|
||||
return F("Accepted");
|
||||
case 203:
|
||||
return F("Non-Authoritative Information");
|
||||
case 204:
|
||||
return F("No Content");
|
||||
case 205:
|
||||
return F("Reset Content");
|
||||
case 206:
|
||||
return F("Partial Content");
|
||||
case 300:
|
||||
return F("Multiple Choices");
|
||||
case 301:
|
||||
return F("Moved Permanently");
|
||||
case 302:
|
||||
return F("Found");
|
||||
case 303:
|
||||
return F("See Other");
|
||||
case 304:
|
||||
return F("Not Modified");
|
||||
case 305:
|
||||
return F("Use Proxy");
|
||||
case 307:
|
||||
return F("Temporary Redirect");
|
||||
case 400:
|
||||
return F("Bad Request");
|
||||
case 401:
|
||||
return F("Unauthorized");
|
||||
case 402:
|
||||
return F("Payment Required");
|
||||
case 403:
|
||||
return F("Forbidden");
|
||||
case 404:
|
||||
return F("Not Found");
|
||||
case 405:
|
||||
return F("Method Not Allowed");
|
||||
case 406:
|
||||
return F("Not Acceptable");
|
||||
case 407:
|
||||
return F("Proxy Authentication Required");
|
||||
case 408:
|
||||
return F("Request Time-out");
|
||||
case 409:
|
||||
return F("Conflict");
|
||||
case 410:
|
||||
return F("Gone");
|
||||
case 411:
|
||||
return F("Length Required");
|
||||
case 412:
|
||||
return F("Precondition Failed");
|
||||
case 413:
|
||||
return F("Request Entity Too Large");
|
||||
case 414:
|
||||
return F("Request-URI Too Large");
|
||||
case 415:
|
||||
return F("Unsupported Media Type");
|
||||
case 416:
|
||||
return F("Requested range not satisfiable");
|
||||
case 417:
|
||||
return F("Expectation Failed");
|
||||
case 500:
|
||||
return F("Internal Server Error");
|
||||
case 501:
|
||||
return F("Not Implemented");
|
||||
case 502:
|
||||
return F("Bad Gateway");
|
||||
case 503:
|
||||
return F("Service Unavailable");
|
||||
case 504:
|
||||
return F("Gateway Time-out");
|
||||
case 505:
|
||||
return F("HTTP Version not supported");
|
||||
default:
|
||||
return F("");
|
||||
}
|
||||
}
|
||||
|
||||
#endif // LT_ARD_HAS_WIFI
|
||||
224
cores/common/arduino/libraries/ext/WebServer/WebServer.h
Normal file
224
cores/common/arduino/libraries/ext/WebServer/WebServer.h
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
WebServer.h - Dead simple web-server.
|
||||
Supports only one simultaneous client, knows how to handle GET and POST.
|
||||
|
||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "HTTP_Method.h"
|
||||
#include "Uri.h"
|
||||
#include <WiFi.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, UPLOAD_FILE_ABORTED };
|
||||
|
||||
enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE };
|
||||
|
||||
enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
|
||||
|
||||
#define HTTP_DOWNLOAD_UNIT_SIZE 1436
|
||||
|
||||
#ifndef HTTP_UPLOAD_BUFLEN
|
||||
#define HTTP_UPLOAD_BUFLEN 1436
|
||||
#endif
|
||||
|
||||
#define HTTP_MAX_DATA_WAIT 5000 // ms to wait for the client to send the request
|
||||
#define HTTP_MAX_POST_WAIT 5000 // ms to wait for POST data to arrive
|
||||
#define HTTP_MAX_SEND_WAIT 5000 // ms to wait for data chunk to be ACKed
|
||||
#define HTTP_MAX_CLOSE_WAIT 2000 // ms to wait for the client to close the connection
|
||||
|
||||
#define CONTENT_LENGTH_UNKNOWN ((size_t)-1)
|
||||
#define CONTENT_LENGTH_NOT_SET ((size_t)-2)
|
||||
|
||||
class WebServer;
|
||||
|
||||
typedef struct {
|
||||
HTTPUploadStatus status;
|
||||
String filename;
|
||||
String name;
|
||||
String type;
|
||||
size_t totalSize; // file size
|
||||
size_t currentSize; // size of data currently in buf
|
||||
uint8_t buf[HTTP_UPLOAD_BUFLEN];
|
||||
} HTTPUpload;
|
||||
|
||||
#include "detail/RequestHandler.h"
|
||||
|
||||
namespace fs {
|
||||
class FS;
|
||||
}
|
||||
|
||||
class WebServer {
|
||||
public:
|
||||
WebServer(IPAddress addr, int port = 80);
|
||||
WebServer(int port = 80);
|
||||
virtual ~WebServer();
|
||||
|
||||
virtual void begin();
|
||||
virtual void begin(uint16_t port);
|
||||
virtual void handleClient();
|
||||
|
||||
virtual void close();
|
||||
void stop();
|
||||
|
||||
bool authenticate(const char *username, const char *password);
|
||||
void requestAuthentication(
|
||||
HTTPAuthMethod mode = BASIC_AUTH, const char *realm = NULL, const String &authFailMsg = String("")
|
||||
);
|
||||
|
||||
typedef std::function<void(void)> THandlerFunction;
|
||||
void on(const Uri &uri, THandlerFunction fn);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); // ufn handles file uploads
|
||||
void addHandler(RequestHandler *handler);
|
||||
void serveStatic(const char *uri, fs::FS &fs, const char *path, const char *cache_header = NULL);
|
||||
void onNotFound(THandlerFunction fn); // called when handler is not assigned
|
||||
void onFileUpload(THandlerFunction ufn); // handle file uploads
|
||||
|
||||
String uri() {
|
||||
return _currentUri;
|
||||
}
|
||||
|
||||
HTTPMethod method() {
|
||||
return _currentMethod;
|
||||
}
|
||||
|
||||
virtual WiFiClient client() {
|
||||
return _currentClient;
|
||||
}
|
||||
|
||||
HTTPUpload &upload() {
|
||||
return *_currentUpload;
|
||||
}
|
||||
|
||||
String pathArg(unsigned int i); // get request path argument by number
|
||||
String arg(String name); // get request argument value by name
|
||||
String arg(int i); // get request argument value by number
|
||||
String argName(int i); // get request argument name by number
|
||||
int args(); // get arguments count
|
||||
bool hasArg(String name); // check if argument exists
|
||||
void collectHeaders(const char *headerKeys[], const size_t headerKeysCount); // set the request headers to collect
|
||||
String header(String name); // get request header value by name
|
||||
String header(int i); // get request header value by number
|
||||
String headerName(int i); // get request header name by number
|
||||
int headers(); // get header count
|
||||
bool hasHeader(String name); // check if header exists
|
||||
|
||||
String hostHeader(); // get request host header if available or empty String if not
|
||||
|
||||
// send response to the client
|
||||
// code - HTTP response code, can be 200 or 404
|
||||
// content_type - HTTP content type, like "text/plain" or "image/png"
|
||||
// content - actual content body
|
||||
void send(int code, const char *content_type = NULL, const String &content = String(""));
|
||||
void send(int code, char *content_type, const String &content);
|
||||
void send(int code, const String &content_type, const String &content);
|
||||
void send_P(int code, PGM_P content_type, PGM_P content);
|
||||
void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength);
|
||||
|
||||
void enableDelay(boolean value);
|
||||
void enableCORS(boolean value = true);
|
||||
void enableCrossOrigin(boolean value = true);
|
||||
|
||||
void setContentLength(const size_t contentLength);
|
||||
void sendHeader(const String &name, const String &value, bool first = false);
|
||||
void sendContent(const String &content);
|
||||
void sendContent(const char *content, size_t contentLength);
|
||||
void sendContent_P(PGM_P content);
|
||||
void sendContent_P(PGM_P content, size_t size);
|
||||
|
||||
static String urlDecode(const String &text);
|
||||
|
||||
template <typename T>
|
||||
size_t streamFile(T &file, const String &contentType) {
|
||||
_streamFileCore(file.size(), file.name(), contentType);
|
||||
return _currentClient.write(file);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual size_t _currentClientWrite(const char *b, size_t l) {
|
||||
return _currentClient.write(b, l);
|
||||
}
|
||||
|
||||
virtual size_t _currentClientWrite_P(PGM_P b, size_t l) {
|
||||
return _currentClient.write_P(b, l);
|
||||
}
|
||||
|
||||
void _addRequestHandler(RequestHandler *handler);
|
||||
void _handleRequest();
|
||||
void _finalizeResponse();
|
||||
bool _parseRequest(WiFiClient &client);
|
||||
void _parseArguments(String data);
|
||||
static String _responseCodeToString(int code);
|
||||
bool _parseForm(WiFiClient &client, String boundary, uint32_t len);
|
||||
bool _parseFormUploadAborted();
|
||||
void _uploadWriteByte(uint8_t b);
|
||||
int _uploadReadByte(WiFiClient &client);
|
||||
void _prepareHeader(String &response, int code, const char *content_type, size_t contentLength);
|
||||
bool _collectHeader(const char *headerName, const char *headerValue);
|
||||
|
||||
void _streamFileCore(const size_t fileSize, const String &fileName, const String &contentType);
|
||||
|
||||
String _getRandomHexString();
|
||||
// for extracting Auth parameters
|
||||
String _extractParam(String &authReq, const String ¶m, const char delimit = '"');
|
||||
|
||||
struct RequestArgument {
|
||||
String key;
|
||||
String value;
|
||||
};
|
||||
|
||||
boolean _corsEnabled;
|
||||
WiFiServer _server;
|
||||
|
||||
WiFiClient _currentClient;
|
||||
HTTPMethod _currentMethod;
|
||||
String _currentUri;
|
||||
uint8_t _currentVersion;
|
||||
HTTPClientStatus _currentStatus;
|
||||
unsigned long _statusChange;
|
||||
boolean _nullDelay;
|
||||
|
||||
RequestHandler *_currentHandler;
|
||||
RequestHandler *_firstHandler;
|
||||
RequestHandler *_lastHandler;
|
||||
THandlerFunction _notFoundHandler;
|
||||
THandlerFunction _fileUploadHandler;
|
||||
|
||||
int _currentArgCount;
|
||||
RequestArgument *_currentArgs;
|
||||
int _postArgsLen;
|
||||
RequestArgument *_postArgs;
|
||||
|
||||
std::unique_ptr<HTTPUpload> _currentUpload;
|
||||
|
||||
int _headerKeysCount;
|
||||
RequestArgument *_currentHeaders;
|
||||
size_t _contentLength;
|
||||
String _responseHeaders;
|
||||
|
||||
String _hostHeader;
|
||||
bool _chunked;
|
||||
|
||||
String _snonce; // Store noance and opaque for future comparison
|
||||
String _sopaque;
|
||||
String _srealm; // Store the Auth realm between Calls
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
|
||||
class RequestHandler {
|
||||
public:
|
||||
virtual ~RequestHandler() {}
|
||||
|
||||
virtual bool canHandle(HTTPMethod method, String uri) {
|
||||
(void)method;
|
||||
(void)uri;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool canUpload(String uri) {
|
||||
(void)uri;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool handle(WebServer &server, HTTPMethod requestMethod, String requestUri) {
|
||||
(void)server;
|
||||
(void)requestMethod;
|
||||
(void)requestUri;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void upload(WebServer &server, String requestUri, HTTPUpload &upload) {
|
||||
(void)server;
|
||||
(void)requestUri;
|
||||
(void)upload;
|
||||
}
|
||||
|
||||
RequestHandler *next() {
|
||||
return _next;
|
||||
}
|
||||
|
||||
void next(RequestHandler *r) {
|
||||
_next = r;
|
||||
}
|
||||
|
||||
private:
|
||||
RequestHandler *_next = nullptr;
|
||||
|
||||
protected:
|
||||
std::vector<String> pathArgs;
|
||||
|
||||
public:
|
||||
const String &pathArg(unsigned int i) {
|
||||
assert(i < pathArgs.size());
|
||||
return pathArgs[i];
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,149 @@
|
||||
#pragma once
|
||||
|
||||
#include "RequestHandler.h"
|
||||
#include "Uri.h"
|
||||
#include "WString.h"
|
||||
#include "mimetable.h"
|
||||
|
||||
using namespace mime;
|
||||
|
||||
class FunctionRequestHandler : public RequestHandler {
|
||||
public:
|
||||
FunctionRequestHandler(
|
||||
WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn, const Uri &uri, HTTPMethod method
|
||||
)
|
||||
: _fn(fn), _ufn(ufn), _uri(uri.clone()), _method(method) {
|
||||
_uri->initPathArgs(pathArgs);
|
||||
}
|
||||
|
||||
~FunctionRequestHandler() {
|
||||
delete _uri;
|
||||
}
|
||||
|
||||
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
|
||||
if (_method != HTTP_ANY && _method != requestMethod)
|
||||
return false;
|
||||
|
||||
return _uri->canHandle(requestUri, pathArgs);
|
||||
}
|
||||
|
||||
bool canUpload(String requestUri) override {
|
||||
if (!_ufn || !canHandle(HTTP_POST, requestUri))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handle(WebServer &server, HTTPMethod requestMethod, String requestUri) override {
|
||||
(void)server;
|
||||
if (!canHandle(requestMethod, requestUri))
|
||||
return false;
|
||||
|
||||
_fn();
|
||||
return true;
|
||||
}
|
||||
|
||||
void upload(WebServer &server, String requestUri, HTTPUpload &upload) override {
|
||||
(void)server;
|
||||
(void)upload;
|
||||
if (canUpload(requestUri))
|
||||
_ufn();
|
||||
}
|
||||
|
||||
protected:
|
||||
WebServer::THandlerFunction _fn;
|
||||
WebServer::THandlerFunction _ufn;
|
||||
Uri *_uri;
|
||||
HTTPMethod _method;
|
||||
};
|
||||
|
||||
class StaticRequestHandler : public RequestHandler {
|
||||
public:
|
||||
StaticRequestHandler(FS &fs, const char *path, const char *uri, const char *cache_header)
|
||||
: _fs(fs), _uri(uri), _path(path), _cache_header(cache_header) {
|
||||
File f = fs.open(path);
|
||||
_isFile = (f && (!f.isDirectory()));
|
||||
log_v(
|
||||
"StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n",
|
||||
path,
|
||||
uri,
|
||||
_isFile,
|
||||
cache_header ? cache_header : ""
|
||||
); // issue 5506 - cache_header can be nullptr
|
||||
_baseUriLength = _uri.length();
|
||||
}
|
||||
|
||||
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
|
||||
if (requestMethod != HTTP_GET)
|
||||
return false;
|
||||
|
||||
if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handle(WebServer &server, HTTPMethod requestMethod, String requestUri) override {
|
||||
if (!canHandle(requestMethod, requestUri))
|
||||
return false;
|
||||
|
||||
log_v("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
|
||||
|
||||
String path(_path);
|
||||
|
||||
if (!_isFile) {
|
||||
// Base URI doesn't point to a file.
|
||||
// If a directory is requested, look for index file.
|
||||
if (requestUri.endsWith("/"))
|
||||
requestUri += "index.htm";
|
||||
|
||||
// Append whatever follows this URI in request to get the file path.
|
||||
path += requestUri.substring(_baseUriLength);
|
||||
}
|
||||
log_v("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
|
||||
|
||||
String contentType = getContentType(path);
|
||||
|
||||
// look for gz file, only if the original specified path is not a gz. So part only works to send gzip via
|
||||
// content encoding when a non compressed is asked for if you point the the path to gzip you will serve the gzip
|
||||
// as content type "application/x-gzip", not text or javascript etc...
|
||||
if (!path.endsWith(FPSTR(mimeTable[gz].endsWith)) && !_fs.exists(path)) {
|
||||
String pathWithGz = path + FPSTR(mimeTable[gz].endsWith);
|
||||
if (_fs.exists(pathWithGz))
|
||||
path += FPSTR(mimeTable[gz].endsWith);
|
||||
}
|
||||
|
||||
File f = _fs.open(path, "r");
|
||||
if (!f || !f.available())
|
||||
return false;
|
||||
|
||||
if (_cache_header.length() != 0)
|
||||
server.sendHeader("Cache-Control", _cache_header);
|
||||
|
||||
server.streamFile(f, contentType);
|
||||
return true;
|
||||
}
|
||||
|
||||
static String getContentType(const String &path) {
|
||||
char buff[sizeof(mimeTable[0].mimeType)];
|
||||
// Check all entries but last one for match, return if found
|
||||
for (size_t i = 0; i < sizeof(mimeTable) / sizeof(mimeTable[0]) - 1; i++) {
|
||||
strcpy_P(buff, mimeTable[i].endsWith);
|
||||
if (path.endsWith(buff)) {
|
||||
strcpy_P(buff, mimeTable[i].mimeType);
|
||||
return String(buff);
|
||||
}
|
||||
}
|
||||
// Fall-through and just return default type
|
||||
strcpy_P(buff, mimeTable[sizeof(mimeTable) / sizeof(mimeTable[0]) - 1].mimeType);
|
||||
return String(buff);
|
||||
}
|
||||
|
||||
protected:
|
||||
FS _fs;
|
||||
String _uri;
|
||||
String _path;
|
||||
String _cache_header;
|
||||
bool _isFile;
|
||||
size_t _baseUriLength;
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "mimetable.h"
|
||||
#include "pgmspace.h"
|
||||
|
||||
namespace mime {
|
||||
|
||||
// Table of extension->MIME strings stored in PROGMEM, needs to be global due to GCC section typing rules
|
||||
const Entry mimeTable[maxType] = {
|
||||
{".html", "text/html" },
|
||||
{".htm", "text/html" },
|
||||
{".css", "text/css" },
|
||||
{".txt", "text/plain" },
|
||||
{".js", "application/javascript" },
|
||||
{".json", "application/json" },
|
||||
{".png", "image/png" },
|
||||
{".gif", "image/gif" },
|
||||
{".jpg", "image/jpeg" },
|
||||
{".ico", "image/x-icon" },
|
||||
{".svg", "image/svg+xml" },
|
||||
{".ttf", "application/x-font-ttf" },
|
||||
{".otf", "application/x-font-opentype" },
|
||||
{".woff", "application/font-woff" },
|
||||
{".woff2", "application/font-woff2" },
|
||||
{".eot", "application/vnd.ms-fontobject"},
|
||||
{".sfnt", "application/font-sfnt" },
|
||||
{".xml", "text/xml" },
|
||||
{".pdf", "application/pdf" },
|
||||
{".zip", "application/zip" },
|
||||
{".gz", "application/x-gzip" },
|
||||
{".appcache", "text/cache-manifest" },
|
||||
{"", "application/octet-stream" }
|
||||
};
|
||||
|
||||
} // namespace mime
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
namespace mime {
|
||||
|
||||
enum type {
|
||||
html,
|
||||
htm,
|
||||
css,
|
||||
txt,
|
||||
js,
|
||||
json,
|
||||
png,
|
||||
gif,
|
||||
jpg,
|
||||
ico,
|
||||
svg,
|
||||
ttf,
|
||||
otf,
|
||||
woff,
|
||||
woff2,
|
||||
eot,
|
||||
sfnt,
|
||||
xml,
|
||||
pdf,
|
||||
zip,
|
||||
gz,
|
||||
appcache,
|
||||
none,
|
||||
maxType
|
||||
};
|
||||
|
||||
struct Entry {
|
||||
const char endsWith[16];
|
||||
const char mimeType[32];
|
||||
};
|
||||
|
||||
extern const Entry mimeTable[maxType];
|
||||
} // namespace mime
|
||||
61
cores/common/arduino/libraries/ext/WebServer/uri/UriBraces.h
Normal file
61
cores/common/arduino/libraries/ext/WebServer/uri/UriBraces.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "Uri.h"
|
||||
|
||||
class UriBraces : public Uri {
|
||||
|
||||
public:
|
||||
explicit UriBraces(const char *uri) : Uri(uri){};
|
||||
explicit UriBraces(const String &uri) : Uri(uri){};
|
||||
|
||||
Uri *clone() const override final {
|
||||
return new UriBraces(_uri);
|
||||
};
|
||||
|
||||
void initPathArgs(std::vector<String> &pathArgs) override final {
|
||||
int numParams = 0, start = 0;
|
||||
do {
|
||||
start = _uri.indexOf("{}", start);
|
||||
if (start > 0) {
|
||||
numParams++;
|
||||
start += 2;
|
||||
}
|
||||
} while (start > 0);
|
||||
pathArgs.resize(numParams);
|
||||
}
|
||||
|
||||
bool canHandle(const String &requestUri, std::vector<String> &pathArgs) override final {
|
||||
if (Uri::canHandle(requestUri, pathArgs))
|
||||
return true;
|
||||
|
||||
size_t uriLength = _uri.length();
|
||||
unsigned int pathArgIndex = 0;
|
||||
unsigned int requestUriIndex = 0;
|
||||
for (unsigned int i = 0; i < uriLength; i++, requestUriIndex++) {
|
||||
char uriChar = _uri[i];
|
||||
char requestUriChar = requestUri[requestUriIndex];
|
||||
|
||||
if (uriChar == requestUriChar)
|
||||
continue;
|
||||
if (uriChar != '{')
|
||||
return false;
|
||||
|
||||
i += 2; // index of char after '}'
|
||||
if (i >= uriLength) {
|
||||
// there is no char after '}'
|
||||
pathArgs[pathArgIndex] = requestUri.substring(requestUriIndex);
|
||||
return pathArgs[pathArgIndex].indexOf("/") == -1; // path argument may not contain a '/'
|
||||
} else {
|
||||
char charEnd = _uri[i];
|
||||
int uriIndex = requestUri.indexOf(charEnd, requestUriIndex);
|
||||
if (uriIndex < 0)
|
||||
return false;
|
||||
pathArgs[pathArgIndex] = requestUri.substring(requestUriIndex, uriIndex);
|
||||
requestUriIndex = (unsigned int)uriIndex;
|
||||
}
|
||||
pathArgIndex++;
|
||||
}
|
||||
|
||||
return requestUriIndex >= requestUri.length();
|
||||
}
|
||||
};
|
||||
19
cores/common/arduino/libraries/ext/WebServer/uri/UriGlob.h
Normal file
19
cores/common/arduino/libraries/ext/WebServer/uri/UriGlob.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "Uri.h"
|
||||
#include <fnmatch.h>
|
||||
|
||||
class UriGlob : public Uri {
|
||||
|
||||
public:
|
||||
explicit UriGlob(const char *uri) : Uri(uri){};
|
||||
explicit UriGlob(const String &uri) : Uri(uri){};
|
||||
|
||||
Uri *clone() const override final {
|
||||
return new UriGlob(_uri);
|
||||
};
|
||||
|
||||
bool canHandle(const String &requestUri, __attribute__((unused)) std::vector<String> &pathArgs) override final {
|
||||
return fnmatch(_uri.c_str(), requestUri.c_str(), 0) == 0;
|
||||
}
|
||||
};
|
||||
41
cores/common/arduino/libraries/ext/WebServer/uri/UriRegex.h
Normal file
41
cores/common/arduino/libraries/ext/WebServer/uri/UriRegex.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "Uri.h"
|
||||
#include <regex>
|
||||
|
||||
class UriRegex : public Uri {
|
||||
|
||||
public:
|
||||
explicit UriRegex(const char *uri) : Uri(uri){};
|
||||
explicit UriRegex(const String &uri) : Uri(uri){};
|
||||
|
||||
Uri *clone() const override final {
|
||||
return new UriRegex(_uri);
|
||||
};
|
||||
|
||||
void initPathArgs(std::vector<String> &pathArgs) override final {
|
||||
std::regex rgx((_uri + "|").c_str());
|
||||
std::smatch matches;
|
||||
std::string s{""};
|
||||
std::regex_search(s, matches, rgx);
|
||||
pathArgs.resize(matches.size() - 1);
|
||||
}
|
||||
|
||||
bool canHandle(const String &requestUri, std::vector<String> &pathArgs) override final {
|
||||
if (Uri::canHandle(requestUri, pathArgs))
|
||||
return true;
|
||||
|
||||
unsigned int pathArgIndex = 0;
|
||||
std::regex rgx(_uri.c_str());
|
||||
std::smatch matches;
|
||||
std::string s(requestUri.c_str());
|
||||
if (std::regex_search(s, matches, rgx)) {
|
||||
for (size_t i = 1; i < matches.size(); ++i) { // skip first
|
||||
pathArgs[pathArgIndex] = String(matches[i].str().c_str());
|
||||
pathArgIndex++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
248
cores/common/arduino/libraries/ext/WiFiMulti/WiFiMulti.cpp
Normal file
248
cores/common/arduino/libraries/ext/WiFiMulti/WiFiMulti.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
/**
|
||||
*
|
||||
* @file WiFiMulti.cpp
|
||||
* @date 16.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the esp8266 core for Arduino environment.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#if LT_ARD_HAS_WIFI
|
||||
|
||||
#include "WiFiMulti.h"
|
||||
#include <Arduino.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
WiFiMulti::WiFiMulti() {}
|
||||
|
||||
WiFiMulti::~WiFiMulti() {
|
||||
for (uint32_t i = 0; i < APlist.size(); i++) {
|
||||
WifiAPlist_t entry = APlist[i];
|
||||
if (entry.ssid) {
|
||||
free(entry.ssid);
|
||||
}
|
||||
if (entry.passphrase) {
|
||||
free(entry.passphrase);
|
||||
}
|
||||
}
|
||||
APlist.clear();
|
||||
}
|
||||
|
||||
bool WiFiMulti::addAP(const char *ssid, const char *passphrase) {
|
||||
WifiAPlist_t newAP;
|
||||
|
||||
if (!ssid || *ssid == 0x00 || strlen(ssid) > 31) {
|
||||
// fail SSID too long or missing!
|
||||
LT_EM(WIFI, "SSID missing or too long");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (passphrase && strlen(passphrase) > 64) {
|
||||
// fail passphrase too long!
|
||||
LT_EM(WIFI, "Passphrase too long");
|
||||
return false;
|
||||
}
|
||||
|
||||
newAP.ssid = strdup(ssid);
|
||||
|
||||
if (!newAP.ssid) {
|
||||
LT_EM(WIFI, "Fail newAP.ssid == 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (passphrase && *passphrase != 0x00) {
|
||||
newAP.passphrase = strdup(passphrase);
|
||||
if (!newAP.passphrase) {
|
||||
LT_EM(WIFI, "Fail newAP.passphrase == 0");
|
||||
free(newAP.ssid);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
newAP.passphrase = NULL;
|
||||
}
|
||||
|
||||
APlist.push_back(newAP);
|
||||
LT_VM(WIFI, "Add SSID: %s", newAP.ssid);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t WiFiMulti::run(uint32_t connectTimeout) {
|
||||
int8_t scanResult;
|
||||
uint8_t status = WiFi.status();
|
||||
if (status == WL_CONNECTED) {
|
||||
for (uint32_t x = 0; x < APlist.size(); x++) {
|
||||
if (WiFi.SSID() == APlist[x].ssid) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
WiFi.disconnect(false);
|
||||
delay(10);
|
||||
status = WiFi.status();
|
||||
}
|
||||
|
||||
scanResult = WiFi.scanNetworks();
|
||||
if (scanResult == WIFI_SCAN_RUNNING) {
|
||||
// scan is running
|
||||
return WL_NO_SSID_AVAIL;
|
||||
} else if (scanResult >= 0) {
|
||||
// scan done analyze
|
||||
WifiAPlist_t bestNetwork{NULL, NULL};
|
||||
int bestNetworkDb = INT_MIN;
|
||||
uint8_t bestBSSID[6];
|
||||
int32_t bestChannel = 0;
|
||||
|
||||
LT_IM(WIFI, "Scan finished");
|
||||
|
||||
if (scanResult == 0) {
|
||||
LT_IM(WIFI, "No networks found");
|
||||
} else {
|
||||
LT_IM(WIFI, "%d networks found", scanResult);
|
||||
for (int8_t i = 0; i < scanResult; ++i) {
|
||||
|
||||
String ssid_scan;
|
||||
int32_t rssi_scan;
|
||||
WiFiAuthMode sec_scan;
|
||||
uint8_t *BSSID_scan;
|
||||
int32_t chan_scan;
|
||||
|
||||
WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, BSSID_scan, chan_scan);
|
||||
|
||||
bool known = false;
|
||||
for (uint32_t x = APlist.size(); x > 0; x--) {
|
||||
WifiAPlist_t entry = APlist[x - 1];
|
||||
|
||||
if (ssid_scan == entry.ssid) { // SSID match
|
||||
known = true;
|
||||
if (rssi_scan > bestNetworkDb) { // best network
|
||||
if (sec_scan == WIFI_AUTH_OPEN ||
|
||||
entry.passphrase) { // check for passphrase if not open wlan
|
||||
bestNetworkDb = rssi_scan;
|
||||
bestChannel = chan_scan;
|
||||
memcpy((void *)&bestNetwork, (void *)&entry, sizeof(bestNetwork));
|
||||
memcpy((void *)&bestBSSID, (void *)BSSID_scan, sizeof(bestBSSID));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (known) {
|
||||
LT_DM(
|
||||
WIFI,
|
||||
" ---> %d: [%d][%02X:%02X:%02X:%02X:%02X:%02X] %s (%d) %c",
|
||||
i,
|
||||
chan_scan,
|
||||
BSSID_scan[0],
|
||||
BSSID_scan[1],
|
||||
BSSID_scan[2],
|
||||
BSSID_scan[3],
|
||||
BSSID_scan[4],
|
||||
BSSID_scan[5],
|
||||
ssid_scan.c_str(),
|
||||
rssi_scan,
|
||||
(sec_scan == WIFI_AUTH_OPEN) ? ' ' : '*'
|
||||
);
|
||||
} else {
|
||||
LT_DM(
|
||||
WIFI,
|
||||
" %d: [%d][%02X:%02X:%02X:%02X:%02X:%02X] %s (%d) %c",
|
||||
i,
|
||||
chan_scan,
|
||||
BSSID_scan[0],
|
||||
BSSID_scan[1],
|
||||
BSSID_scan[2],
|
||||
BSSID_scan[3],
|
||||
BSSID_scan[4],
|
||||
BSSID_scan[5],
|
||||
ssid_scan.c_str(),
|
||||
rssi_scan,
|
||||
(sec_scan == WIFI_AUTH_OPEN) ? ' ' : '*'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clean up ram
|
||||
WiFi.scanDelete();
|
||||
|
||||
if (bestNetwork.ssid) {
|
||||
LT_IM(
|
||||
WIFI,
|
||||
"Connecting to BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channel: %d (%d)",
|
||||
bestBSSID[0],
|
||||
bestBSSID[1],
|
||||
bestBSSID[2],
|
||||
bestBSSID[3],
|
||||
bestBSSID[4],
|
||||
bestBSSID[5],
|
||||
bestNetwork.ssid,
|
||||
bestChannel,
|
||||
bestNetworkDb
|
||||
);
|
||||
|
||||
WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID);
|
||||
status = WiFi.status();
|
||||
|
||||
auto startTime = millis();
|
||||
// wait for connection, fail, or timeout
|
||||
while (status != WL_CONNECTED && status != WL_NO_SSID_AVAIL && status != WL_CONNECT_FAILED &&
|
||||
(millis() - startTime) <= connectTimeout) {
|
||||
delay(10);
|
||||
status = WiFi.status();
|
||||
}
|
||||
|
||||
IPAddress ip;
|
||||
switch (status) {
|
||||
case WL_CONNECTED:
|
||||
LT_IM(WIFI, "Connecting done");
|
||||
LT_DM(WIFI, "SSID: %s", WiFi.SSID().c_str());
|
||||
// TODO fix this after implementing IP format for printf()
|
||||
ip = WiFi.localIP();
|
||||
LT_DM(WIFI, "IP: %u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
|
||||
LT_DM(WIFI, "MAC: %s", WiFi.BSSIDstr().c_str());
|
||||
LT_DM(WIFI, "Channel: %d", WiFi.channel());
|
||||
break;
|
||||
case WL_NO_SSID_AVAIL:
|
||||
LT_EM(WIFI, "Connecting failed; AP not found");
|
||||
break;
|
||||
case WL_CONNECT_FAILED:
|
||||
LT_EM(WIFI, "Connecting failed");
|
||||
break;
|
||||
default:
|
||||
LT_EM(WIFI, "Connecting failed (%d)", status);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LT_EM(WIFI, "No matching network found!");
|
||||
}
|
||||
} else {
|
||||
// start scan
|
||||
LT_VM(WIFI, "Delete old wifi config...");
|
||||
WiFi.disconnect();
|
||||
|
||||
LT_DM(WIFI, "Start scan");
|
||||
// scan wifi async mode
|
||||
WiFi.scanNetworks(true);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#endif // LT_ARD_HAS_WIFI
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user