diff --git a/esp-idf-examples/net_pppos_usb/.gitignore b/esp-idf-examples/net_pppos_usb/.gitignore new file mode 100644 index 00000000..9f8a8368 --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/.gitignore @@ -0,0 +1,16 @@ +nim.cfg +main/nimcache +*.out +.DS_Store + +.tool-versions +*.dSYM/ +/*.png +nimble.develop +nimble.paths +deps +sdkconfig +build/ +sdkconfig.old +.cache/ +managed_components/ diff --git a/esp-idf-examples/net_pppos_usb/AGENTS.md b/esp-idf-examples/net_pppos_usb/AGENTS.md new file mode 100644 index 00000000..fc9c3f9f --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/AGENTS.md @@ -0,0 +1,41 @@ +# Repository Guidelines + +## Project Structure & Module Organization +- main/: Nim sources and component config. Entrypoint: main/main.nim. Generated C lives in main/nimcache/. +- deps/: Vendored Nim dependencies (nesper, unittest2, etc.). Managed by Nimble/Atlas. +- build/: ESP‑IDF CMake/Ninja outputs and flash args. Not for commits. +- CMakeLists.txt: Top‑level ESP‑IDF project file. +- config.nims, nim.cfg: Nim/nesper build configuration and include paths. +- sdkconfig, sdkconfig.defaults, partitions.csv: ESP‑IDF configuration and partition table. + +## Build, Test, and Development Commands +- nim espBuild: Compile Nim and build the ESP‑IDF project (preferred dev loop). +- nim espCompile: Compile Nim to C into main/nimcache/ (no ESP‑IDF build). +- idf.py set-target esp32s3: Ensure target matches config.nims. +- idf.py -p /dev/ttyUSB0 flash monitor: Flash and open serial monitor (adjust port). +- idf.py build: Rebuild from generated C when Nim hasn’t changed. +- Clean: idf.py fullclean && rm -rf main/nimcache + +Environment: export IDF_PATH and source "$IDF_PATH/export.sh" before building. + +## Coding Style & Naming Conventions +- Indentation: 2 spaces; keep lines concise. +- Nim style: camelCase procs, PascalCase types, UPPER_SNAKE constants. Exported symbols end with *. +- Modules: lower_snake filenames (e.g., ppp_client.nim). +- Formatting: nimpretty --indent:2 main/*.nim +- Use nesper APIs where possible; isolate raw ESP‑IDF calls behind small Nim wrappers. + +## Testing Guidelines +- Current repo has no tests. Add Nim tests under tests/ using unittest2 (deps/unittest2). +- Naming: tests/test_*.nim (e.g., tests/test_ppp.nim). +- Run: nim c -r tests/test_ppp.nim. Consider a nimble test task for CI later. + +## Commit & Pull Request Guidelines +- Commits: short, imperative summaries (e.g., "ppp: add link setup"). +- PRs: include purpose, build/flash steps, linked issues, and serial logs/screenshots for hardware changes. +- Scope: keep changes focused; avoid committing build/ outputs or local sdkconfig changes unless intentional. + +## Security & Configuration Tips +- Do not commit credentials or secrets in sdkconfig or sources. +- Match target to hardware (esp32s3 by default in config.nims); update if using another SoC. +- Document required pins/peripherals in PRs when relevant. diff --git a/esp-idf-examples/net_pppos_usb/CMakeLists.txt b/esp-idf-examples/net_pppos_usb/CMakeLists.txt new file mode 100644 index 00000000..345c4f5b --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(net_pppos_usb) diff --git a/esp-idf-examples/net_pppos_usb/README-pppos-dualcdc.md b/esp-idf-examples/net_pppos_usb/README-pppos-dualcdc.md new file mode 100644 index 00000000..2fc38567 --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/README-pppos-dualcdc.md @@ -0,0 +1,32 @@ +PPPoS over USB CDC (dual CDC) + +Overview +- CDC ACM 0: PPP data channel feeding `esp_netif` (PPPoS) +- CDC ACM 1: TinyUSB console for ESP logs and stdio + +Files +- `ppp_connect_usb_dualcdc.c`: C example setting up two CDC interfaces, PPP + console. +- `ppp_connect_usb_dualcdc.nim`: Nim example with equivalent behavior. + +Key Config (menuconfig) +- Component config → TinyUSB → Device + - Enable TinyUSB device stack + - Enable CDC and set `CDC count` to `2` or more + - Adjust CDC RX/TX buffer sizes as needed +- Component config → LWIP + - Enable PPP support (esp-netif PPP) + +Usage Notes +- PPP frames are read from CDC0 and passed to `esp_netif_receive`; TCP/IP replies transmit via CDC0. +- Console/log output is redirected to CDC1 using `esp_tusb_init_console(TINYUSB_CDC_ACM_1)`. +- On the host, two serial devices will appear; attach your PPP dialer to the first, and a terminal to the second for logs. + +API Entrypoints +- C: `example_ppp_connect_dualcdc()` / `example_ppp_shutdown_dualcdc()` +- Nim: `examplePppConnectDualCdc()` / `examplePppShutdownDualCdc()` + +References +- `ppp_connect_usb.c`: single-CDC PPPoS reference (upstream ESP-IDF example) +- `tests/c_headers/idf-usb/device/tusb_console/main/tusb_console_main.c`: TinyUSB console redirection +- `tusb_composite_msc_serialdevice.nim`: example TinyUSB CDC usage from Nim + diff --git a/esp-idf-examples/net_pppos_usb/README.md b/esp-idf-examples/net_pppos_usb/README.md new file mode 100644 index 00000000..30217b38 --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/README.md @@ -0,0 +1,53 @@ +USB Networking Modes (PPP and NCM) + +Overview +- PPPoS dual-CDC (default): + - CDC0 = PPP data channel for `esp_netif`. + - CDC1 = USB console/log output via TinyUSB console. + - Files: `ppp_connect_usb_dualcdc.nim`, `ppp_connect_usb_dualcdc.c`. + +- NCM-as-Ethernet dual-CDC: + - Networking over TinyUSB NCM bound to an Ethernet-like `esp_netif`. + - CDC1 = USB console/log output via TinyUSB console. + - File: `tusb_ncm_eth_dualcdc.nim`. + +Alternate (bridge) example +- `tusb_ncm.nim`: Bridges USB NCM <-> WiFi STA (packets forward both ways). Not used by `main.nim` by default. + +Selecting Mode +- Default (PPPoS + CDC console): build as-is. +- NCM-as-Ethernet + CDC console: pass `-d:RUN_NCM` to Nim build (e.g., in your Atlas task or Nim command). + - `main.nim` imports and runs `usb/tusb_ncm_eth_dualcdc.runUsbNcmEthWithConsole()` when `RUN_NCM` is defined. + +Kconfig Requirements +- Common: + - Enable TinyUSB device stack. + - Enable CDC class; set CDC count accordingly (2 for dual console + data). +- PPP mode: + - LWIP → Enable PPP support. +- NCM-as-Ethernet mode: + - TinyUSB → Enable NET (NCM) device class. + - DHCP client runs on the device (default ETH netif config). Ensure host provides DHCP or set static IP as needed. + +Endpoint-safe defaults +- PPP mode (default): `sdkconfig.defaults` (or `sdkconfig.defaults.ppp`) sets CDC count = 2 and disables NCM. +- NCM mode: use `sdkconfig.defaults.ncm` to enable NCM and set CDC count = 1 (console only). + - Having both NCM and two CDCs may exceed endpoint limits on some targets. + +Host Expectations +- PPPoS: + - Host sees two serial ports. Attach PPP dialer to CDC0; logs on CDC1. +- NCM-as-Ethernet: + - Host enumerates a USB network interface. Device requests IP via DHCP; logs on CDC1. + +Useful Tweaks +- CDC RX/TX buffer size: TinyUSB → CDC → adjust `CONFIG_TINYUSB_CDC_RX_BUFSIZE`/`TX_BUFSIZE`. +- Console over USB: handled via `esp_tusb_init_console(TINYUSB_CDC_ACM_1)` in the dual-CDC samples. + +Entrypoints +- PPP: `examplePppConnectDualCdc()` / `examplePppShutdownDualCdc()`. +- NCM ETH: `runUsbNcmEthWithConsole()` / `stopUsbNcmEthWithConsole()`. + +Switching modes +- PPP (default): build normally. +- NCM: either pass `-d:RUN_NCM` at build time or temporarily copy `sdkconfig.defaults.ncm` to `sdkconfig.defaults` and reconfigure. diff --git a/esp-idf-examples/net_pppos_usb/config.nims b/esp-idf-examples/net_pppos_usb/config.nims new file mode 100644 index 00000000..ad38d83f --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/config.nims @@ -0,0 +1,16 @@ +switch("define", "ESP_IDF_VERSION=5.5") +switch("define", "esp32s3") # or other variants +switch("define", "nimNetSocketExtras") +switch("define", "nimIoselectorEventfd") +# switch("define", "TcpEchoServer") +# switch("define", "UdpEchoServer") +switch("define", "FastRpcServer") + +switch("define", "McuUtilsLoggingLevel:lvlInfo") +# switch("define", "RUN_NCM") + +patchFile("stdlib", "ioselectors_select", "../../src/nesper/networking/ioselectors_select_patch.nim") + +patchFile("stdlib", "cpuinfo", "main/cpuinfo.nim") + +include nesper/build_utils/builds diff --git a/esp-idf-examples/net_pppos_usb/dependencies.lock b/esp-idf-examples/net_pppos_usb/dependencies.lock new file mode 100644 index 00000000..b160018b --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/dependencies.lock @@ -0,0 +1,39 @@ +dependencies: + espressif/esp_tinyusb: + component_hash: 96d232ced7afe1976119b62f7fbf1944a2a78b36228ff6f7b9318394ac1153cc + dependencies: + - name: idf + require: private + version: '>=5.0' + - name: espressif/tinyusb + registry_url: https://components.espressif.com + require: public + version: '>=0.14.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.7.6~1 + espressif/tinyusb: + component_hash: aa65639878f27a44d349044afd9c3fc134a92bd560874fdac1d836019b5c07ca + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32s2 + - esp32s3 + - esp32p4 + version: 0.18.0~4 + idf: + source: + type: idf + version: 5.5.0 +direct_dependencies: +- espressif/esp_tinyusb +- idf +manifest_hash: 48b4bc7b2d1d1a86ac200b84fcb27696d651413d6f3c9dda6ae42be38b4b1ee3 +target: esp32s3 +version: 2.0.0 diff --git a/esp-idf-examples/net_pppos_usb/main/CMakeLists.txt b/esp-idf-examples/net_pppos_usb/main/CMakeLists.txt new file mode 100644 index 00000000..65748e6e --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/main/CMakeLists.txt @@ -0,0 +1,16 @@ +idf_component_register(SRC_DIRS "./nimcache" + INCLUDE_DIRS "" + ) + +idf_build_set_property(C_COMPILE_OPTIONS -Wno-unused-label APPEND) +idf_build_set_property(C_COMPILE_OPTIONS -Wno-discarded-qualifiers APPEND) +idf_build_set_property(C_COMPILE_OPTIONS -Wno-ignored-qualifiers APPEND) +idf_build_set_property(C_COMPILE_OPTIONS -Wno-error=unused-label APPEND) +idf_build_set_property(C_COMPILE_OPTIONS -Wno-error=parentheses APPEND) +idf_build_set_property(C_COMPILE_OPTIONS -Wno-error=implicit-function-declaration APPEND) +idf_build_set_property(C_COMPILE_OPTIONS -Wno-error=maybe-uninitialized APPEND) +idf_build_set_property(C_COMPILE_OPTIONS -Wno-error=nonnull APPEND) +idf_build_set_property(C_COMPILE_OPTIONS -Wno-error=address APPEND) +idf_build_set_property(C_COMPILE_OPTIONS -Wno-unused-but-set-variable APPEND) +idf_build_set_property(C_COMPILE_OPTIONS -Wno-maybe-uninitialized APPEND) +idf_build_set_property(C_COMPILE_OPTIONS -Wno-incompatible-pointer-types APPEND) diff --git a/esp-idf-examples/net_pppos_usb/main/config.nims b/esp-idf-examples/net_pppos_usb/main/config.nims new file mode 100644 index 00000000..e6233552 --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/main/config.nims @@ -0,0 +1,14 @@ +--nomain +--compileOnly +# --define:debug +--define:NimAppMain +--nimcache:nimcache + +--gc:arc +--os:freertos +--cpu:arm +--define:useMalloc +--define:noSignalHandler +# --debugger:native +--threads:on +--tls_emulation:off diff --git a/esp-idf-examples/net_pppos_usb/main/cpuinfo.nim b/esp-idf-examples/net_pppos_usb/main/cpuinfo.nim new file mode 100644 index 00000000..ffe9285f --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/main/cpuinfo.nim @@ -0,0 +1,2 @@ + +proc countProcessors*(): int = 1 diff --git a/esp-idf-examples/net_pppos_usb/main/idf_component.yml b/esp-idf-examples/net_pppos_usb/main/idf_component.yml new file mode 100644 index 00000000..2f216ec5 --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/main/idf_component.yml @@ -0,0 +1,17 @@ +## IDF Component Manager Manifest File +dependencies: + ## Required IDF version + idf: + version: '>=4.1.0' + # # Put list of dependencies here + # # For components maintained by Espressif: + # component: "~1.0.0" + # # For 3rd party components: + # username/component: ">=1.0.0,<2.0.0" + # username2/component2: + # version: "~1.0.0" + # # For transient dependencies `public` flag can be set. + # # `public` flag doesn't have an effect dependencies of the `main` component. + # # All dependencies of `main` are public by default. + # public: true + espressif/esp_tinyusb: ^1.7.6~1 diff --git a/esp-idf-examples/net_pppos_usb/main/main.nim b/esp-idf-examples/net_pppos_usb/main/main.nim new file mode 100644 index 00000000..9ac44cbd --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/main/main.nim @@ -0,0 +1,195 @@ +import std/strutils +import nesper +import nesper/[consts, general, timers] +import nesper/esp/esp_system +import nesper/esp/nvs_flash +import nesper/esp/esp_event +import nesper/esp/esp_vfs_eventfd +import nesper/net_utils + +when defined(RUN_NCM): + import usb/usb_ncm_eth_dualcdc as ncm_eth +else: + import nesper/networking/usb_ppp_dualcdc + +const + TAG*: cstring = "main" + +when defined(RpcServer): + import nesper/servers/rpc/rpcsocket_json + proc setupRpc(rt: var RpcRouter) = + rt.rpc("hello") do(input: string) -> string: + # example: ./rpc_cli --ip:$IP -c:1 '{"method": "hello", "params": ["world"]}' + logi(TAG, "rcp hello: %s", input) + result = "Hello " & input + + rt.rpc("add") do(a: int, b: int) -> int: + echo "ADDING!" + logi(TAG, "rcp add: %s, %s", $a, $b) + # example: ./rpc_cli --ip:$IP -c:1 '{"method": "add", "params": [1, 2]}' + result = a + b + +when defined(FastRpcServer): + import fastrpc/server/fastrpcserver + import fastrpc/server/rpcmethods + + # Define RPC Server # + DefineRpcs(name=exampleRpcs): + + proc add(a: int, b: int): int {.rpc.} = + echo "adding: ", a, " + ", b + result = 1 + a + b + + +when defined(TcpEchoServer): + import std/net + import std/nativesockets + + proc runTcpEcho*() = + # Create the server socket + let server = newSocket(domain=AF_INET6, protocol=IPPROTO_IPV6) + server.setSockOpt(OptReuseAddr, true) + server.bindAddr(Port(5555), address="::") + server.listen() + + echo "Echo server listening on port 5555" + + while true: + try: + # Accept a client connection + echo "Waiting for client connection" + var client: Socket = new(Socket) + server.accept(client) + echo "Client connected" + if client == nil: + loge(TAG, "Error accepting client connection") + continue + + # Echo loop for this client + while true: + try: + echo "Client waiting for data" + let data = client.recv(128) + echo "Client waiting received data: ", data.repr() + if data.len == 0: + break # Client disconnected + client.send(data) # Echo back the data + except Defect, CatchableError: + loge(TAG, "Error receiving from client: %s", getCurrentExceptionMsg()) + + echo "Client disconnected" + client.close() + except Defect, CatchableError: + loge(TAG, "Error receiving from TCP socket: %s", getCurrentExceptionMsg()) + +when defined(UdpEchoServer): + import std/net + + proc runUdpEcho*() = + + # Create UDP socket + let server = newSocket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP) + server.bindAddr(Port(9090), address="::") + + echo "UDP echo server listening on port 9090" + + while true: + try: + var + data: string + address: string + port: Port + + echo "UDP echo server waiting for data" + # Receive data from any client + let res = server.recvFrom(data, 1024, address, port) + + echo "Received from ", address, ":", port, " -> ", data.repr() + + # Echo back to the sender + server.sendTo(address, port, data) + except Defect, CatchableError: + loge(TAG, "Error receiving from UDP socket: %s", getCurrentExceptionMsg()) + +app_main: + logi(TAG, "esp starting ... ") + + # Print chip information + var chipInfo: esp_chip_info_t + esp_chip_info(addr chipInfo) + var features = "" + try: + features &= "This is a " & $chipInfo.model + features &= " chip with " & $chipInfo.cores & " CPU core(s)" + if (chipInfo.features and CHIP_FEATURE_WIFI_BGN) != 0: + features &= "WiFi/" + if (chipInfo.features and CHIP_FEATURE_BT) != 0: + features &= "BT" + if (chipInfo.features and CHIP_FEATURE_BLE) != 0: + features &= "BLE" + if (chipInfo.features and CHIP_FEATURE_IEEE802154) != 0: + features &= "802.15.4 (Zigbee/Thread)" + logi(TAG, "chip features: %s", features.cstring) + except Defect, CatchableError: + logi(TAG, "Error getting chip info") + + let ipAddr = parseIpAddress("fd12:4FE6:B3B5:0E74::2") + + when defined(RUN_NCM): + # Start USB NCM-as-Ethernet with separate CDC console + ncm_eth.runUsbNcmEthWithConsole() + else: + # Basic system setup: NVS, esp-netif, default event loop + setupNetworking() + # Start PPPoS over USB with dual CDC (CDC0=PPP, CDC1=console) + if initPppConnectDualCdc() == ESP_OK: + logi(TAG, "PPPoS connected. Idle loop ...") + + # Demo: add a static IPv6 address to the PPP netif + let netif = pppInterface() + let rc = addIpv6ToNetif(netif, ipAddr) + if rc == ESP_OK: + logi(TAG, "Added IPv6 %s to PPP interface", $ipAddr) + else: + logw(TAG, "Failed to add IPv6 (%d)", rc) + else: + loge(TAG, "PPPoS connect failed") + + when defined(RpcServer): + var rt: RpcRouter = createRpcRouter(4096) + rt.setupRpc() + when defined(FastRpcServer): + try: + let cfg = ESP_VFS_EVENTD_CONFIG_DEFAULT() + logi(TAG, "cfg: %s", repr(cfg)) + check esp_vfs_eventfd_register(addr cfg) + + logi(TAG, "setting up fast rpc router") + let inetAddrs = [ + newInetAddr("::", 5555, Protocol.IPPROTO_UDP), + ] + logi(TAG, "newing fast rpc router") + var rt: FastRpcRouter = newFastRpcRouter() + logi(TAG, "registering rpcs") + rt.registerRpcs(exampleRpcs) + logi(TAG, "newing fast rpc server") + var frpcServer = newFastRpcServer(rt, prefixMsgSize=true, threaded=false) + logi(TAG, "starting fast rpc server") + startSocketServer(inetAddrs, frpcServer) + logi(TAG, "fast rpc server done") + except CatchableError as e: + loge(TAG, "Error starting FastRpcServer: %s, %s", $e.name, $e.msg) + for ste in getCurrentException().getStackTraceEntries(): + loge(TAG, "Error: %s", $ste) + + when defined(TcpEchoServer): + delay(10_000.Millis) + runTcpEcho() + when defined(UdpEchoServer): + delay(10_000.Millis) + runUdpEcho() + + # Keep app alive; clean shutdown not triggered in this minimal example + while true: + logi(TAG, "PPPoS connected. Idle loop ...") + delay(1_000.Millis) diff --git a/esp-idf-examples/net_pppos_usb/main/usb/tusb_composite_msc_serialdevice.nim b/esp-idf-examples/net_pppos_usb/main/usb/tusb_composite_msc_serialdevice.nim new file mode 100644 index 00000000..e808a1b8 --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/main/usb/tusb_composite_msc_serialdevice.nim @@ -0,0 +1,154 @@ +import nesper +import nesper/[general] +import nesper/esp/queue +import nesper/esp/esp_vfs_fat # for wl_handle_t and WL_INVALID_HANDLE +import nesper/components/esp_tinyusb/tinyusb +import nesper/components/esp_tinyusb/tinyusb + +const + TAG* = "usb_composite" + RXBufSz = 64 + BasePath = "/usb" + +## C stdlib and ESP-IDF imports needed by the example + +proc esp_partition_find_first(ptype: uint8; psubtype: uint8; label: cstring): pointer {.cdecl, importc: "esp_partition_find_first", header: "esp_partition.h".} +proc wl_mount(partition: pointer; outHandle: ptr wl_handle_t): esp_err_t {.cdecl, importc: "wl_mount", header: "wear_levelling/wear_levelling.h".} + +## TinyUSB CDC ACM from wrappers +proc tinyusb_cdcacm_read(itf: tinyusb_cdcacm_itf_t; buf: ptr uint8; bufsize: csize_t; rx_size: ptr csize_t): esp_err_t {.cdecl, importc: "tinyusb_cdcacm_read", header: "tusb_cdc_acm.h".} +proc tinyusb_cdcacm_write_queue(itf: tinyusb_cdcacm_itf_t; buf: ptr uint8; size: csize_t): csize_t {.cdecl, importc: "tinyusb_cdcacm_write_queue", header: "tusb_cdc_acm.h".} +proc tinyusb_cdcacm_write_flush(itf: tinyusb_cdcacm_itf_t; timeout_ticks: uint32): esp_err_t {.cdecl, importc: "tinyusb_cdcacm_write_flush", header: "tusb_cdc_acm.h".} + +## TinyUSB MSC storage minimal imports (no emit) +type + tinyusb_msc_spiflash_config_t* {.importc: "tinyusb_msc_spiflash_config_t", header: "tusb_msc_storage.h", bycopy.} = object + wl_handle* {.importc: "wl_handle".}: wl_handle_t + +proc tinyusb_msc_storage_init_spiflash*(cfg: ptr tinyusb_msc_spiflash_config_t): esp_err_t {.cdecl, importc: "tinyusb_msc_storage_init_spiflash", header: "tusb_msc_storage.h".} +proc tinyusb_msc_storage_mount*(base_path: cstring): esp_err_t {.cdecl, importc: "tinyusb_msc_storage_mount", header: "tusb_msc_storage.h".} + +# C stdlib helpers used by the example file operations +type Stat* {.importc: "struct stat", header: "sys/stat.h", bycopy.} = object +proc stat*(path: cstring; st: ptr Stat): cint {.cdecl, importc: "stat", header: "sys/stat.h".} +proc mkdir*(path: cstring; mode: cint): cint {.cdecl, importc: "mkdir", header: "sys/stat.h".} +proc strerror*(errnum: cint): cstring {.cdecl, importc: "strerror", header: "string.h".} +proc fopen*(path, mode: cstring): pointer {.cdecl, importc: "fopen", header: "stdio.h".} +proc fprintf*(f: pointer; fmt: cstring): cint {.varargs, cdecl, importc: "fprintf", header: "stdio.h".} +proc fclose*(f: pointer): cint {.cdecl, importc: "fclose", header: "stdio.h".} +proc fgets*(s: cstring; n: cint; f: pointer): cstring {.cdecl, importc: "fgets", header: "stdio.h".} +proc strchr*(s: cstring; c: cint): cstring {.cdecl, importc: "strchr", header: "string.h".} +var errno* {.importc: "errno", header: "errno.h".}: cint + +const + ESP_PARTITION_TYPE_DATA* = 0x01'u8 + ESP_PARTITION_SUBTYPE_DATA_FAT* = 0x81'u8 + +type + AppMessage = object + buf: array[RXBufSz + 1, uint8] + bufLen: csize_t + itf: uint8 + +var + appQueue: QueueHandle_t + rxBuf {.volatile.}: array[RXBufSz + 1, uint8] + +proc onCdcRx(itf: cint; event: ptr cdcacm_event_t) {.cdecl.} = + var rxSize: csize_t = 0 + let ret = tinyusb_cdcacm_read(tinyusb_cdcacm_itf_t(itf), addr rxBuf[0], RXBufSz.csize_t, addr rxSize) + if ret == ESP_OK: + var msg: AppMessage + msg.bufLen = rxSize + msg.itf = uint8(itf) + if rxSize > 0 and rxSize <= RXBufSz.csize_t: + copyMem(addr msg.buf[0], unsafeAddr rxBuf[0], rxSize) + discard xQueueSendToBack(appQueue, addr msg, 0) + else: + loge(TAG, "Read Error") + +proc onCdcLineState(itf: cint; event: ptr cdcacm_event_t) {.cdecl.} = + logi(TAG, "Line state changed on channel %d", itf) + +proc fileExists(path: cstring): bool = + var s: Stat + result = stat(path, addr s) == 0 + +proc fileOperations() = + let directory = "/usb/esp" + let filePath = "/usb/esp/test.txt" + + var s: Stat + let directoryExists = (stat(directory, addr s) == 0) + if not directoryExists: + if mkdir(directory, 0o775) != 0: + loge(TAG, "mkdir failed: %s", strerror(errno)) + + if not fileExists(filePath): + logi(TAG, "Creating file") + let f = fopen(filePath, "w") + if f == nil: + loge(TAG, "Failed to open file for writing") + return + discard fprintf(f, "Hello World!\n") + discard fclose(f) + + logi(TAG, "Reading file") + let f = fopen(filePath, "r") + if f == nil: + loge(TAG, "Failed to open file for reading") + return + var line: array[64, char] + discard fgets(cast[cstring](addr line[0]), line.len.cint, f) + discard fclose(f) + var p = strchr(cast[cstring](addr line[0]), '\n'.int) + if p != nil: + p[] = '\0' + logi(TAG, "Read from file: '%s'", cast[cstring](addr line[0])) + +proc storageInitSpiFlash(wlOut: var wl_handle_t): esp_err_t = + logi(TAG, "Initializing wear levelling") + let part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, nil) + if part == nil: + loge(TAG, "Failed to find FATFS partition. Check the partition table.") + return ESP_ERR_NOT_FOUND + result = wl_mount(part, addr wlOut) + +proc runUsbCompositeMscSerial*() = + appQueue = xQueueCreate(UBaseType_t(5), UBaseType_t(sizeof(AppMessage))) + assert appQueue != nil + + logi(TAG, "Initializing storage...") + var wl: wl_handle_t = WL_INVALID_HANDLE + check: storageInitSpiFlash(wl) + var mscCfg: tinyusb_msc_spiflash_config_t + mscCfg.wl_handle = wl + check: tinyusb_msc_storage_init_spiflash(addr mscCfg) + check: tinyusb_msc_storage_mount(BasePath) + fileOperations() + + logi(TAG, "USB Composite initialization") + var tusbCfg: tinyusb_config_t + tusbCfg.external_phy = false + check: tinyusb_driver_install(addr tusbCfg) + + var acmCfg: tinyusb_config_cdcacm_t + acmCfg.usb_dev = TINYUSB_USBDEV_0 + acmCfg.cdc_port = TINYUSB_CDC_ACM_0 + acmCfg.rx_unread_buf_sz = RXBufSz + acmCfg.callback_rx = onCdcRx + acmCfg.callback_rx_wanted_char = nil + acmCfg.callback_line_state_changed = onCdcLineState + acmCfg.callback_line_coding_changed = nil + check: tusb_cdc_acm_init(addr acmCfg) + logi(TAG, "USB Composite initialization DONE") + + var msg: AppMessage + while true: + if xQueueReceive(appQueue, addr msg, portMAX_DELAY) == 1: + if msg.bufLen > 0: + logi(TAG, "Data from channel %d", msg.itf.int) + discard tinyusb_cdcacm_write_queue(tinyusb_cdcacm_itf_t(msg.itf.int), addr msg.buf[0], msg.bufLen) + let err = tinyusb_cdcacm_write_flush(tinyusb_cdcacm_itf_t(msg.itf.int), 0) + if err != ESP_OK: + loge(TAG, "CDC ACM write flush error: %s", esp_err_to_name(err)) diff --git a/esp-idf-examples/net_pppos_usb/main/usb/tusb_ncm.nim b/esp-idf-examples/net_pppos_usb/main/usb/tusb_ncm.nim new file mode 100644 index 00000000..1fd9d510 --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/main/usb/tusb_ncm.nim @@ -0,0 +1,110 @@ +import nesper +import nesper/[general, events, wifi] +import nesper/esp/esp_system +import nesper/esp/nvs_flash +import nesper/net_utils +import nesper/components/esp_tinyusb/tinyusb +import nesper/consts + +const + TAG* = "USB_NCM" + WIFI_SSID {.strdefine.}: string = "NOSSID" + WIFI_PASS {.strdefine.}: string = "" + +# TinyUSB NET type/procs (no emit) +type + tinyusb_net_config_t* {.importc: "tinyusb_net_config_t", header: "tinyusb_net.h", bycopy.} = object + mac_addr* {.importc: "mac_addr".}: array[6, uint8] + on_recv_callback* {.importc: "on_recv_callback".}: proc (buffer: pointer; len: uint16; ctx: pointer): esp_err_t {.cdecl.} + free_tx_buffer* {.importc: "free_tx_buffer".}: proc (eb: pointer; ctx: pointer) {.cdecl.} + on_init_callback* {.importc: "on_init_callback".}: proc (ctx: pointer) {.cdecl.} + user_context* {.importc: "user_context".}: pointer + +proc tinyusb_net_init*(dev: tinyusb_usbdev_t; cfg: ptr tinyusb_net_config_t): esp_err_t {.cdecl, importc: "tinyusb_net_init", header: "tinyusb_net.h".} +proc tinyusb_net_send_sync*(buffer: pointer; len: uint16; eb: pointer; timeout_ticks: uint32): esp_err_t {.cdecl, importc: "tinyusb_net_send_sync", header: "tinyusb_net.h".} + +# WiFi internal helpers (signatures only) +proc esp_wifi_internal_tx(ifx: wifi_interface_t; buffer: pointer; len: uint16): esp_err_t {.cdecl, importc: "esp_wifi_internal_tx", header: "esp_private/wifi.h".} +proc esp_wifi_internal_reg_rxcb(ifx: wifi_interface_t; + rx_cb: proc (buffer: pointer; len: uint16; eb: pointer): esp_err_t {.cdecl.}): esp_err_t {.cdecl, importc: "esp_wifi_internal_reg_rxcb", header: "esp_private/wifi.h".} +proc esp_wifi_internal_free_rx_buffer(eb: pointer) {.cdecl, importc: "esp_wifi_internal_free_rx_buffer", header: "esp_private/wifi.h".} + +var sIsWifiConnected {.volatile.}: bool = false + +# USB -> WiFi callback: forward received USB frame to WiFi TX when connected +proc usbRecvCallback(buffer: pointer; len: uint16; ctx: pointer): esp_err_t {.cdecl.} = + let isConnected = cast[ptr bool](ctx) + if isConnected[]: + discard esp_wifi_internal_tx(WIFI_IF_STA, buffer, len) + return ESP_OK + +# Free WiFi RX buffer after USB send completes or on failure +proc wifiPktFree(eb: pointer; ctx: pointer) {.cdecl.} = + esp_wifi_internal_free_rx_buffer(eb) + +# WiFi -> USB path: send WiFi RX packet out over USB NCM +proc pktWifi2Usb(buffer: pointer; len: uint16; eb: pointer): esp_err_t {.cdecl.} = + if tinyusb_net_send_sync(buffer, len, eb, portMAX_DELAY.uint32) != ESP_OK: + esp_wifi_internal_free_rx_buffer(eb) + return ESP_OK + +# WiFi event handler to manage link and RX callback registration +proc wifiEventHandler(arg: pointer; event_base: esp_event_base_t; event_id: int32; event_data: pointer) {.cdecl.} = + let isConnected = cast[ptr bool](arg) + if event_base == WIFI_EVENT: + if event_id == WIFI_EVENT_STA_DISCONNECTED.int32: + logi(TAG, "WiFi STA disconnected") + isConnected[] = false + discard esp_wifi_internal_reg_rxcb(WIFI_IF_STA, nil) + discard esp_wifi_connect() + elif event_id == WIFI_EVENT_STA_CONNECTED.int32: + logi(TAG, "WiFi STA connected") + discard esp_wifi_internal_reg_rxcb(WIFI_IF_STA, pktWifi2Usb) + isConnected[] = true + +proc startWifi(isConnected: ptr bool): esp_err_t = + check: esp_event_loop_create_default() + + var wcfg = wifi_init_config_default() + check: esp_wifi_init(addr wcfg) + check: esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifiEventHandler, cast[pointer](isConnected)) + check: esp_wifi_set_mode(WIFI_MODE_STA) + check: esp_wifi_start() + + var wifiConfig: wifi_config_t + wifiConfig.sta.ssid.setFromString(WIFI_SSID) + wifiConfig.sta.password.setFromString(WIFI_PASS) + check: esp_wifi_set_config(ESP_IF_WIFI_STA, addr wifiConfig) + return esp_wifi_connect() + +proc runUsbNcmBridge*() = + # Initialize NVS (PHY calibration data) + var ret = nvs_flash_init() + if ret == ESP_ERR_NVS_NO_FREE_PAGES or ret == ESP_ERR_NVS_NEW_VERSION_FOUND: + check: nvs_flash_erase() + ret = nvs_flash_init() + check: ret + + # Read MAC before enabling USB, to set MAC string immediately after install + var mac: array[6, uint8] + check: esp_read_mac(addr mac[0], ESP_MAC_WIFI_STA) + logi(TAG, "Network interface HW address: %02x:%02x:%02x:%02x:%02x:%02x", + mac[0].int, mac[1].int, mac[2].int, mac[3].int, mac[4].int, mac[5].int) + + logi(TAG, "USB NCM device initialization") + var tusbCfg: tinyusb_config_t + tusbCfg.external_phy = false + check: tinyusb_driver_install(addr tusbCfg) + + # Initialize TinyUSB NET class with callbacks and MAC + var ncfg: tinyusb_net_config_t + ncfg.on_recv_callback = usbRecvCallback + ncfg.free_tx_buffer = wifiPktFree + ncfg.user_context = addr sIsWifiConnected + for i in 0..5: ncfg.mac_addr[i] = mac[i] + check: tinyusb_net_init(TINYUSB_USBDEV_0, addr ncfg) + + logi(TAG, "WiFi initialization") + check: startWifi(addr sIsWifiConnected) + + logi(TAG, "USB NCM and WiFi initialized and started") diff --git a/esp-idf-examples/net_pppos_usb/main/usb/tusb_ncm_eth.nim b/esp-idf-examples/net_pppos_usb/main/usb/tusb_ncm_eth.nim new file mode 100644 index 00000000..e6caae28 --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/main/usb/tusb_ncm_eth.nim @@ -0,0 +1,95 @@ +import nesper +import nesper/[general, events] +import nesper/esp/esp_system +import nesper/esp/nvs_flash +import nesper/esp/net/esp_netif +import nesper/esp/net/esp_netif_types +import nesper/esp/net/esp_netif_defaults +import nesper/components/esp_tinyusb/tinyusb + +# TinyUSB NET imports +type + tinyusb_net_config_t* {.importc: "tinyusb_net_config_t", header: "tinyusb_net.h", bycopy.} = object + mac_addr* {.importc: "mac_addr".}: array[6, uint8] + on_recv_callback* {.importc: "on_recv_callback".}: proc (buffer: pointer; len: uint16; ctx: pointer): esp_err_t {.cdecl.} + free_tx_buffer* {.importc: "free_tx_buffer".}: proc (eb: pointer; ctx: pointer) {.cdecl.} + on_init_callback* {.importc: "on_init_callback".}: proc (ctx: pointer) {.cdecl.} + user_context* {.importc: "user_context".}: pointer + +proc tinyusb_net_init*(dev: tinyusb_usbdev_t; cfg: ptr tinyusb_net_config_t): esp_err_t {.cdecl, importc: "tinyusb_net_init", header: "tinyusb_net.h".} +proc tinyusb_net_send_sync*(buffer: pointer; len: uint16; eb: pointer; timeout_ticks: uint32): esp_err_t {.cdecl, importc: "tinyusb_net_send_sync", header: "tinyusb_net.h".} + +const TAG* = "USB_NCM_ETH" + +var + sNetif: ptr esp_netif_t + +# RX from USB NCM into esp-netif (L3 stack) +proc ncmRecvToNetif(buffer: pointer; len: uint16; ctx: pointer): esp_err_t {.cdecl.} = + if sNetif != nil and len.uint32 > 0'u32: + discard esp_netif_receive(sNetif, buffer, len.csize_t, nil) + return ESP_OK + +# esp-netif transmit to USB NCM +proc ncmTransmit(h: pointer; buffer: pointer; len: csize_t): esp_err_t {.cdecl.} = + let res = tinyusb_net_send_sync(buffer, uint16(len), nil, portMAX_DELAY.uint32) + return res + +# IP event logging +proc onIpEvent(arg: pointer; event_base: esp_event_base_t; event_id: int32; event_data: pointer) {.cdecl.} = + if event_base == IP_EVENT and event_id == IP_EVENT_GOT_IP6.int32: + let ev6 = cast[ptr ip_event_got_ip6_t](event_data) + if ev6.esp_netif == sNetif: + logi(TAG, "Got IPv6: %s", $ev6.ip6_info.ip) + elif event_base == IP_EVENT and event_id == IP_EVENT_ETH_GOT_IP.int32: + let ev = cast[ptr ip_event_got_ip_t](event_data) + if ev.esp_netif == sNetif: + logi(TAG, "Got IPv4: %s", $ev.ip_info.ip) + +proc runUsbNcmAsEth*() = + # Netif + event loop + check: esp_netif_init() + check: esp_event_loop_create_default() + + # MAC address + var mac: array[6, uint8] + check: esp_read_mac(addr mac[0], ESP_MAC_WIFI_STA) + logi(TAG, "Using MAC: %02x:%02x:%02x:%02x:%02x:%02x", mac[0].int, mac[1].int, mac[2].int, mac[3].int, mac[4].int, mac[5].int) + + # TinyUSB init + var tusbCfg: tinyusb_config_t + tusbCfg.external_phy = false + check: tinyusb_driver_install(addr tusbCfg) + + # TinyUSB NET config + var ncfg: tinyusb_net_config_t + for i in 0..5: ncfg.mac_addr[i] = mac[i] + ncfg.on_recv_callback = ncmRecvToNetif + ncfg.free_tx_buffer = nil + ncfg.on_init_callback = nil + ncfg.user_context = nil + check: tinyusb_net_init(TINYUSB_USBDEV_0, addr ncfg) + + # Create an Ethernet-like esp-netif bound to NCM + var baseCfg = ESP_NETIF_INHERENT_DEFAULT_ETH() + baseCfg.if_desc = "usb_ncm_eth" + var driverCfg: esp_netif_driver_ifconfig_t + driverCfg.handle = cast[pointer](1) + driverCfg.transmit = ncmTransmit + var netifCfg: esp_netif_config_t + netifCfg.base = addr baseCfg + netifCfg.driver = addr driverCfg + netifCfg.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH + + sNetif = esp_netif_new(addr netifCfg) + doAssert sNetif != nil + discard esp_netif_set_mac(sNetif, addr mac[0]) + + # Listen for IP events + check: esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, onIpEvent, nil) + + # Bring up interface; DHCP client should run per default ETH config + esp_netif_action_start(sNetif, nil, 0, nil) + esp_netif_action_connected(sNetif, nil, 0, nil) + + logi(TAG, "USB NCM Ethernet-like interface started") diff --git a/esp-idf-examples/net_pppos_usb/main/usb/tusb_serial_device.nim b/esp-idf-examples/net_pppos_usb/main/usb/tusb_serial_device.nim new file mode 100644 index 00000000..17a89c1b --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/main/usb/tusb_serial_device.nim @@ -0,0 +1,71 @@ +import nesper +import nesper/[general] +import nesper/esp/queue +import nesper/components/esp_tinyusb/tinyusb +import nesper/components/esp_tinyusb/tinyusb + +const + TAG* = "usb_serial" + RXBufSz = 64 # Match example default; align with Kconfig if changed + +# Use tinyusb wrappers from nesper/components/esp_tinyusb + +type + AppMessage = object + buf: array[RXBufSz + 1, uint8] + bufLen: csize_t + itf: uint8 + +var + appQueue: QueueHandle_t + rxBuf {.volatile.}: array[RXBufSz + 1, uint8] + +# RX callback: read bytes and forward to application queue +proc onCdcRx(itf: cint; event: ptr cdcacm_event_t) {.cdecl.} = + var rxSize: csize_t = 0 + let ret = tinyusb_cdcacm_read(tinyusb_cdcacm_itf_t(itf), addr rxBuf[0], RXBufSz.csize_t, addr rxSize) + if ret == ESP_OK: + var msg: AppMessage + msg.bufLen = rxSize + msg.itf = uint8(itf) + if rxSize > 0 and rxSize <= RXBufSz.csize_t: + copyMem(addr msg.buf[0], unsafeAddr rxBuf[0], rxSize) + discard xQueueSendToBack(appQueue, addr msg, 0) + else: + loge(TAG, "Read Error") + +# Line state change callback (log only) +proc onCdcLineState(itf: cint; event: ptr cdcacm_event_t) {.cdecl.} = + logi(TAG, "Line state changed on channel %d", itf) + +proc runUsbSerialDevice*() = + # Create queue for app messages + appQueue = xQueueCreate(UBaseType_t(5), UBaseType_t(sizeof(AppMessage))) + assert appQueue != nil + + logi(TAG, "USB initialization") + var tusbCfg: tinyusb_config_t + tusbCfg.external_phy = false + check: tinyusb_driver_install(addr tusbCfg) + + var acmCfg: tinyusb_config_cdcacm_t + acmCfg.usb_dev = TINYUSB_USBDEV_0 + acmCfg.cdc_port = TINYUSB_CDC_ACM_0 + acmCfg.rx_unread_buf_sz = RXBufSz + acmCfg.callback_rx = onCdcRx + acmCfg.callback_rx_wanted_char = nil + acmCfg.callback_line_state_changed = onCdcLineState + acmCfg.callback_line_coding_changed = nil + check: tusb_cdc_acm_init(addr acmCfg) + + logi(TAG, "USB initialization DONE") + + var msg: AppMessage + while true: + if xQueueReceive(appQueue, addr msg, portMAX_DELAY) == 1: # pdTRUE + if msg.bufLen > 0: + logi(TAG, "Data from channel %d", msg.itf.int) + discard tinyusb_cdcacm_write_queue(tinyusb_cdcacm_itf_t(msg.itf.int), addr msg.buf[0], msg.bufLen) + let err = tinyusb_cdcacm_write_flush(tinyusb_cdcacm_itf_t(msg.itf.int), 0) + if err != ESP_OK: + loge(TAG, "CDC ACM write flush error: %s", esp_err_to_name(err)) diff --git a/esp-idf-examples/net_pppos_usb/main/usb/usb_ncm_eth_dualcdc.nim b/esp-idf-examples/net_pppos_usb/main/usb/usb_ncm_eth_dualcdc.nim new file mode 100644 index 00000000..74fe68b4 --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/main/usb/usb_ncm_eth_dualcdc.nim @@ -0,0 +1,100 @@ +import nesper +import nesper/[general, events] +import nesper/esp/esp_log +import nesper/esp/esp_system +import nesper/esp/nvs_flash +import nesper/esp/net/esp_netif +import nesper/esp/net/esp_netif_types +import nesper/esp/net/esp_netif_defaults +import nesper/components/esp_tinyusb/tinyusb +import nesper/components/esp_tinyusb/tusb_cdc_acm + + +const TAG* = "ncm_eth_dualcdc" + +var + sNetif: ptr esp_netif_t + +# NCM RX -> L3 stack +proc ncmRecvToNetif(buffer: pointer; len: uint16; ctx: pointer): esp_err_t {.cdecl.} = + if sNetif != nil and len.uint32 > 0'u32: + discard esp_netif_receive(sNetif, buffer, len.csize_t, nil) + return ESP_OK + +# L3 TX -> NCM +proc ncmTransmit(h: pointer; buffer: pointer; len: csize_t): esp_err_t {.cdecl.} = + result = tinyusb_net_send_sync(buffer, uint16(len), nil, portMAX_DELAY.uint32) + +# IP event logging +proc onIpEvent(arg: pointer; event_base: esp_event_base_t; event_id: int32; event_data: pointer) {.cdecl.} = + if event_base == IP_EVENT and event_id == IP_EVENT_ETH_GOT_IP.int32: + let ev = cast[ptr ip_event_got_ip_t](event_data) + if ev.esp_netif == sNetif: + logi(TAG, "Got IPv4: %s", $ev.ip_info.ip) + elif event_base == IP_EVENT and event_id == IP_EVENT_GOT_IP6.int32: + let ev6 = cast[ptr ip_event_got_ip6_t](event_data) + if ev6.esp_netif == sNetif: + logi(TAG, "Got IPv6: %s", $ev6.ip6_info.ip) + +proc runUsbNcmEthWithConsole*() = + # Netif + event loop + check: esp_netif_init() + check: esp_event_loop_create_default() + + # Read MAC before enabling USB so we can set it immediately + var mac: array[6, uint8] + check: esp_read_mac(addr mac[0], ESP_MAC_WIFI_STA) + logi(TAG, "Using MAC: %02x:%02x:%02x:%02x:%02x:%02x", mac[0].int, mac[1].int, mac[2].int, mac[3].int, mac[4].int, mac[5].int) + + # TinyUSB device stack + var tusbCfg: tinyusb_config_t + tusbCfg.external_phy = false + check: tinyusb_driver_install(addr tusbCfg) + + # Initialize TinyUSB NET (NCM) as early as possible so host can read MAC string + var ncfg: tinyusb_net_config_t + for i in 0..5: ncfg.mac_addr[i] = mac[i] + ncfg.on_recv_callback = ncmRecvToNetif + ncfg.free_tx_buffer = nil + ncfg.on_init_callback = nil + ncfg.user_context = nil + check: tinyusb_net_init(TINYUSB_USBDEV_0, addr ncfg) + + # CDC ACM for console on CDC1 (leave CDC0 free if desired) + var acmLog: tinyusb_config_cdcacm_t + acmLog.usb_dev = TINYUSB_USBDEV_0 + acmLog.cdc_port = TINYUSB_CDC_ACM_1 + check: tusb_cdc_acm_init(addr acmLog) + check: esp_tusb_init_console(TINYUSB_CDC_ACM_1) + + # Create Ethernet-like netif backed by NCM + var baseCfg = ESP_NETIF_INHERENT_DEFAULT_ETH() + baseCfg.if_desc = "usb_ncm_eth" + var driverCfg: esp_netif_driver_ifconfig_t + driverCfg.handle = cast[pointer](1) + driverCfg.transmit = ncmTransmit + var netifCfg: esp_netif_config_t + netifCfg.base = addr baseCfg + netifCfg.driver = addr driverCfg + netifCfg.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH + + sNetif = esp_netif_new(addr netifCfg) + doAssert sNetif != nil + discard esp_netif_set_mac(sNetif, addr mac[0]) + + # Listen for IP events + check: esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, onIpEvent, nil) + + # Bring up netif; DHCP client will run per default ETH config + esp_netif_action_start(sNetif, nil, 0, nil) + esp_netif_action_connected(sNetif, nil, 0, nil) + + logi(TAG, "USB NCM Ethernet + CDC console started") + +proc stopUsbNcmEthWithConsole*() = + if sNetif != nil: + esp_netif_action_disconnected(sNetif, nil, 0, nil) + esp_netif_action_stop(sNetif, nil, 0, nil) + esp_netif_destroy(sNetif) + sNetif = nil + discard esp_tusb_deinit_console(TINYUSB_CDC_ACM_1) diff --git a/esp-idf-examples/net_pppos_usb/net_pppos_usb.nimble b/esp-idf-examples/net_pppos_usb/net_pppos_usb.nimble new file mode 100644 index 00000000..2ab7d97c --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/net_pppos_usb.nimble @@ -0,0 +1,2 @@ +requires "nesper" + diff --git a/esp-idf-examples/net_pppos_usb/partitions.csv b/esp-idf-examples/net_pppos_usb/partitions.csv new file mode 100644 index 00000000..a1521751 --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/partitions.csv @@ -0,0 +1,6 @@ +# ESP-IDF Partition Table for XIAO ESP32S3 (8MB flash) +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000 +otadata, data, ota, 0xE000, 0x2000 +ota_0, app, ota_0, 0x10000, 0x3F0000 +ota_1, app, ota_1, 0x410000, 0x3F0000 \ No newline at end of file diff --git a/esp-idf-examples/net_pppos_usb/sdkconfig.defaults b/esp-idf-examples/net_pppos_usb/sdkconfig.defaults new file mode 120000 index 00000000..16ada53c --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/sdkconfig.defaults @@ -0,0 +1 @@ +sdkconfig.defaults.ppp \ No newline at end of file diff --git a/esp-idf-examples/net_pppos_usb/sdkconfig.defaults.ncm b/esp-idf-examples/net_pppos_usb/sdkconfig.defaults.ncm new file mode 100644 index 00000000..fbe774f8 --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/sdkconfig.defaults.ncm @@ -0,0 +1,20 @@ +# NCM-as-Ethernet + one CDC for console +CONFIG_IDF_TARGET="esp32s3" +CONFIG_APP_NO_BLOBS=y +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=n +CONFIG_ESP_CONSOLE_SECONDARY_NONE=y + +# Disable PPP +# CONFIG_LWIP_PPP_SUPPORT is not set +CONFIG_LWIP_MULTICAST_PING=y +CONFIG_LWIP_BROADCAST_PING=y + +# TinyUSB +CONFIG_TINYUSB_DEBUG_LEVEL=02 +CONFIG_TINYUSB_CDC_ENABLED=y +CONFIG_TINYUSB_CDC_COUNT=1 +CONFIG_TINYUSB_NET_MODE_NCM=y + diff --git a/esp-idf-examples/net_pppos_usb/sdkconfig.defaults.ppp b/esp-idf-examples/net_pppos_usb/sdkconfig.defaults.ppp new file mode 100644 index 00000000..6c22b462 --- /dev/null +++ b/esp-idf-examples/net_pppos_usb/sdkconfig.defaults.ppp @@ -0,0 +1,36 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32s3" +CONFIG_APP_NO_BLOBS=y +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=n +CONFIG_ESP_CONSOLE_SECONDARY_NONE=y + +CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384 +CONFIG_LWIP_MAX_SOCKETS=16 +CONFIG_LWIP_SO_RCVBUF=y +CONFIG_LWIP_IPV6_AUTOCONFIG=y +CONFIG_LWIP_IPV6_DHCP6=y + +# LWIP PPP +CONFIG_LWIP_PPP_SUPPORT=y +CONFIG_LWIP_PPP_PAP_SUPPORT=y +CONFIG_LWIP_PPP_CHAP_SUPPORT=y +CONFIG_LWIP_ENABLE_LCP_ECHO=y +CONFIG_LWIP_PPP_DEBUG_ON=y +CONFIG_LWIP_MULTICAST_PING=y +CONFIG_LWIP_BROADCAST_PING=y +CONFIG_TINYUSB_DEBUG_LEVEL=02 +CONFIG_TINYUSB_CDC_ENABLED=y +CONFIG_TINYUSB_CDC_COUNT=2 + +CONFIG_LWIP_USE_ONLY_LWIP_SELECT=n +CONFIG_VFS_SUPPORT_SELECT=y + +CONFIG_VFS_SUPPORT_IO=y +CONFIG_VFS_SUPPORT_DIR=y +CONFIG_VFS_SUPPORT_SELECT=y + diff --git a/esp-idf-examples/simplewifi/.gitignore b/esp-idf-examples/simplewifi/.gitignore index da0a7506..132287db 100644 --- a/esp-idf-examples/simplewifi/.gitignore +++ b/esp-idf-examples/simplewifi/.gitignore @@ -3,3 +3,5 @@ src/nimcache/ src/server src/localnim/ .env +sdkconfig +sdkconfig.old diff --git a/esp-idf-examples/simplewifi/config.nims b/esp-idf-examples/simplewifi/config.nims index b7ce864d..6edc6f79 100644 --- a/esp-idf-examples/simplewifi/config.nims +++ b/esp-idf-examples/simplewifi/config.nims @@ -1,26 +1,16 @@ -import os, strutils +switch("define", "ESP_IDF_VERSION=5.5") +switch("define", "esp32s3") # or other variants +switch("define", "nimNetSocketExtras") +switch("define", "nimIoselectorEventfd") -task prepare, "Compile to C code": - mkDir("./main/nimcache") - let nimbasepath = selfExe().splitFile.dir.parentDir / "lib" / "nimbase.h" - let nimcachepath = "main" / "nimcache" - cpFile(nimbasepath, nimcachepath / "nimbase.h") - let params = commandLineParams() - let file = params[1] - let rest = params[2..high(params)].join(" ") - - let wifi_ssid = getEnv("WIFI_SSID") - let wifi_pass = getEnv("WIFI_PASSWORD") +switch("define", "FastRpcServer") - let wifidefs = - if wifi_ssid != "" and wifi_pass != "": - "-d:WIFI_SSID=$1 -d:WIFI_PASSWORD=$2 " % [wifi_ssid, wifi_pass] - else: - "" +switch("define", "McuUtilsLoggingLevel:lvlInfo") - let - cmd = "nim c --os:freertos --cpu:esp --nomain --nimcache:$1 --compileOnly -d:NimAppMain $4 $3 $2 " % - [nimcachepath, file, rest, wifidefs] +patchFile("stdlib", "ioselectors_select", "../../src/nesper/networking/ioselectors_select_patch.nim") - echo("cmd: " & cmd) - exec(cmd) +switch("define", "EXAMPLE_ESP_WIFI_SSID=" & getEnv("WIFI_SSID")) +switch("define", "EXAMPLE_ESP_WIFI_PASS=" & getEnv("WIFI_PASSWORD")) + + +include nesper/build_utils/builds diff --git a/esp-idf-examples/simplewifi/main/CMakeLists.txt b/esp-idf-examples/simplewifi/main/CMakeLists.txt index b144d1b8..12f8ce0d 100644 --- a/esp-idf-examples/simplewifi/main/CMakeLists.txt +++ b/esp-idf-examples/simplewifi/main/CMakeLists.txt @@ -1,7 +1,7 @@ idf_component_register(SRC_DIRS "./nimcache" INCLUDE_DIRS "" - REQUIRES lwip newlib nvs_flash) + ) # set(C_COMPILE_OPTIONS "${C_COMPILE_OPTIONS} -Wno-error=unused-label") # list(APPEND C_COMPILE_OPTIONS "-Wno-error=unused-label ") @@ -16,3 +16,4 @@ idf_build_set_property(C_COMPILE_OPTIONS -Wno-error=maybe-uninitialized APPEND) idf_build_set_property(C_COMPILE_OPTIONS -Wno-error=nonnull APPEND) idf_build_set_property(C_COMPILE_OPTIONS -Wno-error=address APPEND) idf_build_set_property(C_COMPILE_OPTIONS -Wno-unused-but-set-variable APPEND) +idf_build_set_property(C_COMPILE_OPTIONS -Wno-discarded-qualifiers APPEND) diff --git a/esp-idf-examples/simplewifi/main/config.nims b/esp-idf-examples/simplewifi/main/config.nims new file mode 100644 index 00000000..3259fd64 --- /dev/null +++ b/esp-idf-examples/simplewifi/main/config.nims @@ -0,0 +1,14 @@ +--nomain +--compileOnly +--define:debug +--define:NimAppMain +--nimcache:nimcache + +--gc:arc +--os:freertos +--cpu:arm +--define:useMalloc +--define:noSignalHandler +# --debugger:native +--threads:on +--tls_emulation:off diff --git a/esp-idf-examples/simplewifi/main/main.nim b/esp-idf-examples/simplewifi/main/main.nim new file mode 100644 index 00000000..9ee7ca98 --- /dev/null +++ b/esp-idf-examples/simplewifi/main/main.nim @@ -0,0 +1,149 @@ +import nesper +import nesper/general +import nesper/timers +import nesper/events +import nesper/wifi +import nesper/net_utils +import nesper/esp/nvs +import nesper/esp/nvs_flash +import nesper/nvs_utils + +# Config (match the C example names) +const EXAMPLE_ESP_WIFI_SSID* {.strdefine.}: string = "" +const EXAMPLE_ESP_WIFI_PASS* {.strdefine.}: string = "" +const EXAMPLE_ESP_MAXIMUM_RETRY* {.intdefine.}: int = 5 + +const TAG: cstring = "Wifi Example" + +const WIFI_CONNECTED = 1 shl 0 +const WIFI_FAIL = 1 shl 1 + +var sWifiEventGroup: EventGroupHandle_t +var sRetryNum = 0 + +proc eventHandler(arg: pointer; event_base: esp_event_base_t; event_id: int32; event_data: pointer) {.cdecl.} = + logi(TAG, "Wifi Event Handler called: base: %s eventid: %s wifiEvent: %s ipEvent: %s", + $event_base, $event_id, $(event_base == WIFI_EVENT), $(event_base == IP_EVENT)) + if event_base == WIFI_EVENT and wifi_event_t(event_id) == WIFI_EVENT_STA_START: + discard esp_wifi_connect() + elif event_base == WIFI_EVENT and wifi_event_t(event_id) == WIFI_EVENT_STA_DISCONNECTED: + if sRetryNum < EXAMPLE_ESP_MAXIMUM_RETRY: + discard esp_wifi_connect() + inc sRetryNum + loge(TAG, "retry to connect to the AP") + else: + discard xEventGroupSetBits(sWifiEventGroup, EventBits_t(WIFI_FAIL)) + loge(TAG, "Connect to the AP failed") + elif event_base == IP_EVENT and ip_event_t(event_id) == IP_EVENT_STA_GOT_IP: + let ev = cast[ptr ip_event_got_ip_t](event_data) + logi(TAG, "Wifi Got IP: %s", $ev.ip_info.ip) + sRetryNum = 0 + discard xEventGroupSetBits(sWifiEventGroup, EventBits_t(WIFI_CONNECTED)) + +proc wifiInitSta() = + logw(TAG, "Wifi Init Sta starting...") + sWifiEventGroup = xEventGroupCreate() + + check: esp_netif_init() + + check: esp_event_loop_create_default() + let netif = esp_netif_create_default_wifi_sta() + + let cfg = WIFI_INIT_CONFIG_DEFAULT() + check: esp_wifi_init(unsafeAddr cfg) + + # Register for all WIFI_EVENT IDs and for IP_EVENT_STA_GOT_IP + var instance_any_id: esp_event_handler_instance_t + var instance_got_ip: esp_event_handler_instance_t + + eventRegister(WIFI_EVENT, + ESP_EVENT_ANY_ID.int32, + eventHandler, + nil, addr instance_any_id) + eventRegister(IP_EVENT, + ESP_EVENT_ANY_ID.int32, + eventHandler, + nil, addr instance_got_ip) + + var wifi_config: wifi_config_t + wifi_config.sta.ssid.setFromString(EXAMPLE_ESP_WIFI_SSID) + wifi_config.sta.password.setFromString(EXAMPLE_ESP_WIFI_PASS) + wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA3_PSK + wifi_config.sta.sae_pwe_h2e = WPA3_SAE_PWE_HUNT_AND_PECK + wifi_config.sta.sae_h2e_identifier.setFromString("") + + check: esp_wifi_set_mode(WIFI_MODE_STA) + check: esp_wifi_set_config(WIFI_IF_STA, addr wifi_config) + check: esp_wifi_start() + + logw(TAG, "Wifi Init Sta finished.") + + logw(TAG, "Wifi Init Sta waiting for events...") + let bits = xEventGroupWaitBits(sWifiEventGroup, EventBits_t(WIFI_CONNECTED or WIFI_FAIL), pdFALSE, pdFALSE, portMAX_DELAY) + + if (bits and EventBits_t(WIFI_CONNECTED)) != 0: + logi(TAG, "connected to ap SSID:%s password:%s", EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS) + elif (bits and EventBits_t(WIFI_FAIL)) != 0: + logi(TAG, "Failed to connect to SSID:%s, password:%s", EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS) + else: + loge(TAG, "UNEXPECTED EVENT") + +when defined(FastRpcServer): + import fastrpc/server/fastrpcserver + import fastrpc/server/rpcmethods + import nesper/esp/esp_vfs_eventfd + + # Define RPC Server # + DefineRpcs(name=exampleRpcs): + + proc add(a: int, b: int): int {.rpc.} = + echo "RPC: adding: ", a, " + ", b + result = 1 + a + b + + proc runFastRpcServer() = + try: + let cfg = ESP_VFS_EVENTD_CONFIG_DEFAULT() + logi(TAG, "cfg: %s", repr(cfg)) + check esp_vfs_eventfd_register(addr cfg) + + logi(TAG, "setting up fast rpc router") + let inetAddrs = [ + newInetAddr("0.0.0.0", 5556, Protocol.IPPROTO_UDP), + newInetAddr("::", 5555, Protocol.IPPROTO_UDP), + ] + logi(TAG, "newing fast rpc router") + var rt: FastRpcRouter = newFastRpcRouter() + logi(TAG, "registering rpcs") + rt.registerRpcs(exampleRpcs) + logi(TAG, "newing fast rpc server") + var frpcServer = newFastRpcServer(rt, prefixMsgSize=true, threaded=false) + logi(TAG, "starting fast rpc server") + startSocketServer(inetAddrs, frpcServer) + logi(TAG, "fast rpc server done") + except CatchableError as e: + loge(TAG, "Error starting FastRpcServer: %s, %s", $e.name, $e.msg) + for ste in getCurrentException().getStackTraceEntries(): + loge(TAG, "Error: %s", $ste) + +app_main(): + logw(TAG, "Running main app...") + + logi(TAG, "Initializing NVS...") + initNVS() + # var ret = nvs_flash_init() + # if ret == ESP_ERR_NVS_NO_FREE_PAGES or ret == ESP_ERR_NVS_NEW_VERSION_FOUND: + # check: nvs_flash_erase() + # ret = nvs_flash_init() + # check: ret + + when EXAMPLE_ESP_WIFI_SSID == "" or EXAMPLE_ESP_WIFI_PASS == "": + {.error: "EXAMPLE_ESP_WIFI_SSID and EXAMPLE_ESP_WIFI_PASS are not set".} + else: + wifiInitSta() + + when defined(FastRpcServer): + runFastRpcServer() + + while true: + echo "looping..." + delayMillis(10_000) diff --git a/esp-idf-examples/simplewifi/main/station_example_main.c b/esp-idf-examples/simplewifi/main/station_example_main.c new file mode 100644 index 00000000..d5ed5eb1 --- /dev/null +++ b/esp-idf-examples/simplewifi/main/station_example_main.c @@ -0,0 +1,179 @@ +/* WiFi station Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" + +#include "lwip/err.h" +#include "lwip/sys.h" + +/* The examples use WiFi configuration that you can set via project configuration menu + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID +#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD +#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY + +#if CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HUNT_AND_PECK +#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_HUNT_AND_PECK +#define EXAMPLE_H2E_IDENTIFIER "" +#elif CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HASH_TO_ELEMENT +#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_HASH_TO_ELEMENT +#define EXAMPLE_H2E_IDENTIFIER CONFIG_ESP_WIFI_PW_ID +#elif CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_BOTH +#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_BOTH +#define EXAMPLE_H2E_IDENTIFIER CONFIG_ESP_WIFI_PW_ID +#endif +#if CONFIG_ESP_WIFI_AUTH_OPEN +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN +#elif CONFIG_ESP_WIFI_AUTH_WEP +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP +#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK +#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK +#endif + +/* FreeRTOS event group to signal when we are connected*/ +static EventGroupHandle_t s_wifi_event_group; + +/* The event group allows multiple bits for each event, but we only care about two events: + * - we are connected to the AP with an IP + * - we failed to connect after the maximum amount of retries */ +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 + +static const char *TAG = "wifi station"; + +static int s_retry_num = 0; + + +static void event_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + esp_wifi_connect(); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) { + esp_wifi_connect(); + s_retry_num++; + ESP_LOGI(TAG, "retry to connect to the AP"); + } else { + xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + } + ESP_LOGI(TAG,"connect to the AP fail"); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; + ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); + s_retry_num = 0; + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + } +} + +void wifi_init_sta(void) +{ + s_wifi_event_group = xEventGroupCreate(); + + ESP_ERROR_CHECK(esp_netif_init()); + + ESP_ERROR_CHECK(esp_event_loop_create_default()); + esp_netif_create_default_wifi_sta(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + esp_event_handler_instance_t instance_any_id; + esp_event_handler_instance_t instance_got_ip; + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + ESP_EVENT_ANY_ID, + &event_handler, + NULL, + &instance_any_id)); + ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, + IP_EVENT_STA_GOT_IP, + &event_handler, + NULL, + &instance_got_ip)); + + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_ESP_WIFI_SSID, + .password = EXAMPLE_ESP_WIFI_PASS, + /* Authmode threshold resets to WPA2 as default if password matches WPA2 standards (password len => 8). + * If you want to connect the device to deprecated WEP/WPA networks, Please set the threshold value + * to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set the password with length and format matching to + * WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards. + */ + .threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD, + .sae_pwe_h2e = ESP_WIFI_SAE_MODE, + .sae_h2e_identifier = EXAMPLE_H2E_IDENTIFIER, + }, + }; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + ESP_ERROR_CHECK(esp_wifi_start() ); + + ESP_LOGI(TAG, "wifi_init_sta finished."); + + /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum + * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ + EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, + WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, + pdFALSE, + pdFALSE, + portMAX_DELAY); + + /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually + * happened. */ + if (bits & WIFI_CONNECTED_BIT) { + ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", + EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); + } else if (bits & WIFI_FAIL_BIT) { + ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", + EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); + } else { + ESP_LOGE(TAG, "UNEXPECTED EVENT"); + } +} + +void app_main(void) +{ + //Initialize NVS + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + if (CONFIG_LOG_MAXIMUM_LEVEL > CONFIG_LOG_DEFAULT_LEVEL) { + /* If you only want to open more logs in the wifi module, you need to make the max level greater than the default level, + * and call esp_log_level_set() before esp_wifi_init() to improve the log level of the wifi module. */ + esp_log_level_set("wifi", CONFIG_LOG_MAXIMUM_LEVEL); + } + + ESP_LOGI(TAG, "ESP_WIFI_MODE_STA"); + wifi_init_sta(); +} diff --git a/esp-idf-examples/simplewifi/main/wifi_example_main.nim b/esp-idf-examples/simplewifi/main/wifi_example_main.nim deleted file mode 100644 index 014916b2..00000000 --- a/esp-idf-examples/simplewifi/main/wifi_example_main.nim +++ /dev/null @@ -1,124 +0,0 @@ -import nesper -import nesper/net_utils -import nesper/nvs_utils -import nesper/events -import nesper/wifi -import nesper/tasks - -import server - -# Get Password -const WIFI_SSID {.strdefine.}: string = "NOSSID" -const WIFI_PASSWORD {.strdefine.}: string = "" - -# const CONFIG_EXAMPLE_WIFI_SSID = getEnv("WIFI_SSID") -# const CONFIG_EXAMPLE_WIFI_PASSWORD = getEnv("WIFI_PASSWORD") - -const - GOT_IPV4_BIT* = EventBits_t(BIT(1)) - CONNECTED_BITS* = (GOT_IPV4_BIT) - -const TAG*: cstring = "simplewifi" -var sConnectEventGroup*: EventGroupHandle_t -var sIpAddr*: IpAddress -var sConnectionName*: cstring - -proc ipReceivedHandler*(arg: esp_event_base_t; event_base: esp_event_base_t; event_id: int32; - event_data: pointer) {.cdecl.} = - var event: ptr ip_event_got_ip_t = cast[ptr ip_event_got_ip_t](event_data) - logi TAG, "event.ip_info.ip: %s", $(event.ip_info.ip) - - sIpAddr = toIpAddress(event.ip_info.ip) - # memcpy(addr(sIpAddr), addr(event.ip_info.ip), sizeof((sIpAddr))) - logw TAG, "got event ip: %s", $sIpAddr - discard xEventGroupSetBits(sConnectEventGroup, GOT_IPV4_BIT) - -proc onWifiDisconnect*(arg: esp_event_base_t; - event_base: esp_event_base_t; - event_id: int32; - event_data: pointer) {.cdecl.} = - logi(TAG, "Wi-Fi disconnected, trying to reconnect...") - check: esp_wifi_connect() - -proc wifiStart*() = - ## set up connection, Wi-Fi or Ethernet - let wcfg: wifi_init_config_t = wifi_init_config_default() - - discard esp_wifi_init(unsafeAddr(wcfg)) - - WIFI_EVENT_STA_DISCONNECTED.eventRegister(onWifiDisconnect, nil) - IP_EVENT_STA_GOT_IP.eventRegister(ipReceivedHandler, nil) - - check: esp_wifi_set_storage(WIFI_STORAGE_RAM) - - var wifi_config: wifi_config_t - wifi_config.sta.ssid.setFromString(WIFI_SSID) - wifi_config.sta.password.setFromString(WIFI_PASSWORD) - - logi(TAG, "Connecting to %s...", wifi_config.sta.ssid) - check: esp_wifi_set_mode(WIFI_MODE_STA) - check: esp_wifi_set_config(ESP_IF_WIFI_STA, addr(wifi_config)) - check: esp_wifi_start() - check: esp_wifi_connect() - - sConnectionName = WIFI_SSID - -proc wifiStop*() = - ## tear down connection, release resources - WIFI_EVENT_STA_DISCONNECTED.eventUnregister(onWifiDisconnect) - IP_EVENT_STA_GOT_IP.eventUnregister(ipReceivedHandler) - - check: esp_wifiStop() - check: esp_wifi_deinit() - -proc exampleConnect*(): esp_err_t = - if sConnectEventGroup != nil: - return ESP_ERR_INVALID_STATE - - sConnectEventGroup = xEventGroupCreate() - - wifiStart() - discard xEventGroupWaitBits(sConnectEventGroup, CONNECTED_BITS, 1, 1, portMAX_DELAY) - - logi(TAG, "Connected to %s", sConnectionName) - logi(TAG, "IPv4 address: %s", $sIpAddr) - - echo("run_http_server\n") - run_http_server() - - return ESP_OK - -proc exampleDisconnect*(): esp_err_t = - if sConnectEventGroup == nil: - return ESP_ERR_INVALID_STATE - - vEventGroupDelete(sConnectEventGroup) - sConnectEventGroup = nil - wifiStop() - logi(TAG, "Disconnected from %s", sConnectionName) - sConnectionName = nil - - return ESP_OK - -app_main(): - initNvs() - when defined(ESP_IDF_V4_0): - tcpip_adapter_init() - else: - # Initialize TCP/IP network interface (should be called only once in application) - check: esp_netif_init() - - - # Create default event loop that running in background - check: esp_event_loop_create_default() - - logi(TAG, "wifi setup!\n") - check: exampleConnect() - - ## Register event handlers to stop the server when Wi-Fi or Ethernet is disconnected, - ## and re-start it upon connection. - ## - # IP_EVENT_STA_GOT_IP.eventRegister(ipReceivedHandler, nil) - - echo("Wait done\n") - # vTaskDelay(10000 div portTICK_PERIOD_MS) diff --git a/esp-idf-examples/simplewifi/sdkconfig b/esp-idf-examples/simplewifi/sdkconfig deleted file mode 100644 index 454ebcab..00000000 --- a/esp-idf-examples/simplewifi/sdkconfig +++ /dev/null @@ -1,693 +0,0 @@ -# -# Automatically generated file. DO NOT EDIT. -# Espressif IoT Development Framework (ESP-IDF) Project Configuration -# -CONFIG_IDF_TARGET_ESP32=y -CONFIG_IDF_TARGET="esp32" -CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 - -# -# SDK tool configuration -# -CONFIG_SDK_TOOLPREFIX="xtensa-esp32-elf-" -CONFIG_APP_COMPILE_TIME_DATE=y -# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set -# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set -CONFIG_APP_RETRIEVE_LEN_ELF_SHA=16 -# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set -# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set -# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set -CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y -# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set -# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set -CONFIG_BOOTLOADER_LOG_LEVEL=3 -# CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_8V is not set -CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y -# CONFIG_BOOTLOADER_FACTORY_RESET is not set -# CONFIG_BOOTLOADER_APP_TEST is not set -CONFIG_BOOTLOADER_WDT_ENABLE=y -# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set -CONFIG_BOOTLOADER_WDT_TIME_MS=9000 -# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set -# CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set -# CONFIG_SECURE_BOOT_ENABLED is not set -# CONFIG_SECURE_FLASH_ENC_ENABLED is not set -CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 -# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set -# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set -CONFIG_ESPTOOLPY_FLASHMODE_DIO=y -# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set -CONFIG_ESPTOOLPY_FLASHMODE="dio" -# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set -CONFIG_ESPTOOLPY_FLASHFREQ_40M=y -# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set -CONFIG_ESPTOOLPY_FLASHFREQ="40m" -# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y -# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE="2MB" -CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y -CONFIG_ESPTOOLPY_BEFORE_RESET=y -# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set -CONFIG_ESPTOOLPY_BEFORE="default_reset" -CONFIG_ESPTOOLPY_AFTER_RESET=y -# CONFIG_ESPTOOLPY_AFTER_NORESET is not set -CONFIG_ESPTOOLPY_AFTER="hard_reset" -# CONFIG_ESPTOOLPY_MONITOR_BAUD_9600B is not set -# CONFIG_ESPTOOLPY_MONITOR_BAUD_57600B is not set -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -# CONFIG_ESPTOOLPY_MONITOR_BAUD_230400B is not set -# CONFIG_ESPTOOLPY_MONITOR_BAUD_921600B is not set -# CONFIG_ESPTOOLPY_MONITOR_BAUD_2MB is not set -# CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER is not set -CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER_VAL=115200 -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 -CONFIG_PARTITION_TABLE_SINGLE_APP=y -# CONFIG_PARTITION_TABLE_TWO_OTA is not set -# CONFIG_PARTITION_TABLE_CUSTOM is not set -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" -CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" -CONFIG_PARTITION_TABLE_OFFSET=0x8000 -CONFIG_PARTITION_TABLE_MD5=y -CONFIG_EXAMPLE_WIFI_SSID="myssid" -CONFIG_EXAMPLE_WIFI_PASSWORD="mypassword" -CONFIG_EXAMPLE_CONNECT_WIFI=y -# CONFIG_EXAMPLE_CONNECT_ETHERNET is not set -CONFIG_EXAMPLE_CONNECT_IPV6=y -CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y -# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set -CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y -# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set -# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set -# CONFIG_COMPILER_CXX_EXCEPTIONS is not set -CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y -# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set -# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set -# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set -# CONFIG_COMPILER_STACK_CHECK is not set -CONFIG_COMPILER_WARN_WRITE_STRINGS=y -# CONFIG_COMPILER_DISABLE_GCC8_WARNINGS is not set -# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set -CONFIG_ESP32_APPTRACE_DEST_NONE=y -# CONFIG_ESP32_APPTRACE_ENABLE is not set -CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y -# CONFIG_BT_ENABLED is not set -CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_EFF=0 -# CONFIG_BTDM_CTRL_AUTO_LATENCY_EFF is not set -# CONFIG_BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT_EFF is not set -CONFIG_BTDM_CTRL_BLE_MAX_CONN_EFF=0 -CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN_EFF=0 -CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN_EFF=0 -CONFIG_BTDM_CTRL_PINNED_TO_CORE=0 -CONFIG_BTDM_BLE_SLEEP_CLOCK_ACCURACY_INDEX_EFF=1 -CONFIG_BT_RESERVE_DRAM=0 -# CONFIG_BLE_MESH is not set -# CONFIG_ADC_FORCE_XPD_FSM is not set -CONFIG_ADC_DISABLE_DAC=y -# CONFIG_SPI_MASTER_IN_IRAM is not set -CONFIG_SPI_MASTER_ISR_IN_IRAM=y -# CONFIG_SPI_SLAVE_IN_IRAM is not set -CONFIG_SPI_SLAVE_ISR_IN_IRAM=y -# CONFIG_EFUSE_CUSTOM_TABLE is not set -# CONFIG_EFUSE_VIRTUAL is not set -# CONFIG_EFUSE_CODE_SCHEME_COMPAT_NONE is not set -CONFIG_EFUSE_CODE_SCHEME_COMPAT_3_4=y -# CONFIG_EFUSE_CODE_SCHEME_COMPAT_REPEAT is not set -CONFIG_EFUSE_MAX_BLK_LEN=192 -# CONFIG_ESP_TLS_SERVER is not set -CONFIG_ESP32_REV_MIN_0=y -# CONFIG_ESP32_REV_MIN_1 is not set -# CONFIG_ESP32_REV_MIN_2 is not set -# CONFIG_ESP32_REV_MIN_3 is not set -CONFIG_ESP32_REV_MIN=0 -CONFIG_ESP32_DPORT_WORKAROUND=y -# CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set -# CONFIG_ESP32_DEFAULT_CPU_FREQ_160 is not set -CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y -CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 -# CONFIG_ESP32_SPIRAM_SUPPORT is not set -# CONFIG_ESP32_MEMMAP_TRACEMEM is not set -# CONFIG_ESP32_MEMMAP_TRACEMEM_TWOBANKS is not set -# CONFIG_ESP32_TRAX is not set -CONFIG_ESP32_TRACEMEM_RESERVE_DRAM=0x0 -# CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_TWO is not set -CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_FOUR=y -CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES=4 -# CONFIG_ESP32_ULP_COPROC_ENABLED is not set -CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=0 -# CONFIG_ESP32_PANIC_PRINT_HALT is not set -CONFIG_ESP32_PANIC_PRINT_REBOOT=y -# CONFIG_ESP32_PANIC_SILENT_REBOOT is not set -# CONFIG_ESP32_PANIC_GDBSTUB is not set -CONFIG_ESP32_DEBUG_OCDAWARE=y -CONFIG_ESP32_DEBUG_STUBS_ENABLE=y -CONFIG_ESP32_BROWNOUT_DET=y -CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_0=y -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_1 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_2 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_3 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_4 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_5 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_6 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_7 is not set -CONFIG_ESP32_BROWNOUT_DET_LVL=0 -CONFIG_ESP32_REDUCE_PHY_TX_POWER=y -CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y -# CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set -# CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set -# CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set -CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y -# CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS is not set -# CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC is not set -# CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256 is not set -CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 -CONFIG_ESP32_RTC_XTAL_CAL_RETRY=1 -CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 -CONFIG_ESP32_XTAL_FREQ_40=y -# CONFIG_ESP32_XTAL_FREQ_26 is not set -# CONFIG_ESP32_XTAL_FREQ_AUTO is not set -CONFIG_ESP32_XTAL_FREQ=40 -# CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE is not set -# CONFIG_ESP32_NO_BLOBS is not set -# CONFIG_ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set -# CONFIG_ESP32_USE_FIXED_STATIC_RAM_SIZE is not set -CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL=5 -# CONFIG_PM_ENABLE is not set -CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y -CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y -CONFIG_ADC_CAL_LUT_ENABLE=y -# CONFIG_ESP_TIMER_PROFILING is not set -CONFIG_ESP_ERR_TO_NAME_LOOKUP=y -CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 -CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 -CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584 -CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 -CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 -CONFIG_ESP_CONSOLE_UART_DEFAULT=y -# CONFIG_ESP_CONSOLE_UART_CUSTOM is not set -# CONFIG_ESP_CONSOLE_UART_NONE is not set -CONFIG_ESP_CONSOLE_UART_NUM=0 -CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 -CONFIG_ESP_INT_WDT=y -CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 -CONFIG_ESP_INT_WDT_CHECK_CPU1=y -CONFIG_ESP_TASK_WDT=y -# CONFIG_ESP_TASK_WDT_PANIC is not set -CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 -CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y -CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y -CONFIG_ETH_USE_ESP32_EMAC=y -CONFIG_ETH_PHY_INTERFACE_RMII=y -# CONFIG_ETH_PHY_INTERFACE_MII is not set -CONFIG_ETH_RMII_CLK_INPUT=y -# CONFIG_ETH_RMII_CLK_OUTPUT is not set -CONFIG_ETH_RMII_CLK_IN_GPIO=0 -CONFIG_ETH_DMA_BUFFER_SIZE=512 -CONFIG_ETH_DMA_RX_BUFFER_NUM=10 -CONFIG_ETH_DMA_TX_BUFFER_NUM=10 -CONFIG_ETH_USE_SPI_ETHERNET=y -CONFIG_ETH_SPI_ETHERNET_DM9051=y -# CONFIG_ESP_EVENT_LOOP_PROFILING is not set -CONFIG_ESP_EVENT_POST_FROM_ISR=y -CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y -CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y -# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set -CONFIG_HTTPD_MAX_REQ_HDR_LEN=512 -CONFIG_HTTPD_MAX_URI_LEN=512 -CONFIG_HTTPD_ERR_RESP_NO_DELAY=y -CONFIG_HTTPD_PURGE_BUF_LEN=32 -# CONFIG_HTTPD_LOG_PURGE_DATA is not set -# CONFIG_OTA_ALLOW_HTTP is not set -# CONFIG_ESP_HTTPS_SERVER_ENABLE is not set -CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10 -CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32 -# CONFIG_ESP32_WIFI_STATIC_TX_BUFFER is not set -CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y -CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1 -CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32 -# CONFIG_ESP32_WIFI_CSI_ENABLED is not set -CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y -CONFIG_ESP32_WIFI_TX_BA_WIN=6 -CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y -CONFIG_ESP32_WIFI_RX_BA_WIN=6 -CONFIG_ESP32_WIFI_NVS_ENABLED=y -CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y -# CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 is not set -CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752 -CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32 -# CONFIG_ESP32_WIFI_DEBUG_LOG_ENABLE is not set -CONFIG_ESP32_WIFI_IRAM_OPT=y -CONFIG_ESP32_WIFI_RX_IRAM_OPT=y -CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE=y -CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y -# CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set -CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 -CONFIG_ESP32_PHY_MAX_TX_POWER=20 -# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set -# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set -CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y -# CONFIG_ESP32_ENABLE_COREDUMP is not set -# CONFIG_FATFS_CODEPAGE_DYNAMIC is not set -CONFIG_FATFS_CODEPAGE_437=y -# CONFIG_FATFS_CODEPAGE_720 is not set -# CONFIG_FATFS_CODEPAGE_737 is not set -# CONFIG_FATFS_CODEPAGE_771 is not set -# CONFIG_FATFS_CODEPAGE_775 is not set -# CONFIG_FATFS_CODEPAGE_850 is not set -# CONFIG_FATFS_CODEPAGE_852 is not set -# CONFIG_FATFS_CODEPAGE_855 is not set -# CONFIG_FATFS_CODEPAGE_857 is not set -# CONFIG_FATFS_CODEPAGE_860 is not set -# CONFIG_FATFS_CODEPAGE_861 is not set -# CONFIG_FATFS_CODEPAGE_862 is not set -# CONFIG_FATFS_CODEPAGE_863 is not set -# CONFIG_FATFS_CODEPAGE_864 is not set -# CONFIG_FATFS_CODEPAGE_865 is not set -# CONFIG_FATFS_CODEPAGE_866 is not set -# CONFIG_FATFS_CODEPAGE_869 is not set -# CONFIG_FATFS_CODEPAGE_932 is not set -# CONFIG_FATFS_CODEPAGE_936 is not set -# CONFIG_FATFS_CODEPAGE_949 is not set -# CONFIG_FATFS_CODEPAGE_950 is not set -CONFIG_FATFS_CODEPAGE=437 -CONFIG_FATFS_LFN_NONE=y -# CONFIG_FATFS_LFN_HEAP is not set -# CONFIG_FATFS_LFN_STACK is not set -CONFIG_FATFS_FS_LOCK=0 -CONFIG_FATFS_TIMEOUT_MS=10000 -CONFIG_FATFS_PER_FILE_CACHE=y -CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=150 -CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200 -CONFIG_FMB_QUEUE_LENGTH=20 -CONFIG_FMB_SERIAL_TASK_STACK_SIZE=2048 -CONFIG_FMB_SERIAL_BUF_SIZE=256 -CONFIG_FMB_SERIAL_TASK_PRIO=10 -# CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT is not set -CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT=20 -CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 -CONFIG_FMB_CONTROLLER_STACK_SIZE=4096 -CONFIG_FMB_EVENT_QUEUE_TIMEOUT=20 -CONFIG_FMB_TIMER_PORT_ENABLED=y -CONFIG_FMB_TIMER_GROUP=0 -CONFIG_FMB_TIMER_INDEX=0 -# CONFIG_FREERTOS_UNICORE is not set -CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF -CONFIG_FREERTOS_CORETIMER_0=y -# CONFIG_FREERTOS_CORETIMER_1 is not set -CONFIG_FREERTOS_HZ=100 -CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y -# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set -# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set -CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y -# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set -CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y -CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 -CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y -# CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set -# CONFIG_FREERTOS_ASSERT_DISABLE is not set -CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 -CONFIG_FREERTOS_ISR_STACKSIZE=1536 -# CONFIG_FREERTOS_LEGACY_HOOKS is not set -CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 -# CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION is not set -CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 -CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 -CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 -# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set -# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set -# CONFIG_FREERTOS_DEBUG_INTERNALS is not set -CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y -CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y -# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set -CONFIG_HEAP_POISONING_DISABLED=y -# CONFIG_HEAP_POISONING_LIGHT is not set -# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set -CONFIG_HEAP_TRACING_OFF=y -# CONFIG_HEAP_TRACING_STANDALONE is not set -# CONFIG_HEAP_TRACING_TOHOST is not set -# CONFIG_HEAP_TRACING is not set -# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set -# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set -# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set -# CONFIG_LOG_DEFAULT_LEVEL_INFO is not set -CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y -# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set -CONFIG_LOG_DEFAULT_LEVEL=4 -CONFIG_LOG_COLORS=y -CONFIG_LWIP_LOCAL_HOSTNAME="espressif" -# CONFIG_LWIP_L2_TO_L3_COPY is not set -# CONFIG_LWIP_IRAM_OPTIMIZATION is not set -CONFIG_LWIP_TIMERS_ONDEMAND=y -CONFIG_LWIP_MAX_SOCKETS=10 -# CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set -# CONFIG_LWIP_SO_LINGER is not set -CONFIG_LWIP_SO_REUSE=y -CONFIG_LWIP_SO_REUSE_RXTOALL=y -# CONFIG_LWIP_SO_RCVBUF is not set -CONFIG_LWIP_IP4_FRAG=y -CONFIG_LWIP_IP6_FRAG=y -# CONFIG_LWIP_IP4_REASSEMBLY is not set -# CONFIG_LWIP_IP6_REASSEMBLY is not set -# CONFIG_LWIP_STATS is not set -# CONFIG_LWIP_ETHARP_TRUST_IP_MAC is not set -CONFIG_LWIP_ESP_GRATUITOUS_ARP=y -CONFIG_LWIP_GARP_TMR_INTERVAL=60 -CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32 -CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y -# CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set -CONFIG_LWIP_DHCPS_LEASE_UNIT=60 -CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 -# CONFIG_LWIP_AUTOIP is not set -# CONFIG_LWIP_IPV6_AUTOCONFIG is not set -CONFIG_LWIP_NETIF_LOOPBACK=y -CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 -CONFIG_LWIP_MAX_ACTIVE_TCP=16 -CONFIG_LWIP_MAX_LISTENING_TCP=16 -CONFIG_LWIP_TCP_MAXRTX=12 -CONFIG_LWIP_TCP_SYNMAXRTX=6 -CONFIG_LWIP_TCP_MSS=1440 -CONFIG_LWIP_TCP_MSL=60000 -CONFIG_LWIP_TCP_SND_BUF_DEFAULT=5744 -CONFIG_LWIP_TCP_WND_DEFAULT=5744 -CONFIG_LWIP_TCP_RECVMBOX_SIZE=6 -CONFIG_LWIP_TCP_QUEUE_OOSEQ=y -# CONFIG_LWIP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set -CONFIG_LWIP_TCP_OVERSIZE_MSS=y -# CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set -# CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set -CONFIG_LWIP_TCP_RTO_TIME=3000 -CONFIG_LWIP_MAX_UDP_PCBS=16 -CONFIG_LWIP_UDP_RECVMBOX_SIZE=6 -CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=3072 -CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY=y -# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0 is not set -# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU1 is not set -CONFIG_LWIP_TCPIP_TASK_AFFINITY=0x7FFFFFFF -# CONFIG_LWIP_PPP_SUPPORT is not set -CONFIG_LWIP_IPV6_MEMP_NUM_ND6_QUEUE=3 -CONFIG_LWIP_IPV6_ND6_NUM_NEIGHBORS=5 -# CONFIG_LWIP_MULTICAST_PING is not set -# CONFIG_LWIP_BROADCAST_PING is not set -CONFIG_LWIP_MAX_RAW_PCBS=16 -CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 -CONFIG_LWIP_SNTP_UPDATE_DELAY=3600000 -CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y -# CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set -# CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set -CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y -CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 -CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 -# CONFIG_MBEDTLS_DEBUG is not set -# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set -# CONFIG_MBEDTLS_CMAC_C is not set -CONFIG_MBEDTLS_HARDWARE_AES=y -# CONFIG_MBEDTLS_HARDWARE_MPI is not set -CONFIG_MBEDTLS_HARDWARE_SHA=y -CONFIG_MBEDTLS_HAVE_TIME=y -# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set -CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y -# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set -# CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set -# CONFIG_MBEDTLS_TLS_DISABLED is not set -CONFIG_MBEDTLS_TLS_SERVER=y -CONFIG_MBEDTLS_TLS_CLIENT=y -CONFIG_MBEDTLS_TLS_ENABLED=y -# CONFIG_MBEDTLS_PSK_MODES is not set -CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y -CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y -CONFIG_MBEDTLS_SSL_RENEGOTIATION=y -# CONFIG_MBEDTLS_SSL_PROTO_SSL3 is not set -CONFIG_MBEDTLS_SSL_PROTO_TLS1=y -CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y -CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y -# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set -CONFIG_MBEDTLS_SSL_ALPN=y -CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y -CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y -CONFIG_MBEDTLS_AES_C=y -# CONFIG_MBEDTLS_CAMELLIA_C is not set -# CONFIG_MBEDTLS_DES_C is not set -CONFIG_MBEDTLS_RC4_DISABLED=y -# CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT is not set -# CONFIG_MBEDTLS_RC4_ENABLED is not set -# CONFIG_MBEDTLS_BLOWFISH_C is not set -# CONFIG_MBEDTLS_XTEA_C is not set -CONFIG_MBEDTLS_CCM_C=y -CONFIG_MBEDTLS_GCM_C=y -# CONFIG_MBEDTLS_RIPEMD160_C is not set -CONFIG_MBEDTLS_PEM_PARSE_C=y -CONFIG_MBEDTLS_PEM_WRITE_C=y -CONFIG_MBEDTLS_X509_CRL_PARSE_C=y -CONFIG_MBEDTLS_X509_CSR_PARSE_C=y -CONFIG_MBEDTLS_ECP_C=y -CONFIG_MBEDTLS_ECDH_C=y -CONFIG_MBEDTLS_ECDSA_C=y -CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y -CONFIG_MBEDTLS_ECP_NIST_OPTIM=y -CONFIG_MDNS_MAX_SERVICES=10 -CONFIG_MQTT_PROTOCOL_311=y -CONFIG_MQTT_TRANSPORT_SSL=y -CONFIG_MQTT_TRANSPORT_WEBSOCKET=y -CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y -# CONFIG_MQTT_USE_CUSTOM_CONFIG is not set -# CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED is not set -# CONFIG_MQTT_CUSTOM_OUTBOX is not set -CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y -# CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set -# CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set -# CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set -# CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set -CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y -# CONFIG_NEWLIB_NANO_FORMAT is not set -# CONFIG_OPENSSL_DEBUG is not set -# CONFIG_OPENSSL_ASSERT_DO_NOTHING is not set -CONFIG_OPENSSL_ASSERT_EXIT=y -CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 -CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 -CONFIG_PTHREAD_STACK_MIN=768 -CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY=y -# CONFIG_PTHREAD_DEFAULT_CORE_0 is not set -# CONFIG_PTHREAD_DEFAULT_CORE_1 is not set -CONFIG_PTHREAD_TASK_CORE_DEFAULT=-1 -CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread" -# CONFIG_SPI_FLASH_VERIFY_WRITE is not set -# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set -CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y -CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y -# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set -# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set -# CONFIG_SPI_FLASH_USE_LEGACY_IMPL is not set -# CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set -CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y -CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 -CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 -CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y -CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y -CONFIG_SPIFFS_MAX_PARTITIONS=3 -CONFIG_SPIFFS_CACHE=y -CONFIG_SPIFFS_CACHE_WR=y -# CONFIG_SPIFFS_CACHE_STATS is not set -CONFIG_SPIFFS_PAGE_CHECK=y -CONFIG_SPIFFS_GC_MAX_RUNS=10 -# CONFIG_SPIFFS_GC_STATS is not set -CONFIG_SPIFFS_PAGE_SIZE=256 -CONFIG_SPIFFS_OBJ_NAME_LEN=32 -CONFIG_SPIFFS_USE_MAGIC=y -CONFIG_SPIFFS_USE_MAGIC_LENGTH=y -CONFIG_SPIFFS_META_LENGTH=4 -CONFIG_SPIFFS_USE_MTIME=y -# CONFIG_SPIFFS_DBG is not set -# CONFIG_SPIFFS_API_DBG is not set -# CONFIG_SPIFFS_GC_DBG is not set -# CONFIG_SPIFFS_CACHE_DBG is not set -# CONFIG_SPIFFS_CHECK_DBG is not set -# CONFIG_SPIFFS_TEST_VISUALISATION is not set -CONFIG_NETIF_IP_LOST_TIMER_INTERVAL=120 -CONFIG_TCPIP_LWIP=y -CONFIG_UNITY_ENABLE_FLOAT=y -CONFIG_UNITY_ENABLE_DOUBLE=y -# CONFIG_UNITY_ENABLE_COLOR is not set -CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y -# CONFIG_UNITY_ENABLE_FIXTURE is not set -# CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL is not set -CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT=y -CONFIG_VFS_SUPPORT_TERMIOS=y -CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1 -CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN=128 -# CONFIG_WL_SECTOR_SIZE_512 is not set -CONFIG_WL_SECTOR_SIZE_4096=y -CONFIG_WL_SECTOR_SIZE=4096 -CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16 -CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 -CONFIG_WPA_MBEDTLS_CRYPTO=y -# CONFIG_WPA_TLS_V12 is not set -# CONFIG_WPA_WPS_WARS is not set -# CONFIG_LEGACY_INCLUDE_COMMON_HEADERS is not set - -# Deprecated options for backward compatibility -CONFIG_TOOLPREFIX="xtensa-esp32-elf-" -# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set -# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set -# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set -CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y -# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set -# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set -CONFIG_LOG_BOOTLOADER_LEVEL=3 -# CONFIG_APP_ROLLBACK_ENABLE is not set -# CONFIG_FLASH_ENCRYPTION_ENABLED is not set -# CONFIG_FLASHMODE_QIO is not set -# CONFIG_FLASHMODE_QOUT is not set -CONFIG_FLASHMODE_DIO=y -# CONFIG_FLASHMODE_DOUT is not set -# CONFIG_MONITOR_BAUD_9600B is not set -# CONFIG_MONITOR_BAUD_57600B is not set -CONFIG_MONITOR_BAUD_115200B=y -# CONFIG_MONITOR_BAUD_230400B is not set -# CONFIG_MONITOR_BAUD_921600B is not set -# CONFIG_MONITOR_BAUD_2MB is not set -# CONFIG_MONITOR_BAUD_OTHER is not set -CONFIG_MONITOR_BAUD_OTHER_VAL=115200 -CONFIG_MONITOR_BAUD=115200 -CONFIG_OPTIMIZATION_LEVEL_DEBUG=y -# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set -CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y -# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set -# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set -# CONFIG_CXX_EXCEPTIONS is not set -CONFIG_STACK_CHECK_NONE=y -# CONFIG_STACK_CHECK_NORM is not set -# CONFIG_STACK_CHECK_STRONG is not set -# CONFIG_STACK_CHECK_ALL is not set -# CONFIG_STACK_CHECK is not set -CONFIG_WARN_WRITE_STRINGS=y -# CONFIG_DISABLE_GCC8_WARNINGS is not set -CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF=0 -CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_EFF=0 -CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF=0 -CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 -CONFIG_ADC2_DISABLE_DAC=y -# CONFIG_SPIRAM_SUPPORT is not set -# CONFIG_MEMMAP_TRACEMEM is not set -# CONFIG_MEMMAP_TRACEMEM_TWOBANKS is not set -CONFIG_TRACEMEM_RESERVE_DRAM=0x0 -# CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set -CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y -CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 -# CONFIG_ULP_COPROC_ENABLED is not set -CONFIG_ULP_COPROC_RESERVE_MEM=0 -CONFIG_BROWNOUT_DET=y -CONFIG_BROWNOUT_DET_LVL_SEL_0=y -# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set -CONFIG_BROWNOUT_DET_LVL=0 -CONFIG_REDUCE_PHY_TX_POWER=y -CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y -# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL is not set -# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC is not set -# CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 is not set -# CONFIG_DISABLE_BASIC_ROM_CONSOLE is not set -# CONFIG_NO_BLOBS is not set -# CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set -CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 -CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 -CONFIG_MAIN_TASK_STACK_SIZE=3584 -CONFIG_IPC_TASK_STACK_SIZE=1024 -CONFIG_TIMER_TASK_STACK_SIZE=3584 -CONFIG_CONSOLE_UART_DEFAULT=y -# CONFIG_CONSOLE_UART_CUSTOM is not set -# CONFIG_CONSOLE_UART_NONE is not set -CONFIG_CONSOLE_UART_NUM=0 -CONFIG_CONSOLE_UART_BAUDRATE=115200 -CONFIG_INT_WDT=y -CONFIG_INT_WDT_TIMEOUT_MS=300 -CONFIG_INT_WDT_CHECK_CPU1=y -CONFIG_TASK_WDT=y -# CONFIG_TASK_WDT_PANIC is not set -CONFIG_TASK_WDT_TIMEOUT_S=5 -CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y -CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y -# CONFIG_EVENT_LOOP_PROFILING is not set -CONFIG_POST_EVENTS_FROM_ISR=y -CONFIG_POST_EVENTS_FROM_IRAM_ISR=y -CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND=150 -CONFIG_MB_MASTER_DELAY_MS_CONVERT=200 -CONFIG_MB_QUEUE_LENGTH=20 -CONFIG_MB_SERIAL_TASK_STACK_SIZE=2048 -CONFIG_MB_SERIAL_BUF_SIZE=256 -CONFIG_MB_SERIAL_TASK_PRIO=10 -# CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT is not set -CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT=20 -CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 -CONFIG_MB_CONTROLLER_STACK_SIZE=4096 -CONFIG_MB_EVENT_QUEUE_TIMEOUT=20 -CONFIG_MB_TIMER_PORT_ENABLED=y -CONFIG_MB_TIMER_GROUP=0 -CONFIG_MB_TIMER_INDEX=0 -# CONFIG_SUPPORT_STATIC_ALLOCATION is not set -CONFIG_TIMER_TASK_PRIORITY=1 -CONFIG_TIMER_TASK_STACK_DEPTH=2048 -CONFIG_TIMER_QUEUE_LENGTH=10 -# CONFIG_L2_TO_L3_COPY is not set -# CONFIG_USE_ONLY_LWIP_SELECT is not set -CONFIG_ESP_GRATUITOUS_ARP=y -CONFIG_GARP_TMR_INTERVAL=60 -CONFIG_TCPIP_RECVMBOX_SIZE=32 -CONFIG_TCP_MAXRTX=12 -CONFIG_TCP_SYNMAXRTX=6 -CONFIG_TCP_MSS=1440 -CONFIG_TCP_MSL=60000 -CONFIG_TCP_SND_BUF_DEFAULT=5744 -CONFIG_TCP_WND_DEFAULT=5744 -CONFIG_TCP_RECVMBOX_SIZE=6 -CONFIG_TCP_QUEUE_OOSEQ=y -# CONFIG_ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set -CONFIG_TCP_OVERSIZE_MSS=y -# CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set -# CONFIG_TCP_OVERSIZE_DISABLE is not set -CONFIG_UDP_RECVMBOX_SIZE=6 -CONFIG_TCPIP_TASK_STACK_SIZE=3072 -CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y -# CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set -# CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set -CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF -# CONFIG_PPP_SUPPORT is not set -CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 -CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 -CONFIG_ESP32_PTHREAD_STACK_MIN=768 -CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y -# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set -# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set -CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1 -CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" -CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y -# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set -# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set -CONFIG_IP_LOST_TIMER_INTERVAL=120 -CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y -CONFIG_SUPPORT_TERMIOS=y -# End of deprecated options diff --git a/esp-idf-examples/simplewifi/sdkconfig.defaults b/esp-idf-examples/simplewifi/sdkconfig.defaults new file mode 100644 index 00000000..7d82cdfc --- /dev/null +++ b/esp-idf-examples/simplewifi/sdkconfig.defaults @@ -0,0 +1,9 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32s3" +CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384 +CONFIG_LWIP_MAX_SOCKETS=16 +CONFIG_LWIP_SO_RCVBUF=y +CONFIG_LWIP_IPV6_AUTOCONFIG=y +CONFIG_LWIP_IPV6_DHCP6=y diff --git a/esp-idf-examples/simplewifi/sdkconfig.old b/esp-idf-examples/simplewifi/sdkconfig.old deleted file mode 100644 index aba46562..00000000 --- a/esp-idf-examples/simplewifi/sdkconfig.old +++ /dev/null @@ -1,955 +0,0 @@ -# -# Automatically generated file. DO NOT EDIT. -# Espressif IoT Development Framework (ESP-IDF) Project Configuration -# -CONFIG_IDF_CMAKE=y -CONFIG_IDF_TARGET="esp32" -CONFIG_IDF_TARGET_ESP32=y -CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 - -# -# SDK tool configuration -# -CONFIG_SDK_TOOLPREFIX="xtensa-esp32-elf-" -# CONFIG_SDK_TOOLCHAIN_SUPPORTS_TIME_WIDE_64_BITS is not set -# end of SDK tool configuration - -# -# Build type -# -CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y -# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set -CONFIG_APP_BUILD_GENERATE_BINARIES=y -CONFIG_APP_BUILD_BOOTLOADER=y -CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y -# end of Build type - -# -# Application manager -# -CONFIG_APP_COMPILE_TIME_DATE=y -# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set -# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set -CONFIG_APP_RETRIEVE_LEN_ELF_SHA=16 -# end of Application manager - -# -# Bootloader config -# -CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y -# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set -# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF is not set -# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set -# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set -# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set -# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set -CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y -# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set -# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set -CONFIG_BOOTLOADER_LOG_LEVEL=3 -# CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_8V is not set -CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y -# CONFIG_BOOTLOADER_FACTORY_RESET is not set -# CONFIG_BOOTLOADER_APP_TEST is not set -CONFIG_BOOTLOADER_WDT_ENABLE=y -# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set -CONFIG_BOOTLOADER_WDT_TIME_MS=9000 -# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set -# CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set -CONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0 -# CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC is not set -# end of Bootloader config - -# -# Security features -# -# CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set -# CONFIG_SECURE_BOOT is not set -# CONFIG_SECURE_FLASH_ENC_ENABLED is not set -# end of Security features - -# -# Serial flasher config -# -CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 -# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set -# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set -CONFIG_ESPTOOLPY_FLASHMODE_DIO=y -# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set -CONFIG_ESPTOOLPY_FLASHMODE="dio" -# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set -CONFIG_ESPTOOLPY_FLASHFREQ_40M=y -# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set -CONFIG_ESPTOOLPY_FLASHFREQ="40m" -# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y -# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE="2MB" -CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y -CONFIG_ESPTOOLPY_BEFORE_RESET=y -# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set -CONFIG_ESPTOOLPY_BEFORE="default_reset" -CONFIG_ESPTOOLPY_AFTER_RESET=y -# CONFIG_ESPTOOLPY_AFTER_NORESET is not set -CONFIG_ESPTOOLPY_AFTER="hard_reset" -# CONFIG_ESPTOOLPY_MONITOR_BAUD_9600B is not set -# CONFIG_ESPTOOLPY_MONITOR_BAUD_57600B is not set -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -# CONFIG_ESPTOOLPY_MONITOR_BAUD_230400B is not set -# CONFIG_ESPTOOLPY_MONITOR_BAUD_921600B is not set -# CONFIG_ESPTOOLPY_MONITOR_BAUD_2MB is not set -# CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER is not set -CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER_VAL=115200 -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 -# end of Serial flasher config - -# -# Partition Table -# -CONFIG_PARTITION_TABLE_SINGLE_APP=y -# CONFIG_PARTITION_TABLE_TWO_OTA is not set -# CONFIG_PARTITION_TABLE_CUSTOM is not set -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" -CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" -CONFIG_PARTITION_TABLE_OFFSET=0x8000 -CONFIG_PARTITION_TABLE_MD5=y -# end of Partition Table - -# -# Wifi Example Configuration -# -CONFIG_EXAMPLE_WIFI_SSID="myssid" -CONFIG_EXAMPLE_WIFI_PASSWORD="mypassword" -# end of Wifi Example Configuration - -# -# Example Connection Configuration -# -CONFIG_EXAMPLE_CONNECT_WIFI=y -# CONFIG_EXAMPLE_CONNECT_ETHERNET is not set -CONFIG_EXAMPLE_CONNECT_IPV6=y -# end of Example Connection Configuration - -# -# Compiler options -# -CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y -# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set -# CONFIG_COMPILER_OPTIMIZATION_PERF is not set -# CONFIG_COMPILER_OPTIMIZATION_NONE is not set -CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y -# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set -# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set -# CONFIG_COMPILER_CXX_EXCEPTIONS is not set -# CONFIG_COMPILER_CXX_RTTI is not set -CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y -# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set -# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set -# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set -# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set -# CONFIG_COMPILER_DISABLE_GCC8_WARNINGS is not set -# end of Compiler options - -# -# Component config -# - -# -# Application Level Tracing -# -# CONFIG_APPTRACE_DEST_TRAX is not set -CONFIG_APPTRACE_DEST_NONE=y -CONFIG_APPTRACE_LOCK_ENABLE=y -# end of Application Level Tracing - -# -# Bluetooth -# -# CONFIG_BT_ENABLED is not set -CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_EFF=0 -CONFIG_BTDM_CTRL_PCM_ROLE_EFF=0 -CONFIG_BTDM_CTRL_PCM_POLAR_EFF=0 -CONFIG_BTDM_CTRL_BLE_MAX_CONN_EFF=0 -CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN_EFF=0 -CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN_EFF=0 -CONFIG_BTDM_CTRL_PINNED_TO_CORE=0 -CONFIG_BTDM_BLE_SLEEP_CLOCK_ACCURACY_INDEX_EFF=1 -CONFIG_BT_RESERVE_DRAM=0 -# end of Bluetooth - -# CONFIG_BLE_MESH is not set - -# -# CoAP Configuration -# -CONFIG_COAP_MBEDTLS_PSK=y -# CONFIG_COAP_MBEDTLS_PKI is not set -# CONFIG_COAP_MBEDTLS_DEBUG is not set -CONFIG_COAP_LOG_DEFAULT_LEVEL=0 -# end of CoAP Configuration - -# -# Driver configurations -# - -# -# ADC configuration -# -# CONFIG_ADC_FORCE_XPD_FSM is not set -CONFIG_ADC_DISABLE_DAC=y -# end of ADC configuration - -# -# SPI configuration -# -# CONFIG_SPI_MASTER_IN_IRAM is not set -CONFIG_SPI_MASTER_ISR_IN_IRAM=y -# CONFIG_SPI_SLAVE_IN_IRAM is not set -CONFIG_SPI_SLAVE_ISR_IN_IRAM=y -# end of SPI configuration - -# -# UART configuration -# -# CONFIG_UART_ISR_IN_IRAM is not set -# end of UART configuration - -# -# RTCIO configuration -# -# CONFIG_RTCIO_SUPPORT_RTC_GPIO_DESC is not set -# end of RTCIO configuration -# end of Driver configurations - -# -# eFuse Bit Manager -# -# CONFIG_EFUSE_CUSTOM_TABLE is not set -# CONFIG_EFUSE_VIRTUAL is not set -# CONFIG_EFUSE_CODE_SCHEME_COMPAT_NONE is not set -CONFIG_EFUSE_CODE_SCHEME_COMPAT_3_4=y -# CONFIG_EFUSE_CODE_SCHEME_COMPAT_REPEAT is not set -CONFIG_EFUSE_MAX_BLK_LEN=192 -# end of eFuse Bit Manager - -# -# ESP-TLS -# -CONFIG_ESP_TLS_USING_MBEDTLS=y -# CONFIG_ESP_TLS_SERVER is not set -# CONFIG_ESP_TLS_PSK_VERIFICATION is not set -# end of ESP-TLS - -# -# ESP32-specific -# -CONFIG_ESP32_REV_MIN_0=y -# CONFIG_ESP32_REV_MIN_1 is not set -# CONFIG_ESP32_REV_MIN_2 is not set -# CONFIG_ESP32_REV_MIN_3 is not set -CONFIG_ESP32_REV_MIN=0 -CONFIG_ESP32_DPORT_WORKAROUND=y -# CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set -# CONFIG_ESP32_DEFAULT_CPU_FREQ_160 is not set -CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y -CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 -# CONFIG_ESP32_SPIRAM_SUPPORT is not set -# CONFIG_ESP32_TRAX is not set -CONFIG_ESP32_TRACEMEM_RESERVE_DRAM=0x0 -# CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_TWO is not set -CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_FOUR=y -CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES=4 -# CONFIG_ESP32_ULP_COPROC_ENABLED is not set -CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=0 -# CONFIG_ESP32_PANIC_PRINT_HALT is not set -CONFIG_ESP32_PANIC_PRINT_REBOOT=y -# CONFIG_ESP32_PANIC_SILENT_REBOOT is not set -# CONFIG_ESP32_PANIC_GDBSTUB is not set -CONFIG_ESP32_DEBUG_OCDAWARE=y -CONFIG_ESP32_BROWNOUT_DET=y -CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_0=y -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_1 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_2 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_3 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_4 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_5 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_6 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_7 is not set -CONFIG_ESP32_BROWNOUT_DET_LVL=0 -CONFIG_ESP32_REDUCE_PHY_TX_POWER=y -CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y -# CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set -# CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set -# CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set -CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y -# CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS is not set -# CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC is not set -# CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256 is not set -CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 -CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 -CONFIG_ESP32_XTAL_FREQ_40=y -# CONFIG_ESP32_XTAL_FREQ_26 is not set -# CONFIG_ESP32_XTAL_FREQ_AUTO is not set -CONFIG_ESP32_XTAL_FREQ=40 -# CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE is not set -# CONFIG_ESP32_NO_BLOBS is not set -# CONFIG_ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set -# CONFIG_ESP32_USE_FIXED_STATIC_RAM_SIZE is not set -CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL=5 -# end of ESP32-specific - -# -# Power Management -# -# CONFIG_PM_ENABLE is not set -# end of Power Management - -# -# ADC-Calibration -# -CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y -CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y -CONFIG_ADC_CAL_LUT_ENABLE=y -# end of ADC-Calibration - -# -# Common ESP-related -# -# CONFIG_ESP_TIMER_PROFILING is not set -CONFIG_ESP_ERR_TO_NAME_LOOKUP=y -CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 -CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 -CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584 -CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 -CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y -CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 -CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 -CONFIG_ESP_CONSOLE_UART_DEFAULT=y -# CONFIG_ESP_CONSOLE_UART_CUSTOM is not set -# CONFIG_ESP_CONSOLE_UART_NONE is not set -CONFIG_ESP_CONSOLE_UART_NUM=0 -CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 -CONFIG_ESP_INT_WDT=y -CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 -CONFIG_ESP_INT_WDT_CHECK_CPU1=y -CONFIG_ESP_TASK_WDT=y -# CONFIG_ESP_TASK_WDT_PANIC is not set -CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 -CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y -CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y -# CONFIG_ESP_PANIC_HANDLER_IRAM is not set -# end of Common ESP-related - -# -# Ethernet -# -CONFIG_ETH_ENABLED=y -CONFIG_ETH_USE_ESP32_EMAC=y -CONFIG_ETH_PHY_INTERFACE_RMII=y -# CONFIG_ETH_PHY_INTERFACE_MII is not set -CONFIG_ETH_RMII_CLK_INPUT=y -# CONFIG_ETH_RMII_CLK_OUTPUT is not set -CONFIG_ETH_RMII_CLK_IN_GPIO=0 -CONFIG_ETH_DMA_BUFFER_SIZE=512 -CONFIG_ETH_DMA_RX_BUFFER_NUM=10 -CONFIG_ETH_DMA_TX_BUFFER_NUM=10 -CONFIG_ETH_USE_SPI_ETHERNET=y -CONFIG_ETH_SPI_ETHERNET_DM9051=y -# CONFIG_ETH_USE_OPENETH is not set -# end of Ethernet - -# -# Event Loop Library -# -# CONFIG_ESP_EVENT_LOOP_PROFILING is not set -CONFIG_ESP_EVENT_POST_FROM_ISR=y -CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y -# end of Event Loop Library - -# -# GDB Stub -# -# end of GDB Stub - -# -# ESP HTTP client -# -CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y -# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set -# end of ESP HTTP client - -# -# HTTP Server -# -CONFIG_HTTPD_MAX_REQ_HDR_LEN=512 -CONFIG_HTTPD_MAX_URI_LEN=512 -CONFIG_HTTPD_ERR_RESP_NO_DELAY=y -CONFIG_HTTPD_PURGE_BUF_LEN=32 -# CONFIG_HTTPD_LOG_PURGE_DATA is not set -# end of HTTP Server - -# -# ESP HTTPS OTA -# -# CONFIG_OTA_ALLOW_HTTP is not set -# end of ESP HTTPS OTA - -# -# ESP HTTPS server -# -# CONFIG_ESP_HTTPS_SERVER_ENABLE is not set -# end of ESP HTTPS server - -# -# ESP NETIF Adapter -# -CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL=120 -CONFIG_ESP_NETIF_TCPIP_LWIP=y -# CONFIG_ESP_NETIF_LOOPBACK is not set -CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER=y -# end of ESP NETIF Adapter - -# -# Wi-Fi -# -CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10 -CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32 -# CONFIG_ESP32_WIFI_STATIC_TX_BUFFER is not set -CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y -CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1 -CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32 -# CONFIG_ESP32_WIFI_CSI_ENABLED is not set -CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y -CONFIG_ESP32_WIFI_TX_BA_WIN=6 -CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y -CONFIG_ESP32_WIFI_RX_BA_WIN=6 -CONFIG_ESP32_WIFI_NVS_ENABLED=y -CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y -# CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 is not set -CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752 -CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32 -# CONFIG_ESP32_WIFI_DEBUG_LOG_ENABLE is not set -CONFIG_ESP32_WIFI_IRAM_OPT=y -CONFIG_ESP32_WIFI_RX_IRAM_OPT=y -CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE=y -# end of Wi-Fi - -# -# PHY -# -CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y -# CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set -CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 -CONFIG_ESP32_PHY_MAX_TX_POWER=20 -# end of PHY - -# -# Core dump -# -# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set -# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set -CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y -# end of Core dump - -# -# FAT Filesystem support -# -# CONFIG_FATFS_CODEPAGE_DYNAMIC is not set -CONFIG_FATFS_CODEPAGE_437=y -# CONFIG_FATFS_CODEPAGE_720 is not set -# CONFIG_FATFS_CODEPAGE_737 is not set -# CONFIG_FATFS_CODEPAGE_771 is not set -# CONFIG_FATFS_CODEPAGE_775 is not set -# CONFIG_FATFS_CODEPAGE_850 is not set -# CONFIG_FATFS_CODEPAGE_852 is not set -# CONFIG_FATFS_CODEPAGE_855 is not set -# CONFIG_FATFS_CODEPAGE_857 is not set -# CONFIG_FATFS_CODEPAGE_860 is not set -# CONFIG_FATFS_CODEPAGE_861 is not set -# CONFIG_FATFS_CODEPAGE_862 is not set -# CONFIG_FATFS_CODEPAGE_863 is not set -# CONFIG_FATFS_CODEPAGE_864 is not set -# CONFIG_FATFS_CODEPAGE_865 is not set -# CONFIG_FATFS_CODEPAGE_866 is not set -# CONFIG_FATFS_CODEPAGE_869 is not set -# CONFIG_FATFS_CODEPAGE_932 is not set -# CONFIG_FATFS_CODEPAGE_936 is not set -# CONFIG_FATFS_CODEPAGE_949 is not set -# CONFIG_FATFS_CODEPAGE_950 is not set -CONFIG_FATFS_CODEPAGE=437 -CONFIG_FATFS_LFN_NONE=y -# CONFIG_FATFS_LFN_HEAP is not set -# CONFIG_FATFS_LFN_STACK is not set -CONFIG_FATFS_FS_LOCK=0 -CONFIG_FATFS_TIMEOUT_MS=10000 -CONFIG_FATFS_PER_FILE_CACHE=y -# end of FAT Filesystem support - -# -# Modbus configuration -# -CONFIG_FMB_COMM_MODE_RTU_EN=y -CONFIG_FMB_COMM_MODE_ASCII_EN=y -CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=150 -CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200 -CONFIG_FMB_QUEUE_LENGTH=20 -CONFIG_FMB_SERIAL_TASK_STACK_SIZE=2048 -CONFIG_FMB_SERIAL_BUF_SIZE=256 -CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB=8 -CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS=1000 -CONFIG_FMB_SERIAL_TASK_PRIO=10 -# CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT is not set -CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT=20 -CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 -CONFIG_FMB_CONTROLLER_STACK_SIZE=4096 -CONFIG_FMB_EVENT_QUEUE_TIMEOUT=20 -CONFIG_FMB_TIMER_PORT_ENABLED=y -CONFIG_FMB_TIMER_GROUP=0 -CONFIG_FMB_TIMER_INDEX=0 -# CONFIG_FMB_TIMER_ISR_IN_IRAM is not set -# end of Modbus configuration - -# -# FreeRTOS -# -# CONFIG_FREERTOS_UNICORE is not set -CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF -CONFIG_FREERTOS_CORETIMER_0=y -# CONFIG_FREERTOS_CORETIMER_1 is not set -CONFIG_FREERTOS_HZ=100 -CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y -# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set -# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set -CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y -# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set -CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y -CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 -CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y -# CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set -# CONFIG_FREERTOS_ASSERT_DISABLE is not set -CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 -CONFIG_FREERTOS_ISR_STACKSIZE=1536 -# CONFIG_FREERTOS_LEGACY_HOOKS is not set -CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 -# CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION is not set -CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 -CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 -CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 -# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set -# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set -# CONFIG_FREERTOS_DEBUG_INTERNALS is not set -CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y -CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y -# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set -CONFIG_FREERTOS_DEBUG_OCDAWARE=y -# end of FreeRTOS - -# -# Heap memory debugging -# -CONFIG_HEAP_POISONING_DISABLED=y -# CONFIG_HEAP_POISONING_LIGHT is not set -# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set -CONFIG_HEAP_TRACING_OFF=y -# CONFIG_HEAP_TRACING_STANDALONE is not set -# CONFIG_HEAP_TRACING_TOHOST is not set -# end of Heap memory debugging - -# -# jsmn -# -# CONFIG_JSMN_PARENT_LINKS is not set -# CONFIG_JSMN_STRICT is not set -# end of jsmn - -# -# libsodium -# -# end of libsodium - -# -# Log output -# -# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set -# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set -# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set -# CONFIG_LOG_DEFAULT_LEVEL_INFO is not set -CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y -# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set -CONFIG_LOG_DEFAULT_LEVEL=4 -CONFIG_LOG_COLORS=y -CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y -# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set -# end of Log output - -# -# LWIP -# -CONFIG_LWIP_LOCAL_HOSTNAME="espressif" -CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y -# CONFIG_LWIP_L2_TO_L3_COPY is not set -# CONFIG_LWIP_IRAM_OPTIMIZATION is not set -CONFIG_LWIP_TIMERS_ONDEMAND=y -CONFIG_LWIP_MAX_SOCKETS=10 -# CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set -# CONFIG_LWIP_SO_LINGER is not set -CONFIG_LWIP_SO_REUSE=y -CONFIG_LWIP_SO_REUSE_RXTOALL=y -# CONFIG_LWIP_SO_RCVBUF is not set -# CONFIG_LWIP_NETBUF_RECVINFO is not set -CONFIG_LWIP_IP4_FRAG=y -CONFIG_LWIP_IP6_FRAG=y -# CONFIG_LWIP_IP4_REASSEMBLY is not set -# CONFIG_LWIP_IP6_REASSEMBLY is not set -# CONFIG_LWIP_STATS is not set -# CONFIG_LWIP_ETHARP_TRUST_IP_MAC is not set -CONFIG_LWIP_ESP_GRATUITOUS_ARP=y -CONFIG_LWIP_GARP_TMR_INTERVAL=60 -CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32 -CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y -# CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set - -# -# DHCP server -# -CONFIG_LWIP_DHCPS_LEASE_UNIT=60 -CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 -# end of DHCP server - -# CONFIG_LWIP_AUTOIP is not set -# CONFIG_LWIP_IPV6_AUTOCONFIG is not set -CONFIG_LWIP_NETIF_LOOPBACK=y -CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 - -# -# TCP -# -CONFIG_LWIP_MAX_ACTIVE_TCP=16 -CONFIG_LWIP_MAX_LISTENING_TCP=16 -CONFIG_LWIP_TCP_MAXRTX=12 -CONFIG_LWIP_TCP_SYNMAXRTX=6 -CONFIG_LWIP_TCP_MSS=1440 -CONFIG_LWIP_TCP_TMR_INTERVAL=250 -CONFIG_LWIP_TCP_MSL=60000 -CONFIG_LWIP_TCP_SND_BUF_DEFAULT=5744 -CONFIG_LWIP_TCP_WND_DEFAULT=5744 -CONFIG_LWIP_TCP_RECVMBOX_SIZE=6 -CONFIG_LWIP_TCP_QUEUE_OOSEQ=y -# CONFIG_LWIP_TCP_SACK_OUT is not set -# CONFIG_LWIP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set -CONFIG_LWIP_TCP_OVERSIZE_MSS=y -# CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set -# CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set -CONFIG_LWIP_TCP_RTO_TIME=3000 -# end of TCP - -# -# UDP -# -CONFIG_LWIP_MAX_UDP_PCBS=16 -CONFIG_LWIP_UDP_RECVMBOX_SIZE=6 -# end of UDP - -CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=3072 -CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY=y -# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0 is not set -# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU1 is not set -CONFIG_LWIP_TCPIP_TASK_AFFINITY=0x7FFFFFFF -# CONFIG_LWIP_PPP_SUPPORT is not set -CONFIG_LWIP_IPV6_MEMP_NUM_ND6_QUEUE=3 -CONFIG_LWIP_IPV6_ND6_NUM_NEIGHBORS=5 - -# -# ICMP -# -# CONFIG_LWIP_MULTICAST_PING is not set -# CONFIG_LWIP_BROADCAST_PING is not set -# end of ICMP - -# -# LWIP RAW API -# -CONFIG_LWIP_MAX_RAW_PCBS=16 -# end of LWIP RAW API - -# -# SNTP -# -CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 -CONFIG_LWIP_SNTP_UPDATE_DELAY=3600000 -# end of SNTP -# end of LWIP - -# -# mbedTLS -# -CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y -# CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set -# CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set -CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y -CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 -CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 -# CONFIG_MBEDTLS_DEBUG is not set -# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set -# CONFIG_MBEDTLS_CMAC_C is not set -CONFIG_MBEDTLS_HARDWARE_AES=y -# CONFIG_MBEDTLS_HARDWARE_MPI is not set -CONFIG_MBEDTLS_HARDWARE_SHA=y -CONFIG_MBEDTLS_HAVE_TIME=y -# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set -CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y -# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set -# CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set -# CONFIG_MBEDTLS_TLS_DISABLED is not set -CONFIG_MBEDTLS_TLS_SERVER=y -CONFIG_MBEDTLS_TLS_CLIENT=y -CONFIG_MBEDTLS_TLS_ENABLED=y - -# -# TLS Key Exchange Methods -# -# CONFIG_MBEDTLS_PSK_MODES is not set -CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y -CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y -# end of TLS Key Exchange Methods - -CONFIG_MBEDTLS_SSL_RENEGOTIATION=y -# CONFIG_MBEDTLS_SSL_PROTO_SSL3 is not set -CONFIG_MBEDTLS_SSL_PROTO_TLS1=y -CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y -CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y -# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set -CONFIG_MBEDTLS_SSL_ALPN=y -CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y -CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y - -# -# Symmetric Ciphers -# -CONFIG_MBEDTLS_AES_C=y -# CONFIG_MBEDTLS_CAMELLIA_C is not set -# CONFIG_MBEDTLS_DES_C is not set -CONFIG_MBEDTLS_RC4_DISABLED=y -# CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT is not set -# CONFIG_MBEDTLS_RC4_ENABLED is not set -# CONFIG_MBEDTLS_BLOWFISH_C is not set -# CONFIG_MBEDTLS_XTEA_C is not set -CONFIG_MBEDTLS_CCM_C=y -CONFIG_MBEDTLS_GCM_C=y -# end of Symmetric Ciphers - -# CONFIG_MBEDTLS_RIPEMD160_C is not set - -# -# Certificates -# -CONFIG_MBEDTLS_PEM_PARSE_C=y -CONFIG_MBEDTLS_PEM_WRITE_C=y -CONFIG_MBEDTLS_X509_CRL_PARSE_C=y -CONFIG_MBEDTLS_X509_CSR_PARSE_C=y -# end of Certificates - -CONFIG_MBEDTLS_ECP_C=y -CONFIG_MBEDTLS_ECDH_C=y -CONFIG_MBEDTLS_ECDSA_C=y -CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y -CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y -CONFIG_MBEDTLS_ECP_NIST_OPTIM=y -# CONFIG_MBEDTLS_SECURITY_RISKS is not set -# end of mbedTLS - -# -# mDNS -# -CONFIG_MDNS_MAX_SERVICES=10 -CONFIG_MDNS_TASK_PRIORITY=1 -# CONFIG_MDNS_TASK_AFFINITY_NO_AFFINITY is not set -CONFIG_MDNS_TASK_AFFINITY_CPU0=y -# CONFIG_MDNS_TASK_AFFINITY_CPU1 is not set -CONFIG_MDNS_TASK_AFFINITY=0x0 -CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS=2000 -CONFIG_MDNS_TIMER_PERIOD_MS=100 -# end of mDNS - -# -# ESP-MQTT Configurations -# -CONFIG_MQTT_PROTOCOL_311=y -CONFIG_MQTT_TRANSPORT_SSL=y -CONFIG_MQTT_TRANSPORT_WEBSOCKET=y -CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y -# CONFIG_MQTT_USE_CUSTOM_CONFIG is not set -# CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED is not set -# CONFIG_MQTT_CUSTOM_OUTBOX is not set -# end of ESP-MQTT Configurations - -# -# Newlib -# -CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y -# CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set -# CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set -# CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set -# CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set -CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y -# CONFIG_NEWLIB_NANO_FORMAT is not set -# end of Newlib - -# -# NVS -# -# end of NVS - -# -# OpenSSL -# -# CONFIG_OPENSSL_DEBUG is not set -# CONFIG_OPENSSL_ASSERT_DO_NOTHING is not set -CONFIG_OPENSSL_ASSERT_EXIT=y -# end of OpenSSL - -# -# PThreads -# -CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 -CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 -CONFIG_PTHREAD_STACK_MIN=768 -CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY=y -# CONFIG_PTHREAD_DEFAULT_CORE_0 is not set -# CONFIG_PTHREAD_DEFAULT_CORE_1 is not set -CONFIG_PTHREAD_TASK_CORE_DEFAULT=-1 -CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread" -# end of PThreads - -# -# SPI Flash driver -# -# CONFIG_SPI_FLASH_VERIFY_WRITE is not set -# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set -CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y -CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y -# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set -# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set -# CONFIG_SPI_FLASH_USE_LEGACY_IMPL is not set -# CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set -CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y -CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 -CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 - -# -# Auto-detect flash chips -# -CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y -CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y -# end of Auto-detect flash chips -# end of SPI Flash driver - -# -# SPIFFS Configuration -# -CONFIG_SPIFFS_MAX_PARTITIONS=3 - -# -# SPIFFS Cache Configuration -# -CONFIG_SPIFFS_CACHE=y -CONFIG_SPIFFS_CACHE_WR=y -# CONFIG_SPIFFS_CACHE_STATS is not set -# end of SPIFFS Cache Configuration - -CONFIG_SPIFFS_PAGE_CHECK=y -CONFIG_SPIFFS_GC_MAX_RUNS=10 -# CONFIG_SPIFFS_GC_STATS is not set -CONFIG_SPIFFS_PAGE_SIZE=256 -CONFIG_SPIFFS_OBJ_NAME_LEN=32 -# CONFIG_SPIFFS_FOLLOW_SYMLINKS is not set -CONFIG_SPIFFS_USE_MAGIC=y -CONFIG_SPIFFS_USE_MAGIC_LENGTH=y -CONFIG_SPIFFS_META_LENGTH=4 -CONFIG_SPIFFS_USE_MTIME=y - -# -# Debug Configuration -# -# CONFIG_SPIFFS_DBG is not set -# CONFIG_SPIFFS_API_DBG is not set -# CONFIG_SPIFFS_GC_DBG is not set -# CONFIG_SPIFFS_CACHE_DBG is not set -# CONFIG_SPIFFS_CHECK_DBG is not set -# CONFIG_SPIFFS_TEST_VISUALISATION is not set -# end of Debug Configuration -# end of SPIFFS Configuration - -# -# Unity unit testing library -# -CONFIG_UNITY_ENABLE_FLOAT=y -CONFIG_UNITY_ENABLE_DOUBLE=y -# CONFIG_UNITY_ENABLE_COLOR is not set -CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y -# CONFIG_UNITY_ENABLE_FIXTURE is not set -# CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL is not set -# end of Unity unit testing library - -# -# Virtual file system -# -CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT=y -CONFIG_VFS_SUPPORT_TERMIOS=y - -# -# Host File System I/O (Semihosting) -# -CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1 -CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN=128 -# end of Host File System I/O (Semihosting) -# end of Virtual file system - -# -# Wear Levelling -# -# CONFIG_WL_SECTOR_SIZE_512 is not set -CONFIG_WL_SECTOR_SIZE_4096=y -CONFIG_WL_SECTOR_SIZE=4096 -# end of Wear Levelling - -# -# Wi-Fi Provisioning Manager -# -CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16 -CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 -# end of Wi-Fi Provisioning Manager - -# -# Supplicant -# -CONFIG_WPA_MBEDTLS_CRYPTO=y -# CONFIG_WPA_TLS_V12 is not set -# CONFIG_WPA_WPS_WARS is not set -# end of Supplicant -# end of Component config - -# -# Compatibility options -# -# CONFIG_LEGACY_INCLUDE_COMMON_HEADERS is not set -# end of Compatibility options diff --git a/nesper.nimble b/nesper.nimble index e96be411..9aede13a 100644 --- a/nesper.nimble +++ b/nesper.nimble @@ -1,6 +1,6 @@ # Package -version = "0.8.1" +version = "0.8.2" author = "Jaremy Creechley" description = "Nim wrappers for ESP-IDF (ESP32)" license = "Apache-2.0" @@ -13,3 +13,5 @@ requires "msgpack4nim >= 0.3.1" requires "stew >= 0.1.0" requires "bytesequtils" +feature "examples": + requires "https://github.com/elcritch/fastrpc >= 0.5.1" \ No newline at end of file diff --git a/src/nesper/build_utils/builds.nim b/src/nesper/build_utils/builds.nim index 8a700e82..629f940a 100644 --- a/src/nesper/build_utils/builds.nim +++ b/src/nesper/build_utils/builds.nim @@ -37,6 +37,11 @@ task espClean, "clean the Nim code": rmDir(cacheDir) mkDir(cacheDir) +task espDistClean, "clean the Nim code": + espCleanTask() + rmDir("build") + rmFile("sdkconfig") # this will trigger going back to sdkconfig.defaults + task espSetup, "setup the esp-idf project": let idfPath = getEnv("IDF_PATH") if idfPath == "": diff --git a/src/nesper/components/esp_tinyusb/tinyusb.nim b/src/nesper/components/esp_tinyusb/tinyusb.nim new file mode 100644 index 00000000..f6818ac8 --- /dev/null +++ b/src/nesper/components/esp_tinyusb/tinyusb.nim @@ -0,0 +1,62 @@ +## +## SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +## +## SPDX-License-Identifier: Apache-2.0 +## +import ../../consts +import ./tusb_types +import ./tusb_cdc_acm +import ./tusb_tasks +import ./tusb_config + +import ./tinyusb_types + +export tusb_types, tusb_cdc_acm, tinyusb_types, tusb_tasks, tusb_config + +type + + tinyusb_config_t* {.importc: "tinyusb_config_t", header: "tinyusb.h", bycopy.} = object ## + ## + ## @brief Configuration structure of the TinyUSB core + ## + ## USB specification mandates self-powered devices to monitor USB VBUS to detect connection/disconnection events. + ## If you want to use this feature, connected VBUS to any free GPIO through a voltage divider or voltage comparator. + ## The voltage divider output should be (0.75 * Vdd) if VBUS is 4.4V (lowest valid voltage at device port). + ## The comparator thresholds should be set with hysteresis: 4.35V (falling edge) and 4.75V (raising edge). + ## + device_descriptor* {.importc: "device_descriptor".}: ptr tusb_desc_device_t ## + ## !< Pointer to a device descriptor. If set to NULL, the TinyUSB device will use a default device descriptor whose values are set in Kconfig + string_descriptor* {.importc: "string_descriptor".}: cstringArray ## + ## !< Pointer to an array of string descriptors + external_phy* {.importc: "external_phy".}: bool ## + ## !< Should USB use an external PHY + configuration_descriptor* {.importc: "configuration_descriptor".}: ptr uint8 ## + ## !< Pointer to a configuration descriptor. If set to NULL, TinyUSB device will use a default configuration descriptor whose values are set in Kconfig + self_powered* {.importc: "self_powered".}: bool ## + ## !< This is a self-powered USB device. USB VBUS must be monitored. + vbus_monitor_io* {.importc: "vbus_monitor_io".}: cint + ## !< GPIO for VBUS monitoring. Ignored if not self_powered. + + + +proc tinyusb_driver_install*(config: ptr tinyusb_config_t): esp_err_t {.cdecl, + importc: "tinyusb_driver_install", header: "tinyusb.h".} + ## + ## + ## @brief This is an all-in-one helper function, including: + ## 1. USB device driver initialization + ## 2. Descriptors preparation + ## 3. TinyUSB stack initialization + ## 4. Creates and start a task to handle usb events + ## + ## @note Don't change Custom descriptor, but if it has to be done, + ## Suggest to define as follows in order to match the Interface Association Descriptor (IAD): + ## bDeviceClass = TUSB_CLASS_MISC, + ## bDeviceSubClass = MISC_SUBCLASS_COMMON, + ## + ## @param config tinyusb stack specific configuration + ## @retval ESP_ERR_INVALID_ARG Install driver and tinyusb stack failed because of invalid argument + ## @retval ESP_FAIL Install driver and tinyusb stack failed because of internal error + ## @retval ESP_OK Install driver and tinyusb stack successfully + ## +## TODO esp_err_t tinyusb_driver_uninstall(void); (IDF-1474) diff --git a/src/nesper/components/esp_tinyusb/tinyusb_net.nim b/src/nesper/components/esp_tinyusb/tinyusb_net.nim new file mode 100644 index 00000000..a415a769 --- /dev/null +++ b/src/nesper/components/esp_tinyusb/tinyusb_net.nim @@ -0,0 +1,20 @@ +import ../../consts + +import ./tinyusb_types +import ./tusb_cdc_acm + +# TinyUSB NET (NCM) imports +type + tinyusb_net_config_t* {.importc: "tinyusb_net_config_t", header: "tinyusb_net.h", bycopy.} = object + mac_addr* {.importc: "mac_addr".}: array[6, uint8] + on_recv_callback* {.importc: "on_recv_callback".}: proc (buffer: pointer; len: uint16; ctx: pointer): esp_err_t {.cdecl.} + free_tx_buffer* {.importc: "free_tx_buffer".}: proc (eb: pointer; ctx: pointer) {.cdecl.} + on_init_callback* {.importc: "on_init_callback".}: proc (ctx: pointer) {.cdecl.} + user_context* {.importc: "user_context".}: pointer + +proc tinyusb_net_init*(dev: tinyusb_usbdev_t; cfg: ptr tinyusb_net_config_t): esp_err_t {.cdecl, importc: "tinyusb_net_init", header: "tinyusb_net.h".} +proc tinyusb_net_send_sync*(buffer: pointer; len: uint16; eb: pointer; timeout_ticks: uint32): esp_err_t {.cdecl, importc: "tinyusb_net_send_sync", header: "tinyusb_net.h".} + +# tinyusb console helpers (not wrapped): +proc esp_tusb_init_console*(itf: tinyusb_cdcacm_itf_t): esp_err_t {.cdecl, importc: "esp_tusb_init_console", header: "tusb_console.h".} +proc esp_tusb_deinit_console*(itf: tinyusb_cdcacm_itf_t): esp_err_t {.cdecl, importc: "esp_tusb_deinit_console", header: "tusb_console.h".} diff --git a/src/nesper/components/esp_tinyusb/tinyusb_types.nim b/src/nesper/components/esp_tinyusb/tinyusb_types.nim new file mode 100644 index 00000000..bb6c956a --- /dev/null +++ b/src/nesper/components/esp_tinyusb/tinyusb_types.nim @@ -0,0 +1,16 @@ + +const + USB_ESPRESSIF_VID* = 0x303A ## + ## SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + ## + ## SPDX-License-Identifier: Apache-2.0 + ## + USB_STRING_DESCRIPTOR_ARRAY_SIZE* = 8 + +type + + tinyusb_usbdev_t* {.size: sizeof(cint).} = enum + TINYUSB_USBDEV_0 + + tusb_desc_strarray_device_t* = array[USB_STRING_DESCRIPTOR_ARRAY_SIZE, cstring] + diff --git a/src/nesper/components/esp_tinyusb/tusb_cdc_acm.nim b/src/nesper/components/esp_tinyusb/tusb_cdc_acm.nim new file mode 100644 index 00000000..9f48173a --- /dev/null +++ b/src/nesper/components/esp_tinyusb/tusb_cdc_acm.nim @@ -0,0 +1,215 @@ +## +## SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +## +## SPDX-License-Identifier: Apache-2.0 +## + +import ../../consts +import ./tinyusb_types +import ./tusb_types + +type + + tinyusb_cdcacm_itf_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief CDC ports available to setup + ## + TINYUSB_CDC_ACM_0 = 0x0, TINYUSB_CDC_ACM_1, TINYUSB_CDC_ACM_MAX + + +type + + cdcacm_event_rx_wanted_char_data_t* {.importc: "cdcacm_event_rx_wanted_char_data_t", + header: "tusb_cdc_acm.h", bycopy.} = object ## + ## Callbacks and events + ## ******************************************************************** + ## + ## @brief Data provided to the input of the `callback_rx_wanted_char` callback + ## + wanted_char* {.importc: "wanted_char".}: char + ## !< Wanted character + + + cdcacm_event_line_state_changed_data_t* {. + importc: "cdcacm_event_line_state_changed_data_t", + header: "tusb_cdc_acm.h", bycopy.} = object ## + ## + ## @brief Data provided to the input of the `callback_line_state_changed` callback + ## + dtr* {.importc: "dtr".}: bool ## !< Data Terminal Ready (DTR) line state + rts* {.importc: "rts".}: bool + ## !< Request To Send (RTS) line state + + + cdcacm_event_line_coding_changed_data_t* {. + importc: "cdcacm_event_line_coding_changed_data_t", + header: "tusb_cdc_acm.h", bycopy.} = object ## + ## + ## @brief Data provided to the input of the `line_coding_changed` callback + ## + p_line_coding* {.importc: "p_line_coding".}: ptr cdc_line_coding_t + ## !< New line coding value + + + cdcacm_event_type_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Types of CDC ACM events + ## + CDC_EVENT_RX, CDC_EVENT_RX_WANTED_CHAR, CDC_EVENT_LINE_STATE_CHANGED, + CDC_EVENT_LINE_CODING_CHANGED + + +type + + cdcacm_event_t* {.importc: "cdcacm_event_t", header: "tusb_cdc_acm.h", bycopy.} = object ## + ## + ## @brief Describes an event passing to the input of a callbacks + ## + `type`* {.importc: "type".}: cdcacm_event_type_t ## + ## !< Event type + rx_wanted_char_data* {.importc: "rx_wanted_char_data".}: cdcacm_event_rx_wanted_char_data_t ## + ## !< Data input of the `callback_rx_wanted_char` callback + line_state_changed_data* {.importc: "line_state_changed_data".}: cdcacm_event_line_state_changed_data_t ## + ## !< Data input of the `callback_line_state_changed` callback + line_coding_changed_data* {.importc: "line_coding_changed_data".}: cdcacm_event_line_coding_changed_data_t + ## !< Data input of the `line_coding_changed` callback + + + tusb_cdcacm_callback_t* = proc (itf: cint; event: ptr cdcacm_event_t) {.cdecl.} ## + ## + ## @brief CDC-ACM callback type + ## + + tinyusb_config_cdcacm_t* {.importc: "tinyusb_config_cdcacm_t", + header: "tusb_cdc_acm.h", bycopy.} = object ## + ## ********************************************************************* Callbacks and events + ## Other structs + ## ******************************************************************** + ## + ## @brief Configuration structure for CDC-ACM + ## + usb_dev* {.importc: "usb_dev".}: tinyusb_usbdev_t ## + ## !< Usb device to set up + cdc_port* {.importc: "cdc_port".}: tinyusb_cdcacm_itf_t ## + ## !< CDC port + rx_unread_buf_sz* {.importc: "rx_unread_buf_sz".}: csize_t ## + ## !< Amount of data that can be passed to the ACM at once + callback_rx* {.importc: "callback_rx".}: tusb_cdcacm_callback_t ## + ## !< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback + callback_rx_wanted_char* {.importc: "callback_rx_wanted_char".}: tusb_cdcacm_callback_t ## + ## !< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback + callback_line_state_changed* {.importc: "callback_line_state_changed".}: tusb_cdcacm_callback_t ## + ## !< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback + callback_line_coding_changed* {.importc: "callback_line_coding_changed".}: tusb_cdcacm_callback_t + ## !< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback + + + +proc tusb_cdc_acm_init*(cfg: ptr tinyusb_config_cdcacm_t): esp_err_t {.cdecl, + importc: "tusb_cdc_acm_init", header: "tusb_cdc_acm.h".} + ## + ## ********************************************************************* Other structs + ## Public functions + ## ******************************************************************** + ## + ## @brief Initialize CDC ACM. Initialization will be finished with + ## the `tud_cdc_line_state_cb` callback + ## + ## @param cfg - init configuration structure + ## @return esp_err_t + ## + +proc tinyusb_cdcacm_register_callback*(itf: tinyusb_cdcacm_itf_t; + event_type: cdcacm_event_type_t; + callback: tusb_cdcacm_callback_t): esp_err_t {. + cdecl, importc: "tinyusb_cdcacm_register_callback", header: "tusb_cdc_acm.h".} + ## + ## + ## @brief Register a callback invoking on CDC event. If the callback had been + ## already registered, it will be overwritten + ## + ## @param itf - number of a CDC object + ## @param event_type - type of registered event for a callback + ## @param callback - callback function + ## @return esp_err_t - ESP_OK or ESP_ERR_INVALID_ARG + ## + +proc tinyusb_cdcacm_unregister_callback*(itf: tinyusb_cdcacm_itf_t; + event_type: cdcacm_event_type_t): esp_err_t {.cdecl, + importc: "tinyusb_cdcacm_unregister_callback", header: "tusb_cdc_acm.h".} + ## + ## + ## @brief Unregister a callback invoking on CDC event. + ## + ## @param itf - number of a CDC object + ## @param event_type - type of registered event for a callback + ## @return esp_err_t - ESP_OK or ESP_ERR_INVALID_ARG + ## + +proc tinyusb_cdcacm_write_queue_char*(itf: tinyusb_cdcacm_itf_t; ch: char): csize_t {. + cdecl, importc: "tinyusb_cdcacm_write_queue_char", header: "tusb_cdc_acm.h".} + ## + ## + ## @brief Sent one character to a write buffer + ## + ## @param itf - number of a CDC object + ## @param ch - character to send + ## @return size_t - amount of queued bytes + ## + +proc tinyusb_cdcacm_write_queue*(itf: tinyusb_cdcacm_itf_t; in_buf: ptr uint8; + in_size: csize_t): csize_t {.cdecl, + importc: "tinyusb_cdcacm_write_queue", header: "tusb_cdc_acm.h".} + ## + ## + ## @brief Write data to write buffer from a byte array + ## + ## @param itf - number of a CDC object + ## @param in_buf - a source array + ## @param in_size - size to write from arr_src + ## @return size_t - amount of queued bytes + ## + +proc tinyusb_cdcacm_write_flush*(itf: tinyusb_cdcacm_itf_t; + timeout_ticks: uint32): esp_err_t {.cdecl, + importc: "tinyusb_cdcacm_write_flush", header: "tusb_cdc_acm.h".} + ## + ## + ## @brief Send all data from a write buffer. Use `tinyusb_cdcacm_write_queue` to add data to the buffer. + ## + ## WARNING! TinyUSB can block output Endpoint for several RX callbacks, after will do additional flush + ## after the each trasfer. That can leads to the situation when you requested a flush, but it will fail until + ## ont of the next callbacks ends. + ## SO USING OF THE FLUSH WITH TIMEOUTS IN CALLBACKS IS NOT RECOMENDED - YOU CAN GET A LOCK FOR THE TIMEOUT + ## + ## @param itf - number of a CDC object + ## @param timeout_ticks - waiting until flush will be considered as failed + ## @return esp_err_t - ESP_OK if (timeout_ticks > 0) and and flush was successful, + ## ESP_ERR_TIMEOUT if timeout occurred3 or flush was successful with (timeout_ticks == 0) + ## ESP_FAIL if flush was unsuccessful + ## + +proc tinyusb_cdcacm_read*(itf: tinyusb_cdcacm_itf_t; out_buf: ptr uint8; + out_buf_sz: csize_t; rx_data_size: ptr csize_t): esp_err_t {. + cdecl, importc: "tinyusb_cdcacm_read", header: "tusb_cdc_acm.h".} + ## + ## + ## @brief Read a content to the array, and defines it's size to the sz_store + ## + ## @param itf - number of a CDC object + ## @param out_buf - to this array will be stored the object from a CDC buffer + ## @param out_buf_sz - size of buffer for results + ## @param rx_data_size - to this address will be stored the object's size + ## @return esp_err_t ESP_OK, ESP_FAIL or ESP_ERR_INVALID_STATE + ## + +proc tusb_cdc_acm_initialized*(itf: tinyusb_cdcacm_itf_t): bool {.cdecl, + importc: "tusb_cdc_acm_initialized", header: "tusb_cdc_acm.h".} + ## + ## + ## @brief Check if the ACM initialized + ## + ## @param itf - number of a CDC object + ## @return true or false + ## +## ********************************************************************* Public functions diff --git a/src/nesper/components/esp_tinyusb/tusb_config.nim b/src/nesper/components/esp_tinyusb/tusb_config.nim new file mode 100644 index 00000000..4d796440 --- /dev/null +++ b/src/nesper/components/esp_tinyusb/tusb_config.nim @@ -0,0 +1,87 @@ +## +## The MIT License (MIT) +## +## Copyright (c) 2019 Ha Thach (tinyusb.org), +## Additions Copyright (c) 2020, Espressif Systems (Shanghai) PTE LTD +## +## 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. +## +## + +import ../../consts + +# when not defined(CONFIG_TINYUSB_CDC_ENABLED): +# const +# CONFIG_TINYUSB_CDC_ENABLED* = 0 +# when not defined(CONFIG_TINYUSB_CDC_COUNT): +# const +# CONFIG_TINYUSB_CDC_COUNT* = 0 +# when not defined(CONFIG_TINYUSB_MSC_ENABLED): +# const +# CONFIG_TINYUSB_MSC_ENABLED* = 0 +# when not defined(CONFIG_TINYUSB_HID_COUNT): +# const +# CONFIG_TINYUSB_HID_COUNT* = 0 +# when not defined(CONFIG_TINYUSB_MIDI_COUNT): +# const +# CONFIG_TINYUSB_MIDI_COUNT* = 0 +# when not defined(CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED): +# const +# CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED* = 0 +# when not defined(CONFIG_TINYUSB_DEBUG_LEVEL): +# const +# CONFIG_TINYUSB_DEBUG_LEVEL* = 0 + +# const +# CFG_TUSB_RHPORT0_MODE* = OPT_MODE_DEVICE or OPT_MODE_FULL_SPEED +# CFG_TUSB_OS* = OPT_OS_FREERTOS + +## USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. +## Tinyusb use follows macros to declare transferring memory so that they can be put +## into those specific section. +## e.g +## - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) +## - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) +## + +# when not defined(CFG_TUSB_MEM_ALIGN): +# const +# CFG_TUSB_MEM_ALIGN* = TU_ATTR_ALIGNED(4) +# when not defined(CFG_TUD_ENDPOINT0_SIZE): +# const +# CFG_TUD_ENDPOINT0_SIZE* = 64 + +let CONFIG_TINYUSB_CDC_RX_BUFSIZE* {.importc: "$1", header: "tinyusb.h".}: cint + +# const +# CFG_TUSB_DEBUG* = CONFIG_TINYUSB_DEBUG_LEVEL ## Debug Level +# CFG_TUD_CDC_RX_BUFSIZE* = CONFIG_TINYUSB_CDC_RX_BUFSIZE ## +# ## CDC FIFO size of TX and RX +# CFG_TUD_CDC_TX_BUFSIZE* = CONFIG_TINYUSB_CDC_TX_BUFSIZE +# CFG_TUD_MSC_BUFSIZE* = CONFIG_TINYUSB_MSC_BUFSIZE ## +# ## MSC Buffer size of Device Mass storage +# CFG_TUD_MIDI_EP_BUFSIZE* = 64 +# CFG_TUD_MIDI_EPSIZE* = CFG_TUD_MIDI_EP_BUFSIZE +# CFG_TUD_MIDI_RX_BUFSIZE* = 64 +# CFG_TUD_MIDI_TX_BUFSIZE* = 64 +# CFG_TUD_CDC* = CONFIG_TINYUSB_CDC_COUNT ## Enabled device class driver +# CFG_TUD_MSC* = CONFIG_TINYUSB_MSC_ENABLED +# CFG_TUD_HID* = CONFIG_TINYUSB_HID_COUNT +# CFG_TUD_MIDI* = CONFIG_TINYUSB_MIDI_COUNT +# CFG_TUD_CUSTOM_CLASS* = CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED diff --git a/src/nesper/components/esp_tinyusb/tusb_tasks.nim b/src/nesper/components/esp_tinyusb/tusb_tasks.nim new file mode 100644 index 00000000..62e48f3c --- /dev/null +++ b/src/nesper/components/esp_tinyusb/tusb_tasks.nim @@ -0,0 +1,32 @@ +## +## SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +## +## SPDX-License-Identifier: Apache-2.0 +## + +import ../../consts + +proc tusb_run_task*(): esp_err_t {.cdecl, importc: "tusb_run_task", + header: "tusb_tasks.h".} + ## + ## + ## @brief This helper function creates and starts a task which wraps `tud_task()`. + ## + ## The wrapper function basically wraps tud_task and some log. + ## Default parameters: stack size and priority as configured, argument = NULL, not pinned to any core. + ## If you have more requirements for this task, you can create your own task which calls tud_task as the last step. + ## + ## @retval ESP_OK run tinyusb main task successfully + ## @retval ESP_FAIL run tinyusb main task failed of internal error + ## @retval ESP_ERR_INVALID_STATE tinyusb main task has been created before + ## + +proc tusb_stop_task*(): esp_err_t {.cdecl, importc: "tusb_stop_task", + header: "tusb_tasks.h".} + ## + ## + ## @brief This helper function stops and destroys the task created by `tusb_run_task()` + ## + ## @retval ESP_OK stop and destory tinyusb main task successfully + ## @retval ESP_ERR_INVALID_STATE tinyusb main task hasn't been created yet + ## \ No newline at end of file diff --git a/src/nesper/components/esp_tinyusb/tusb_types.nim b/src/nesper/components/esp_tinyusb/tusb_types.nim new file mode 100644 index 00000000..30c3a0ad --- /dev/null +++ b/src/nesper/components/esp_tinyusb/tusb_types.nim @@ -0,0 +1,540 @@ +## +## The MIT License (MIT) +## +## Copyright (c) 2019 Ha Thach (tinyusb.org) +## +## 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. +## +## This file is part of the TinyUSB stack. +## + +## ------------- Device DCache declaration -------------// + +## Declare an endpoint buffer with uint8_t[size] + +## Declare an endpoint buffer with a type + +## ------------- Host DCache declaration -------------// + +## Declare an endpoint buffer with uint8_t[size] + +## Declare an endpoint buffer with a type + +# const hdr_acm = "tusb_cdc_acm.h" +# const hdr = "tusb_types.h" +const hdr_acm = "tusb.h" +const hdr = "tusb.h" + +type + cdc_line_coding_t* {.importc: "cdc_line_coding_t", header: hdr_acm, bycopy.} = object + bit_rate*: uint32 + stop_bits*: uint8 + parity*: uint8 + data_bits*: uint8 + + cdc_line_control_state_t* {.importc: "cdc_line_control_state_t", header: hdr_acm, bycopy.} = object + dtr*: bool + rts*: bool + + +type + + tusb_role_t* {.size: sizeof(cint).} = enum ## ------------------------------------------------------------------ + ## CONSTANTS + ## ------------------------------------------------------------------ + TUSB_ROLE_INVALID = 0, TUSB_ROLE_DEVICE = 0x1, TUSB_ROLE_HOST = 0x2 + + +type + + tusb_speed_t* {.size: sizeof(cint).} = enum ## defined base on EHCI specs value for Endpoint Speed + TUSB_SPEED_FULL = 0, TUSB_SPEED_LOW = 1, TUSB_SPEED_HIGH = 2, + TUSB_SPEED_AUTO = 0xaa, TUSB_SPEED_INVALID = 0xff + + +type + + tusb_xfer_type_t* {.size: sizeof(cint).} = enum ## + ## defined base on USB Specs Endpoint's bmAttributes + TUSB_XFER_CONTROL = 0, TUSB_XFER_ISOCHRONOUS = 1, TUSB_XFER_BULK = 2, + TUSB_XFER_INTERRUPT = 3 + + tusb_dir_t* {.size: sizeof(cint).} = enum + TUSB_DIR_OUT = 0, TUSB_DIR_IN = 1, TUSB_DIR_IN_MASK = 0x80 + +const + TUSB_EPSIZE_BULK_FS* = 64 + TUSB_EPSIZE_BULK_HS* = 512 + TUSB_EPSIZE_ISO_FS_MAX* = 1023 + TUSB_EPSIZE_ISO_HS_MAX* = 1024 + +type + + tusb_iso_ep_attribute_t* {.size: sizeof(cint).} = enum ## + ## Isochronous Endpoint Attributes + TUSB_ISO_EP_ATT_NO_SYNC = 0x00, TUSB_ISO_EP_ATT_ASYNCHRONOUS = 0x04, + TUSB_ISO_EP_ATT_ADAPTIVE = 0x08, TUSB_ISO_EP_ATT_SYNCHRONOUS = 0x0C, TUSB_ISO_EP_ATT_EXPLICIT_FB = 0x10, ## + ## < Feedback End Point + TUSB_ISO_EP_ATT_IMPLICIT_FB = 0x20 ## < Data endpoint that also serves as an implicit feedback + +const + TUSB_ISO_EP_ATT_DATA = TUSB_ISO_EP_ATT_NO_SYNC + +type + + tusb_desc_type_t* {.size: sizeof(cint).} = enum ## + ## USB Descriptor Types + TUSB_DESC_DEVICE = 0x01, TUSB_DESC_CONFIGURATION = 0x02, + TUSB_DESC_STRING = 0x03, TUSB_DESC_INTERFACE = 0x04, + TUSB_DESC_ENDPOINT = 0x05, TUSB_DESC_DEVICE_QUALIFIER = 0x06, + TUSB_DESC_OTHER_SPEED_CONFIG = 0x07, TUSB_DESC_INTERFACE_POWER = 0x08, + TUSB_DESC_OTG = 0x09, TUSB_DESC_DEBUG = 0x0A, + TUSB_DESC_INTERFACE_ASSOCIATION = 0x0B, TUSB_DESC_BOS = 0x0F, + TUSB_DESC_DEVICE_CAPABILITY = 0x10, TUSB_DESC_FUNCTIONAL = 0x21, ## + ## Class Specific Descriptor + TUSB_DESC_CS_CONFIGURATION = 0x22, TUSB_DESC_CS_STRING = 0x23, + TUSB_DESC_CS_INTERFACE = 0x24, TUSB_DESC_CS_ENDPOINT = 0x25, + TUSB_DESC_SUPERSPEED_ENDPOINT_COMPANION = 0x30, + TUSB_DESC_SUPERSPEED_ISO_ENDPOINT_COMPANION = 0x31 + + tusb_request_code_t* {.size: sizeof(cint).} = enum + TUSB_REQ_GET_STATUS = 0, TUSB_REQ_CLEAR_FEATURE = 1, TUSB_REQ_RESERVED = 2, + TUSB_REQ_SET_FEATURE = 3, TUSB_REQ_RESERVED2 = 4, TUSB_REQ_SET_ADDRESS = 5, + TUSB_REQ_GET_DESCRIPTOR = 6, TUSB_REQ_SET_DESCRIPTOR = 7, + TUSB_REQ_GET_CONFIGURATION = 8, TUSB_REQ_SET_CONFIGURATION = 9, + TUSB_REQ_GET_INTERFACE = 10, TUSB_REQ_SET_INTERFACE = 11, + TUSB_REQ_SYNCH_FRAME = 12 + + tusb_request_feature_selector_t* {.size: sizeof(cint).} = enum + TUSB_REQ_FEATURE_EDPT_HALT = 0, TUSB_REQ_FEATURE_REMOTE_WAKEUP = 1, + TUSB_REQ_FEATURE_TEST_MODE = 2 + + tusb_request_type_t* {.size: sizeof(cint).} = enum + TUSB_REQ_TYPE_STANDARD = 0, TUSB_REQ_TYPE_CLASS, TUSB_REQ_TYPE_VENDOR, + TUSB_REQ_TYPE_INVALID + + tusb_request_recipient_t* {.size: sizeof(cint).} = enum + TUSB_REQ_RCPT_DEVICE = 0, TUSB_REQ_RCPT_INTERFACE, TUSB_REQ_RCPT_ENDPOINT, + TUSB_REQ_RCPT_OTHER + +const + TUSB_DESC_CS_DEVICE = TUSB_DESC_FUNCTIONAL + +type + + tusb_class_code_t* {.size: sizeof(cint).} = enum ## + ## https://www.usb.org/defined-class-codes + TUSB_CLASS_UNSPECIFIED = 0, TUSB_CLASS_AUDIO = 1, TUSB_CLASS_CDC = 2, + TUSB_CLASS_HID = 3, TUSB_CLASS_RESERVED_4 = 4, TUSB_CLASS_PHYSICAL = 5, + TUSB_CLASS_IMAGE = 6, TUSB_CLASS_PRINTER = 7, TUSB_CLASS_MSC = 8, + TUSB_CLASS_HUB = 9, TUSB_CLASS_CDC_DATA = 10, TUSB_CLASS_SMART_CARD = 11, + TUSB_CLASS_RESERVED_12 = 12, TUSB_CLASS_CONTENT_SECURITY = 13, + TUSB_CLASS_VIDEO = 14, TUSB_CLASS_PERSONAL_HEALTHCARE = 15, + TUSB_CLASS_AUDIO_VIDEO = 16, TUSB_CLASS_DIAGNOSTIC = 0xDC, + TUSB_CLASS_WIRELESS_CONTROLLER = 0xE0, TUSB_CLASS_MISC = 0xEF, + TUSB_CLASS_APPLICATION_SPECIFIC = 0xFE, TUSB_CLASS_VENDOR_SPECIFIC = 0xFF + + misc_subclass_type_t* {.size: sizeof(cint).} = enum + MISC_SUBCLASS_COMMON = 2 + + misc_protocol_type_t* {.size: sizeof(cint).} = enum + MISC_PROTOCOL_IAD = 1 + + app_subclass_type_t* {.size: sizeof(cint).} = enum + APP_SUBCLASS_DFU_RUNTIME = 0x01, APP_SUBCLASS_USBTMC = 0x03 + + device_capability_type_t* {.size: sizeof(cint).} = enum + DEVICE_CAPABILITY_WIRELESS_USB = 0x01, + DEVICE_CAPABILITY_USB20_EXTENSION = 0x02, + DEVICE_CAPABILITY_SUPERSPEED_USB = 0x03, + DEVICE_CAPABILITY_CONTAINER_id = 0x04, DEVICE_CAPABILITY_PLATFORM = 0x05, + DEVICE_CAPABILITY_POWER_DELIVERY = 0x06, + DEVICE_CAPABILITY_BATTERY_INFO = 0x07, + DEVICE_CAPABILITY_PD_CONSUMER_PORT = 0x08, + DEVICE_CAPABILITY_PD_PROVIDER_PORT = 0x09, + DEVICE_CAPABILITY_SUPERSPEED_PLUS = 0x0A, + DEVICE_CAPABILITY_PRECESION_TIME_MEASUREMENT = 0x0B, + DEVICE_CAPABILITY_WIRELESS_USB_EXT = 0x0C, + DEVICE_CAPABILITY_BILLBOARD = 0x0D, DEVICE_CAPABILITY_AUTHENTICATION = 0x0E, + DEVICE_CAPABILITY_BILLBOARD_EX = 0x0F, + DEVICE_CAPABILITY_CONFIGURATION_SUMMARY = 0x10 + +const + TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP* = 1'u shl 5 + TUSB_DESC_CONFIG_ATT_SELF_POWERED* = 1'u shl 6 + +type + + tusb_feature_test_mode_t* {.size: sizeof(cint).} = enum ## + ## USB 2.0 Spec Table 9-7: Test Mode Selectors + TUSB_FEATURE_TEST_J = 1, TUSB_FEATURE_TEST_K = 2, + TUSB_FEATURE_TEST_SE0_NAK = 3, TUSB_FEATURE_TEST_PACKET = 4, + TUSB_FEATURE_TEST_FORCE_ENABLE = 5 + + +type + + xfer_result_t* {.size: sizeof(cint).} = enum ## --------------------------------------------------------------------+ + ## + ## --------------------------------------------------------------------+ + XFER_RESULT_SUCCESS = 0, XFER_RESULT_FAILED, XFER_RESULT_STALLED, + XFER_RESULT_TIMEOUT, XFER_RESULT_INVALID + +const + DESC_OFFSET_LEN* = 0 ## TODO remove + DESC_OFFSET_TYPE* = 1 + DESC_OFFSET_SUBTYPE* = 2 + INTERFACE_INVALID_NUMBER* = 0xff + +type + + microsoft_os_20_type_t* {.size: sizeof(cint).} = enum + MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00, + MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01, + MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02, + MS_OS_20_FEATURE_COMPATBLE_ID = 0x03, MS_OS_20_FEATURE_REG_PROPERTY = 0x04, + MS_OS_20_FEATURE_MIN_RESUME_TIME = 0x05, MS_OS_20_FEATURE_MODEL_ID = 0x06, + MS_OS_20_FEATURE_CCGP_DEVICE = 0x07, MS_OS_20_FEATURE_VENDOR_REVISION = 0x08 + +const + CONTROL_STAGE_IDLE* = 0 + CONTROL_STAGE_SETUP* = 1 ## 1 + CONTROL_STAGE_DATA* = 2 ## 2 + CONTROL_STAGE_ACK* = 3 ## 3 + TUSB_INDEX_INVALID_8* = 0xFF + +type + + tusb_rhport_init_t* {.importc: "tusb_rhport_init_t", header: hdr, + bycopy.} = object ## --------------------------------------------------------------------+ + ## + ## --------------------------------------------------------------------+ + role* {.importc: "role".}: tusb_role_t + speed* {.importc: "speed".}: tusb_speed_t + + + tusb_desc_device_t* {.importc: "tusb_desc_device_t", header: hdr, + bycopy.} = object ## --------------------------------------------------------------------+ + ## USB Descriptors + ## --------------------------------------------------------------------+ + ## Start of all packed definitions for compiler without per-type packed + ## USB Device Descriptor + bLength* {.importc: "bLength".}: uint8 ## < Size of this descriptor in bytes. + bDescriptorType* {.importc: "bDescriptorType".}: uint8 ## + ## < DEVICE Descriptor Type. + bcdUSB* {.importc: "bcdUSB".}: uint16 ## < BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). + bDeviceClass* {.importc: "bDeviceClass".}: uint8 ## + ## < Class code (assigned by the USB-IF). + bDeviceSubClass* {.importc: "bDeviceSubClass".}: uint8 ## + ## < Subclass code (assigned by the USB-IF). + bDeviceProtocol* {.importc: "bDeviceProtocol".}: uint8 ## + ## < Protocol code (assigned by the USB-IF). + bMaxPacketSize0* {.importc: "bMaxPacketSize0".}: uint8 ## + ## < Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64. + idVendor* {.importc: "idVendor".}: uint16 ## < Vendor ID (assigned by the USB-IF). + idProduct* {.importc: "idProduct".}: uint16 ## < Product ID (assigned by the manufacturer). + bcdDevice* {.importc: "bcdDevice".}: uint16 ## < Device release number in binary-coded decimal. + iManufacturer* {.importc: "iManufacturer".}: uint8 ## + ## < Index of string descriptor describing manufacturer. + iProduct* {.importc: "iProduct".}: uint8 ## < Index of string descriptor describing product. + iSerialNumber* {.importc: "iSerialNumber".}: uint8 ## + ## < Index of string descriptor describing the device's serial number. + bNumConfigurations* {.importc: "bNumConfigurations".}: uint8 + ## < Number of possible configurations. + + +type + + tusb_desc_bos_t* {.importc: "tusb_desc_bos_t", header: hdr, bycopy.} = object ## + ## USB Binary Device Object Store (BOS) Descriptor + bLength* {.importc: "bLength".}: uint8 ## < Size of this descriptor in bytes + bDescriptorType* {.importc: "bDescriptorType".}: uint8 ## + ## < CONFIGURATION Descriptor Type + wTotalLength* {.importc: "wTotalLength".}: uint16 ## + ## < Total length of data returned for this descriptor + bNumDeviceCaps* {.importc: "bNumDeviceCaps".}: uint8 + ## < Number of device capability descriptors in the BOS + + +type + + tusb_desc_configuration_t* {.importc: "tusb_desc_configuration_t", + header: hdr, bycopy.} = object ## + ## USB Configuration Descriptor + bLength* {.importc: "bLength".}: uint8 ## < Size of this descriptor in bytes + bDescriptorType* {.importc: "bDescriptorType".}: uint8 ## + ## < CONFIGURATION Descriptor Type + wTotalLength* {.importc: "wTotalLength".}: uint16 ## + ## < Total length of data returned for this configuration. Includes the combined length of all descriptors (configuration, interface, endpoint, and class- or vendor-specific) returned for this configuration. + bNumInterfaces* {.importc: "bNumInterfaces".}: uint8 ## + ## < Number of interfaces supported by this configuration + bConfigurationValue* {.importc: "bConfigurationValue".}: uint8 ## + ## < Value to use as an argument to the SetConfiguration() request to select this configuration. + iConfiguration* {.importc: "iConfiguration".}: uint8 ## + ## < Index of string descriptor describing this configuration + bmAttributes* {.importc: "bmAttributes".}: uint8 ## + ## < Configuration characteristics \n D7: Reserved (set to one)\n D6: Self-powered \n D5: Remote Wakeup \n D4...0: Reserved (reset to zero) \n D7 is reserved and must be set to one for historical reasons. \n A device configuration that uses power from the bus and a local source reports a non-zero value in bMaxPower to indicate the amount of bus power required and sets D6. The actual power source at runtime may be determined using the GetStatus(DEVICE) request (see USB 2.0 spec Section 9.4.5). \n If a device configuration supports remote wakeup, D5 is set to one. + bMaxPower* {.importc: "bMaxPower".}: uint8 + ## < Maximum power consumption of the USB device from the bus in this specific configuration when the device is fully operational. Expressed in 2 mA units (i.e., 50 = 100 mA). + + +type + + tusb_desc_interface_t* {.importc: "tusb_desc_interface_t", + header: hdr, bycopy.} = object ## + ## USB Interface Descriptor + bLength* {.importc: "bLength".}: uint8 ## < Size of this descriptor in bytes + bDescriptorType* {.importc: "bDescriptorType".}: uint8 ## + ## < INTERFACE Descriptor Type + bInterfaceNumber* {.importc: "bInterfaceNumber".}: uint8 ## + ## < Number of this interface. Zero-based value identifying the index in the array of concurrent interfaces supported by this configuration. + bAlternateSetting* {.importc: "bAlternateSetting".}: uint8 ## + ## < Value used to select this alternate setting for the interface identified in the prior field + bNumEndpoints* {.importc: "bNumEndpoints".}: uint8 ## + ## < Number of endpoints used by this interface (excluding endpoint zero). If this value is zero, this interface only uses the Default Control Pipe. + bInterfaceClass* {.importc: "bInterfaceClass".}: uint8 ## + ## < Class code (assigned by the USB-IF). \li A value of zero is reserved for future standardization. \li If this field is set to FFH, the interface class is vendor-specific. \li All other values are reserved for assignment by the USB-IF. + bInterfaceSubClass* {.importc: "bInterfaceSubClass".}: uint8 ## + ## < Subclass code (assigned by the USB-IF). \n These codes are qualified by the value of the bInterfaceClass field. \li If the bInterfaceClass field is reset to zero, this field must also be reset to zero. \li If the bInterfaceClass field is not set to FFH, all values are reserved for assignment by the USB-IF. + bInterfaceProtocol* {.importc: "bInterfaceProtocol".}: uint8 ## + ## < Protocol code (assigned by the USB). \n These codes are qualified by the value of the bInterfaceClass and the bInterfaceSubClass fields. If an interface supports class-specific requests, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use a class-specific protocol on this interface. \li If this field is set to FFH, the device uses a vendor-specific protocol for this interface. + iInterface* {.importc: "iInterface".}: uint8 + ## < Index of string descriptor describing this interface + + +type + + TU_ATTR_PACKED_tusb_types_1* {.importc: "tusb_desc_endpoint_t::no_name", + header: hdr, bycopy.} = object ## + ## USB Endpoint Descriptor + xfer* {.importc: "xfer", bitsize: 2.}: uint8 ## Control, ISO, Bulk, Interrupt + sync* {.importc: "sync", bitsize: 2.}: uint8 ## None, Asynchronous, Adaptive, Synchronous + usage* {.importc: "usage", bitsize: 2.}: uint8 + ## Data, Feedback, Implicit feedback + + + tusb_desc_endpoint_t* {.importc: "tusb_desc_endpoint_t", + header: hdr, bycopy.} = object + bLength* {.importc: "bLength".}: uint8 ## Size of this descriptor in bytes + bDescriptorType* {.importc: "bDescriptorType".}: uint8 ## + ## ENDPOINT Descriptor Type + bEndpointAddress* {.importc: "bEndpointAddress".}: uint8 ## + ## The address of the endpoint + bmAttributes* {.importc: "bmAttributes".}: TU_ATTR_PACKED_tusb_types_1 + wMaxPacketSize* {.importc: "wMaxPacketSize".}: uint16 ## + ## Bit 10..0 : max packet size, bit 12..11 additional transaction per highspeed micro-frame + bInterval* {.importc: "bInterval".}: uint8 + ## Polling interval, in frames or microframes depending on the operating speed + + +type + + tusb_desc_other_speed_t* {.importc: "tusb_desc_other_speed_t", + header: hdr, bycopy.} = object ## + ## USB Other Speed Configuration Descriptor + bLength* {.importc: "bLength".}: uint8 ## < Size of descriptor + bDescriptorType* {.importc: "bDescriptorType".}: uint8 ## + ## < Other_speed_Configuration Type + wTotalLength* {.importc: "wTotalLength".}: uint16 ## + ## < Total length of data returned + bNumInterfaces* {.importc: "bNumInterfaces".}: uint8 ## + ## < Number of interfaces supported by this speed configuration + bConfigurationValue* {.importc: "bConfigurationValue".}: uint8 ## + ## < Value to use to select configuration + iConfiguration* {.importc: "iConfiguration".}: uint8 ## + ## < Index of string descriptor + bmAttributes* {.importc: "bmAttributes".}: uint8 ## + ## < Same as Configuration descriptor + bMaxPower* {.importc: "bMaxPower".}: uint8 + ## < Same as Configuration descriptor + + + tusb_desc_device_qualifier_t* {.importc: "tusb_desc_device_qualifier_t", + header: hdr, bycopy.} = object ## + ## USB Device Qualifier Descriptor + bLength* {.importc: "bLength".}: uint8 ## < Size of descriptor + bDescriptorType* {.importc: "bDescriptorType".}: uint8 ## + ## < Device Qualifier Type + bcdUSB* {.importc: "bcdUSB".}: uint16 ## < USB specification version number (e.g., 0200H for V2.00) + bDeviceClass* {.importc: "bDeviceClass".}: uint8 ## + ## < Class Code + bDeviceSubClass* {.importc: "bDeviceSubClass".}: uint8 ## + ## < SubClass Code + bDeviceProtocol* {.importc: "bDeviceProtocol".}: uint8 ## + ## < Protocol Code + bMaxPacketSize0* {.importc: "bMaxPacketSize0".}: uint8 ## + ## < Maximum packet size for other speed + bNumConfigurations* {.importc: "bNumConfigurations".}: uint8 ## + ## < Number of Other-speed Configurations + bReserved* {.importc: "bReserved".}: uint8 + ## < Reserved for future use, must be zero + + + +type + + tusb_desc_interface_assoc_t* {.importc: "tusb_desc_interface_assoc_t", + header: hdr, bycopy.} = object ## + ## USB Interface Association Descriptor (IAD ECN) + bLength* {.importc: "bLength".}: uint8 ## < Size of descriptor + bDescriptorType* {.importc: "bDescriptorType".}: uint8 ## + ## < Other_speed_Configuration Type + bFirstInterface* {.importc: "bFirstInterface".}: uint8 ## + ## < Index of the first associated interface. + bInterfaceCount* {.importc: "bInterfaceCount".}: uint8 ## + ## < Total number of associated interfaces. + bFunctionClass* {.importc: "bFunctionClass".}: uint8 ## + ## < Interface class ID. + bFunctionSubClass* {.importc: "bFunctionSubClass".}: uint8 ## + ## < Interface subclass ID. + bFunctionProtocol* {.importc: "bFunctionProtocol".}: uint8 ## + ## < Interface protocol ID. + iFunction* {.importc: "iFunction".}: uint8 + ## < Index of the string descriptor describing the interface association. + + +type + + tusb_desc_string_t* {.importc: "tusb_desc_string_t", header: hdr, + bycopy.} = object ## USB String Descriptor + bLength* {.importc: "bLength".}: uint8 ## < Size of this descriptor in bytes + bDescriptorType* {.importc: "bDescriptorType".}: uint8 ## + ## < Descriptor Type + utf16le* {.importc: "utf16le".}: UncheckedArray[uint16] + + + tusb_desc_bos_platform_t* {.importc: "tusb_desc_bos_platform_t", + header: hdr, bycopy.} = object ## + ## USB Binary Device Object Store (BOS) + bLength* {.importc: "bLength".}: uint8 + bDescriptorType* {.importc: "bDescriptorType".}: uint8 + bDevCapabilityType* {.importc: "bDevCapabilityType".}: uint8 + bReserved* {.importc: "bReserved".}: uint8 + PlatformCapabilityUUID* {.importc: "PlatformCapabilityUUID".}: array[16, + uint8] + CapabilityData* {.importc: "CapabilityData".}: UncheckedArray[uint8] + + + tusb_desc_webusb_url_t* {.importc: "tusb_desc_webusb_url_t", + header: hdr, bycopy.} = object ## + ## USB WebUSB URL Descriptor + bLength* {.importc: "bLength".}: uint8 + bDescriptorType* {.importc: "bDescriptorType".}: uint8 + bScheme* {.importc: "bScheme".}: uint8 + url* {.importc: "url".}: UncheckedArray[char] + + + TU_ATTR_PACKED_tusb_types_5* {.importc: "tusb_desc_dfu_functional_t::no_name", + header: hdr, bycopy.} = object ## + ## DFU Functional Descriptor + bitCanDnload* {.importc: "bitCanDnload", bitsize: 1.}: uint8 + bitCanUpload* {.importc: "bitCanUpload", bitsize: 1.}: uint8 + bitManifestationTolerant* {.importc: "bitManifestationTolerant", bitsize: 1.}: uint8 + bitWillDetach* {.importc: "bitWillDetach", bitsize: 1.}: uint8 + reserved* {.importc: "reserved", bitsize: 4.}: uint8 + + + tusb_desc_dfu_functional_t* {.importc: "tusb_desc_dfu_functional_t", + header: hdr, bycopy.} = object + bLength* {.importc: "bLength".}: uint8 + bDescriptorType* {.importc: "bDescriptorType".}: uint8 + bmAttributes* {.importc: "bmAttributes".}: TU_ATTR_PACKED_tusb_types_5 + bAttributes* {.importc: "bAttributes".}: uint8 + wDetachTimeOut* {.importc: "wDetachTimeOut".}: uint16 + wTransferSize* {.importc: "wTransferSize".}: uint16 + bcdDFUVersion* {.importc: "bcdDFUVersion".}: uint16 + + + TU_ATTR_PACKED_tusb_types_9* {.importc: "tusb_control_request_t::no_name", + header: hdr, bycopy.} = object ## + ## --------------------------------------------------------------------+ + ## + ## --------------------------------------------------------------------+ + recipient* {.importc: "recipient", bitsize: 5.}: uint8 ## + ## < Recipient type tusb_request_recipient_t. + `type`* {.importc: "type", bitsize: 2.}: uint8 ## + ## < Request type tusb_request_type_t. + direction* {.importc: "direction", bitsize: 1.}: uint8 + ## < Direction type. tusb_dir_t + + + tusb_control_request_t* {.importc: "tusb_control_request_t", + header: hdr, bycopy.} = object + bmRequestType_bit* {.importc: "bmRequestType_bit".}: TU_ATTR_PACKED_tusb_types_9 + bmRequestType* {.importc: "bmRequestType".}: uint8 + bRequest* {.importc: "bRequest".}: uint8 + wValue* {.importc: "wValue".}: uint16 + wIndex* {.importc: "wIndex".}: uint16 + wLength* {.importc: "wLength".}: uint16 + + + +proc tu_edpt_dir*(`addr`: uint8): tusb_dir_t {.inline, cdecl, + importc: "tu_edpt_dir".} + ## --------------------------------------------------------------------+ + ## Endpoint helper + ## --------------------------------------------------------------------+ + ## Get direction from Endpoint address + +proc tu_edpt_number*(`addr`: uint8): uint8 {.inline, cdecl, + importc: "tu_edpt_number".} + ## Get Endpoint number from address + +proc tu_edpt_addr*(num: uint8; dir: uint8): uint8 {.inline, cdecl, + importc: "tu_edpt_addr".} + +proc tu_edpt_packet_size*(desc_ep: ptr tusb_desc_endpoint_t): uint16 {.inline, + cdecl, importc: "tu_edpt_packet_size".} + +proc tu_edpt_type_str*(t: tusb_xfer_type_t): cstring {.inline, cdecl, + importc: "tu_edpt_type_str".} + +proc tu_desc_next*(desc: pointer): ptr uint8 {.inline, cdecl, importc: "tu_desc_next".} + ## --------------------------------------------------------------------+ + ## Descriptor helper + ## --------------------------------------------------------------------+ + ## return next descriptor + +proc tu_desc_len*(desc: pointer): uint8 {.inline, cdecl, importc: "tu_desc_len".} + ## + ## get descriptor length + +proc tu_desc_type*(desc: pointer): uint8 {.inline, cdecl, + importc: "tu_desc_type".} + ## get descriptor type + +proc tu_desc_subtype*(desc: pointer): uint8 {.inline, cdecl, + importc: "tu_desc_subtype".} + ## get descriptor subtype + +proc tu_desc_find*(desc: ptr uint8; `end`: ptr uint8; byte1: uint8): ptr uint8 {. + cdecl, importc: "tu_desc_find", header: hdr.} + ## + ## find descriptor that match byte1 (type) + +proc tu_desc_find2*(desc: ptr uint8; `end`: ptr uint8; byte1: uint8; + byte2: uint8): ptr uint8 {.cdecl, importc: "tu_desc_find2", + header: hdr.} + ## find descriptor that match byte1 (type) and byte2 + +proc tu_desc_find3*(desc: ptr uint8; `end`: ptr uint8; byte1: uint8; + byte2: uint8; byte3: uint8): ptr uint8 {.cdecl, + importc: "tu_desc_find3", header: hdr.} + ## + ## find descriptor that match byte1 (type) and byte2 \ No newline at end of file diff --git a/src/nesper/components/esp_tinyusb/vfs_tinyusb.nim b/src/nesper/components/esp_tinyusb/vfs_tinyusb.nim new file mode 100644 index 00000000..06f204ea --- /dev/null +++ b/src/nesper/components/esp_tinyusb/vfs_tinyusb.nim @@ -0,0 +1,31 @@ +import ../../consts + +const + VFS_TUSB_MAX_PATH* = 16 ## + ## SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + ## + ## SPDX-License-Identifier: Apache-2.0 + ## + VFS_TUSB_PATH_DEFAULT* = "/dev/tusb_cdc" + + +proc esp_vfs_tusb_cdc_register*(cdc_intf: cint; path: cstring): esp_err_t {. + cdecl, importc: "esp_vfs_tusb_cdc_register", header: "vfs_tinyusb.h".} + ## + ## + ## @brief Register TinyUSB CDC at VFS with path + ## @param cdc_intf - interface number of TinyUSB's CDC + ## @param path - path where the CDC will be registered, `/dev/tusb_cdc` will be used if left NULL. + ## + ## @return esp_err_t ESP_OK or ESP_FAIL + ## + +proc esp_vfs_tusb_cdc_unregister*(path: cstring): esp_err_t {.cdecl, + importc: "esp_vfs_tusb_cdc_unregister", header: "vfs_tinyusb.h".} + ## + ## + ## @brief Unregister TinyUSB CDC from VFS + ## @param path - path where the CDC will be unregistered if NULL will be used `/dev/tusb_cdc` + ## + ## @return esp_err_t ESP_OK or ESP_FAIL + ## \ No newline at end of file diff --git a/src/nesper/consts.nim b/src/nesper/consts.nim index 9e2f4a39..71c7ba7b 100644 --- a/src/nesper/consts.nim +++ b/src/nesper/consts.nim @@ -172,19 +172,6 @@ type type RingbufHandle_t* {.importc: "ringbuf.h".} = pointer -type - esp_event_base_t* = cstring - -type - ## *< unique pointer to a subsystem that exposes events - esp_event_loop_handle_t* = pointer - -type - ## *< a number that identifies an event with respect to a base - esp_event_handler_t* = proc (event_handler_arg: pointer; - event_base: esp_event_base_t; - event_id: int32; - event_data: pointer) {.cdecl.} type bits* = distinct int diff --git a/src/nesper/esp/esp_event.nim b/src/nesper/esp/esp_event.nim index b96a82f1..071c3dd4 100644 --- a/src/nesper/esp/esp_event.nim +++ b/src/nesper/esp/esp_event.nim @@ -1,530 +1,542 @@ -## Copyright 2018 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 +## SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD ## -## http://www.apache.org/licenses/LICENSE-2.0 +## SPDX-License-Identifier: Apache-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. import ../consts +export consts -when ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0): - import esp_event_legacy +type + esp_event_base_t* {.importc: "esp_event_base_t", header: "esp_event.h".} = pointer - export esp_event_legacy +proc `$`*(base: esp_event_base_t): string = + $(cast[cstring](base)) +type + ## *< unique pointer to a subsystem that exposes events + esp_event_loop_handle_t* = pointer -## / Configuration for creating event loops +type + ## *< a number that identifies an event with respect to a base + esp_event_handler_t* = proc (event_handler_arg: pointer; + event_base: esp_event_base_t; + event_id: int32; + event_data: pointer) {.cdecl.} type - esp_event_loop_args_t* {.importc: "esp_event_loop_args_t", header: "esp_event.h", - bycopy.} = object - queue_size* {.importc: "queue_size".}: int32 ## *< size of the event loop queue - task_name* {.importc: "task_name".}: cstring ## *< name of the event loop task; if NULL, - ## a dedicated task is not created for event loop - task_priority* {.importc: "task_priority".}: UBaseType_t ## *< priority of the event loop task, ignored if task name is NULL - task_stack_size* {.importc: "task_stack_size".}: uint32 ## *< stack size of the event loop task, ignored if task name is NULL - task_core_id* {.importc: "task_core_id".}: BaseType_t ## *< core to which the event loop task is pinned to, - ## ignored if task name is NULL + esp_event_handler_instance_t* {.importc: "esp_event_handler_instance_t", header: "esp_event.h".} = object + +type + + esp_event_loop_args_t* {.importc: "esp_event_loop_args_t", + header: "esp_event.h", bycopy.} = object ## + ## Configuration for creating event loops + queue_size* {.importc: "queue_size".}: int32 ## < size of the event loop queue + task_name* {.importc: "task_name".}: cstring ## < name of the event loop task; if NULL, + ## a dedicated task is not created for event loop + task_priority* {.importc: "task_priority".}: UBaseType_t ## + ## < priority of the event loop task, ignored if task name is NULL + task_stack_size* {.importc: "task_stack_size".}: uint32 ## + ## < stack size of the event loop task, ignored if task name is NULL + task_core_id* {.importc: "task_core_id".}: BaseType_t + ## < core to which the event loop task is pinned to, + ## ignored if task name is NULL -## * -## @brief Create a new event loop. -## -## @param[in] event_loop_args configuration structure for the event loop to create -## @param[out] event_loop handle to the created event loop -## -## @return -## - ESP_OK: Success -## - ESP_ERR_NO_MEM: Cannot allocate memory for event loops list -## - ESP_FAIL: Failed to create task loop -## - Others: Fail -## proc esp_event_loop_create*(event_loop_args: ptr esp_event_loop_args_t; - event_loop: ptr esp_event_loop_handle_t): esp_err_t {. - importc: "esp_event_loop_create", header: "esp_event.h".} -## * -## @brief Delete an existing event loop. -## -## @param[in] event_loop event loop to delete -## -## @return -## - ESP_OK: Success -## - Others: Fail -## + event_loop: ptr esp_event_loop_handle_t): esp_err_t {. + cdecl, importc: "esp_event_loop_create", header: "esp_event.h".} + ## + ## + ## @brief Create a new event loop. + ## + ## @param[in] event_loop_args configuration structure for the event loop to create + ## @param[out] event_loop handle to the created event loop + ## + ## @return + ## - ESP_OK: Success + ## - ESP_ERR_INVALID_ARG: event_loop_args or event_loop was NULL + ## - ESP_ERR_NO_MEM: Cannot allocate memory for event loops list + ## - ESP_FAIL: Failed to create task loop + ## - Others: Fail + ## proc esp_event_loop_delete*(event_loop: esp_event_loop_handle_t): esp_err_t {. - importc: "esp_event_loop_delete", header: "esp_event.h".} -## * -## @brief Create default event loop -## -## @return -## - ESP_OK: Success -## - ESP_ERR_NO_MEM: Cannot allocate memory for event loops list -## - ESP_FAIL: Failed to create task loop -## - Others: Fail -## - -proc esp_event_loop_create_default*(): esp_err_t {. + cdecl, importc: "esp_event_loop_delete", header: "esp_event.h".} + ## + ## + ## @brief Delete an existing event loop. + ## + ## @param[in] event_loop event loop to delete, must not be NULL + ## + ## @return + ## - ESP_OK: Success + ## - Others: Fail + ## + +proc esp_event_loop_create_default*(): esp_err_t {.cdecl, importc: "esp_event_loop_create_default", header: "esp_event.h".} -## * -## @brief Delete the default event loop -## -## @return -## - ESP_OK: Success -## - Others: Fail -## - -proc esp_event_loop_delete_default*(): esp_err_t {. + ## + ## + ## @brief Create default event loop + ## + ## @return + ## - ESP_OK: Success + ## - ESP_ERR_NO_MEM: Cannot allocate memory for event loops list + ## - ESP_ERR_INVALID_STATE: Default event loop has already been created + ## - ESP_FAIL: Failed to create task loop + ## - Others: Fail + ## + +proc esp_event_loop_delete_default*(): esp_err_t {.cdecl, importc: "esp_event_loop_delete_default", header: "esp_event.h".} -## * -## @brief Dispatch events posted to an event loop. -## -## This function is used to dispatch events posted to a loop with no dedicated task, i.e task name was set to NULL -## in event_loop_args argument during loop creation. This function includes an argument to limit the amount of time -## it runs, returning control to the caller when that time expires (or some time afterwards). There is no guarantee -## that a call to this function will exit at exactly the time of expiry. There is also no guarantee that events have -## been dispatched during the call, as the function might have spent all of the alloted time waiting on the event queue. -## Once an event has been unqueued, however, it is guaranteed to be dispatched. This guarantee contributes to not being -## able to exit exactly at time of expiry as (1) blocking on internal mutexes is necessary for dispatching the unqueued -## event, and (2) during dispatch of the unqueued event there is no way to control the time occupied by handler code -## execution. The guaranteed time of exit is therefore the alloted time + amount of time required to dispatch -## the last unqueued event. -## -## In cases where waiting on the queue times out, ESP_OK is returned and not ESP_ERR_TIMEOUT, since it is -## normal behavior. -## -## @param[in] event_loop event loop to dispatch posted events from -## @param[in] ticks_to_run number of ticks to run the loop -## -## @note encountering an unknown event that has been posted to the loop will only generate a warning, not an error. -## -## @return -## - ESP_OK: Success -## - Others: Fail -## + ## + ## + ## @brief Delete the default event loop + ## + ## @return + ## - ESP_OK: Success + ## - Others: Fail + ## proc esp_event_loop_run*(event_loop: esp_event_loop_handle_t; - ticks_to_run: TickType_t): esp_err_t {. + ticks_to_run: TickType_t): esp_err_t {.cdecl, importc: "esp_event_loop_run", header: "esp_event.h".} -## * -## @brief Register an event handler to the system event loop (legacy). -## -## @note This function is obsolete and will be deprecated soon, please use esp_event_handler_instance_register() -## instead. -## -## This function can be used to register a handler for either: (1) specific events, -## (2) all events of a certain event base, or (3) all events known by the system event loop. -## -## - specific events: specify exact event_base and event_id -## - all events of a certain base: specify exact event_base and use ESP_EVENT_ANY_ID as the event_id -## - all events known by the loop: use ESP_EVENT_ANY_BASE for event_base and ESP_EVENT_ANY_ID as the event_id -## -## Registering multiple handlers to events is possible. Registering a single handler to multiple events is -## also possible. However, registering the same handler to the same event multiple times would cause the -## previous registrations to be overwritten. -## -## @param[in] event_base the base id of the event to register the handler for -## @param[in] event_id the id of the event to register the handler for -## @param[in] event_handler the handler function which gets called when the event is dispatched -## @param[in] event_handler_arg data, aside from event data, that is passed to the handler when it is called -## -## @note the event loop library does not maintain a copy of event_handler_arg, therefore the user should -## ensure that event_handler_arg still points to a valid location by the time the handler gets called -## -## @return -## - ESP_OK: Success -## - ESP_ERR_NO_MEM: Cannot allocate memory for the handler -## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id -## - Others: Fail -## - -# proc esp_event_handler_register*(event_base: esp_event_base_t; event_id: int32; -# event_handler: esp_event_handler_t; -# event_handler_arg: pointer): esp_err_t {. -# importc: "esp_event_handler_register", header: "esp_event.h".} -proc esp_event_handler_register*( - event_base: esp_event_base_t; - event_id: cint; - event_handler: esp_event_handler_t; - event_handler_arg: pointer - ): esp_err_t {.importc: "esp_event_handler_register", header: "esp_event.h".} - -## * -## @brief Register an event handler to a specific loop (legacy). -## -## @note This function is obsolete and will be deprecated soon, please use esp_event_handler_instance_register_with() -## instead. -## -## This function behaves in the same manner as esp_event_handler_register, except the additional -## specification of the event loop to register the handler to. -## -## @param[in] event_loop the event loop to register this handler function to -## @param[in] event_base the base id of the event to register the handler for -## @param[in] event_id the id of the event to register the handler for -## @param[in] event_handler the handler function which gets called when the event is dispatched -## @param[in] event_handler_arg data, aside from event data, that is passed to the handler when it is called -## -## @note the event loop library does not maintain a copy of event_handler_arg, therefore the user should -## ensure that event_handler_arg still points to a valid location by the time the handler gets called -## -## @return -## - ESP_OK: Success -## - ESP_ERR_NO_MEM: Cannot allocate memory for the handler -## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id -## - Others: Fail -## + ## + ## + ## @brief Dispatch events posted to an event loop. + ## + ## This function is used to dispatch events posted to a loop with no dedicated task, i.e. task name was set to NULL + ## in event_loop_args argument during loop creation. This function includes an argument to limit the amount of time + ## it runs, returning control to the caller when that time expires (or some time afterwards). There is no guarantee + ## that a call to this function will exit at exactly the time of expiry. There is also no guarantee that events have + ## been dispatched during the call, as the function might have spent all the allotted time waiting on the event queue. + ## Once an event has been dequeued, however, it is guaranteed to be dispatched. This guarantee contributes to not being + ## able to exit exactly at time of expiry as (1) blocking on internal mutexes is necessary for dispatching the dequeued + ## event, and (2) during dispatch of the dequeued event there is no way to control the time occupied by handler code + ## execution. The guaranteed time of exit is therefore the allotted time + amount of time required to dispatch + ## the last dequeued event. + ## + ## In cases where waiting on the queue times out, ESP_OK is returned and not ESP_ERR_TIMEOUT, since it is + ## normal behavior. + ## + ## @param[in] event_loop event loop to dispatch posted events from, must not be NULL + ## @param[in] ticks_to_run number of ticks to run the loop + ## + ## @note encountering an unknown event that has been posted to the loop will only generate a warning, not an error. + ## + ## @return + ## - ESP_OK: Success + ## - Others: Fail + ## + +proc esp_event_handler_register*(event_base: esp_event_base_t; event_id: int32; + event_handler: esp_event_handler_t; + event_handler_arg: pointer): esp_err_t {.cdecl, + importc: "esp_event_handler_register", header: "esp_event.h".} + ## + ## + ## @brief Register an event handler to the system event loop (legacy). + ## + ## This function can be used to register a handler for either: (1) specific events, + ## (2) all events of a certain event base, or (3) all events known by the system event loop. + ## + ## - specific events: specify exact event_base and event_id + ## - all events of a certain base: specify exact event_base and use ESP_EVENT_ANY_ID as the event_id + ## - all events known by the loop: use ESP_EVENT_ANY_BASE for event_base and ESP_EVENT_ANY_ID as the event_id + ## + ## Registering multiple handlers to events is possible. Registering a single handler to multiple events is + ## also possible. However, registering the same handler to the same event multiple times would cause the + ## overwriting of the event_handler_arg but the handler will be kept at the same position in the list associated + ## with the event that triggers it. It means that the call order of registered handlers for that event will remain + ## the same. + ## + ## @param[in] event_base the base ID of the event to register the handler for + ## @param[in] event_id the ID of the event to register the handler for + ## @param[in] event_handler the handler function which gets called when the event is dispatched + ## @param[in] event_handler_arg data, aside from event data, that is passed to the handler when it is called + ## + ## @note the event loop library does not maintain a copy of event_handler_arg, therefore the user should + ## ensure that event_handler_arg still points to a valid location by the time the handler gets called + ## + ## @return + ## - ESP_OK: Success + ## - ESP_ERR_NO_MEM: Cannot allocate memory for the handler + ## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event ID + ## - Others: Fail + ## proc esp_event_handler_register_with*(event_loop: esp_event_loop_handle_t; - event_base: esp_event_base_t; - event_id: int32; - event_handler: esp_event_handler_t; - event_handler_arg: pointer): esp_err_t {. - importc: "esp_event_handler_register_with", header: "esp_event.h".} -## * -## @brief Register an instance of event handler to a specific loop. -## -## This function can be used to register a handler for either: (1) specific events, -## (2) all events of a certain event base, or (3) all events known by the system event loop. -## -## - specific events: specify exact event_base and event_id -## - all events of a certain base: specify exact event_base and use ESP_EVENT_ANY_ID as the event_id -## - all events known by the loop: use ESP_EVENT_ANY_BASE for event_base and ESP_EVENT_ANY_ID as the event_id -## -## Besides the error, the function returns an instance object as output parameter to identify each registration. -## This is necessary to remove (unregister) the registration before the event loop is deleted. -## -## Registering multiple handlers to events, registering a single handler to multiple events as well as registering -## the same handler to the same event multiple times is possible. -## Each registration yields a distinct instance object which identifies it over the registration -## lifetime. -## -## @param[in] event_loop the event loop to register this handler function to -## @param[in] event_base the base id of the event to register the handler for -## @param[in] event_id the id of the event to register the handler for -## @param[in] event_handler the handler function which gets called when the event is dispatched -## @param[in] event_handler_arg data, aside from event data, that is passed to the handler when it is called -## @param[out] instance An event handler instance object related to the registered event handler and data, can be NULL. -## This needs to be kept if the specific callback instance should be unregistered before deleting the whole -## event loop. Registering the same event handler multiple times is possible and yields distinct instance -## objects. The data can be the same for all registrations. -## If no unregistration is needed but the handler should be deleted when the event loop is deleted, -## instance can be NULL. -## -## @note the event loop library does not maintain a copy of event_handler_arg, therefore the user should -## ensure that event_handler_arg still points to a valid location by the time the handler gets called -## -## @return -## - ESP_OK: Success -## - ESP_ERR_NO_MEM: Cannot allocate memory for the handler -## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id or instance is NULL -## - Others: Fail -## - -type - esp_event_handler_instance_t* {.importc: "esp_event_handler_instance_t", header: "esp_event.h".} = object + event_base: esp_event_base_t; + event_id: int32; + event_handler: esp_event_handler_t; + event_handler_arg: pointer): esp_err_t {. + cdecl, importc: "esp_event_handler_register_with", header: "esp_event.h".} + ## + ## + ## @brief Register an event handler to a specific loop (legacy). + ## + ## This function behaves in the same manner as esp_event_handler_register, except the additional + ## specification of the event loop to register the handler to. + ## + ## @param[in] event_loop the event loop to register this handler function to, must not be NULL + ## @param[in] event_base the base ID of the event to register the handler for + ## @param[in] event_id the ID of the event to register the handler for + ## @param[in] event_handler the handler function which gets called when the event is dispatched + ## @param[in] event_handler_arg data, aside from event data, that is passed to the handler when it is called + ## + ## @note the event loop library does not maintain a copy of event_handler_arg, therefore the user should + ## ensure that event_handler_arg still points to a valid location by the time the handler gets called + ## + ## @return + ## - ESP_OK: Success + ## - ESP_ERR_NO_MEM: Cannot allocate memory for the handler + ## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event ID + ## - Others: Fail + ## proc esp_event_handler_instance_register_with*( event_loop: esp_event_loop_handle_t; event_base: esp_event_base_t; event_id: int32; event_handler: esp_event_handler_t; event_handler_arg: pointer; instance: ptr esp_event_handler_instance_t): esp_err_t {. - importc: "esp_event_handler_instance_register_with", header: "esp_event.h".} -## * -## @brief Register an instance of event handler to the default loop. -## -## This function does the same as esp_event_handler_instance_register_with, except that it registers the -## handler to the default event loop. -## -## @param[in] event_base the base id of the event to register the handler for -## @param[in] event_id the id of the event to register the handler for -## @param[in] event_handler the handler function which gets called when the event is dispatched -## @param[in] event_handler_arg data, aside from event data, that is passed to the handler when it is called -## @param[out] instance An event handler instance object related to the registered event handler and data, can be NULL. -## This needs to be kept if the specific callback instance should be unregistered before deleting the whole -## event loop. Registering the same event handler multiple times is possible and yields distinct instance -## objects. The data can be the same for all registrations. -## If no unregistration is needed but the handler should be deleted when the event loop is deleted, -## instance can be NULL. -## -## @note the event loop library does not maintain a copy of event_handler_arg, therefore the user should -## ensure that event_handler_arg still points to a valid location by the time the handler gets called -## -## @return -## - ESP_OK: Success -## - ESP_ERR_NO_MEM: Cannot allocate memory for the handler -## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id or instance is NULL -## - Others: Fail -## - -proc esp_event_handler_instance_register*(event_base: esp_event_base_t; - event_id: int32; event_handler: esp_event_handler_t; - event_handler_arg: pointer; instance: ptr esp_event_handler_instance_t): esp_err_t {. - importc: "esp_event_handler_instance_register", header: "esp_event.h".} -## * -## @brief Unregister a handler with the system event loop (legacy). -## -## @note This function is obsolete and will be deprecated soon, please use esp_event_handler_instance_unregister() -## instead. -## -## This function can be used to unregister a handler so that it no longer gets called during dispatch. -## Handlers can be unregistered for either: (1) specific events, (2) all events of a certain event base, -## or (3) all events known by the system event loop -## -## - specific events: specify exact event_base and event_id -## - all events of a certain base: specify exact event_base and use ESP_EVENT_ANY_ID as the event_id -## - all events known by the loop: use ESP_EVENT_ANY_BASE for event_base and ESP_EVENT_ANY_ID as the event_id -## -## This function ignores unregistration of handlers that has not been previously registered. -## -## @param[in] event_base the base of the event with which to unregister the handler -## @param[in] event_id the id of the event with which to unregister the handler -## @param[in] event_handler the handler to unregister -## -## @return ESP_OK success -## @return ESP_ERR_INVALID_ARG invalid combination of event base and event id -## @return others fail -## - -proc esp_event_handler_unregister*( - event_base: esp_event_base_t; - event_id: int32; - event_handler: esp_event_handler_t - ): esp_err_t {.importc: "esp_event_handler_unregister", header: "esp_event.h".} - -## * -## @brief Unregister a handler from a specific event loop (legacy). -## -## @note This function is obsolete and will be deprecated soon, please use esp_event_handler_instance_unregister_with() -## instead. -## -## This function behaves in the same manner as esp_event_handler_unregister, except the additional specification of -## the event loop to unregister the handler with. -## -## @param[in] event_loop the event loop with which to unregister this handler function -## @param[in] event_base the base of the event with which to unregister the handler -## @param[in] event_id the id of the event with which to unregister the handler -## @param[in] event_handler the handler to unregister -## -## @return -## - ESP_OK: Success -## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id -## - Others: Fail -## + cdecl, importc: "esp_event_handler_instance_register_with", + header: "esp_event.h".} + ## + ## @brief Register an instance of event handler to a specific loop. + ## + ## This function can be used to register a handler for either: (1) specific events, + ## (2) all events of a certain event base, or (3) all events known by the system event loop. + ## + ## - specific events: specify exact event_base and event_id + ## - all events of a certain base: specify exact event_base and use ESP_EVENT_ANY_ID as the event_id + ## - all events known by the loop: use ESP_EVENT_ANY_BASE for event_base and ESP_EVENT_ANY_ID as the event_id + ## + ## Besides the error, the function returns an instance object as output parameter to identify each registration. + ## This is necessary to remove (unregister) the registration before the event loop is deleted. + ## + ## Registering multiple handlers to events, registering a single handler to multiple events as well as registering + ## the same handler to the same event multiple times is possible. + ## Each registration yields a distinct instance object which identifies it over the registration + ## lifetime. + ## + ## @param[in] event_loop the event loop to register this handler function to, must not be NULL + ## @param[in] event_base the base ID of the event to register the handler for + ## @param[in] event_id the ID of the event to register the handler for + ## @param[in] event_handler the handler function which gets called when the event is dispatched + ## @param[in] event_handler_arg data, aside from event data, that is passed to the handler when it is called + ## @param[out] instance An event handler instance object related to the registered event handler and data, can be NULL. + ## This needs to be kept if the specific callback instance should be unregistered before deleting the whole + ## event loop. Registering the same event handler multiple times is possible and yields distinct instance + ## objects. The data can be the same for all registrations. + ## If no unregistration is needed, but the handler should be deleted when the event loop is deleted, + ## instance can be NULL. + ## + ## @note the event loop library does not maintain a copy of event_handler_arg, therefore the user should + ## ensure that event_handler_arg still points to a valid location by the time the handler gets called + ## + ## @note Calling this function with instance set to NULL is equivalent to calling esp_event_handler_register_with. + ## + ## @return + ## - ESP_OK: Success + ## - ESP_ERR_NO_MEM: Cannot allocate memory for the handler + ## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event ID or instance is NULL + ## - Others: Fail + ## + +proc esp_event_handler_instance_register*( + event_base: esp_event_base_t; + event_id: int32; + event_handler: esp_event_handler_t; + event_handler_arg: pointer; + instance: ptr esp_event_handler_instance_t): esp_err_t {. + cdecl, importc: "esp_event_handler_instance_register", header: "esp_event.h".} + ## + ## + ## @brief Register an instance of event handler to the default loop. + ## + ## This function does the same as esp_event_handler_instance_register_with, except that it registers the + ## handler to the default event loop. + ## + ## @param[in] event_base the base ID of the event to register the handler for + ## @param[in] event_id the ID of the event to register the handler for + ## @param[in] event_handler the handler function which gets called when the event is dispatched + ## @param[in] event_handler_arg data, aside from event data, that is passed to the handler when it is called + ## @param[out] instance An event handler instance object related to the registered event handler and data, can be NULL. + ## This needs to be kept if the specific callback instance should be unregistered before deleting the whole + ## event loop. Registering the same event handler multiple times is possible and yields distinct instance + ## objects. The data can be the same for all registrations. + ## If no unregistration is needed, but the handler should be deleted when the event loop is deleted, + ## instance can be NULL. + ## + ## @note the event loop library does not maintain a copy of event_handler_arg, therefore the user should + ## ensure that event_handler_arg still points to a valid location by the time the handler gets called + ## + ## @note Calling this function with instance set to NULL is equivalent to calling esp_event_handler_register. + ## + ## @return + ## - ESP_OK: Success + ## - ESP_ERR_NO_MEM: Cannot allocate memory for the handler + ## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event ID or instance is NULL + ## - Others: Fail + ## + +proc esp_event_handler_unregister*(event_base: esp_event_base_t; + event_id: int32; + event_handler: esp_event_handler_t): esp_err_t {. + cdecl, importc: "esp_event_handler_unregister", header: "esp_event.h".} + ## + ## + ## @brief Unregister a handler with the system event loop (legacy). + ## + ## Unregisters a handler, so it will no longer be called during dispatch. + ## Handlers can be unregistered for any combination of event_base and event_id which were previously registered. + ## To unregister a handler, the event_base and event_id arguments must match exactly the arguments passed to + ## esp_event_handler_register() when that handler was registered. Passing ESP_EVENT_ANY_BASE and/or ESP_EVENT_ANY_ID + ## will only unregister handlers that were registered with the same wildcard arguments. + ## + ## @note When using ESP_EVENT_ANY_ID, handlers registered to specific event IDs using the same base will not be + ## unregistered. When using ESP_EVENT_ANY_BASE, events registered to specific bases will also not be + ## unregistered. This avoids accidental unregistration of handlers registered by other users or components. + ## + ## @param[in] event_base the base of the event with which to unregister the handler + ## @param[in] event_id the ID of the event with which to unregister the handler + ## @param[in] event_handler the handler to unregister + ## + ## @return ESP_OK success + ## @return ESP_ERR_INVALID_ARG invalid combination of event base and event ID + ## @return others fail + ## proc esp_event_handler_unregister_with*(event_loop: esp_event_loop_handle_t; - event_base: esp_event_base_t; - event_id: int32; - event_handler: esp_event_handler_t): esp_err_t {. - importc: "esp_event_handler_unregister_with", header: "esp_event.h".} -## * -## @brief Unregister a handler instance from a specific event loop. -## -## This function can be used to unregister a handler so that it no longer gets called during dispatch. -## Handlers can be unregistered for either: (1) specific events, (2) all events of a certain event base, -## or (3) all events known by the system event loop -## -## - specific events: specify exact event_base and event_id -## - all events of a certain base: specify exact event_base and use ESP_EVENT_ANY_ID as the event_id -## - all events known by the loop: use ESP_EVENT_ANY_BASE for event_base and ESP_EVENT_ANY_ID as the event_id -## -## This function ignores unregistration of handler instances that have not been previously registered. -## -## @param[in] event_loop the event loop with which to unregister this handler function -## @param[in] event_base the base of the event with which to unregister the handler -## @param[in] event_id the id of the event with which to unregister the handler -## @param[in] instance the instance object of the registration to be unregistered -## -## @return -## - ESP_OK: Success -## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id -## - Others: Fail -## + event_base: esp_event_base_t; + event_id: int32; + event_handler: esp_event_handler_t): esp_err_t {. + cdecl, importc: "esp_event_handler_unregister_with", header: "esp_event.h".} + ## + ## + ## @brief Unregister a handler from a specific event loop (legacy). + ## + ## This function behaves in the same manner as esp_event_handler_unregister, except the additional specification of + ## the event loop to unregister the handler with. + ## + ## @param[in] event_loop the event loop with which to unregister this handler function, must not be NULL + ## @param[in] event_base the base of the event with which to unregister the handler + ## @param[in] event_id the ID of the event with which to unregister the handler + ## @param[in] event_handler the handler to unregister + ## + ## @return + ## - ESP_OK: Success + ## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event ID + ## - Others: Fail + ## proc esp_event_handler_instance_unregister_with*( event_loop: esp_event_loop_handle_t; event_base: esp_event_base_t; - event_id: int32; instance: esp_event_handler_instance_t): esp_err_t {. + event_id: int32; instance: esp_event_handler_instance_t): esp_err_t {.cdecl, importc: "esp_event_handler_instance_unregister_with", header: "esp_event.h".} -## * -## @brief Unregister a handler from the system event loop. -## -## This function does the same as esp_event_handler_instance_unregister_with, except that it unregisters the -## handler instance from the default event loop. -## -## @param[in] event_base the base of the event with which to unregister the handler -## @param[in] event_id the id of the event with which to unregister the handler -## @param[in] instance the instance object of the registration to be unregistered -## -## @return -## - ESP_OK: Success -## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id -## - Others: Fail -## + ## + ## + ## @brief Unregister a handler instance from a specific event loop. + ## + ## Unregisters a handler instance, so it will no longer be called during dispatch. + ## Handler instances can be unregistered for any combination of event_base and event_id which were previously + ## registered. To unregister a handler instance, the event_base and event_id arguments must match exactly the + ## arguments passed to esp_event_handler_instance_register() when that handler instance was registered. + ## Passing ESP_EVENT_ANY_BASE and/or ESP_EVENT_ANY_ID will only unregister handler instances that were registered + ## with the same wildcard arguments. + ## + ## @note When using ESP_EVENT_ANY_ID, handlers registered to specific event IDs using the same base will not be + ## unregistered. When using ESP_EVENT_ANY_BASE, events registered to specific bases will also not be + ## unregistered. This avoids accidental unregistration of handlers registered by other users or components. + ## + ## @param[in] event_loop the event loop with which to unregister this handler function, must not be NULL + ## @param[in] event_base the base of the event with which to unregister the handler + ## @param[in] event_id the ID of the event with which to unregister the handler + ## @param[in] instance the instance object of the registration to be unregistered + ## + ## @return + ## - ESP_OK: Success + ## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event ID + ## - Others: Fail + ## proc esp_event_handler_instance_unregister*(event_base: esp_event_base_t; - event_id: int32; instance: esp_event_handler_instance_t): esp_err_t {. + event_id: int32; instance: esp_event_handler_instance_t): esp_err_t {.cdecl, importc: "esp_event_handler_instance_unregister", header: "esp_event.h".} - - -## * -## @brief Posts an event to the system default event loop. The event loop library keeps a copy of event_data and manages -## the copy's lifetime automatically (allocation + deletion); this ensures that the data the -## handler recieves is always valid. -## -## @param[in] event_base the event base that identifies the event -## @param[in] event_id the event id that identifies the event -## @param[in] event_data the data, specific to the event occurence, that gets passed to the handler -## @param[in] event_data_size the size of the event data -## @param[in] ticks_to_wait number of ticks to block on a full event queue -## -## @return -## - ESP_OK: Success -## - ESP_ERR_TIMEOUT: Time to wait for event queue to unblock expired, -## queue full when posting from ISR -## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id -## - Others: Fail -## + ## + ## + ## @brief Unregister a handler from the system event loop. + ## + ## This function does the same as esp_event_handler_instance_unregister_with, except that it unregisters the + ## handler instance from the default event loop. + ## + ## @param[in] event_base the base of the event with which to unregister the handler + ## @param[in] event_id the ID of the event with which to unregister the handler + ## @param[in] instance the instance object of the registration to be unregistered + ## + ## @return + ## - ESP_OK: Success + ## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event ID + ## - Others: Fail + ## proc esp_event_post*(event_base: esp_event_base_t; event_id: int32; - event_data: pointer; event_data_size: csize; - ticks_to_wait: TickType_t): esp_err_t {. + event_data: pointer; event_data_size: csize_t; + ticks_to_wait: TickType_t): esp_err_t {.cdecl, importc: "esp_event_post", header: "esp_event.h".} - - -## * -## @brief Posts an event to the specified event loop. The event loop library keeps a copy of event_data and manages -## the copy's lifetime automatically (allocation + deletion); this ensures that the data the -## handler recieves is always valid. -## -## This function behaves in the same manner as esp_event_post_to, except the additional specification of the event loop -## to post the event to. -## -## @param[in] event_loop the event loop to post to -## @param[in] event_base the event base that identifies the event -## @param[in] event_id the event id that identifies the event -## @param[in] event_data the data, specific to the event occurence, that gets passed to the handler -## @param[in] event_data_size the size of the event data -## @param[in] ticks_to_wait number of ticks to block on a full event queue -## -## @return -## - ESP_OK: Success -## - ESP_ERR_TIMEOUT: Time to wait for event queue to unblock expired, -## queue full when posting from ISR -## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id -## - Others: Fail -## + ## + ## + ## @brief Posts an event to the system default event loop. The event loop library keeps a copy of event_data and manages + ## the copy's lifetime automatically (allocation + deletion); this ensures that the data the + ## handler receives is always valid. + ## + ## @param[in] event_base the event base that identifies the event + ## @param[in] event_id the event ID that identifies the event + ## @param[in] event_data the data, specific to the event occurrence, that gets passed to the handler + ## @param[in] event_data_size the size of the event data + ## @param[in] ticks_to_wait number of ticks to block on a full event queue + ## + ## @return + ## - ESP_OK: Success + ## - ESP_ERR_TIMEOUT: Time to wait for event queue to unblock expired, + ## queue full when posting from ISR + ## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event ID + ## - Others: Fail + ## proc esp_event_post_to*(event_loop: esp_event_loop_handle_t; - event_base: esp_event_base_t; event_id: int32; - event_data: pointer; event_data_size: csize_t; - ticks_to_wait: TickType_t): esp_err_t {. + event_base: esp_event_base_t; event_id: int32; + event_data: pointer; event_data_size: csize_t; + ticks_to_wait: TickType_t): esp_err_t {.cdecl, importc: "esp_event_post_to", header: "esp_event.h".} + ## + ## + ## @brief Posts an event to the specified event loop. The event loop library keeps a copy of event_data and manages + ## the copy's lifetime automatically (allocation + deletion); this ensures that the data the + ## handler receives is always valid. + ## + ## This function behaves in the same manner as esp_event_post, except the additional specification of the event loop + ## to post the event to. + ## + ## @param[in] event_loop the event loop to post to, must not be NULL + ## @param[in] event_base the event base that identifies the event + ## @param[in] event_id the event ID that identifies the event + ## @param[in] event_data the data, specific to the event occurrence, that gets passed to the handler + ## @param[in] event_data_size the size of the event data + ## @param[in] ticks_to_wait number of ticks to block on a full event queue + ## + ## @return + ## - ESP_OK: Success + ## - ESP_ERR_TIMEOUT: Time to wait for event queue to unblock expired, + ## queue full when posting from ISR + ## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event ID + ## - Others: Fail + ## -# when CONFIG_ESP_EVENT_POST_FROM_ISR: -## * -## @brief Special variant of esp_event_post for posting events from interrupt handlers. -## -## @param[in] event_base the event base that identifies the event -## @param[in] event_id the event id that identifies the event -## @param[in] event_data the data, specific to the event occurence, that gets passed to the handler -## @param[in] event_data_size the size of the event data; max is 4 bytes -## @param[out] task_unblocked an optional parameter (can be NULL) which indicates that an event task with -## higher priority than currently running task has been unblocked by the posted event; -## a context switch should be requested before the interrupt is existed. -## -## @note this function is only available when CONFIG_ESP_EVENT_POST_FROM_ISR is enabled -## @note when this function is called from an interrupt handler placed in IRAM, this function should -## be placed in IRAM as well by enabling CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR -## -## @return -## - ESP_OK: Success -## - ESP_FAIL: Event queue for the default event loop full -## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id, -## data size of more than 4 bytes -## - Others: Fail -## proc esp_event_isr_post*(event_base: esp_event_base_t; event_id: int32; - event_data: pointer; event_data_size: csize; - task_unblocked: ptr BaseType_t): esp_err_t {. + event_data: pointer; event_data_size: csize_t; + task_unblocked: ptr BaseType_t): esp_err_t {.cdecl, importc: "esp_event_isr_post", header: "esp_event.h".} -## * -## @brief Special variant of esp_event_post_to for posting events from interrupt handlers -## -## @param[in] event_loop the event loop to post to -## @param[in] event_base the event base that identifies the event -## @param[in] event_id the event id that identifies the event -## @param[in] event_data the data, specific to the event occurence, that gets passed to the handler -## @param[in] event_data_size the size of the event data -## @param[out] task_unblocked an optional parameter (can be NULL) which indicates that an event task with -## higher priority than currently running task has been unblocked by the posted event; -## a context switch should be requested before the interrupt is existed. -## -## @note this function is only available when CONFIG_ESP_EVENT_POST_FROM_ISR is enabled -## @note when this function is called from an interrupt handler placed in IRAM, this function should -## be placed in IRAM as well by enabling CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR -## -## @return -## - ESP_OK: Success -## - ESP_FAIL: Event queue for the loop full -## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id, -## data size of more than 4 bytes -## - Others: Fail -## + ## + ## + ## @brief Special variant of esp_event_post for posting events from interrupt handlers. + ## + ## @param[in] event_base the event base that identifies the event + ## @param[in] event_id the event ID that identifies the event + ## @param[in] event_data the data, specific to the event occurrence, that gets passed to the handler + ## @param[in] event_data_size the size of the event data; max is 4 bytes + ## @param[out] task_unblocked an optional parameter (can be NULL) which indicates that an event task with + ## higher priority than currently running task has been unblocked by the posted event; + ## a context switch should be requested before the interrupt is existed. + ## + ## @note this function is only available when CONFIG_ESP_EVENT_POST_FROM_ISR is enabled + ## @note when this function is called from an interrupt handler placed in IRAM, this function should + ## be placed in IRAM as well by enabling CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR + ## + ## @return + ## - ESP_OK: Success + ## - ESP_FAIL: Event queue for the default event loop full + ## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event ID, + ## data size of more than 4 bytes + ## - Others: Fail + ## + proc esp_event_isr_post_to*(event_loop: esp_event_loop_handle_t; event_base: esp_event_base_t; event_id: int32; - event_data: pointer; event_data_size: csize; + event_data: pointer; event_data_size: csize_t; task_unblocked: ptr BaseType_t): esp_err_t {. - importc: "esp_event_isr_post_to", header: "esp_event.h".} -## * -## @brief Dumps statistics of all event loops. -## -## Dumps event loop info in the format: -## -## @verbatim -## event loop -## handler -## handler -## ... -## event loop -## handler -## handler -## ... -## -## where: -## -## event loop -## format: address,name rx:total_recieved dr:total_dropped -## where: -## address - memory address of the event loop -## name - name of the event loop, 'none' if no dedicated task -## total_recieved - number of successfully posted events -## total_dropped - number of events unsuccessfully posted due to queue being full -## -## handler -## format: address ev:base,id inv:total_invoked run:total_runtime -## where: -## address - address of the handler function -## base,id - the event specified by event base and id this handler executes -## total_invoked - number of times this handler has been invoked -## total_runtime - total amount of time used for invoking this handler -## -## @endverbatim -## -## @param[in] file the file stream to output to -## -## @note this function is a noop when CONFIG_ESP_EVENT_LOOP_PROFILING is disabled -## -## @return -## - ESP_OK: Success -## - ESP_ERR_NO_MEM: Cannot allocate memory for event loops list -## - Others: Fail -## - -proc esp_event_dump*(file: ptr FILE): esp_err_t {.importc: "esp_event_dump", - header: "esp_event.h".} \ No newline at end of file + cdecl, importc: "esp_event_isr_post_to", header: "esp_event.h".} + ## + ## + ## @brief Special variant of esp_event_post_to for posting events from interrupt handlers + ## + ## @param[in] event_loop the event loop to post to, must not be NULL + ## @param[in] event_base the event base that identifies the event + ## @param[in] event_id the event ID that identifies the event + ## @param[in] event_data the data, specific to the event occurrence, that gets passed to the handler + ## @param[in] event_data_size the size of the event data + ## @param[out] task_unblocked an optional parameter (can be NULL) which indicates that an event task with + ## higher priority than currently running task has been unblocked by the posted event; + ## a context switch should be requested before the interrupt is existed. + ## + ## @note this function is only available when CONFIG_ESP_EVENT_POST_FROM_ISR is enabled + ## @note when this function is called from an interrupt handler placed in IRAM, this function should + ## be placed in IRAM as well by enabling CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR + ## + ## @return + ## - ESP_OK: Success + ## - ESP_FAIL: Event queue for the loop full + ## - ESP_ERR_INVALID_ARG: Invalid combination of event base and event ID, + ## data size of more than 4 bytes + ## - Others: Fail + ## + +proc esp_event_dump*(file: ptr FILE): esp_err_t {.cdecl, + importc: "esp_event_dump", header: "esp_event.h".} + ## + ## + ## @brief Dumps statistics of all event loops. + ## + ## Dumps event loop info in the format: + ## + ## @verbatim + ## event loop + ## handler + ## handler + ## ... + ## event loop + ## handler + ## handler + ## ... + ## + ## where: + ## + ## event loop + ## format: address,name rx:total_received dr:total_dropped + ## where: + ## address - memory address of the event loop + ## name - name of the event loop, 'none' if no dedicated task + ## total_received - number of successfully posted events + ## total_dropped - number of events unsuccessfully posted due to queue being full + ## + ## handler + ## format: address ev:base,id inv:total_invoked run:total_runtime + ## where: + ## address - address of the handler function + ## base,id - the event specified by event base and ID this handler executes + ## total_invoked - number of times this handler has been invoked + ## total_runtime - total amount of time used for invoking this handler + ## + ## @endverbatim + ## + ## @param[in] file the file stream to output to + ## + ## @note this function is a noop when CONFIG_ESP_EVENT_LOOP_PROFILING is disabled + ## + ## @return + ## - ESP_OK: Success + ## - ESP_ERR_NO_MEM: Cannot allocate memory for event loops list + ## - Others: Fail + ## \ No newline at end of file diff --git a/src/nesper/esp/esp_event_legacy.nim b/src/nesper/esp/esp_event_legacy.nim index 5bb163b4..e9c9b6aa 100644 --- a/src/nesper/esp/esp_event_legacy.nim +++ b/src/nesper/esp/esp_event_legacy.nim @@ -16,7 +16,7 @@ ## * System event types enumeration import ../consts import net/esp_wifi_types -import net/tcpip_adapter +import net/esp_netif_types type system_event_id_t* {.size: sizeof(cint).} = enum @@ -101,8 +101,8 @@ type ## * Argument structure of event -type - system_event_ap_staipassigned_t* = ip_event_ap_staipassigned_t +# type +# system_event_ap_staipassigned_t* = ip_event_ap_staipassigned_t ## * Argument structure of event @@ -129,7 +129,7 @@ type sta_connected* {.importc: "sta_connected".}: system_event_ap_staconnected_t ## !< a station connected to ESP32 soft-AP sta_disconnected* {.importc: "sta_disconnected".}: system_event_ap_stadisconnected_t ## !< a station disconnected to ESP32 soft-AP ap_probereqrecved* {.importc: "ap_probereqrecved".}: system_event_ap_probe_req_rx_t ## !< ESP32 soft-AP receive probe request packet - ap_staipassigned* {.importc: "ap_staipassigned".}: system_event_ap_staipassigned_t ## *< ESP32 soft-AP assign an IP to the station + # ap_staipassigned* {.importc: "ap_staipassigned".}: system_event_ap_staipassigned_t ## *< ESP32 soft-AP assign an IP to the station got_ip6* {.importc: "got_ip6".}: system_event_got_ip6_t ## !< ESP32 station or ap or ethernet ipv6 addr state change to preferred diff --git a/src/nesper/esp/esp_log.nim b/src/nesper/esp/esp_log.nim index 968c79a5..3a5ccc06 100644 --- a/src/nesper/esp/esp_log.nim +++ b/src/nesper/esp/esp_log.nim @@ -1,6 +1,9 @@ import ../consts -var LOG_LOCAL_LEVEL* {.importc: "CONFIG_LOG_DEFAULT_LEVEL", header: "esp_log.nim".}: cint +when defined(freertos): + var LOG_LOCAL_LEVEL* {.importc: "CONFIG_LOG_DEFAULT_LEVEL", header: "esp_log.nim".}: cint +else: + var LOG_LOCAL_LEVEL*: cint = 4 type esp_log_level_t* {.size: sizeof(cint).} = enum @@ -12,18 +15,26 @@ type ESP_LOG_VERBOSE ## !< Bigger chunks of debugging information, or frequent messages which can potentially flood the output. -proc esp_log_timestamp*(): uint32 {.importc: "esp_log_timestamp", header: "esp_log.h".} +when defined(freertos): + proc esp_log_timestamp*(): uint32 {.importc: "esp_log_timestamp", header: "esp_log.h".} -proc esp_log_write*(level: esp_log_level_t, tag: cstring, format: cstring) {. - importc: "esp_log_write", varargs, header: "esp_log.h".} + proc esp_log_write*(level: esp_log_level_t, tag: cstring, format: cstring) {. + importc: "esp_log_write", varargs, header: "esp_log.h".} -proc loge*(tag: cstring, formatstr: cstring) {.importc: "ESP_LOGE", varargs, header: "esp_log.h".} -proc logw*(tag: cstring, formatstr: cstring) {.importc: "ESP_LOGW", varargs, header: "esp_log.h".} -proc logi*(tag: cstring, formatstr: cstring) {.importc: "ESP_LOGI", varargs, header: "esp_log.h".} -proc logd*(tag: cstring, formatstr: cstring) {.importc: "ESP_LOGD", varargs, header: "esp_log.h".} -proc logv*(tag: cstring, formatstr: cstring) {.importc: "ESP_LOGV", varargs, header: "esp_log.h".} + proc loge*(tag: cstring, formatstr: cstring) {.importc: "ESP_LOGE", varargs, header: "esp_log.h".} + proc logw*(tag: cstring, formatstr: cstring) {.importc: "ESP_LOGW", varargs, header: "esp_log.h".} + proc logi*(tag: cstring, formatstr: cstring) {.importc: "ESP_LOGI", varargs, header: "esp_log.h".} + proc logd*(tag: cstring, formatstr: cstring) {.importc: "ESP_LOGD", varargs, header: "esp_log.h".} + proc logv*(tag: cstring, formatstr: cstring) {.importc: "ESP_LOGV", varargs, header: "esp_log.h".} -proc log_timestamp*(): uint32 {.cdecl, importc: "esp_log_timestamp", header: "esp_log.h".} + proc log_timestamp*(): uint32 {.cdecl, importc: "esp_log_timestamp", header: "esp_log.h".} +else: + proc c_printf*(formatstr: cstring) {.importc: "printf", header: "stdio.h", varargs.} + template loge*(tag: cstring, formatstr: cstring, args: varargs[untyped]) = stdout.write "ERROR: ", tag, ": "; c_printf(formatstr, args); echo "" + template logw*(tag: cstring, formatstr: cstring, args: varargs[untyped]) = stdout.write "WARN: ", tag, ": "; c_printf(formatstr, args); echo "" + template logi*(tag: cstring, formatstr: cstring, args: varargs[untyped]) = stdout.write "INFO: ", tag, ": "; c_printf(formatstr, args); echo "" + template logd*(tag: cstring, formatstr: cstring, args: varargs[untyped]) = stdout.write "DEBUG: ", tag, ": "; c_printf(formatstr, args); echo "" + template logv*(tag: cstring, formatstr: cstring, args: varargs[untyped]) = stdout.write "VERBOSE: ", tag, ": "; c_printf(formatstr, args); echo "" type MallocCapacity* = enum diff --git a/src/nesper/esp/esp_system.nim b/src/nesper/esp/esp_system.nim index 51539a62..e20b5a37 100644 --- a/src/nesper/esp/esp_system.nim +++ b/src/nesper/esp/esp_system.nim @@ -91,7 +91,8 @@ proc esp_unregister_shutdown_handler*(handle: shutdown_handler_t): esp_err_t {. ## This function does not return. ## -proc esp_restart*() {.importc: "esp_restart", header: "esp_system.h".} +## defined in general.nim +## proc esp_restart*() {.importc: "esp_restart", header: "esp_system.h".} ## * diff --git a/src/nesper/esp/esp_vfs_eventfd.nim b/src/nesper/esp/esp_vfs_eventfd.nim new file mode 100644 index 00000000..0ee9184d --- /dev/null +++ b/src/nesper/esp/esp_vfs_eventfd.nim @@ -0,0 +1,61 @@ +import ../consts + +const + EFD_SUPPORT_ISR* = (1 shl 4) ## + ## SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + ## + ## SPDX-License-Identifier: Apache-2.0 + ## + +type + + esp_vfs_eventfd_config_t* {.importc: "esp_vfs_eventfd_config_t", + header: "esp_vfs_eventfd.h", bycopy.} = object ## + ## + ## @brief Eventfd vfs initialization settings + ## + max_fds* {.importc: "max_fds".}: csize_t + ## !< The maximum number of eventfds supported + + + +proc ESP_VFS_EVENTD_CONFIG_DEFAULT*(): esp_vfs_eventfd_config_t = + {.emit: """ + esp_vfs_eventfd_config_t cfg = ESP_VFS_EVENTD_CONFIG_DEFAULT(); + `result` = cfg; + """.} + +proc esp_vfs_eventfd_register*(config: ptr esp_vfs_eventfd_config_t): esp_err_t {. + cdecl, importc: "esp_vfs_eventfd_register", header: "esp_vfs_eventfd.h".} + ## + ## + ## @brief Registers the event vfs. + ## + ## @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are + ## registered. + ## + +proc esp_vfs_eventfd_unregister*(): esp_err_t {.cdecl, + importc: "esp_vfs_eventfd_unregister", header: "esp_vfs_eventfd.h".} + ## + ## + ## @brief Unregisters the event vfs. + ## + ## @return ESP_OK if successful, ESP_ERR_INVALID_STATE if VFS for given prefix + ## hasn't been registered + ## + +proc eventfd*(initval: cuint; flags: cint): cint {.cdecl, importc: "eventfd", + header: "esp_vfs_eventfd.h".} + ## + ## @brief Creates an event file descriptor. + ## + ## The behavior of read, write and select is the same as man(2) eventfd with + ## EFD_SEMAPHORE **NOT** specified. A new flag EFD_SUPPORT_ISR has been added. + ## This flag is required to write to event fds in interrupt handlers. Accessing + ## the control blocks of event fds with EFD_SUPPORT_ISR will cause interrupts to + ## be temporarily blocked (e.g. during read, write and beginning and ending of + ## the * select). + ## + ## @return The file descriptor if successful, -1 if error happens. + ## \ No newline at end of file diff --git a/src/nesper/esp/mdns.nim b/src/nesper/esp/mdns.nim index b7139bd6..05039bc0 100644 --- a/src/nesper/esp/mdns.nim +++ b/src/nesper/esp/mdns.nim @@ -403,7 +403,7 @@ proc mdns_query_txt*(instance_name: cstring; service_type: cstring; proto: cstri ## - ESP_ERR_INVALID_ARG parameter error ## -proc mdns_query_a*(host_name: cstring; timeout: uint32; `addr`: ptr ip4_addr_t): esp_err_t {. +proc mdns_query_a*(host_name: cstring; timeout: uint32; `addr`: ptr esp_ip4_addr_t): esp_err_t {. importc: "mdns_query_a", header: hdr.} ## * ## @brief Query mDNS for A record @@ -419,7 +419,7 @@ proc mdns_query_a*(host_name: cstring; timeout: uint32; `addr`: ptr ip4_addr_t): ## - ESP_ERR_INVALID_ARG parameter error ## -proc mdns_query_aaaa*(host_name: cstring; timeout: uint32; `addr`: ptr ip6_addr_t): esp_err_t {. +proc mdns_query_aaaa*(host_name: cstring; timeout: uint32; `addr`: ptr esp_ip6_addr_t): esp_err_t {. importc: "mdns_query_aaaa", header: hdr.} ## * ## @brief System event handler @@ -430,5 +430,5 @@ proc mdns_query_aaaa*(host_name: cstring; timeout: uint32; `addr`: ptr ip6_addr_ ## @param event The system event ## -proc mdns_handle_system_event*(ctx: pointer; event: ptr system_event_t): esp_err_t {. - importc: "mdns_handle_system_event", header: hdr.} \ No newline at end of file +# proc mdns_handle_system_event*(ctx: pointer; event: ptr system_event_t): esp_err_t {. +# importc: "mdns_handle_system_event", header: hdr.} \ No newline at end of file diff --git a/src/nesper/esp/net/esp_eth_com.nim b/src/nesper/esp/net/esp_eth_com.nim index bc6ce3f6..7e915ad2 100644 --- a/src/nesper/esp/net/esp_eth_com.nim +++ b/src/nesper/esp/net/esp_eth_com.nim @@ -13,6 +13,7 @@ ## limitations under the License. import ../../consts +import ../esp_event ## * diff --git a/src/nesper/esp/net/esp_netif.nim b/src/nesper/esp/net/esp_netif.nim index b576091b..8a42a9ab 100644 --- a/src/nesper/esp/net/esp_netif.nim +++ b/src/nesper/esp/net/esp_netif.nim @@ -20,6 +20,7 @@ import ../../consts ## when defined(ESP_IDF_V4_0): + import esp_netif_ip_addr include tcpip_adapter else: diff --git a/src/nesper/esp/net/esp_netif_defaults.nim b/src/nesper/esp/net/esp_netif_defaults.nim index eaa22603..ca3cb9f9 100644 --- a/src/nesper/esp/net/esp_netif_defaults.nim +++ b/src/nesper/esp/net/esp_netif_defaults.nim @@ -1,92 +1,124 @@ -## 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 +## SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD ## -## 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. +## SPDX-License-Identifier: Apache-2.0 +## + +const hdr = "esp_netif_defaults.h" -import esp_netif_types +import ./esp_netif_types -const netif_def_headers = """#include "esp_netif.h" - #include "esp_netif_defaults.h" """ +let + ESP_NETIF_DEFAULT_ARP_FLAGS* {.importc: "ESP_NETIF_DEFAULT_ARP_FLAGS", header: hdr.}: cint ## + ## + ## Macros to assemble master configs with partial configs from netif, stack and driver + ## + ## If GARP enabled in menuconfig (default), make it also a default config for common netifs -## @brief Default base config (esp-netif inherent) of WIFI STA +## +## @brief Default configuration reference of ethernet interface ## -var - ESP_NETIF_BASE_DEFAULT_WIFI_STA* {.importc: "$1", header: netif_def_headers.}: ptr esp_netif_inherent_config_t - ESP_NETIF_BASE_DEFAULT_WIFI_AP* {.importc: "$1", header: netif_def_headers.}: ptr esp_netif_inherent_config_t - ESP_NETIF_BASE_DEFAULT_ETH* {.importc: "$1", header: netif_def_headers.}: ptr esp_netif_inherent_config_t - ESP_NETIF_BASE_DEFAULT_PPP* {.importc: "$1", header: netif_def_headers.}: ptr esp_netif_inherent_config_t +proc ESP_NETIF_INHERENT_DEFAULT_WIFI_STA*(): esp_netif_inherent_config_t = + {.emit: """ + esp_netif_inherent_config_t cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA(); + `result` = cfg; + """.} - ESP_NETIF_NETSTACK_DEFAULT_ETH* {.importc: "$1", header: netif_def_headers.}: ptr esp_netif_netstack_config_t - ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA* {.importc: "$1", header: netif_def_headers.}: ptr esp_netif_netstack_config_t - ESP_NETIF_NETSTACK_DEFAULT_WIFI_AP* {.importc: "$1", header: netif_def_headers.}: ptr esp_netif_netstack_config_t - ESP_NETIF_NETSTACK_DEFAULT_PPP* {.importc: "$1", header: netif_def_headers.}: ptr esp_netif_netstack_config_t +proc ESP_NETIF_INHERENT_DEFAULT_WIFI_AP*(): esp_netif_inherent_config_t = + {.emit: """ + esp_netif_inherent_config_t cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_AP(); + `result` = cfg; + """.} -## -## Macros to assemble master configs with partial configs from netif, stack and driver -## -## /** -## * @brief Default configuration reference of ethernet interface -## */ -## #define ESP_NETIF_DEFAULT_ETH() \ -## { \ -## .base = ESP_NETIF_BASE_DEFAULT_ETH, \ -## .driver = NULL, \ -## .stack = ESP_NETIF_NETSTACK_DEFAULT_ETH, \ -## } +proc ESP_NETIF_INHERENT_DEFAULT_WIFI_NAN*(): esp_netif_inherent_config_t = + {.emit: """ + esp_netif_inherent_config_t cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_NAN(); + `result` = cfg; + """.} + +proc ESP_NETIF_INHERENT_DEFAULT_ETH*(): esp_netif_inherent_config_t = + {.emit: """ + esp_netif_inherent_config_t cfg = ESP_NETIF_INHERENT_DEFAULT_ETH(); + `result` = cfg; + """.} + +proc ESP_NETIF_INHERENT_DEFAULT_PPP*(): esp_netif_inherent_config_t = + {.emit: """ + esp_netif_inherent_config_t cfg = ESP_NETIF_INHERENT_DEFAULT_PPP(); + `result` = cfg; + """.} + + +proc ESP_NETIF_INHERENT_DEFAULT_BR*(): esp_netif_inherent_config_t = + {.emit: """ + esp_netif_inherent_config_t cfg = ESP_NETIF_INHERENT_DEFAULT_BR(); + `result` = cfg; + """.} + +proc ESP_NETIF_INHERENT_DEFAULT_BR_DHCPS*(): esp_netif_inherent_config_t = + {.emit: """ + esp_netif_inherent_config_t cfg = ESP_NETIF_INHERENT_DEFAULT_BR_DHCPS(); + `result` = cfg; + """.} proc ESP_NETIF_DEFAULT_ETH*(): esp_netif_config_t = - return esp_netif_config_t( - base: ESP_NETIF_BASE_DEFAULT_ETH, - driver: nil, - stack: ESP_NETIF_NETSTACK_DEFAULT_ETH, - ) - -## /** -## * @brief Default configuration reference of WIFI AP -## */ -## #define ESP_NETIF_DEFAULT_WIFI_AP() \ -## { \ -## .base = ESP_NETIF_BASE_DEFAULT_WIFI_AP, \ -## .driver = NULL, \ -## .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_AP, \ -## } -## /** -## * @brief Default configuration reference of WIFI STA -## */ -## #define ESP_NETIF_DEFAULT_WIFI_STA() \ -## { \ -## .base = ESP_NETIF_BASE_DEFAULT_WIFI_STA, \ -## .driver = NULL, \ -## .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA, \ -## } -## /** -## * @brief Default configuration reference of PPP client -## */ -## #define ESP_NETIF_DEFAULT_PPP() \ -## { \ -## .base = ESP_NETIF_BASE_DEFAULT_PPP, \ -## .driver = NULL, \ -## .stack = ESP_NETIF_NETSTACK_DEFAULT_PPP, \ -## } -## * + {.emit: """ + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); + `result` = cfg; + """.} -## -## Include default network stacks configs -## - Network stack configurations are provided in a specific network stack -## implementation that is invisible to user API -## - Here referenced only as opaque pointers -## +proc ESP_NETIF_DEFAULT_WIFI_AP*(): esp_netif_config_t = + {.emit: """ + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_WIFI_AP(); + `result` = cfg; + """.} -## -## * -## @brief Default base config (esp-netif inherent) of WIFI AP -## +proc ESP_NETIF_DEFAULT_WIFI_NAN*(): esp_netif_config_t = + {.emit: """ + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_WIFI_NAN(); + `result` = cfg; + """.} + +proc ESP_NETIF_DEFAULT_WIFI_STA*(): esp_netif_config_t = + {.emit: """ + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_WIFI_STA(); + `result` = cfg; + """.} + +proc ESP_NETIF_DEFAULT_PPP*(): esp_netif_config_t = + {.emit: """ + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_PPP(); + `result` = cfg; + """.} + + +let + ESP_NETIF_BASE_DEFAULT_WIFI_STA* {.importc: "ESP_NETIF_BASE_DEFAULT_WIFI_STA", header: hdr.}: ptr esp_netif_inherent_config_t ## + ## + ## @brief Default base config (esp-netif inherent) of WIFI STA + ## + ESP_NETIF_BASE_DEFAULT_WIFI_AP* {.importc: "ESP_NETIF_BASE_DEFAULT_WIFI_AP", header: hdr.}: ptr esp_netif_inherent_config_t ## + ## + ## @brief Default base config (esp-netif inherent) of WIFI AP + ## + ESP_NETIF_BASE_DEFAULT_WIFI_NAN* {.importc: "ESP_NETIF_BASE_DEFAULT_WIFI_NAN", header: hdr.}: ptr esp_netif_inherent_config_t ## + ## + ## @brief Default base config (esp-netif inherent) of WIFI NAN + ## + ESP_NETIF_BASE_DEFAULT_ETH* {.importc: "ESP_NETIF_BASE_DEFAULT_ETH", header: hdr.}: ptr esp_netif_inherent_config_t ## + ## + ## @brief Default base config (esp-netif inherent) of ethernet interface + ## + ESP_NETIF_BASE_DEFAULT_PPP* {.importc: "ESP_NETIF_BASE_DEFAULT_PPP", header: hdr.}: ptr esp_netif_inherent_config_t ## + ## + ## @brief Default base config (esp-netif inherent) of ppp interface + ## + +let + ESP_NETIF_NETSTACK_DEFAULT_ETH* {.importc: "ESP_NETIF_NETSTACK_DEFAULT_ETH", header: hdr.}: ptr esp_netif_netstack_config_t + ESP_NETIF_NETSTACK_DEFAULT_BR* {.importc: "ESP_NETIF_NETSTACK_DEFAULT_BR", header: hdr.}: ptr esp_netif_netstack_config_t + ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA* {.importc: "ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA", header: hdr.}: ptr esp_netif_netstack_config_t + ESP_NETIF_NETSTACK_DEFAULT_WIFI_AP* {.importc: "ESP_NETIF_NETSTACK_DEFAULT_WIFI_AP", header: hdr.}: ptr esp_netif_netstack_config_t + ESP_NETIF_NETSTACK_DEFAULT_WIFI_NAN* {.importc: "ESP_NETIF_NETSTACK_DEFAULT_WIFI_NAN", header: hdr.}: ptr esp_netif_netstack_config_t + ESP_NETIF_NETSTACK_DEFAULT_PPP* {.importc: "ESP_NETIF_NETSTACK_DEFAULT_PPP", header: hdr.}: ptr esp_netif_netstack_config_t diff --git a/src/nesper/esp/net/esp_netif_impl.nim b/src/nesper/esp/net/esp_netif_impl.nim index a40df2a4..7d0fd699 100644 --- a/src/nesper/esp/net/esp_netif_impl.nim +++ b/src/nesper/esp/net/esp_netif_impl.nim @@ -12,7 +12,7 @@ ## limitations under the License. -import ../../consts +import ../esp_event import esp_netif_ip_addr import esp_netif_types @@ -647,6 +647,16 @@ proc esp_netif_get_ip6_linklocal*(esp_netif: ptr esp_netif_t; proc esp_netif_get_ip6_global*(esp_netif: ptr esp_netif_t; if_ip6: ptr esp_ip6_addr_t): esp_err_t {. importc: "esp_netif_get_ip6_global", header: "esp_netif.h".} + +proc esp_netif_add_ip6_address*(esp_netif: ptr esp_netif_t; + ip6addr: esp_ip6_addr_t, + preferred: bool): esp_err_t {. + importc: "esp_netif_add_ip6_address", header: "esp_netif.h".} ##\ + ## @brief Add a static IPv6 address to the specified interface + ## + ## Adds the given IPv6 address to the interface and triggers related events. + ## + ## * ## @brief Sets IPv4 address to the specified octets ## diff --git a/src/nesper/esp/net/esp_netif_net_stack.nim b/src/nesper/esp/net/esp_netif_net_stack.nim new file mode 100644 index 00000000..415a6f9c --- /dev/null +++ b/src/nesper/esp/net/esp_netif_net_stack.nim @@ -0,0 +1,68 @@ +## +## SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +## +## SPDX-License-Identifier: Apache-2.0 +## + +const hdr = "esp_netif_net_stack.h" + +import esp_netif_types + +proc ethernetif_init*(netif: ptr netif): err_t {.cdecl, + importc, header: hdr.} + ## + ## + ## @brief LWIP's network stack init function for Ethernet + ## @param netif LWIP's network interface handle + ## @return ERR_OK on success + ## + +proc ethernetif_input*(h: pointer; buffer: pointer; len: csize_t; + l2_buff: pointer): esp_netif_recv_ret_t {.cdecl, + importc: "ethernetif_input", header: hdr.} + ## + ## + ## @brief LWIP's network stack input packet function for Ethernet + ## @param h LWIP's network interface handle + ## @param buffer Input buffer pointer + ## @param len Input buffer size + ## @param l2_buff External buffer pointer (to be passed to custom input-buffer free) + ## + +proc wlanif_init_ap*(netif: ptr netif): err_t {.cdecl, + importc: "wlanif_init_ap", header: hdr.} + ## + ## + ## @brief LWIP's network stack init function for WiFi (AP) + ## @param netif LWIP's network interface handle + ## @return ERR_OK on success + ## + +proc wlanif_init_sta*(netif: ptr netif): err_t {.cdecl, + importc: "wlanif_init_sta", header: hdr.} + ## + ## + ## @brief LWIP's network stack init function for WiFi (Station) + ## @param netif LWIP's network interface handle + ## @return ERR_OK on success + ## + +proc wlanif_init_nan*(netif: ptr netif): err_t {.cdecl, + importc: "wlanif_init_nan", header: hdr.} + ## + ## + ## @brief LWIP's network stack init function for WiFi Aware interface (NAN) + ## @param netif LWIP's network interface handle + ## @return ERR_OK on success + ## + +proc wlanif_input*(h: pointer; buffer: pointer; len: csize_t; l2_buff: pointer): esp_netif_recv_ret_t {. + cdecl, importc: "wlanif_input", header: hdr.} + ## + ## + ## @brief LWIP's network stack input packet function for WiFi (both STA/AP) + ## @param h LWIP's network interface handle + ## @param buffer Input buffer pointer + ## @param len Input buffer size + ## @param l2_buff External buffer pointer (to be passed to custom input-buffer free) + ## \ No newline at end of file diff --git a/src/nesper/esp/net/esp_netif_ppp.nim b/src/nesper/esp/net/esp_netif_ppp.nim new file mode 100644 index 00000000..3c18648c --- /dev/null +++ b/src/nesper/esp/net/esp_netif_ppp.nim @@ -0,0 +1,115 @@ +## +## SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD +## +## SPDX-License-Identifier: Apache-2.0 +## +## + +## @brief PPP event base +import ../../consts +import esp_netif_types + +const hdr = "esp_netif_ppp.h" + +const + NETIF_PP_PHASE_OFFSET* = (0x100) ## @brief event id offset for PHASE related events + ## + ## All PPP related events are produced from esp-netif under `NETIF_PPP_STATUS`, this offset defines + ## helps distinguish between error and phase events + ## + NETIF_PPP_INTERNAL_ERR_OFFSET* = (0x200) ## @brief event id offset for internal errors + ## + ## + +type + + esp_netif_ppp_status_event_t* {.size: sizeof(cint).} = enum ## + ## @brief event ids for different PPP related events + ## + ## + NETIF_PPP_ERRORNONE = 0, ## No error. + NETIF_PPP_ERRORPARAM = 1, ## Invalid parameter. + NETIF_PPP_ERROROPEN = 2, ## Unable to open PPP session. + NETIF_PPP_ERRORDEVICE = 3, ## Invalid I/O device for PPP. + NETIF_PPP_ERRORALLOC = 4, ## Unable to allocate resources. + NETIF_PPP_ERRORUSER = 5, ## User interrupt. + NETIF_PPP_ERRORCONNECT = 6, ## Connection lost. + NETIF_PPP_ERRORAUTHFAIL = 7, ## Failed authentication challenge. + NETIF_PPP_ERRORPROTOCOL = 8, ## Failed to meet protocol. + NETIF_PPP_ERRORPEERDEAD = 9, ## Connection timeout + NETIF_PPP_ERRORIDLETIMEOUT = 10, ## Idle Timeout + NETIF_PPP_ERRORCONNECTTIME = 11, ## Max connect time reached + NETIF_PPP_ERRORLOOPBACK = 12, ## Loopback detected + + NETIF_PPP_PHASE_DEAD = NETIF_PP_PHASE_OFFSET + 0, + NETIF_PPP_PHASE_MASTER = NETIF_PP_PHASE_OFFSET + 1, + NETIF_PPP_PHASE_HOLDOFF = NETIF_PP_PHASE_OFFSET + 2, + NETIF_PPP_PHASE_INITIALIZE = NETIF_PP_PHASE_OFFSET + 3, + NETIF_PPP_PHASE_SERIALCONN = NETIF_PP_PHASE_OFFSET + 4, + NETIF_PPP_PHASE_DORMANT = NETIF_PP_PHASE_OFFSET + 5, + NETIF_PPP_PHASE_ESTABLISH = NETIF_PP_PHASE_OFFSET + 6, + NETIF_PPP_PHASE_AUTHENTICATE = NETIF_PP_PHASE_OFFSET + 7, + NETIF_PPP_PHASE_CALLBACK = NETIF_PP_PHASE_OFFSET + 8, + NETIF_PPP_PHASE_NETWORK = NETIF_PP_PHASE_OFFSET + 9, + NETIF_PPP_PHASE_RUNNING = NETIF_PP_PHASE_OFFSET + 10, + NETIF_PPP_PHASE_TERMINATE = NETIF_PP_PHASE_OFFSET + 11, + NETIF_PPP_PHASE_DISCONNECT = NETIF_PP_PHASE_OFFSET + 12, + + NETIF_PPP_CONNECT_FAILED = NETIF_PPP_INTERNAL_ERR_OFFSET + 0 + +type + + esp_netif_auth_type_t* {.size: sizeof(cint).} = enum ## + ## @brief definitions of different authorisation types + ## + ## + NETIF_PPP_AUTHTYPE_NONE = 0x00, + NETIF_PPP_AUTHTYPE_PAP = 0x01, + NETIF_PPP_AUTHTYPE_CHAP = 0x02, + NETIF_PPP_AUTHTYPE_MSCHAP = 0x04, + NETIF_PPP_AUTHTYPE_MSCHAP_V2 = 0x08, + NETIF_PPP_AUTHTYPE_EAP = 0x10 + + + +proc esp_netif_ppp_set_auth*(netif: ptr esp_netif_t; + authtype: esp_netif_auth_type_t; user: cstring; + passwd: cstring): esp_err_t {.cdecl, + importc: "esp_netif_ppp_set_auth", header: hdr.} + ## + ## @brief Sets the auth parameters for the supplied esp-netif. + ## + ## @param[in] esp_netif Handle to esp-netif instance + ## @param[in] authtype Authorisation type + ## @param[in] user User name + ## @param[in] passwd Password + ## + ## @return ESP_OK on success, + ## ESP_ERR_ESP_NETIF_INVALID_PARAMS if the supplied netif is not of PPP type, or netif is null + ## + +proc esp_netif_ppp_set_params*(netif: ptr esp_netif_t; + config: ptr esp_netif_ppp_config_t): esp_err_t {. + cdecl, importc: "esp_netif_ppp_set_params", header: hdr.} + ## + ## @brief Sets common parameters for the supplied esp-netif. + ## + ## @param[in] esp_netif Handle to esp-netif instance + ## @param[in] config Pointer to PPP netif configuration structure + ## + ## @return ESP_OK on success, + ## ESP_ERR_ESP_NETIF_INVALID_PARAMS if the supplied netif is not of PPP type, or netif is null + ## + +proc esp_netif_ppp_get_params*(netif: ptr esp_netif_t; + config: ptr esp_netif_ppp_config_t): esp_err_t {. + cdecl, importc: "esp_netif_ppp_get_params", header: hdr.} + ## + ## @brief Gets parameters configured in the supplied esp-netif. + ## + ## @param[in] esp_netif Handle to esp-netif instance + ## @param[out] config Pointer to PPP netif configuration structure + ## + ## @return ESP_OK on success, + ## ESP_ERR_ESP_NETIF_INVALID_PARAMS if the supplied netif is not of PPP type, or netif is null + ## \ No newline at end of file diff --git a/src/nesper/esp/net/esp_netif_sntp.nim b/src/nesper/esp/net/esp_netif_sntp.nim new file mode 100644 index 00000000..c7fee0de --- /dev/null +++ b/src/nesper/esp/net/esp_netif_sntp.nim @@ -0,0 +1,118 @@ +## +## SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD +## +## SPDX-License-Identifier: Apache-2.0 +## + +import std/posix +import ../../consts +import ./esp_netif_types + +type + + esp_sntp_time_cb_t* = proc (tv: ptr TimeVal) {.cdecl.} ## + ## + ## @defgroup ESP_NETIF_SNTP_API ESP-NETIF SNTP API + ## @brief SNTP API for underlying TCP/IP stack + ## + ## + ## @addtogroup ESP_NETIF_SNTP_API + ## @{ + ## + ## + ## @brief Time sync notification function + ## + +## +## @brief Utility macro for providing multiple servers in parentheses +## + +## +## @brief Default configuration to init SNTP with multiple servers +## @param servers_in_list Number of servers in the list +## @param list_of_servers List of servers (use ESP_SNTP_SERVER_LIST(...)) +## +## + +## +## @brief Default configuration with a single server +## + +type + + esp_sntp_config_t* {.importc: "esp_sntp_config_t", header: "esp_netif_sntp.h", + bycopy.} = object ## + ## @brief SNTP configuration struct + ## + smooth_sync* {.importc: "smooth_sync".}: bool ## < set to true if smooth sync required + server_from_dhcp* {.importc: "server_from_dhcp".}: bool ## + ## < set to true to request NTP server config from DHCP + wait_for_sync* {.importc: "wait_for_sync".}: bool ## + ## < if true, we create a semaphore to signal time sync event + start* {.importc: "start".}: bool ## < set to true to automatically start the SNTP service + sync_cb* {.importc: "sync_cb".}: esp_sntp_time_cb_t ## + ## < optionally sets callback function on time sync event + renew_servers_after_new_IP* {.importc: "renew_servers_after_new_IP".}: bool ## + ## < this is used to refresh server list if NTP provided by DHCP (which cleans other pre-configured servers) + ip_event_to_renew* {.importc: "ip_event_to_renew".}: ip_event_t ## + ## < set the IP event id on which we refresh server list (if renew_servers_after_new_IP=true) + index_of_first_server* {.importc: "index_of_first_server".}: csize_t ## + ## < refresh server list after this server (if renew_servers_after_new_IP=true) + num_of_servers* {.importc: "num_of_servers".}: csize_t ## + ## < number of preconfigured NTP servers + servers* {.importc: "servers".}: UncheckedArray[cstring] + ## < list of servers + + +proc esp_netif_sntp_init*(config: ptr esp_sntp_config_t): esp_err_t {.cdecl, + importc: "esp_netif_sntp_init", header: "esp_netif_sntp.h".} + ## + ## + ## @brief Initialize SNTP with supplied config struct + ## @param config Config struct + ## @return ESP_OK on success + ## + +proc esp_netif_sntp_start*(): esp_err_t {.cdecl, + importc: "esp_netif_sntp_start", header: "esp_netif_sntp.h".} + ## + ## + ## @brief Start SNTP service + ## if it wasn't started during init (config.start = false) + ## or restart it if already started + ## @return ESP_OK on success + ## + +proc esp_netif_sntp_deinit*() {.cdecl, importc: "esp_netif_sntp_deinit", + header: "esp_netif_sntp.h".} + ## + ## + ## @brief Deinitialize esp_netif SNTP module + ## + +proc esp_netif_sntp_sync_wait*(tout: TickType_t): esp_err_t {.cdecl, + importc: "esp_netif_sntp_sync_wait", header: "esp_netif_sntp.h".} + ## + ## + ## @brief Wait for time sync event + ## @param tout Specified timeout in RTOS ticks + ## @return ESP_TIMEOUT if sync event didn't came within the timeout + ## ESP_ERR_NOT_FINISHED if the sync event came, but we're in smooth update mode and still in progress (SNTP_SYNC_STATUS_IN_PROGRESS) + ## ESP_OK if time sync'ed + ## + +proc esp_netif_sntp_reachability*(index: cuint; reachability: ptr cuint): esp_err_t {. + cdecl, importc: "esp_netif_sntp_reachability", header: "esp_netif_sntp.h".} + ## + ## + ## @brief Returns SNTP server's reachability shift register as described in RFC 5905. + ## + ## @param index Index of the SERVER + ## @param reachability reachability shift register + ## @return ESP_OK on success, + ## ESP_ERR_INVALID_STATE if SNTP not initialized + ## ESP_ERR_INVALID_ARG if invalid arguments + ## +## +## @} +## diff --git a/src/nesper/esp/net/esp_netif_types.nim b/src/nesper/esp/net/esp_netif_types.nim index d945c3d5..62d935c0 100644 --- a/src/nesper/esp/net/esp_netif_types.nim +++ b/src/nesper/esp/net/esp_netif_types.nim @@ -1,220 +1,374 @@ -## Copyright 2015-2019 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 +## SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD ## -## 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. - -## * -## @brief Definition of ESP-NETIF based errors +## SPDX-License-Identifier: Apache-2.0 ## -import ../../consts -import esp_netif_ip_addr +import ../esp_event +import ./esp_netif_ip_addr + +const hdr = "esp_netif_types.h" const - ESP_ERR_ESP_NETIF_BASE* = 0x00005000 - ESP_ERR_ESP_NETIF_INVALID_PARAMS* = ESP_ERR_ESP_NETIF_BASE + 0x00000001 - ESP_ERR_ESP_NETIF_IF_NOT_READY* = ESP_ERR_ESP_NETIF_BASE + 0x00000002 - ESP_ERR_ESP_NETIF_DHCPC_START_FAILED* = ESP_ERR_ESP_NETIF_BASE + 0x00000003 - ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED* = ESP_ERR_ESP_NETIF_BASE + 0x00000004 - ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED* = ESP_ERR_ESP_NETIF_BASE + 0x00000005 - ESP_ERR_ESP_NETIF_NO_MEM* = ESP_ERR_ESP_NETIF_BASE + 0x00000006 - ESP_ERR_ESP_NETIF_DHCP_NOT_STOPPED* = ESP_ERR_ESP_NETIF_BASE + 0x00000007 - ESP_ERR_ESP_NETIF_DRIVER_ATTACH_FAILED* = ESP_ERR_ESP_NETIF_BASE + 0x00000008 - ESP_ERR_ESP_NETIF_INIT_FAILED* = ESP_ERR_ESP_NETIF_BASE + 0x00000009 - ESP_ERR_ESP_NETIF_DNS_NOT_CONFIGURED* = ESP_ERR_ESP_NETIF_BASE + 0x0000000A - -## * @brief Type of esp_netif_object server -const hdr = """#include - #include "esp_netif_types.h" """ + ESP_ERR_ESP_NETIF_BASE* = 0x5000 ## + ## @brief Definition of ESP-NETIF based errors + ## + ESP_ERR_ESP_NETIF_INVALID_PARAMS* = ESP_ERR_ESP_NETIF_BASE + 0x01 + ESP_ERR_ESP_NETIF_IF_NOT_READY* = ESP_ERR_ESP_NETIF_BASE + 0x02 + ESP_ERR_ESP_NETIF_DHCPC_START_FAILED* = ESP_ERR_ESP_NETIF_BASE + 0x03 + ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED* = ESP_ERR_ESP_NETIF_BASE + 0x04 + ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED* = ESP_ERR_ESP_NETIF_BASE + 0x05 + ESP_ERR_ESP_NETIF_NO_MEM* = ESP_ERR_ESP_NETIF_BASE + 0x06 + ESP_ERR_ESP_NETIF_DHCP_NOT_STOPPED* = ESP_ERR_ESP_NETIF_BASE + 0x07 + ESP_ERR_ESP_NETIF_DRIVER_ATTACH_FAILED* = ESP_ERR_ESP_NETIF_BASE + 0x08 + ESP_ERR_ESP_NETIF_INIT_FAILED* = ESP_ERR_ESP_NETIF_BASE + 0x09 + ESP_ERR_ESP_NETIF_DNS_NOT_CONFIGURED* = ESP_ERR_ESP_NETIF_BASE + 0x0A + ESP_ERR_ESP_NETIF_MLD6_FAILED* = ESP_ERR_ESP_NETIF_BASE + 0x0B + ESP_ERR_ESP_NETIF_IP6_ADDR_FAILED* = ESP_ERR_ESP_NETIF_BASE + 0x0C + ESP_ERR_ESP_NETIF_DHCPS_START_FAILED* = ESP_ERR_ESP_NETIF_BASE + 0x0D + ESP_ERR_ESP_NETIF_TX_FAILED* = ESP_ERR_ESP_NETIF_BASE + 0x0E + ESP_NETIF_BR_FLOOD* = -1 ## + ## @brief Definition of ESP-NETIF bridge control + ## + ESP_NETIF_BR_DROP* = 0 + ESP_NETIF_BR_FDW_CPU* = (1'u64 shl 63) + +## @brief Type of esp_netif_object server type - esp_netif_t* {.importc: "esp_netif_t", header: hdr, bycopy.} = object -## * @brief Type of DNS server + esp_netif_t* {.importc: "esp_netif_t", header: hdr, bycopy.} = object -type - esp_netif_dns_type_t* {.size: sizeof(cint).} = enum - ESP_NETIF_DNS_MAIN = 0, ## *< DNS main server address - ESP_NETIF_DNS_BACKUP, ## *< DNS backup server address (Wi-Fi STA and Ethernet only) - ESP_NETIF_DNS_FALLBACK, ## *< DNS fallback server address (Wi-Fi STA and Ethernet only) + esp_netif_dns_type_t* {.size: sizeof(cint).} = enum ## + ## @brief Type of DNS server + ESP_NETIF_DNS_MAIN = 0, ## < DNS main server address + ESP_NETIF_DNS_BACKUP, ## < DNS backup server address (Wi-Fi STA and Ethernet only) + ESP_NETIF_DNS_FALLBACK, ## < DNS fallback server address (Wi-Fi STA and Ethernet only) ESP_NETIF_DNS_MAX -## * @brief DNS server info - type - esp_netif_dns_info_t* {.importc: "esp_netif_dns_info_t", - header: hdr, bycopy.} = object - ip* {.importc: "ip".}: esp_ip_addr_t ## *< IPV4 address of DNS server + esp_netif_dns_info_t* {.importc: "esp_netif_dns_info_t", + header: hdr, bycopy.} = object ## + ## @brief DNS server info + ip* {.importc: "ip".}: esp_ip_addr_t + ## < IPV4 address of DNS server -## * @brief Status of DHCP client or DHCP server -type - esp_netif_dhcp_status_t* {.size: sizeof(cint).} = enum - ESP_NETIF_DHCP_INIT = 0, ## *< DHCP client/server is in initial state (not yet started) - ESP_NETIF_DHCP_STARTED, ## *< DHCP client/server has been started - ESP_NETIF_DHCP_STOPPED, ## *< DHCP client/server has been stopped + esp_netif_dhcp_status_t* {.size: sizeof(cint).} = enum ## + ## @brief Status of DHCP client or DHCP server + ESP_NETIF_DHCP_INIT = 0, ## < DHCP client/server is in initial state (not yet started) + ESP_NETIF_DHCP_STARTED, ## < DHCP client/server has been started + ESP_NETIF_DHCP_STOPPED, ## < DHCP client/server has been stopped ESP_NETIF_DHCP_STATUS_MAX -## * @brief Mode for DHCP client or DHCP server option functions - type - esp_netif_dhcp_option_mode_t* {.size: sizeof(cint).} = enum - ESP_NETIF_OP_START = 0, ESP_NETIF_OP_SET, ## *< Set option - ESP_NETIF_OP_GET, ## *< Get option - ESP_NETIF_OP_MAX + esp_netif_dhcp_option_mode_t* {.size: sizeof(cint).} = enum ## + ## @brief Mode for DHCP client or DHCP server option functions + ESP_NETIF_OP_START = 0, ESP_NETIF_OP_SET, ## < Set option + ESP_NETIF_OP_GET, ## < Get option + ESP_NETIF_OP_MAX -## * @brief Supported options for DHCP client or DHCP server type - esp_netif_dhcp_option_id_t* {.size: sizeof(cint).} = enum - ESP_NETIF_DOMAIN_NAME_SERVER = 6, ## *< Domain name server - ESP_NETIF_ROUTER_SOLICITATION_ADDRESS = 32, ## *< Solicitation router address - ESP_NETIF_REQUESTED_IP_ADDRESS = 50, ## *< Request specific IP address - ESP_NETIF_IP_ADDRESS_LEASE_TIME = 51, ## *< Request IP address lease time - ESP_NETIF_IP_REQUEST_RETRY_TIME = 52 ## *< Request IP address retry counter + esp_netif_dhcp_option_id_t* {.size: sizeof(cint).} = enum ## + ## @brief Supported options for DHCP client or DHCP server + ESP_NETIF_SUBNET_MASK = 1, ## < Network mask + ESP_NETIF_DOMAIN_NAME_SERVER = 6, ## < Domain name server + ESP_NETIF_ROUTER_SOLICITATION_ADDRESS = 32, ## < Solicitation router address + ESP_NETIF_VENDOR_SPECIFIC_INFO = 43, ## < Vendor Specific Information of a DHCP server + ESP_NETIF_REQUESTED_IP_ADDRESS = 50, ## < Request specific IP address + ESP_NETIF_IP_ADDRESS_LEASE_TIME = 51, ## < Request IP address lease time + ESP_NETIF_IP_REQUEST_RETRY_TIME = 52, ## < Request IP address retry counter + ESP_NETIF_VENDOR_CLASS_IDENTIFIER = 60, ## < Vendor Class Identifier of a DHCP client + ESP_NETIF_CAPTIVEPORTAL_URI = 114 ## < Captive Portal Identification -## * IP event declarations type - ip_event_t* {.size: sizeof(cint).} = enum - IP_EVENT_STA_GOT_IP, ## !< station got IP from connected AP - IP_EVENT_STA_LOST_IP, ## !< station lost IP and the IP is reset to 0 - IP_EVENT_AP_STAIPASSIGNED, ## !< soft-AP assign an IP to a connected station - IP_EVENT_GOT_IP6, ## !< station or ap or ethernet interface v6IP addr is preferred - IP_EVENT_ETH_GOT_IP, ## !< ethernet got IP from connected AP - IP_EVENT_PPP_GOT_IP, ## !< PPP interface got IP - IP_EVENT_PPP_LOST_IP ## !< PPP interface lost IP + ip_event_t* {.size: sizeof(cint).} = enum ## IP event declarations + IP_EVENT_STA_GOT_IP, ## !< station got IP from connected AP + IP_EVENT_STA_LOST_IP, ## !< station lost IP and the IP is reset to 0 + IP_EVENT_AP_STAIPASSIGNED, ## !< soft-AP assign an IP to a connected station + IP_EVENT_GOT_IP6, ## !< station or ap or ethernet interface v6IP addr is preferred + IP_EVENT_ETH_GOT_IP, ## !< ethernet got IP from connected AP + IP_EVENT_ETH_LOST_IP, ## !< ethernet lost IP and the IP is reset to 0 + IP_EVENT_PPP_GOT_IP, ## !< PPP interface got IP + IP_EVENT_PPP_LOST_IP, ## !< PPP interface lost IP + IP_EVENT_TX_RX ## !< transmitting/receiving data packet -## * @brief IP event base declaration -# ESP_EVENT_DECLARE_BASE(IP_EVENT) -var IP_EVENT* {.importc: "$1", header: hdr.}: esp_event_base_t +## @brief IP event base declaration -## * Event structure for IP_EVENT_STA_GOT_IP, IP_EVENT_ETH_GOT_IP events +let IP_EVENT* {.importc: "IP_EVENT", header: "esp_netif_types.h".}: esp_event_base_t type - esp_netif_ip_info_t* {.importc: "esp_netif_ip_info_t", - header: hdr, bycopy.} = object - ip* {.importc: "ip".}: esp_ip4_addr_t ## *< Interface IPV4 address - netmask* {.importc: "netmask".}: esp_ip4_addr_t ## *< Interface IPV4 netmask - gw* {.importc: "gw".}: esp_ip4_addr_t ## *< Interface IPV4 gateway address + esp_netif_ip_info_t* {.importc: "esp_netif_ip_info_t", + header: hdr, bycopy.} = object ## + ## Event structure for IP_EVENT_STA_GOT_IP, IP_EVENT_ETH_GOT_IP events + ip* {.importc: "ip".}: esp_ip4_addr_t ## < Interface IPV4 address + netmask* {.importc: "netmask".}: esp_ip4_addr_t ## + ## < Interface IPV4 netmask + gw* {.importc: "gw".}: esp_ip4_addr_t + ## < Interface IPV4 gateway address -## * @brief IPV6 IP address information -## -type esp_netif_ip6_info_t* {.importc: "esp_netif_ip6_info_t", - header: hdr, bycopy.} = object - ip* {.importc: "ip".}: esp_ip6_addr_t ## *< Interface IPV6 address + header: hdr, bycopy.} = object ## + ## @brief IPV6 IP address information + ## + ip* {.importc: "ip".}: esp_ip6_addr_t + ## < Interface IPV6 address + + + ip_event_got_ip_t* {.importc: "ip_event_got_ip_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Event structure for IP_EVENT_GOT_IP event + ## + ## + esp_netif* {.importc: "esp_netif".}: ptr esp_netif_t ## + ## !< Pointer to corresponding esp-netif object + ip_info* {.importc: "ip_info".}: esp_netif_ip_info_t ## + ## !< IP address, netmask, gateway IP address + ip_changed* {.importc: "ip_changed".}: bool + ## !< Whether the assigned IP has changed or not + + + ip_event_got_ip6_t* {.importc: "ip_event_got_ip6_t", + header: hdr, bycopy.} = object ## + ## Event structure for IP_EVENT_GOT_IP6 event + esp_netif* {.importc: "esp_netif".}: ptr esp_netif_t ## + ## !< Pointer to corresponding esp-netif object + ip6_info* {.importc: "ip6_info".}: esp_netif_ip6_info_t ## + ## !< IPv6 address of the interface + ip_index* {.importc: "ip_index".}: cint + ## !< IPv6 address index + + + ip_event_add_ip6_t* {.importc: "ip_event_add_ip6_t", + header: hdr, bycopy.} = object ## + ## Event structure for ADD_IP6 event + `addr`* {.importc: "addr".}: esp_ip6_addr_t ## !< The address to be added to the interface + preferred* {.importc: "preferred".}: bool + ## !< The default preference of the address - ip_event_got_ip_t* {.importc: "ip_event_got_ip_t", header: hdr, - bycopy.} = object - if_index* {.importc: "if_index".}: cint ## !< Interface index for which the event is received (left for legacy compilation) - esp_netif* {.importc: "esp_netif".}: ptr esp_netif_t ## !< Pointer to corresponding esp-netif object - ip_info* {.importc: "ip_info".}: esp_netif_ip_info_t ## !< IP address, netmask, gatway IP address - ip_changed* {.importc: "ip_changed".}: bool ## !< Whether the assigned IP has changed or not + ip_event_ap_staipassigned_t* {.importc: "ip_event_ap_staipassigned_t", + header: hdr, bycopy.} = object ## + ## Event structure for IP_EVENT_AP_STAIPASSIGNED event + esp_netif* {.importc: "esp_netif".}: ptr esp_netif_t ## + ## !< Pointer to the associated netif handle + ip* {.importc: "ip".}: esp_ip4_addr_t ## !< IP address which was assigned to the station + mac* {.importc: "mac".}: array[6, uint8] + ## !< MAC address of the connected client -## * Event structure for IP_EVENT_GOT_IP6 event -type - ip_event_got_ip6_t* {.importc: "ip_event_got_ip6_t", header: hdr, - bycopy.} = object - if_index* {.importc: "if_index".}: cint ## !< Interface index for which the event is received (left for legacy compilation) - esp_netif* {.importc: "esp_netif".}: ptr esp_netif_t ## !< Pointer to corresponding esp-netif object - ip6_info* {.importc: "ip6_info".}: esp_netif_ip6_info_t ## !< IPv6 address of the interface + esp_netif_tx_rx_direction_t* {.size: sizeof(cint).} = enum + ESP_NETIF_TX = 0, ## Data is being transmitted. + ESP_NETIF_RX = 1 ## Data is being received. + +when defined(CONFIG_ESP_NETIF_REPORT_DATA_TRAFFIC): + ## Event structure for IP_EVENT_TRANSMIT and IP_EVENT_RECEIVE + type -## * Event structure for IP_EVENT_AP_STAIPASSIGNED event + ip_event_tx_rx_t* {.importc: "ip_event_tx_rx_t", + header: hdr, bycopy.} = object + esp_netif* {.importc: "esp_netif".}: ptr esp_netif_t ## + ## !< Pointer to the associated netif handle + len* {.importc: "len".}: csize_t ## !< Length of the data + dir* {.importc: "dir".}: esp_netif_tx_rx_direction_t + ## !< Directions for data transfer > type - ip_event_ap_staipassigned_t* {.importc: "ip_event_ap_staipassigned_t", - header: hdr, bycopy.} = object - ip* {.importc: "ip".}: esp_ip4_addr_t ## !< IP address which was assigned to the station esp_netif_flags_t* {.size: sizeof(cint).} = enum ESP_NETIF_DHCP_CLIENT = 1 shl 0, ESP_NETIF_DHCP_SERVER = 1 shl 1, ESP_NETIF_FLAG_AUTOUP = 1 shl 2, ESP_NETIF_FLAG_GARP = 1 shl 3, - ESP_NETIF_FLAG_EVENT_IP_MODIFIED = 1 shl 4, ESP_NETIF_FLAG_IS_PPP = 1 shl 5 + ESP_NETIF_FLAG_EVENT_IP_MODIFIED = 1 shl 4, ESP_NETIF_FLAG_IS_PPP = 1 shl 5, + ESP_NETIF_FLAG_IS_BRIDGE = 1 shl 6, ESP_NETIF_FLAG_MLDV6_REPORT = 1 shl 7, + ESP_NETIF_FLAG_IPV6_AUTOCONFIG_ENABLED = 1 shl 8 + esp_netif_ip_event_type_t* {.size: sizeof(cint).} = enum ESP_NETIF_IP_EVENT_GOT_IP = 1, ESP_NETIF_IP_EVENT_LOST_IP = 2 +when defined(CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS): + type + esp_netif_recv_ret_t* = esp_err_t +else: + type + esp_netif_recv_ret_t* = void -## -## ESP-NETIF interface configuration: -## 1) general (behavioral) config (esp_netif_config_t) -## 2) (peripheral) driver specific config (esp_netif_driver_ifconfig_t) -## 3) network stack specific config (esp_netif_net_stack_ifconfig_t) -- no publicly available -## - type + + err_t* = distinct int8 + netif* {.importc: "netif", header: hdr, bycopy.} = object + mtu* {.importc: "mtu".}: uint16 + + init_fn_t* = proc (a1: pointer): err_t {.cdecl.} + + input_fn_t* = proc (netif: pointer; buffer: pointer; len: csize_t; eb: pointer): esp_netif_recv_ret_t {. + cdecl.} + + esp_netif_netstack_lwip_vanilla_config* {. + importc: "esp_netif_netstack_lwip_vanilla_config", + header: "esp_netif_net_stack.h", bycopy.} = object + init_fn* {.importc: "init_fn".}: init_fn_t + input_fn* {.importc: "input_fn".}: input_fn_t + + + esp_netif_netstack_lwip_ppp_config* {.importc: "esp_netif_netstack_lwip_ppp_config", + header: "esp_netif_net_stack.h", bycopy.} = object + input_fn* {.importc: "input_fn".}: input_fn_t + ppp_events* {.importc: "ppp_events".}: esp_netif_ppp_config_t + + + esp_netif_netstack_config* {.importc: "esp_netif_netstack_config", + header: "esp_netif_net_stack.h", bycopy.} = object ## + ## LWIP netif specific network stack configuration + lwip* {.importc: "lwip".}: esp_netif_netstack_lwip_vanilla_config + lwip_ppp* {.importc: "lwip_ppp".}: esp_netif_netstack_lwip_ppp_config + + esp_netif_ppp_config_t* {.importc: "esp_netif_ppp_config_t", + header: "esp_netif_ppp.h", bycopy.} = object ## + ## @brief Configuration structure for PPP network interface + ## + ## + ppp_phase_event_enabled* {.importc: "ppp_phase_event_enabled".}: bool ## + ## < Enables events coming from PPP PHASE change + ppp_error_event_enabled* {.importc: "ppp_error_event_enabled".}: bool ## + ## < Enables events from main PPP state machine producing errors + when defined(CONFIG_LWIP_ENABLE_LCP_ECHO): + ppp_lcp_echo_disabled* {.importc: "ppp_lcp_echo_disabled".}: bool ## + ## #ifdef CONFIG_LWIP_ENABLE_LCP_ECHO + ## + when defined(CONFIG_LWIP_PPP_SERVER_SUPPORT): + ppp_our_ip4_addr* {.importc: "ppp_our_ip4_addr".}: esp_ip4_addr_t ## + ## #endif // CONFIG_LWIP_ENABLE_LCP_ECHO + ## #ifdef CONFIG_LWIP_PPP_SERVER_SUPPORT + ppp_their_ip4_addr* {.importc: "ppp_their_ip4_addr".}: esp_ip4_addr_t ## + ## < Set our preferred address, typically used when we're the PPP server + ppp_dns1_addr* {.importc: "ppp_dns1_addr".}: esp_ip4_addr_t ## + ## < DNS to provide if peer asks for it, typically used when we're the PPP server + ppp_dns2_addr* {.importc: "ppp_dns2_addr".}: esp_ip4_addr_t ## + ## < DNS to provide if peer asks for it, typically used when we're the PPP server + ppp_passive* {.importc: "ppp_passive".}: bool ## < Try once to initiate connection, stay silent if it fails, typically used when we're the PPP server + ## #endif // CONFIG_LWIP_PPP_SERVER_SUPPORT + + bridgeif_config_t* {.importc: "bridgeif_config_t", + header: hdr, bycopy.} = object ## + ## LwIP bridge configuration + max_fdb_dyn_entries* {.importc: "max_fdb_dyn_entries".}: uint16 ## + ## !< maximum number of entries in dynamic forwarding database + max_fdb_sta_entries* {.importc: "max_fdb_sta_entries".}: uint16 ## + ## !< maximum number of entries in static forwarding database + max_ports* {.importc: "max_ports".}: uint8 + ## !< maximum number of ports the bridge can consist of + + esp_netif_inherent_config_t* {.importc: "esp_netif_inherent_config_t", - header: hdr, bycopy.} = object - flags* {.importc: "flags".}: esp_netif_flags_t ## !< flags that define esp-netif behavior + header: hdr, bycopy.} = object ## + ## + ## ESP-NETIF interface configuration: + ## 1) general (behavioral) config (esp_netif_config_t) + ## 2) (peripheral) driver specific config (esp_netif_driver_ifconfig_t) + ## 3) network stack specific config (esp_netif_net_stack_ifconfig_t) -- no publicly available + ## + ## + ## @brief ESP-netif inherent config parameters + ## + ## + flags* {.importc: "flags".}: esp_netif_flags_t ## + ## !< flags that define esp-netif behavior mac* {.importc: "mac".}: array[6, uint8] ## !< initial mac address for this interface - ip_info* {.importc: "ip_info".}: ptr esp_netif_ip_info_t ## !< initial ip address for this interface - get_ip_event* {.importc: "get_ip_event".}: uint32 ## !< event id to be raised when interface gets an IP - lost_ip_event* {.importc: "lost_ip_event".}: uint32 ## !< event id to be raised when interface losts its IP + ip_info* {.importc: "ip_info".}: ptr esp_netif_ip_info_t ## + ## !< initial ip address for this interface + get_ip_event* {.importc: "get_ip_event".}: uint32 ## + ## !< event id to be raised when interface gets an IP + lost_ip_event* {.importc: "lost_ip_event".}: uint32 ## + ## !< event id to be raised when interface losts its IP if_key* {.importc: "if_key".}: cstring ## !< string identifier of the interface if_desc* {.importc: "if_desc".}: cstring ## !< textual description of the interface route_prio* {.importc: "route_prio".}: cint ## !< numeric priority of this interface to become a default - ## routing if (if other netifs are up) + ## routing if (if other netifs are up). + ## A higher value of route_prio indicates + ## a higher priority + bridge_info* {.importc: "bridge_info".}: ptr bridgeif_config_t + ## !< LwIP bridge configuration + - ## @brief IO driver handle type - esp_netif_iodriver_handle* = pointer + esp_netif_iodriver_handle* = pointer ## + ## @brief IO driver handle type + ## esp_netif_driver_base_t* {.importc: "esp_netif_driver_base_t", - header: hdr, bycopy.} = object + header: hdr, bycopy.} = object ## + ## + ## @brief ESP-netif driver base handle + ## + ## post_attach* {.importc: "post_attach".}: proc (netif: ptr esp_netif_t; - h: esp_netif_iodriver_handle): esp_err_t + h: esp_netif_iodriver_handle): esp_err_t {.cdecl.} ## + ## !< post attach function pointer netif* {.importc: "netif".}: ptr esp_netif_t + ## !< netif handle - ## @brief Specific IO driver configuration - esp_netif_driver_ifconfig_t* {.importc: "esp_netif_driver_ifconfig_t", - header: hdr, bycopy.} = object - handle* {.importc: "handle".}: esp_netif_iodriver_handle - transmit* {.importc: "transmit".}: proc (h: pointer; buffer: pointer; len: csize_t): esp_err_t - driver_free_rx_buffer* {.importc: "driver_free_rx_buffer".}: proc (h: pointer; - buffer: pointer) - - esp_netif_netstack_lwip_vanilla_config* {.bycopy.} = object - init_fn*: proc (a1: pointer): esp_err_t - input_fn*: proc (netif: pointer; buffer: pointer; len: csize_t; eb: pointer) - esp_netif_netstack_config_t* {.importc: "$1", header: hdr, bycopy.} = object - lwip: esp_netif_netstack_lwip_vanilla_config + esp_netif_driver_ifconfig_t* {.importc: "esp_netif_driver_ifconfig_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Specific IO driver configuration + ## + handle* {.importc: "handle".}: esp_netif_iodriver_handle ## + ## !< io-driver handle + transmit* {.importc: "transmit".}: proc (h: pointer; buffer: pointer; + len: csize_t): esp_err_t {.cdecl.} ## !< transmit function pointer + transmit_wrap* {.importc: "transmit_wrap".}: proc (h: pointer; + buffer: pointer; len: csize_t; netstack_buffer: pointer): esp_err_t {. + cdecl.} ## !< transmit wrap function pointer + driver_free_rx_buffer* {.importc: "driver_free_rx_buffer".}: proc ( + h: pointer; buffer: pointer) {.cdecl.} ## !< free rx buffer function pointer + driver_set_mac_filter* {.importc: "driver_set_mac_filter".}: proc ( + h: pointer; mac: ptr uint8; mac_len: csize_t; add: bool): esp_err_t {. + cdecl.} + ## !< set mac filter function pointer + + esp_netif_netstack_config_t* = esp_netif_netstack_config ## + ## + ## @brief Specific L3 network stack configuration + ## - ## @brief Generic esp_netif configuration esp_netif_config_t* {.importc: "esp_netif_config_t", header: hdr, - bycopy.} = object - base* {.importc: "base".}: ptr esp_netif_inherent_config_t - driver* {.importc: "driver".}: ptr esp_netif_driver_ifconfig_t + bycopy.} = object ## + ## @brief Generic esp_netif configuration + ## + base* {.importc: "base".}: ptr esp_netif_inherent_config_t ## + ## !< base config + driver* {.importc: "driver".}: ptr esp_netif_driver_ifconfig_t ## + ## !< driver config stack* {.importc: "stack".}: ptr esp_netif_netstack_config_t + ## !< stack config + esp_netif_pair_mac_ip_t* {.importc: "esp_netif_pair_mac_ip_t", + header: hdr, bycopy.} = object ## + ## + ## @brief DHCP client's addr info (pair of MAC and IP address) + ## + mac* {.importc: "mac".}: array[6, uint8] ## < Clients MAC address + ip* {.importc: "ip".}: esp_ip4_addr_t + ## < Clients IP address - ## @brief Specific L3 network stack configuration - # esp_netif_config_t* = esp_netif_config - - -type - ## @brief ESP-NETIF Receive function type esp_netif_receive_t* = proc (esp_netif: ptr esp_netif_t; buffer: pointer; - len: csize_t; eb: pointer): esp_err_t + len: csize_t; eb: pointer): esp_err_t {.cdecl.} ## + ## + ## @brief ESP-NETIF Receive function type + ## diff --git a/src/nesper/esp/net/esp_wifi.nim b/src/nesper/esp/net/esp_wifi.nim index b67beee3..b8484321 100644 --- a/src/nesper/esp/net/esp_wifi.nim +++ b/src/nesper/esp/net/esp_wifi.nim @@ -57,13 +57,12 @@ import ../esp_event import ../../net_utils import esp_wifi_types +export esp_wifi_types + type wifi_osi_funcs_t* {.importc: "wifi_osi_funcs_t", header: "esp_wifi.h", bycopy.} = object wpa_crypto_funcs_t* {.importc: "wpa_crypto_funcs_t", header: "esp_wifi.h", bycopy.} = object -# import - # esp_err, esp_wifi_types, esp_event, esp_private/esp_wifi_private - const ESP_ERR_WIFI_NOT_INIT* = (ESP_ERR_WIFI_BASE + 1) ## !< WiFi driver was not installed by esp_wifi_init ESP_ERR_WIFI_NOT_STARTED* = (ESP_ERR_WIFI_BASE + 2) ## !< WiFi driver was not started by esp_wifi_start @@ -132,61 +131,25 @@ type magic* {.importc: "magic".}: cint ## *< WiFi init magic number, it should be the last field -# {.emit: """ -# wifi_init_config_t _wifi_init_config_default() { -# wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); -# return cfg; -# } -# """.} - -# proc wifi_init_config_default*(): wifi_init_config_t {.importc:"_wifi_init_config_default", header: "esp_wifi.h".} - -proc wifi_init_config_default*(): wifi_init_config_t = - {.emit: ["wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); "].} - {.emit: ["result = cfg; "].} - -# when defined(CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM): -# const -# WIFI_STATIC_TX_BUFFER_NUM* = CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM -# else: -# const -# WIFI_STATIC_TX_BUFFER_NUM* = 0 -# when defined(CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM): -# const -# WIFI_DYNAMIC_TX_BUFFER_NUM* = CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM -# else: -# const -# WIFI_DYNAMIC_TX_BUFFER_NUM* = 0 -# when CONFIG_ESP32_WIFI_CSI_ENABLED: -# const -# WIFI_CSI_ENABLED* = 1 -# else: -# const -# WIFI_CSI_ENABLED* = 0 -# when CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED: -# const -# WIFI_AMPDU_RX_ENABLED* = 1 -# else: -# const -# WIFI_AMPDU_RX_ENABLED* = 0 -# when CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED: -# const -# WIFI_AMPDU_TX_ENABLED* = 1 -# else: -# const -# WIFI_AMPDU_TX_ENABLED* = 0 -# when CONFIG_ESP32_WIFI_NVS_ENABLED: -# const -# WIFI_NVS_ENABLED* = 1 -# else: -# const -# WIFI_NVS_ENABLED* = 0 -# when CONFIG_NEWLIB_NANO_FORMAT: -# const -# WIFI_NANO_FORMAT_ENABLED* = 1 -# else: -# const -# WIFI_NANO_FORMAT_ENABLED* = 0 +proc WIFI_INIT_CONFIG_DEFAULT*(): wifi_init_config_t = + {.emit: """ + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + `result` = cfg; + """.} + + +# let ESP_WIFI_MAX_CONN_NUM* {.importc: "ESP_WIFI_MAX_CONN_NUM", header: hdr.}: cint ## *< station list + +type + wifi_sta_list_t* {.importc: "wifi_sta_list_t", header: "esp_wifi_types.h", bycopy.} = object + sta* {.importc: "sta".}: UncheckedArray[wifi_sta_info_t] ## *< station list + num* {.importc: "num".}: cint ## *< number of stations in the list (other entries are invalid) + + wifi_storage_t* {.size: sizeof(cint).} = enum + WIFI_STORAGE_FLASH, ## *< all configuration will store in both memory and flash + WIFI_STORAGE_RAM ## *< all configuration will only store in the memory + + var g_wifi_default_wpa_crypto_funcs* {.importc: "g_wifi_default_wpa_crypto_funcs", header: "esp_wifi.h".}: wpa_crypto_funcs_t @@ -195,80 +158,6 @@ var g_wifi_feature_caps* {.importc: "g_wifi_feature_caps", header: "esp_wifi.h". const WIFI_INIT_CONFIG_MAGIC* = 0x1F2F3F4F -# when defined(CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED): -# const -# WIFI_DEFAULT_TX_BA_WIN* = CONFIG_ESP32_WIFI_TX_BA_WIN -# else: -# const -# WIFI_DEFAULT_TX_BA_WIN* = 0 -# when defined(CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED): -# const -# WIFI_DEFAULT_RX_BA_WIN* = CONFIG_ESP32_WIFI_RX_BA_WIN -# else: -# const -# WIFI_DEFAULT_RX_BA_WIN* = 0 -# when CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1: -# const -# WIFI_TASK_CORE_ID* = 1 -# else: -# const -# WIFI_TASK_CORE_ID* = 0 -# when defined(CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN): -# const -# WIFI_SOFTAP_BEACON_MAX_LEN* = CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN -# else: -# const -# WIFI_SOFTAP_BEACON_MAX_LEN* = 752 -# when defined(CONFIG_ESP32_WIFI_MGMT_SBUF_NUM): -# const -# WIFI_MGMT_SBUF_NUM* = CONFIG_ESP32_WIFI_MGMT_SBUF_NUM -# else: -# const -# WIFI_MGMT_SBUF_NUM* = 32 -# const -# CONFIG_FEATURE_WPA3_SAE_BIT* = (1 shl 0) - -## #define WIFI_INIT_CONFIG_DEFAULT() { \ -## .event_handler = &esp_event_send, \ -## .osi_funcs = &g_wifi_osi_funcs, \ -## .wpa_crypto_funcs = g_wifi_default_wpa_crypto_funcs, \ -## .static_rx_buf_num = CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM,\ -## .dynamic_rx_buf_num = CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM,\ -## .tx_buf_type = CONFIG_ESP32_WIFI_TX_BUFFER_TYPE,\ -## .static_tx_buf_num = WIFI_STATIC_TX_BUFFER_NUM,\ -## .dynamic_tx_buf_num = WIFI_DYNAMIC_TX_BUFFER_NUM,\ -## .csi_enable = WIFI_CSI_ENABLED,\ -## .ampdu_rx_enable = WIFI_AMPDU_RX_ENABLED,\ -## .ampdu_tx_enable = WIFI_AMPDU_TX_ENABLED,\ -## .nvs_enable = WIFI_NVS_ENABLED,\ -## .nano_enable = WIFI_NANO_FORMAT_ENABLED,\ -## .tx_ba_win = WIFI_DEFAULT_TX_BA_WIN,\ -## .rx_ba_win = WIFI_DEFAULT_RX_BA_WIN,\ -## .wifi_task_core_id = WIFI_TASK_CORE_ID,\ -## .beacon_max_len = WIFI_SOFTAP_BEACON_MAX_LEN, \ -## .mgmt_sbuf_num = WIFI_MGMT_SBUF_NUM, \ -## .feature_caps = g_wifi_feature_caps, \ -## .magic = WIFI_INIT_CONFIG_MAGIC\ -## }; -## * -## @brief Init WiFi -## Alloc resource for WiFi driver, such as WiFi control structure, RX/TX buffer, -## WiFi NVS structure etc, this WiFi also start WiFi task -## -## @attention 1. This API must be called before all other WiFi API can be called -## @attention 2. Always use WIFI_INIT_CONFIG_DEFAULT macro to init the config to default values, this can -## guarantee all the fields got correct value when more fields are added into wifi_init_config_t -## in future release. If you want to set your owner initial values, overwrite the default values -## which are set by WIFI_INIT_CONFIG_DEFAULT, please be notified that the field 'magic' of -## wifi_init_config_t should always be WIFI_INIT_CONFIG_MAGIC! -## -## @param config pointer to WiFi init configuration structure; can point to a temporary variable. -## -## @return -## - ESP_OK: succeed -## - ESP_ERR_NO_MEM: out of memory -## - others: refer to error code esp_err.h -## proc esp_wifi_init*(config: ptr wifi_init_config_t): esp_err_t {. importc: "esp_wifi_init", header: "esp_wifi.h".} @@ -838,7 +727,7 @@ proc esp_wifi_get_promiscuous_ctrl_filter*(filter: ptr wifi_promiscuous_filter_t ## - others: refer to the erro code in esp_err.h ## -proc esp_wifi_set_config*(`interface`: wifi_interface_t; conf: ptr wifi_config_t): esp_err_t {. +proc esp_wifi_set_config*(intf: wifi_interface_t; conf: ptr wifi_config_t): esp_err_t {. importc: "esp_wifi_set_config", header: "esp_wifi.h".} ## * ## @brief Get configuration of specified interface @@ -853,7 +742,7 @@ proc esp_wifi_set_config*(`interface`: wifi_interface_t; conf: ptr wifi_config_t ## - ESP_ERR_WIFI_IF: invalid interface ## -proc esp_wifi_get_config*(`interface`: wifi_interface_t; conf: ptr wifi_config_t): esp_err_t {. +proc esp_wifi_get_config*(intf: wifi_interface_t; conf: ptr wifi_config_t): esp_err_t {. importc: "esp_wifi_get_config", header: "esp_wifi.h".} ## * ## @brief Get STAs associated with soft-AP diff --git a/src/nesper/esp/net/esp_wifi_default.nim b/src/nesper/esp/net/esp_wifi_default.nim new file mode 100644 index 00000000..a22c055b --- /dev/null +++ b/src/nesper/esp/net/esp_wifi_default.nim @@ -0,0 +1,166 @@ +## +## SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD +## +## SPDX-License-Identifier: Apache-2.0 +## + +import ../../consts +import ../../net_utils +import ./esp_netif_types + +proc esp_netif_attach_wifi_station*(esp_netif: ptr esp_netif_t): esp_err_t {. + cdecl, importc: "esp_netif_attach_wifi_station", + header: "esp_wifi_default.h".} + ## + ## @brief Attaches wifi station interface to supplied netif + ## + ## @param esp_netif instance to attach the wifi station to + ## + ## @return + ## - ESP_OK on success + ## - ESP_FAIL if attach failed + ## + +proc esp_netif_attach_wifi_ap*(esp_netif: ptr esp_netif_t): esp_err_t {.cdecl, + importc: "esp_netif_attach_wifi_ap", header: "esp_wifi_default.h".} + ## + ## + ## @brief Attaches wifi soft AP interface to supplied netif + ## + ## @param esp_netif instance to attach the wifi AP to + ## + ## @return + ## - ESP_OK on success + ## - ESP_FAIL if attach failed + ## + +proc esp_wifi_set_default_wifi_sta_handlers*(): esp_err_t {.cdecl, + importc: "esp_wifi_set_default_wifi_sta_handlers", + header: "esp_wifi_default.h".} + ## + ## @brief Sets default wifi event handlers for STA interface + ## + ## @return + ## - ESP_OK on success, error returned from esp_event_handler_register if failed + ## + +proc esp_wifi_set_default_wifi_ap_handlers*(): esp_err_t {.cdecl, + importc: "esp_wifi_set_default_wifi_ap_handlers", + header: "esp_wifi_default.h".} + ## + ## @brief Sets default wifi event handlers for AP interface + ## + ## @return + ## - ESP_OK on success, error returned from esp_event_handler_register if failed + ## + +proc esp_wifi_set_default_wifi_nan_handlers*(): esp_err_t {.cdecl, + importc: "esp_wifi_set_default_wifi_nan_handlers", + header: "esp_wifi_default.h".} + ## + ## @brief Sets default wifi event handlers for NAN interface + ## + ## @return + ## - ESP_OK on success, error returned from esp_event_handler_register if failed + ## + +proc esp_wifi_clear_default_wifi_driver_and_handlers*(esp_netif: pointer): esp_err_t {. + cdecl, importc: "esp_wifi_clear_default_wifi_driver_and_handlers", + header: "esp_wifi_default.h".} + ## + ## @brief Clears default wifi event handlers for supplied network interface + ## + ## @param esp_netif instance of corresponding if object + ## + ## @return + ## - ESP_OK on success, error returned from esp_event_handler_register if failed + ## + +proc esp_netif_create_default_wifi_ap*(): ptr esp_netif_t {.cdecl, + importc: "esp_netif_create_default_wifi_ap", header: "esp_wifi_default.h".} + ## + ## + ## @brief Creates default WIFI AP. In case of any init error this API aborts. + ## + ## @note The API creates esp_netif object with default WiFi access point config, + ## attaches the netif to wifi and registers wifi handlers to the default event loop. + ## This API uses assert() to check for potential errors, so it could abort the program. + ## (Note that the default event loop needs to be created prior to calling this API) + ## + ## @return pointer to esp-netif instance + ## + +proc esp_netif_create_default_wifi_sta*(): ptr esp_netif_t {.cdecl, + importc: "esp_netif_create_default_wifi_sta", header: "esp_wifi_default.h".} + ## + ## + ## @brief Creates default WIFI STA. In case of any init error this API aborts. + ## + ## @note The API creates esp_netif object with default WiFi station config, + ## attaches the netif to wifi and registers wifi handlers to the default event loop. + ## This API uses assert() to check for potential errors, so it could abort the program. + ## (Note that the default event loop needs to be created prior to calling this API) + ## + ## @return pointer to esp-netif instance + ## + +proc esp_netif_create_default_wifi_nan*(): ptr esp_netif_t {.cdecl, + importc: "esp_netif_create_default_wifi_nan", header: "esp_wifi_default.h".} + ## + ## + ## @brief Creates default WIFI NAN. In case of any init error this API aborts. + ## + ## @note The API creates esp_netif object with default WiFi station config, + ## attaches the netif to wifi and registers wifi handlers to the default event loop. + ## (Note that the default event loop needs to be created prior to calling this API) + ## + ## @return pointer to esp-netif instance + ## + +proc esp_netif_destroy_default_wifi*(esp_netif: pointer) {.cdecl, + importc: "esp_netif_destroy_default_wifi", header: "esp_wifi_default.h".} + ## + ## + ## @brief Destroys default WIFI netif created with esp_netif_create_default_wifi_...() API. + ## + ## @param[in] esp_netif object to detach from WiFi and destroy + ## + ## @note This API unregisters wifi handlers and detaches the created object from the wifi. + ## (this function is a no-operation if esp_netif is NULL) + ## + +proc esp_netif_create_wifi*(wifi_if: wifi_interface_t; + esp_netif_config: ptr esp_netif_inherent_config_t): ptr esp_netif_t {. + cdecl, importc: "esp_netif_create_wifi", header: "esp_wifi_default.h".} + ## + ## + ## @brief Creates esp_netif WiFi object based on the custom configuration. + ## + ## @attention This API DOES NOT register default handlers! + ## + ## @param[in] wifi_if type of wifi interface + ## @param[in] esp_netif_config inherent esp-netif configuration pointer + ## + ## @return pointer to esp-netif instance + ## + +proc esp_netif_create_default_wifi_mesh_netifs*( + p_netif_sta: ptr ptr esp_netif_t; p_netif_ap: ptr ptr esp_netif_t): esp_err_t {. + cdecl, importc: "esp_netif_create_default_wifi_mesh_netifs", + header: "esp_wifi_default.h".} + ## + ## @brief Creates default STA and AP network interfaces for esp-mesh. + ## + ## Both netifs are almost identical to the default station and softAP, but with + ## DHCP client and server disabled. Please note that the DHCP client is typically + ## enabled only if the device is promoted to a root node. + ## + ## Returns created interfaces which could be ignored setting parameters to NULL + ## if an application code does not need to save the interface instances + ## for further processing. + ## + ## @param[out] p_netif_sta pointer where the resultant STA interface is saved (if non NULL) + ## @param[out] p_netif_ap pointer where the resultant AP interface is saved (if non NULL) + ## + ## @return ESP_OK on success + ## \ No newline at end of file diff --git a/src/nesper/esp/net/esp_wifi_types.nim b/src/nesper/esp/net/esp_wifi_types.nim index 981371f7..dce38d50 100644 --- a/src/nesper/esp/net/esp_wifi_types.nim +++ b/src/nesper/esp/net/esp_wifi_types.nim @@ -1,377 +1,893 @@ -## 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. -import ../../consts +import ../esp_event + +const hdr = "esp_wifi_types.h" + +let + WIFI_AP_DEFAULT_MAX_IDLE_PERIOD* {.importc: "WIFI_AP_DEFAULT_MAX_IDLE_PERIOD", header: hdr.}: cint ## + ## SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + ## + ## SPDX-License-Identifier: Apache-2.0 + ## type - wifi_country_policy_t* {.size: sizeof(cint).} = enum - WIFI_COUNTRY_POLICY_AUTO, ## *< Country policy is auto, use the country info of AP to which the station is connected - WIFI_COUNTRY_POLICY_MANUAL ## *< Country policy is manual, always use the configured country info + wifi_mode_t* {.size: sizeof(cint).} = enum ## + ## @brief Wi-Fi mode type + ## + WIFI_MODE_NULL = 0, ## < Null mode + WIFI_MODE_STA, ## < Wi-Fi station mode + WIFI_MODE_AP, ## < Wi-Fi soft-AP mode + WIFI_MODE_APSTA, ## < Wi-Fi station + soft-AP mode + WIFI_MODE_NAN, ## < Wi-Fi NAN mode + WIFI_MODE_MAX -## * @brief Structure describing WiFi country-based regional restrictions. type - wifi_country_t* {.importc: "wifi_country_t", header: "esp_wifi_types.h", bycopy.} = object - cc* {.importc: "cc".}: array[3, char] ## *< country code string - schan* {.importc: "schan".}: uint8 ## *< start channel - nchan* {.importc: "nchan".}: uint8 ## *< total channel number - max_tx_power* {.importc: "max_tx_power".}: int8 ## *< This field is used for getting WiFi maximum transmitting power, call esp_wifi_set_max_tx_power to set the maximum transmitting power. - policy* {.importc: "policy".}: wifi_country_policy_t ## *< country policy - wifi_auth_mode_t* {.size: sizeof(cint).} = 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_MAX - wifi_err_reason_t* {.size: sizeof(cint).} = 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_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_second_chan_t* {.size: sizeof(cint).} = enum - WIFI_SECOND_CHAN_NONE = 0, ## *< the channel width is HT20 - WIFI_SECOND_CHAN_ABOVE, ## *< the channel width is HT40 and the secondary channel is above the primary channel - WIFI_SECOND_CHAN_BELOW ## *< the channel width is HT40 and the secondary channel is below the primary channel - wifi_scan_type_t* {.size: sizeof(cint).} = enum - WIFI_SCAN_TYPE_ACTIVE = 0, ## *< active scan - WIFI_SCAN_TYPE_PASSIVE ## *< passive scan - - - - - -## * @brief Range of active scan times per channel + wifi_interface_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Wi-Fi interface type + ## + WIFI_IF_STA, ## < Station interface + WIFI_IF_AP, ## < Soft-AP interface + WIFI_IF_NAN ## < NAN interface -type - wifi_active_scan_time_t* {.importc: "wifi_active_scan_time_t", - header: "esp_wifi_types.h", bycopy.} = object - min* {.importc: "min".}: uint32 ## *< minimum active scan time per channel, units: millisecond - max* {.importc: "max".}: uint32 ## *< maximum active scan time per channel, units: millisecond, values above 1500ms may - ## cause station to disconnect from AP and are not recommended. + wifi_action_tx_t* {.size: sizeof(cint).} = enum + WIFI_OFFCHAN_TX_CANCEL, ## < Cancel off-channel transmission + WIFI_OFFCHAN_TX_REQ ## < Request off-channel transmission + wifi_roc_t* {.size: sizeof(cint).} = enum + WIFI_ROC_CANCEL, ## < Cancel remain on channel + WIFI_ROC_REQ ## < Request remain on channel -## * @brief Aggregate of active & passive scan time per channel type - wifi_scan_time_t* {.importc: "wifi_scan_time_t", header: "esp_wifi_types.h", bycopy, union.} = object - active* {.importc: "active".}: wifi_active_scan_time_t ## *< active scan time per channel, units: millisecond. - passive* {.importc: "passive".}: uint32 ## *< passive scan time per channel, units: millisecond, values above 1500ms may - ## cause station to disconnect from AP and are not recommended. + wifi_country_policy_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Wi-Fi country policy + ## + WIFI_COUNTRY_POLICY_AUTO, ## < Country policy is auto, use the country info of AP to which the station is connected + WIFI_COUNTRY_POLICY_MANUAL ## < Country policy is manual, always use the configured country info -## * @brief Parameters for an SSID scan. type - wifi_scan_config_t* {.importc: "wifi_scan_config_t", header: "esp_wifi_types.h", - bycopy.} = object - ssid* {.importc: "ssid".}: ptr uint8 ## *< SSID of AP - bssid* {.importc: "bssid".}: ptr uint8 ## *< MAC address of AP - channel* {.importc: "channel".}: uint8 ## *< channel, scan the specific channel - show_hidden* {.importc: "show_hidden".}: bool ## *< enable to scan AP whose SSID is hidden - scan_type* {.importc: "scan_type".}: wifi_scan_type_t ## *< scan type, active or passive - scan_time* {.importc: "scan_time".}: wifi_scan_time_t ## *< scan time per channel - - wifi_cipher_type_t* {.size: sizeof(cint).} = enum - WIFI_CIPHER_TYPE_NONE = 0, ## *< the cipher type is none - WIFI_CIPHER_TYPE_WEP40, ## *< the cipher type is WEP40 - WIFI_CIPHER_TYPE_WEP104, ## *< the cipher type is WEP104 - WIFI_CIPHER_TYPE_TKIP, ## *< the cipher type is TKIP - WIFI_CIPHER_TYPE_CCMP, ## *< the cipher type is CCMP - WIFI_CIPHER_TYPE_TKIP_CCMP, ## *< the cipher type is TKIP and CCMP - WIFI_CIPHER_TYPE_AES_CMAC128, ## *< the cipher type is AES-CMAC-128 - WIFI_CIPHER_TYPE_UNKNOWN ## *< the cipher type is unknown - - -## * -## @brief WiFi antenna -## -## + + wifi_country_t* {.importc: "wifi_country_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Structure describing Wi-Fi country-based regional restrictions. + ## + cc* {.importc: "cc".}: array[3, char] ## < Country code string + schan* {.importc: "schan".}: uint8 ## < Start channel of the allowed 2.4GHz Wi-Fi channels + nchan* {.importc: "nchan".}: uint8 ## < Total channel number of the allowed 2.4GHz Wi-Fi channels + max_tx_power* {.importc: "max_tx_power".}: int8 ## + ## < This field is used for getting Wi-Fi maximum transmitting power, call esp_wifi_set_max_tx_power to set the maximum transmitting power. + policy* {.importc: "policy".}: wifi_country_policy_t ## + ## < Country policy + when defined(CONFIG_SOC_WIFI_SUPPORT_5G): + wifi_5g_channel_mask* {.importc: "wifi_5g_channel_mask".}: uint32 + ## < A bitmask representing the allowed 5GHz Wi-Fi channels. + ## Each bit in the mask corresponds to a specific channel as wifi_5g_channel_bit_t shown. + ## Bitmask set to 0 indicates 5GHz channels are allowed according to local regulatory rules. + ## Please note that configured bitmask takes effect only when policy is manual. + + + wifi_auth_mode_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Wi-Fi authmode type + ## Strength of authmodes + ## Personal Networks : OPEN < WEP < WPA_PSK < OWE < WPA2_PSK = WPA_WPA2_PSK < WAPI_PSK < WPA3_PSK = WPA2_WPA3_PSK = DPP + ## Enterprise Networks : WIFI_AUTH_WPA_ENTERPRISE < WIFI_AUTH_WPA2_ENTERPRISE < WIFI_AUTH_WPA3_ENTERPRISE = WIFI_AUTH_WPA2_WPA3_ENTERPRISE < WIFI_AUTH_WPA3_ENT_192 + ## + 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_ENTERPRISE, ## < Authenticate mode : Wi-Fi EAP security, treated the same as WIFI_AUTH_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_OWE, ## < Authenticate mode : OWE + WIFI_AUTH_WPA3_ENT_192, ## < Authenticate mode : WPA3_ENT_SUITE_B_192_BIT + WIFI_AUTH_WPA3_EXT_PSK, ## < This authentication mode will yield same result as WIFI_AUTH_WPA3_PSK and not recommended to be used. It will be deprecated in future, please use WIFI_AUTH_WPA3_PSK instead. + WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE, ## < This authentication mode will yield same result as WIFI_AUTH_WPA3_PSK and not recommended to be used. It will be deprecated in future, please use WIFI_AUTH_WPA3_PSK instead. + WIFI_AUTH_DPP, ## < Authenticate mode : DPP + WIFI_AUTH_WPA3_ENTERPRISE, ## < Authenticate mode : WPA3-Enterprise Only Mode + WIFI_AUTH_WPA2_WPA3_ENTERPRISE, ## < Authenticate mode : WPA3-Enterprise Transition Mode + WIFI_AUTH_WPA_ENTERPRISE, ## < Authenticate mode : WPA-Enterprise security + WIFI_AUTH_MAX + +const + WIFI_AUTH_WPA2_ENTERPRISE* = WIFI_AUTH_ENTERPRISE type - wifi_ant_t* {.size: sizeof(cint).} = enum - WIFI_ANT_ANT0, ## *< WiFi antenna 0 - WIFI_ANT_ANT1, ## *< WiFi antenna 1 - WIFI_ANT_MAX ## *< Invalid WiFi antenna + wifi_err_reason_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Wi-Fi disconnection reason codes + ## + ## These reason codes are used to indicate the cause of disconnection. + ## + WIFI_REASON_UNSPECIFIED = 1, ## < Unspecified reason + WIFI_REASON_AUTH_EXPIRE = 2, ## < Authentication expired + WIFI_REASON_AUTH_LEAVE = 3, ## < Deauthentication due to leaving + WIFI_REASON_ASSOC_EXPIRE = 4, ## < Deprecated, will be removed in next IDF major release + WIFI_REASON_ASSOC_TOOMANY = 5, ## < Too many associated stations + WIFI_REASON_NOT_AUTHED = 6, ## < Deprecated, will be removed in next IDF major release + WIFI_REASON_NOT_ASSOCED = 7, ## < Deprecated, will be removed in next IDF major release + WIFI_REASON_ASSOC_LEAVE = 8, ## < Deassociated due to leaving + WIFI_REASON_ASSOC_NOT_AUTHED = 9, ## < Association but not authenticated + WIFI_REASON_DISASSOC_PWRCAP_BAD = 10, ## < Disassociated due to poor power capability + WIFI_REASON_DISASSOC_SUPCHAN_BAD = 11, ## < Disassociated due to unsupported channel + WIFI_REASON_BSS_TRANSITION_DISASSOC = 12, ## < Disassociated due to BSS transition + WIFI_REASON_IE_INVALID = 13, ## < Invalid Information Element (IE) + WIFI_REASON_MIC_FAILURE = 14, ## < MIC failure + WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT = 15, ## < 4-way handshake timeout + WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT = 16, ## < Group key update timeout + WIFI_REASON_IE_IN_4WAY_DIFFERS = 17, ## < IE differs in 4-way handshake + WIFI_REASON_GROUP_CIPHER_INVALID = 18, ## < Invalid group cipher + WIFI_REASON_PAIRWISE_CIPHER_INVALID = 19, ## < Invalid pairwise cipher + WIFI_REASON_AKMP_INVALID = 20, ## < Invalid AKMP + WIFI_REASON_UNSUPP_RSN_IE_VERSION = 21, ## < Unsupported RSN IE version + WIFI_REASON_INVALID_RSN_IE_CAP = 22, ## < Invalid RSN IE capabilities + WIFI_REASON_802_1X_AUTH_FAILED = 23, ## < 802.1X authentication failed + WIFI_REASON_CIPHER_SUITE_REJECTED = 24, ## < Cipher suite rejected + WIFI_REASON_TDLS_PEER_UNREACHABLE = 25, ## < TDLS peer unreachable + WIFI_REASON_TDLS_UNSPECIFIED = 26, ## < TDLS unspecified + WIFI_REASON_SSP_REQUESTED_DISASSOC = 27, ## < SSP requested disassociation + WIFI_REASON_NO_SSP_ROAMING_AGREEMENT = 28, ## < No SSP roaming agreement + WIFI_REASON_BAD_CIPHER_OR_AKM = 29, ## < Bad cipher or AKM + WIFI_REASON_NOT_AUTHORIZED_THIS_LOCATION = 30, ## + ## < Not authorized in this location + WIFI_REASON_SERVICE_CHANGE_PERCLUDES_TS = 31, ## + ## < Service change precludes TS + WIFI_REASON_UNSPECIFIED_QOS = 32, ## < Unspecified QoS reason + WIFI_REASON_NOT_ENOUGH_BANDWIDTH = 33, ## < Not enough bandwidth + WIFI_REASON_MISSING_ACKS = 34, ## < Missing ACKs + WIFI_REASON_EXCEEDED_TXOP = 35, ## < Exceeded TXOP + WIFI_REASON_STA_LEAVING = 36, ## < Station leaving + WIFI_REASON_END_BA = 37, ## < End of Block Ack (BA) + WIFI_REASON_UNKNOWN_BA = 38, ## < Unknown Block Ack (BA) + WIFI_REASON_TIMEOUT = 39, ## < Timeout + WIFI_REASON_PEER_INITIATED = 46, ## < Peer initiated disassociation + WIFI_REASON_AP_INITIATED = 47, ## < AP initiated disassociation + WIFI_REASON_INVALID_FT_ACTION_FRAME_COUNT = 48, ## + ## < Invalid FT action frame count + WIFI_REASON_INVALID_PMKID = 49, ## < Invalid PMKID + WIFI_REASON_INVALID_MDE = 50, ## < Invalid MDE + WIFI_REASON_INVALID_FTE = 51, ## < Invalid FTE + WIFI_REASON_TRANSMISSION_LINK_ESTABLISH_FAILED = 67, ## + ## < Transmission link establishment failed + WIFI_REASON_ALTERATIVE_CHANNEL_OCCUPIED = 68, ## + ## < Alternative channel occupied + WIFI_REASON_BEACON_TIMEOUT = 200, ## < Beacon timeout + WIFI_REASON_NO_AP_FOUND = 201, ## < No AP found + WIFI_REASON_AUTH_FAIL = 202, ## < Authentication failed + WIFI_REASON_ASSOC_FAIL = 203, ## < Association failed + WIFI_REASON_HANDSHAKE_TIMEOUT = 204, ## < Handshake timeout + WIFI_REASON_CONNECTION_FAIL = 205, ## < Connection failed + WIFI_REASON_AP_TSF_RESET = 206, ## < AP TSF reset + WIFI_REASON_ROAMING = 207, ## < Roaming + WIFI_REASON_ASSOC_COMEBACK_TIME_TOO_LONG = 208, ## + ## < Association comeback time too long + WIFI_REASON_SA_QUERY_TIMEOUT = 209, ## < SA query timeout + WIFI_REASON_NO_AP_FOUND_W_COMPATIBLE_SECURITY = 210, ## + ## < No AP found with compatible security + WIFI_REASON_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD = 211, ## + ## < No AP found in auth mode threshold + WIFI_REASON_NO_AP_FOUND_IN_RSSI_THRESHOLD = 212 ## + ## < No AP found in RSSI threshold -## * @brief Description of a WiFi AP +const + WIFI_REASON_DISASSOC_DUE_TO_INACTIVITY = WIFI_REASON_ASSOC_EXPIRE + WIFI_REASON_CLASS2_FRAME_FROM_NONAUTH_STA = WIFI_REASON_NOT_AUTHED + WIFI_REASON_CLASS3_FRAME_FROM_NONASSOC_STA = WIFI_REASON_NOT_ASSOCED type - wifi_ap_record_t* {.importc: "wifi_ap_record_t", header: "esp_wifi_types.h", bycopy.} = object - bssid* {.importc: "bssid".}: array[6, uint8] ## *< MAC address of AP - ssid* {.importc: "ssid".}: array[33, uint8] ## *< SSID of AP - primary* {.importc: "primary".}: uint8 ## *< channel of AP - second* {.importc: "second".}: wifi_second_chan_t ## *< secondary channel of AP - rssi* {.importc: "rssi".}: int8 ## *< signal strength of AP - authmode* {.importc: "authmode".}: wifi_auth_mode_t ## *< authmode of AP - pairwise_cipher* {.importc: "pairwise_cipher".}: wifi_cipher_type_t ## *< pairwise cipher of AP - group_cipher* {.importc: "group_cipher".}: wifi_cipher_type_t ## *< group cipher of AP - ant* {.importc: "ant".}: wifi_ant_t ## *< antenna used to receive beacon from AP - phy_11b* {.importc: "phy_11b", bitsize: 1.}: uint32 ## *< bit: 0 flag to identify if 11b mode is enabled or not - phy_11g* {.importc: "phy_11g", bitsize: 1.}: uint32 ## *< bit: 1 flag to identify if 11g mode is enabled or not - phy_11n* {.importc: "phy_11n", bitsize: 1.}: uint32 ## *< bit: 2 flag to identify if 11n mode is enabled or not - phy_lr* {.importc: "phy_lr", bitsize: 1.}: uint32 ## *< bit: 3 flag to identify if low rate is enabled or not - wps* {.importc: "wps", bitsize: 1.}: uint32 ## *< bit: 4 flag to identify if WPS is supported or not - reserved* {.importc: "reserved", bitsize: 27.}: uint32 ## *< bit: 5..31 reserved - country* {.importc: "country".}: wifi_country_t ## *< country information of AP - wifi_scan_method_t* {.size: sizeof(cint).} = enum - WIFI_FAST_SCAN = 0, ## *< Do fast scan, scan will end after find SSID match AP - WIFI_ALL_CHANNEL_SCAN ## *< All channel scan, scan will end after scan all the channel - wifi_sort_method_t* {.size: sizeof(cint).} = enum - WIFI_CONNECT_AP_BY_SIGNAL = 0, ## *< Sort match AP in scan list by RSSI - WIFI_CONNECT_AP_BY_SECURITY ## *< Sort match AP in scan list by security mode + wifi_second_chan_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Wi-Fi second channel type + ## + WIFI_SECOND_CHAN_NONE = 0, ## < The channel width is HT20 + WIFI_SECOND_CHAN_ABOVE, ## < The channel width is HT40 and the secondary channel is above the primary channel + WIFI_SECOND_CHAN_BELOW ## < The channel width is HT40 and the secondary channel is below the primary channel +let + WIFI_ACTIVE_SCAN_MIN_DEFAULT_TIME* {.importc, header: hdr.}: cint + WIFI_ACTIVE_SCAN_MAX_DEFAULT_TIME* {.importc, header: hdr.}: cint + WIFI_PASSIVE_SCAN_DEFAULT_TIME* {.importc, header: hdr.}: cint + WIFI_SCAN_HOME_CHANNEL_DWELL_DEFAULT_TIME* {.importc, header: hdr.}: cint + +type + wifi_scan_type_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Wi-Fi scan type + ## + WIFI_SCAN_TYPE_ACTIVE = 0, ## < Active scan + WIFI_SCAN_TYPE_PASSIVE ## < Passive scan -## * @brief Structure describing parameters for a WiFi fast scan type - wifi_scan_threshold_t* {.importc: "wifi_scan_threshold_t", - header: "esp_wifi_types.h", bycopy.} = object - rssi* {.importc: "rssi".}: int8 ## *< The minimum rssi to accept in the fast scan mode - authmode* {.importc: "authmode".}: wifi_auth_mode_t ## *< The weakest authmode to accept in the fast scan mode - wifi_ps_type_t* {.size: sizeof(cint).} = enum - WIFI_PS_NONE, ## *< No power save - WIFI_PS_MIN_MODEM, ## *< Minimum modem power saving. In this mode, station wakes up to receive beacon every DTIM period - WIFI_PS_MAX_MODEM ## *< Maximum modem power saving. In this mode, interval to receive beacons is determined by the listen_interval parameter in wifi_sta_config_t + wifi_active_scan_time_t* {.importc: "wifi_active_scan_time_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Range of active scan times per channel + ## + min* {.importc: "min".}: uint32 ## < Minimum active scan time per channel, units: millisecond + max* {.importc: "max".}: uint32 + ## < Maximum active scan time per channel, units: millisecond, values above 1500 ms may + ## cause station to disconnect from AP and are not recommended. + + + wifi_scan_time_t* {.importc: "wifi_scan_time_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Aggregate of active & passive scan time per channel + ## + active* {.importc: "active".}: wifi_active_scan_time_t ## + ## < Active scan time per channel, units: millisecond. + passive* {.importc: "passive".}: uint32 + ## < Passive scan time per channel, units: millisecond, values above 1500 ms may + ## cause station to disconnect from AP and are not recommended. + + + wifi_scan_channel_bitmap_t* {.importc: "wifi_scan_channel_bitmap_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Channel bitmap for setting specific channels to be scanned + ## + ghz_2_channels* {.importc: "ghz_2_channels".}: uint16 ## + ## < Represents 2.4 GHz channels, that bits can be set as wifi_2g_channel_bit_t shown. + ghz_5_channels* {.importc: "ghz_5_channels".}: uint32 + ## < Represents 5 GHz channels, that bits can be set as wifi_5g_channel_bit_t shown. + + + wifi_scan_config_t* {.importc: "wifi_scan_config_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Parameters for an SSID scan + ## + ssid* {.importc: "ssid".}: ptr uint8 ## < SSID of AP + bssid* {.importc: "bssid".}: ptr uint8 ## < MAC address of AP + channel* {.importc: "channel".}: uint8 ## < Channel, scan the specific channel + show_hidden* {.importc: "show_hidden".}: bool ## < Enable it to scan AP whose SSID is hidden + scan_type* {.importc: "scan_type".}: wifi_scan_type_t ## + ## < Scan type, active or passive + scan_time* {.importc: "scan_time".}: wifi_scan_time_t ## + ## < Scan time per channel + home_chan_dwell_time* {.importc: "home_chan_dwell_time".}: uint8 ## + ## < Time spent at home channel between scanning consecutive channels. + channel_bitmap* {.importc: "channel_bitmap".}: wifi_scan_channel_bitmap_t ## + ## < Channel bitmap for setting specific channels to be scanned. + ## Please note that the 'channel' parameter above needs to be set to 0 to allow scanning by bitmap. + ## Also, note that only allowed channels configured by wifi_country_t can be scanned. + coex_background_scan* {.importc: "coex_background_scan".}: bool + ## < Enable it to scan return home channel under coexist + + + wifi_scan_default_params_t* {.importc: "wifi_scan_default_params_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Parameters default scan configurations + ## + scan_time* {.importc: "scan_time".}: wifi_scan_time_t ## + ## < Scan time per channel + home_chan_dwell_time* {.importc: "home_chan_dwell_time".}: uint8 + ## < Time spent at home channel between scanning consecutive channels. + + + wifi_cipher_type_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Wi-Fi cipher type + ## + WIFI_CIPHER_TYPE_NONE = 0, ## < The cipher type is none + WIFI_CIPHER_TYPE_WEP40, ## < The cipher type is WEP40 + WIFI_CIPHER_TYPE_WEP104, ## < The cipher type is WEP104 + WIFI_CIPHER_TYPE_TKIP, ## < The cipher type is TKIP + WIFI_CIPHER_TYPE_CCMP, ## < The cipher type is CCMP + WIFI_CIPHER_TYPE_TKIP_CCMP, ## < The cipher type is TKIP and CCMP + WIFI_CIPHER_TYPE_AES_CMAC128, ## < The cipher type is AES-CMAC-128 + WIFI_CIPHER_TYPE_SMS4, ## < The cipher type is SMS4 + WIFI_CIPHER_TYPE_GCMP, ## < The cipher type is GCMP + WIFI_CIPHER_TYPE_GCMP256, ## < The cipher type is GCMP-256 + WIFI_CIPHER_TYPE_AES_GMAC128, ## < The cipher type is AES-GMAC-128 + WIFI_CIPHER_TYPE_AES_GMAC256, ## < The cipher type is AES-GMAC-256 + WIFI_CIPHER_TYPE_UNKNOWN ## < The cipher type is unknown + + +type + wifi_bandwidth_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Wi-Fi bandwidth type + ## + WIFI_BW_HT20 = 1, ## < Bandwidth is HT20 + WIFI_BW_HT40 = 2, ## < Bandwidth is HT40 + WIFI_BW80 = 3, ## < Bandwidth is 80 MHz + WIFI_BW160 = 4, ## < Bandwidth is 160 MHz + WIFI_BW80_BW80 = 5 ## < Bandwidth is 80 + 80 MHz const - WIFI_PROTOCOL_11B* = 1 - WIFI_PROTOCOL_11G* = 2 - WIFI_PROTOCOL_11N* = 4 - WIFI_PROTOCOL_LR* = 8 + WIFI_BW20* = WIFI_BW_HT20 + WIFI_BW40* = WIFI_BW_HT40 type - wifi_bandwidth_t* {.size: sizeof(cint).} = enum - WIFI_BW_HT20 = 1, ## Bandwidth is HT20 - WIFI_BW_HT40 ## Bandwidth is HT40 + wifi_ant_t* {.size: sizeof(cint).} = enum ## + ## @brief Wi-Fi antenna + ## + WIFI_ANT_ANT0, ## < Wi-Fi antenna 0 + WIFI_ANT_ANT1, ## < Wi-Fi antenna 1 + WIFI_ANT_MAX ## < Invalid Wi-Fi antenna -## * Configuration structure for Protected Management Frame type - wifi_pmf_config_t* {.importc: "wifi_pmf_config_t", header: "esp_wifi_types.h", - bycopy.} = object - capable* {.importc: "capable".}: bool ## *< Advertizes support for Protected Management Frame. Device will prefer to connect in PMF mode if other device also advertizes PMF capability. - required* {.importc: "required".}: bool ## *< Advertizes that Protected Management Frame is required. Device will not associate to non-PMF capable devices. + wifi_he_ap_info_t* {.importc: "wifi_he_ap_info_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Description of a Wi-Fi AP HE Info + ## + bss_color* {.importc: "bss_color", bitsize: 6.}: uint8 ## + ## < The BSS Color value associated with the AP's corresponding BSS + partial_bss_color* {.importc: "partial_bss_color", bitsize: 1.}: uint8 ## + ## < Indicates whether an AID assignment rule is based on the BSS color + bss_color_disabled* {.importc: "bss_color_disabled", bitsize: 1.}: uint8 ## + ## < Indicates whether the BSS color usage is disabled + bssid_index* {.importc: "bssid_index".}: uint8 + ## < In a M-BSSID set, identifies the non-transmitted BSSID + + + wifi_ap_record_t* {.importc: "wifi_ap_record_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Description of a Wi-Fi AP + ## + bssid* {.importc: "bssid".}: array[6, uint8] ## < MAC address of AP + ssid* {.importc: "ssid".}: array[33, uint8] ## < SSID of AP + primary* {.importc: "primary".}: uint8 ## < Channel of AP + second* {.importc: "second".}: wifi_second_chan_t ## + ## < Secondary channel of AP + rssi* {.importc: "rssi".}: int8 ## < Signal strength of AP. Note that in some rare cases where signal strength is very strong, RSSI values can be slightly positive + authmode* {.importc: "authmode".}: wifi_auth_mode_t ## + ## < Auth mode of AP + pairwise_cipher* {.importc: "pairwise_cipher".}: wifi_cipher_type_t ## + ## < Pairwise cipher of AP + group_cipher* {.importc: "group_cipher".}: wifi_cipher_type_t ## + ## < Group cipher of AP + ant* {.importc: "ant".}: wifi_ant_t ## < Antenna used to receive beacon from AP + phy_11b* {.importc: "phy_11b", bitsize: 1.}: uint32 ## + ## < Bit: 0 flag to identify if 11b mode is enabled or not + phy_11g* {.importc: "phy_11g", bitsize: 1.}: uint32 ## + ## < Bit: 1 flag to identify if 11g mode is enabled or not + phy_11n* {.importc: "phy_11n", bitsize: 1.}: uint32 ## + ## < Bit: 2 flag to identify if 11n mode is enabled or not + phy_lr* {.importc: "phy_lr", bitsize: 1.}: uint32 ## + ## < Bit: 3 flag to identify if low rate is enabled or not + phy_11a* {.importc: "phy_11a", bitsize: 1.}: uint32 ## + ## < Bit: 4 flag to identify if 11ax mode is enabled or not + phy_11ac* {.importc: "phy_11ac", bitsize: 1.}: uint32 ## + ## < Bit: 5 flag to identify if 11ax mode is enabled or not + phy_11ax* {.importc: "phy_11ax", bitsize: 1.}: uint32 ## + ## < Bit: 6 flag to identify if 11ax mode is enabled or not + wps* {.importc: "wps", bitsize: 1.}: uint32 ## < Bit: 7 flag to identify if WPS is supported or not + ftm_responder* {.importc: "ftm_responder", bitsize: 1.}: uint32 ## + ## < Bit: 8 flag to identify if FTM is supported in responder mode + ftm_initiator* {.importc: "ftm_initiator", bitsize: 1.}: uint32 ## + ## < Bit: 9 flag to identify if FTM is supported in initiator mode + reserved* {.importc: "reserved", bitsize: 22.}: uint32 ## + ## < Bit: 10..31 reserved + country* {.importc: "country".}: wifi_country_t ## + ## < Country information of AP + he_ap* {.importc: "he_ap".}: wifi_he_ap_info_t ## + ## < HE AP info + bandwidth* {.importc: "bandwidth".}: wifi_bandwidth_t ## + ## < Bandwidth of AP + vht_ch_freq1* {.importc: "vht_ch_freq1".}: uint8 ## + ## < This fields are used only AP bandwidth is 80 and 160 MHz, to transmit the center channel + ## frequency of the BSS. For AP bandwidth is 80 + 80 MHz, it is the center channel frequency + ## of the lower frequency segment. + vht_ch_freq2* {.importc: "vht_ch_freq2".}: uint8 + ## < this fields are used only AP bandwidth is 80 + 80 MHz, and is used to transmit the center + ## channel frequency of the second segment. + + + wifi_scan_method_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Wi-Fi scan method + ## + WIFI_FAST_SCAN = 0, ## < Do fast scan, scan will end after find SSID match AP + WIFI_ALL_CHANNEL_SCAN ## < All channel scan, scan will end after scan all the channel -## * @brief Soft-AP configuration settings for the ESP32 type - wifi_ap_config_t* {.importc: "wifi_ap_config_t", header: "esp_wifi_types.h", bycopy.} = object - ssid* {.importc: "ssid".}: array[32, uint8] ## *< SSID of ESP32 soft-AP. If ssid_len field is 0, this must be a Null terminated string. Otherwise, length is set according to ssid_len. - password* {.importc: "password".}: array[64, uint8] ## *< Password of ESP32 soft-AP. Null terminated string. - ssid_len* {.importc: "ssid_len".}: uint8 ## *< Optional length of SSID field. - channel* {.importc: "channel".}: uint8 ## *< Channel of ESP32 soft-AP - authmode* {.importc: "authmode".}: wifi_auth_mode_t ## *< Auth mode of ESP32 soft-AP. Do not support AUTH_WEP in soft-AP mode - ssid_hidden* {.importc: "ssid_hidden".}: uint8 ## *< Broadcast SSID or not, default 0, broadcast the SSID - max_connection* {.importc: "max_connection".}: uint8 ## *< Max number of stations allowed to connect in, default 4, max 10 - beacon_interval* {.importc: "beacon_interval".}: uint16 ## *< Beacon interval, 100 ~ 60000 ms, default 100 ms + wifi_sort_method_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Wi-Fi sort AP method + ## + WIFI_CONNECT_AP_BY_SIGNAL = 0, ## < Sort match AP in scan list by RSSI + WIFI_CONNECT_AP_BY_SECURITY ## < Sort match AP in scan list by security mode -## * @brief STA configuration settings for the ESP32 type - wifi_sta_config_t* {.importc: "wifi_sta_config_t", header: "esp_wifi_types.h", - bycopy.} = object - ssid* {.importc: "ssid".}: array[32, uint8] ## *< SSID of target AP. Null terminated string. - password* {.importc: "password".}: array[64, uint8] ## *< Password of target AP. Null terminated string. - scan_method* {.importc: "scan_method".}: wifi_scan_method_t ## *< do all channel scan or fast scan - bssid_set* {.importc: "bssid_set".}: bool ## *< whether set MAC address of target AP or not. Generally, station_config.bssid_set needs to be 0; and it needs to be 1 only when users need to check the MAC address of the AP. - bssid* {.importc: "bssid".}: array[6, uint8] ## *< MAC address of target AP - channel* {.importc: "channel".}: uint8 ## *< channel of target AP. Set to 1~13 to scan starting from the specified channel before connecting to AP. If the channel of AP is unknown, set it to 0. - listen_interval* {.importc: "listen_interval".}: uint16 ## *< Listen interval for ESP32 station to receive beacon when WIFI_PS_MAX_MODEM is set. Units: AP beacon intervals. Defaults to 3 if set to 0. - sort_method* {.importc: "sort_method".}: wifi_sort_method_t ## *< sort the connect AP in the list by rssi or security mode - threshold* {.importc: "threshold".}: wifi_scan_threshold_t ## *< When sort_method is set, only APs which have an auth mode that is more secure than the selected auth mode and a signal stronger than the minimum RSSI will be used. - pmf_cfg* {.importc: "pmf_cfg".}: wifi_pmf_config_t ## *< Configuration for Protected Management Frame. Will be advertized in RSN Capabilities in RSN IE. - - -## * @brief Configuration data for ESP32 AP or STA. -## -## The usage of this union (for ap or sta configuration) is determined by the accompanying -## interface argument passed to esp_wifi_set_config() or esp_wifi_get_config() -## -## + + wifi_scan_threshold_t* {.importc: "wifi_scan_threshold_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Structure describing parameters for a Wi-Fi fast scan + ## + rssi* {.importc: "rssi".}: int8 ## < The minimum rssi to accept in the fast scan mode. Defaults to -127 if set to >= 0 + authmode* {.importc: "authmode".}: wifi_auth_mode_t ## + ## < The weakest auth mode to accept in the fast scan mode + ## Note: In case this value is not set and password is set as per WPA2 standards(password len >= 8), it will be defaulted to WPA2 and device won't connect to deprecated WEP/WPA networks. Please set auth mode threshold as WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK to connect to WEP/WPA networks + rssi_5g_adjustment* {.importc: "rssi_5g_adjustment".}: uint8 + ## < The RSSI value of the 5G AP is within the rssi_5g_adjustment range compared to the 2G AP, the 5G AP will be given priority for connection. + + + wifi_ps_type_t* {.size: sizeof(cint).} = enum ## + ## @brief Wi-Fi power save type + ## + WIFI_PS_NONE, ## < No power save + WIFI_PS_MIN_MODEM, ## < Minimum modem power saving. In this mode, station wakes up to receive beacon every DTIM period + WIFI_PS_MAX_MODEM ## < Maximum modem power saving. In this mode, interval to receive beacons is determined by the listen_interval parameter in wifi_sta_config_t + type - wifi_config_t* {.importc: "wifi_config_t", header: "esp_wifi_types.h", bycopy, union.} = object - ap* {.importc: "ap".}: wifi_ap_config_t ## *< configuration of AP - sta* {.importc: "sta".}: wifi_sta_config_t ## *< configuration of STA + wifi_band_t* {.size: sizeof(cint).} = enum ## + ## @brief Argument structure for Wi-Fi band + ## + WIFI_BAND_2G = 1, ## < Band is 2.4 GHz + WIFI_BAND_5G = 2 ## < Band is 5 GHz -## * @brief Description of STA associated with AP type - wifi_sta_info_t* {.importc: "wifi_sta_info_t", header: "esp_wifi_types.h", bycopy.} = object - mac* {.importc: "mac".}: array[6, uint8] ## *< mac address - rssi* {.importc: "rssi".}: int8 ## *< current average rssi of sta connected - phy_11b* {.importc: "phy_11b", bitsize: 1.}: uint32 ## *< bit: 0 flag to identify if 11b mode is enabled or not - phy_11g* {.importc: "phy_11g", bitsize: 1.}: uint32 ## *< bit: 1 flag to identify if 11g mode is enabled or not - phy_11n* {.importc: "phy_11n", bitsize: 1.}: uint32 ## *< bit: 2 flag to identify if 11n mode is enabled or not - phy_lr* {.importc: "phy_lr", bitsize: 1.}: uint32 ## *< bit: 3 flag to identify if low rate is enabled or not - reserved* {.importc: "reserved", bitsize: 28.}: uint32 ## *< bit: 4..31 reserved + wifi_band_mode_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Argument structure for Wi-Fi band mode + ## + WIFI_BAND_MODE_2G_ONLY = 1, ## < Wi-Fi band mode is 2.4 GHz only + WIFI_BAND_MODE_5G_ONLY = 2, ## < Wi-Fi band mode is 5 GHz only + WIFI_BAND_MODE_AUTO = 3 ## < Wi-Fi band mode is 2.4 GHz + 5 GHz -const - ESP_WIFI_MAX_CONN_NUM* = (10) ## *< max number of stations which can connect to ESP32 soft-AP -## * @brief List of stations associated with the ESP32 Soft-AP +proc BIT(nr: cint): cint = (1.cint shl nr) type - wifi_sta_list_t* {.importc: "wifi_sta_list_t", header: "esp_wifi_types.h", bycopy.} = object - sta* {.importc: "sta".}: array[ESP_WIFI_MAX_CONN_NUM, wifi_sta_info_t] ## *< station list - num* {.importc: "num".}: cint ## *< number of stations in the list (other entries are invalid) - wifi_storage_t* {.size: sizeof(cint).} = enum - WIFI_STORAGE_FLASH, ## *< all configuration will store in both memory and flash - WIFI_STORAGE_RAM ## *< all configuration will only store in the memory + wifi_2g_channel_bit_t* {.size: sizeof(cint).} = enum ## + ## Argument structure for 2.4G channels + WIFI_CHANNEL_1 = BIT(1), ## < Wi-Fi channel 1 + WIFI_CHANNEL_2 = BIT(2), ## < Wi-Fi channel 2 + WIFI_CHANNEL_3 = BIT(3), ## < Wi-Fi channel 3 + WIFI_CHANNEL_4 = BIT(4), ## < Wi-Fi channel 4 + WIFI_CHANNEL_5 = BIT(5), ## < Wi-Fi channel 5 + WIFI_CHANNEL_6 = BIT(6), ## < Wi-Fi channel 6 + WIFI_CHANNEL_7 = BIT(7), ## < Wi-Fi channel 7 + WIFI_CHANNEL_8 = BIT(8), ## < Wi-Fi channel 8 + WIFI_CHANNEL_9 = BIT(9), ## < Wi-Fi channel 9 + WIFI_CHANNEL_10 = BIT(10), ## < Wi-Fi channel 10 + WIFI_CHANNEL_11 = BIT(11), ## < Wi-Fi channel 11 + WIFI_CHANNEL_12 = BIT(12), ## < Wi-Fi channel 12 + WIFI_CHANNEL_13 = BIT(13), ## < Wi-Fi channel 13 + WIFI_CHANNEL_14 = BIT(14) ## < Wi-Fi channel 14 -## * -## @brief Vendor Information Element type -## -## Determines the frame type that the IE will be associated with. -## +type + + wifi_5g_channel_bit_t* {.size: sizeof(cint).} = enum ## + ## Argument structure for 5G channels + WIFI_CHANNEL_36 = BIT(1), ## < Wi-Fi channel 36 + WIFI_CHANNEL_40 = BIT(2), ## < Wi-Fi channel 40 + WIFI_CHANNEL_44 = BIT(3), ## < Wi-Fi channel 44 + WIFI_CHANNEL_48 = BIT(4), ## < Wi-Fi channel 48 + WIFI_CHANNEL_52 = BIT(5), ## < Wi-Fi channel 52 + WIFI_CHANNEL_56 = BIT(6), ## < Wi-Fi channel 56 + WIFI_CHANNEL_60 = BIT(7), ## < Wi-Fi channel 60 + WIFI_CHANNEL_64 = BIT(8), ## < Wi-Fi channel 64 + WIFI_CHANNEL_100 = BIT(9), ## < Wi-Fi channel 100 + WIFI_CHANNEL_104 = BIT(10), ## < Wi-Fi channel 104 + WIFI_CHANNEL_108 = BIT(11), ## < Wi-Fi channel 108 + WIFI_CHANNEL_112 = BIT(12), ## < Wi-Fi channel 112 + WIFI_CHANNEL_116 = BIT(13), ## < Wi-Fi channel 116 + WIFI_CHANNEL_120 = BIT(14), ## < Wi-Fi channel 120 + WIFI_CHANNEL_124 = BIT(15), ## < Wi-Fi channel 124 + WIFI_CHANNEL_128 = BIT(16), ## < Wi-Fi channel 128 + WIFI_CHANNEL_132 = BIT(17), ## < Wi-Fi channel 132 + WIFI_CHANNEL_136 = BIT(18), ## < Wi-Fi channel 136 + WIFI_CHANNEL_140 = BIT(19), ## < Wi-Fi channel 140 + WIFI_CHANNEL_144 = BIT(20), ## < Wi-Fi channel 144 + WIFI_CHANNEL_149 = BIT(21), ## < Wi-Fi channel 149 + WIFI_CHANNEL_153 = BIT(22), ## < Wi-Fi channel 153 + WIFI_CHANNEL_157 = BIT(23), ## < Wi-Fi channel 157 + WIFI_CHANNEL_161 = BIT(24), ## < Wi-Fi channel 161 + WIFI_CHANNEL_165 = BIT(25), ## < Wi-Fi channel 165 + WIFI_CHANNEL_169 = BIT(26), ## < Wi-Fi channel 169 + WIFI_CHANNEL_173 = BIT(27), ## < Wi-Fi channel 173 + WIFI_CHANNEL_177 = BIT(28) ## < Wi-Fi channel 177 + +let + WIFI_PROTOCOL_11B* {.importc, header: hdr.}: uint16 = 0x1 + WIFI_PROTOCOL_11G* {.importc, header: hdr.}: uint16 = 0x2 + WIFI_PROTOCOL_11N* {.importc, header: hdr.}: uint16 = 0x4 + WIFI_PROTOCOL_LR* {.importc, header: hdr.}: uint16 = 0x8 + WIFI_PROTOCOL_11A* {.importc, header: hdr.}: uint16 = 0x10 + WIFI_PROTOCOL_11AC* {.importc, header: hdr.}: uint16 = 0x20 + WIFI_PROTOCOL_11AX* {.importc, header: hdr.}: uint16 = 0x40 type - wifi_vendor_ie_type_t* {.size: sizeof(cint).} = enum - WIFI_VND_IE_TYPE_BEACON, WIFI_VND_IE_TYPE_PROBE_REQ, - WIFI_VND_IE_TYPE_PROBE_RESP, WIFI_VND_IE_TYPE_ASSOC_REQ, - WIFI_VND_IE_TYPE_ASSOC_RESP + wifi_protocols_t* {.importc: "wifi_protocols_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Description of a Wi-Fi protocols + ## + ghz_2g* {.importc: "ghz_2g".}: uint16 ## < Represents 2.4 GHz protocol, support 802.11b or 802.11g or 802.11n or 802.11ax or LR mode + ghz_5g* {.importc: "ghz_5g".}: uint16 + ## < Represents 5 GHz protocol, support 802.11a or 802.11n or 802.11ac or 802.11ax + + + wifi_bandwidths_t* {.importc: "wifi_bandwidths_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Description of a Wi-Fi band bandwidths + ## + ghz_2g* {.importc: "ghz_2g".}: wifi_bandwidth_t ## + ## < Represents 2.4 GHz bandwidth + ghz_5g* {.importc: "ghz_5g".}: wifi_bandwidth_t + ## < Represents 5 GHz bandwidth + + + wifi_pmf_config_t* {.importc: "wifi_pmf_config_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Configuration structure for Protected Management Frame + ## + capable* {.importc: "capable".}: bool ## < Deprecated variable. Device will always connect in PMF mode if other device also advertises PMF capability. + required* {.importc: "required".}: bool + ## < Advertises that Protected Management Frame is required. Device will not associate to non-PMF capable devices. + + + wifi_sae_pwe_method_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Configuration for SAE PWE derivation + ## + WPA3_SAE_PWE_UNSPECIFIED, WPA3_SAE_PWE_HUNT_AND_PECK, + WPA3_SAE_PWE_HASH_TO_ELEMENT, WPA3_SAE_PWE_BOTH -## * -## @brief Vendor Information Element index -## -## Each IE type can have up to two associated vendor ID elements. -## type - wifi_vendor_ie_id_t* {.size: sizeof(cint).} = enum - WIFI_VND_IE_ID_0, WIFI_VND_IE_ID_1 + + wifi_sae_pk_mode_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Configuration for SAE-PK + ## + WPA3_SAE_PK_MODE_AUTOMATIC = 0, WPA3_SAE_PK_MODE_ONLY = 1, + WPA3_SAE_PK_MODE_DISABLED = 2 + + +type + + wifi_bss_max_idle_config_t* {.importc: "wifi_bss_max_idle_config_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Configuration structure for BSS max idle + ## + period* {.importc: "period".}: uint16 ## < Sets BSS Max idle period (1 Unit = 1000TUs OR 1.024 Seconds). If there are no frames for this period from a STA, SoftAP will disassociate due to inactivity. Setting it to 0 disables the feature + protected_keep_alive* {.importc: "protected_keep_alive".}: bool + ## < Requires clients to use protected keep alive frames for BSS Max Idle period + + + wifi_ap_config_t* {.importc: "wifi_ap_config_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Soft-AP configuration settings for the device + ## + ssid* {.importc: "ssid".}: array[32, uint8] ## < SSID of soft-AP. If ssid_len field is 0, this must be a Null terminated string. Otherwise, length is set according to ssid_len. + password* {.importc: "password".}: array[64, uint8] ## + ## < Password of soft-AP. + ssid_len* {.importc: "ssid_len".}: uint8 ## < Optional length of SSID field. + channel* {.importc: "channel".}: uint8 ## < Channel of soft-AP + authmode* {.importc: "authmode".}: wifi_auth_mode_t ## + ## < Auth mode of soft-AP. Do not support AUTH_WEP, AUTH_WAPI_PSK and AUTH_OWE in soft-AP mode. When the auth mode is set to WPA2_PSK, WPA2_WPA3_PSK or WPA3_PSK, the pairwise cipher will be overwritten with WIFI_CIPHER_TYPE_CCMP by default, unless explicitly set. + ssid_hidden* {.importc: "ssid_hidden".}: uint8 ## + ## < Broadcast SSID or not, default 0, broadcast the SSID + max_connection* {.importc: "max_connection".}: uint8 ## + ## < Max number of stations allowed to connect in + beacon_interval* {.importc: "beacon_interval".}: uint16 ## + ## < Beacon interval which should be multiples of 100. Unit: TU(time unit, 1 TU = 1024 us). Range: 100 ~ 60000. Default value: 100 + csa_count* {.importc: "csa_count".}: uint8 ## < Channel Switch Announcement Count. Notify the station that the channel will switch after the csa_count beacon intervals. Default value: 3 + dtim_period* {.importc: "dtim_period".}: uint8 ## + ## < Dtim period of soft-AP. Range: 1 ~ 10. Default value: 1 + pairwise_cipher* {.importc: "pairwise_cipher".}: wifi_cipher_type_t ## + ## < Pairwise cipher of SoftAP, group cipher will be derived using this. Cipher values are valid starting from WIFI_CIPHER_TYPE_TKIP, enum values before that will be considered as invalid and default cipher suites(TKIP+CCMP) will be used. Valid cipher suites in softAP mode are WIFI_CIPHER_TYPE_TKIP, WIFI_CIPHER_TYPE_CCMP, WIFI_CIPHER_TYPE_TKIP_CCMP, WIFI_CIPHER_TYPE_GCMP and WIFI_CIPHER_TYPE_GCMP256. + ftm_responder* {.importc: "ftm_responder".}: bool ## + ## < Enable FTM Responder mode + pmf_cfg* {.importc: "pmf_cfg".}: wifi_pmf_config_t ## + ## < Configuration for Protected Management Frame + sae_pwe_h2e* {.importc: "sae_pwe_h2e".}: wifi_sae_pwe_method_t ## + ## < Configuration for SAE PWE derivation method + transition_disable* {.importc: "transition_disable".}: uint8 ## + ## < Whether to enable transition disable feature + sae_ext* {.importc: "sae_ext".}: uint8 ## < Enable SAE EXT feature. SOC_GCMP_SUPPORT is required for this feature. + bss_max_idle_cfg* {.importc: "bss_max_idle_cfg".}: wifi_bss_max_idle_config_t ## + ## < Configuration for bss max idle, effective if CONFIG_WIFI_BSS_MAX_IDLE_SUPPORT is enabled + gtk_rekey_interval* {.importc: "gtk_rekey_interval".}: uint16 + ## < GTK rekeying interval in seconds. If set to 0, GTK rekeying is disabled. Range: 60 ~ 65535 including 0. const - WIFI_VENDOR_IE_ELEMENT_ID* = 0x000000DD + SAE_H2E_IDENTIFIER_LEN* = 32 + +type + + wifi_sta_config_t* {.importc: "wifi_sta_config_t", + header: hdr, bycopy.} = object ## + ## + ## @brief STA configuration settings for the device + ## + ssid* {.importc: "ssid".}: array[32, uint8] ## < SSID of target AP. + password* {.importc: "password".}: array[64, uint8] ## + ## < Password of target AP. + scan_method* {.importc: "scan_method".}: wifi_scan_method_t ## + ## < Do all channel scan or fast scan + bssid_set* {.importc: "bssid_set".}: bool ## < Whether set MAC address of target AP or not. Generally, station_config.bssid_set needs to be 0; and it needs to be 1 only when users need to check the MAC address of the AP. + bssid* {.importc: "bssid".}: array[6, uint8] ## < MAC address of target AP + channel* {.importc: "channel".}: uint8 ## < Channel hint for target AP. For 2.4G AP, set to 1~13 to scan starting from the specified channel before connecting to AP. For 5G AP, set to 36~177 (36, 40, 44 ... 177) to scan starting from the specified channel before connecting to AP. Set to 0 for no preference + listen_interval* {.importc: "listen_interval".}: uint16 ## + ## < Listen interval for ESP32 station to receive beacon when WIFI_PS_MAX_MODEM is set. Units: AP beacon intervals. Defaults to 3 if set to 0. + sort_method* {.importc: "sort_method".}: wifi_sort_method_t ## + ## < Sort the connect AP in the list by rssi or security mode + threshold* {.importc: "threshold".}: wifi_scan_threshold_t ## + ## < When scan_threshold is set, only APs which have an auth mode that is more secure than the selected auth mode and a signal stronger than the minimum RSSI will be used. + pmf_cfg* {.importc: "pmf_cfg".}: wifi_pmf_config_t ## + ## < Configuration for Protected Management Frame. Will be advertised in RSN Capabilities in RSN IE. + rm_enabled* {.importc: "rm_enabled", bitsize: 1.}: uint32 ## + ## < Whether Radio Measurements are enabled for the connection + btm_enabled* {.importc: "btm_enabled", bitsize: 1.}: uint32 ## + ## < Whether BSS Transition Management is enabled for the connection. Note that when btm is enabled, the application itself should not set specific bssid (i.e using bssid_set and bssid in this config)or channel to connect to. This defeats the purpose of a BTM supported network, and hence if btm is supported and a specific bssid or channel is set in this config, it will be cleared from the config at the first disconnection or connection so that the device can roam to other BSS. It is recommended not to set BSSID when BTM is enabled. + mbo_enabled* {.importc: "mbo_enabled", bitsize: 1.}: uint32 ## + ## < Whether MBO is enabled for the connection. Note that when mbo is enabled, the application itself should not set specific bssid (i.e using bssid_set and bssid in this config)or channel to connect to. This defeats the purpose of a MBO supported network, and hence if btm is supported and a specific bssid or channel is set in this config, it will be cleared from the config at the first disconnection or connection so that the device can roam to other BSS. It is recommended not to set BSSID when MBO is enabled. Enabling mbo here, automatically enables btm and rm above. + ft_enabled* {.importc: "ft_enabled", bitsize: 1.}: uint32 ## + ## < Whether FT is enabled for the connection + owe_enabled* {.importc: "owe_enabled", bitsize: 1.}: uint32 ## + ## < Whether OWE is enabled for the connection + transition_disable* {.importc: "transition_disable", bitsize: 1.}: uint32 ## + ## < Whether to enable transition disable feature + reserved1* {.importc: "reserved1", bitsize: 26.}: uint32 ## + ## < Reserved for future feature set + sae_pwe_h2e* {.importc: "sae_pwe_h2e".}: wifi_sae_pwe_method_t ## + ## < Configuration for SAE PWE derivation method + sae_pk_mode* {.importc: "sae_pk_mode".}: wifi_sae_pk_mode_t ## + ## < Configuration for SAE-PK (Public Key) Authentication method + failure_retry_cnt* {.importc: "failure_retry_cnt".}: uint8 ## + ## < Number of connection retries station will do before moving to next AP. scan_method should be set as WIFI_ALL_CHANNEL_SCAN to use this config. + ## Note: Enabling this may cause connection time to increase in case best AP doesn't behave properly. + he_dcm_set* {.importc: "he_dcm_set", bitsize: 1.}: uint32 ## + ## < Whether DCM max.constellation for transmission and reception is set. + he_dcm_max_constellation_tx* {.importc: "he_dcm_max_constellation_tx", + bitsize: 2.}: uint32 ## + ## < Indicate the max.constellation for DCM in TB PPDU the STA supported. 0: not supported. 1: BPSK, 2: QPSK, 3: 16-QAM. The default value is 3. + he_dcm_max_constellation_rx* {.importc: "he_dcm_max_constellation_rx", + bitsize: 2.}: uint32 ## + ## < Indicate the max.constellation for DCM in both Data field and HE-SIG-B field the STA supported. 0: not supported. 1: BPSK, 2: QPSK, 3: 16-QAM. The default value is 3. + he_mcs9_enabled* {.importc: "he_mcs9_enabled", bitsize: 1.}: uint32 ## + ## < Whether to support HE-MCS8 and HE-MCS9. The default value is 0. + he_su_beamformee_disabled* {.importc: "he_su_beamformee_disabled", + bitsize: 1.}: uint32 ## + ## < Whether to disable support for operation as an SU beamformee. + he_trig_su_bmforming_feedback_disabled* + {.importc: "he_trig_su_bmforming_feedback_disabled", bitsize: 1.}: uint32 ## + ## < Whether to disable support the transmission of SU feedback in an HE TB sounding sequence. + he_trig_mu_bmforming_partial_feedback_disabled* {. + importc: "he_trig_mu_bmforming_partial_feedback_disabled", bitsize: 1.}: uint32 ## + ## < Whether to disable support the transmission of partial-bandwidth MU feedback in an HE TB sounding sequence. + he_trig_cqi_feedback_disabled* {.importc: "he_trig_cqi_feedback_disabled", + bitsize: 1.}: uint32 ## + ## < Whether to disable support the transmission of CQI feedback in an HE TB sounding sequence. + vht_su_beamformee_disabled* {.importc: "vht_su_beamformee_disabled", + bitsize: 1.}: uint32 ## + ## < Whether to disable support for operation as an VHT SU beamformee. + vht_mu_beamformee_disabled* {.importc: "vht_mu_beamformee_disabled", + bitsize: 1.}: uint32 ## + ## < Whether to disable support for operation as an VHT MU beamformee. + vht_mcs8_enabled* {.importc: "vht_mcs8_enabled", bitsize: 1.}: uint32 ## + ## < Whether to support VHT-MCS8. The default value is 0. + reserved2* {.importc: "reserved2", bitsize: 19.}: uint32 ## + ## < Reserved for future feature set + sae_h2e_identifier* {.importc: "sae_h2e_identifier".}: array[ + SAE_H2E_IDENTIFIER_LEN, uint8] + ## < Password identifier for H2E. this needs to be null terminated string + + + wifi_nan_config_t* {.importc: "wifi_nan_config_t", + header: hdr, bycopy.} = object ## + ## + ## @brief NAN Discovery start configuration + ## + op_channel* {.importc: "op_channel".}: uint8 ## < NAN Discovery operating channel + master_pref* {.importc: "master_pref".}: uint8 ## + ## < Device's preference value to serve as NAN Master + scan_time* {.importc: "scan_time".}: uint8 ## < Scan time in seconds while searching for a NAN cluster + warm_up_sec* {.importc: "warm_up_sec".}: uint16 + ## < Warm up time before assuming NAN Anchor Master role + + + wifi_config_t* {.importc: "wifi_config_t", header: hdr, + bycopy, union.} = object ## + ## @brief Configuration data for device's AP or STA or NAN. + ## + ## The usage of this union (for ap, sta or nan configuration) is determined by the accompanying + ## interface argument passed to esp_wifi_set_config() or esp_wifi_get_config() + ## + ## + ap* {.importc: "ap".}: wifi_ap_config_t ## < Configuration of AP + sta* {.importc: "sta".}: wifi_sta_config_t ## < Configuration of STA + nan* {.importc: "nan".}: wifi_nan_config_t + ## < Configuration of NAN + + + wifi_sta_info_t* {.importc: "wifi_sta_info_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Description of STA associated with AP + ## + mac* {.importc: "mac".}: array[6, uint8] ## < MAC address + rssi* {.importc: "rssi".}: int8 ## < Current average rssi of sta connected + phy_11b* {.importc: "phy_11b", bitsize: 1.}: uint32 ## + ## < Bit: 0 flag to identify if 11b mode is enabled or not + phy_11g* {.importc: "phy_11g", bitsize: 1.}: uint32 ## + ## < Bit: 1 flag to identify if 11g mode is enabled or not + phy_11n* {.importc: "phy_11n", bitsize: 1.}: uint32 ## + ## < Bit: 2 flag to identify if 11n mode is enabled or not + phy_lr* {.importc: "phy_lr", bitsize: 1.}: uint32 ## + ## < Bit: 3 flag to identify if low rate is enabled or not + phy_11a* {.importc: "phy_11a", bitsize: 1.}: uint32 ## + ## < Bit: 4 flag to identify if 11ax mode is enabled or not + phy_11ac* {.importc: "phy_11ac", bitsize: 1.}: uint32 ## + ## < Bit: 5 flag to identify if 11ax mode is enabled or not + phy_11ax* {.importc: "phy_11ax", bitsize: 1.}: uint32 ## + ## < Bit: 6 flag to identify if 11ax mode is enabled or not + is_mesh_child* {.importc: "is_mesh_child", bitsize: 1.}: uint32 ## + ## < Bit: 7 flag to identify mesh child + reserved* {.importc: "reserved", bitsize: 24.}: uint32 + ## < Bit: 8..31 reserved + + + wifi_storage_t* {.size: sizeof(cint).} = enum ## + ## @brief Wi-Fi storage type + ## + WIFI_STORAGE_FLASH, ## < All configuration will store in both memory and flash + WIFI_STORAGE_RAM ## < All configuration will only store in the memory -## * -## @brief Vendor Information Element header -## -## The first bytes of the Information Element will match this header. Payload follows. -## type - vendor_ie_data_t* {.importc: "vendor_ie_data_t", header: "esp_wifi_types.h", bycopy.} = object - element_id* {.importc: "element_id".}: uint8 ## *< Should be set to WIFI_VENDOR_IE_ELEMENT_ID (0xDD) - length* {.importc: "length".}: uint8 ## *< Length of all bytes in the element data following this field. Minimum 4. - vendor_oui* {.importc: "vendor_oui".}: array[3, uint8] ## *< Vendor identifier (OUI). - vendor_oui_type* {.importc: "vendor_oui_type".}: uint8 ## *< Vendor-specific OUI type. - payload* {.importc: "payload".}: UncheckedArray[uint8] ## *< Payload. Length is equal to value in 'length' field, minus 4. - wifi_pkt_rx_ctrl_t* {.importc: "wifi_pkt_rx_ctrl_t", header: "esp_wifi_types.h", - bycopy.} = object - rssi* {.importc: "rssi", bitsize: 8.}: cint ## *< Received Signal Strength Indicator(RSSI) of packet. unit: dBm - rate* {.importc: "rate", bitsize: 5.}: cuint ## *< PHY rate encoding of the packet. Only valid for non HT(11bg) packet - r1* {.importc: "r1", bitsize: 1.}: cuint ## *< reserve - sig_mode* {.importc: "sig_mode", bitsize: 2.}: cuint ## *< 0: non HT(11bg) packet; 1: HT(11n) packet; 3: VHT(11ac) packet - r2* {.importc: "r2", bitsize: 16.}: cuint ## *< reserve - mcs* {.importc: "mcs", bitsize: 7.}: cuint ## *< Modulation Coding Scheme. If is HT(11n) packet, shows the modulation, range from 0 to 76(MSC0 ~ MCS76) - cwb* {.importc: "cwb", bitsize: 1.}: cuint ## *< Channel Bandwidth of the packet. 0: 20MHz; 1: 40MHz - r3* {.importc: "r3", bitsize: 16.}: cuint ## *< reserve - smoothing* {.importc: "smoothing", bitsize: 1.}: cuint ## *< reserve - not_sounding* {.importc: "not_sounding", bitsize: 1.}: cuint ## *< reserve - r4* {.importc: "r4", bitsize: 1.}: cuint ## *< reserve - aggregation* {.importc: "aggregation", bitsize: 1.}: cuint ## *< Aggregation. 0: MPDU packet; 1: AMPDU packet - stbc* {.importc: "stbc", bitsize: 2.}: cuint ## *< Space Time Block Code(STBC). 0: non STBC packet; 1: STBC packet - fec_coding* {.importc: "fec_coding", bitsize: 1.}: cuint ## *< Flag is set for 11n packets which are LDPC - sgi* {.importc: "sgi", bitsize: 1.}: cuint ## *< Short Guide Interval(SGI). 0: Long GI; 1: Short GI - noise_floor* {.importc: "noise_floor", bitsize: 8.}: cint ## *< noise floor of Radio Frequency Module(RF). unit: 0.25dBm - ampdu_cnt* {.importc: "ampdu_cnt", bitsize: 8.}: cuint ## *< ampdu cnt - channel* {.importc: "channel", bitsize: 4.}: cuint ## *< primary channel on which this packet is received - secondary_channel* {.importc: "secondary_channel", bitsize: 4.}: cuint ## *< secondary channel on which this packet is received. 0: none; 1: above; 2: below - r5* {.importc: "r5", bitsize: 8.}: cuint ## *< reserve - timestamp* {.importc: "timestamp", bitsize: 32.}: cuint ## *< timestamp. The local time when this packet is received. It is precise only if modem sleep or light sleep is not enabled. unit: microsecond - r6* {.importc: "r6", bitsize: 32.}: cuint ## *< reserve - r7* {.importc: "r7", bitsize: 31.}: cuint ## *< reserve - ant* {.importc: "ant", bitsize: 1.}: cuint ## *< antenna number from which this packet is received. 0: WiFi antenna 0; 1: WiFi antenna 1 - sig_len* {.importc: "sig_len", bitsize: 12.}: cuint ## *< length of packet including Frame Check Sequence(FCS) - r8* {.importc: "r8", bitsize: 12.}: cuint ## *< reserve - rx_state* {.importc: "rx_state", bitsize: 8.}: cuint ## *< state of the packet. 0: no error; others: error numbers which are not public - - wifi_promiscuous_pkt_type_t* {.size: sizeof(cint).} = enum - WIFI_PKT_MGMT, ## *< Management frame, indicates 'buf' argument is wifi_promiscuous_pkt_t - WIFI_PKT_CTRL, ## *< Control frame, indicates 'buf' argument is wifi_promiscuous_pkt_t - WIFI_PKT_DATA, ## *< Data frame, indiciates 'buf' argument is wifi_promiscuous_pkt_t - WIFI_PKT_MISC ## *< Other type, such as MIMO etc. 'buf' argument is wifi_promiscuous_pkt_t but the payload is zero length. + wifi_vendor_ie_type_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Vendor Information Element type + ## + ## Determines the frame type that the IE will be associated with. + ## + WIFI_VND_IE_TYPE_BEACON, ## < Beacon frame + WIFI_VND_IE_TYPE_PROBE_REQ, ## < Probe request frame + WIFI_VND_IE_TYPE_PROBE_RESP, ## < Probe response frame + WIFI_VND_IE_TYPE_ASSOC_REQ, ## < Association request frame + WIFI_VND_IE_TYPE_ASSOC_RESP ## < Association response frame + + +type + + wifi_vendor_ie_id_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Vendor Information Element index + ## + ## Each IE type can have up to two associated vendor ID elements. + ## + WIFI_VND_IE_ID_0, ## < Vendor ID element 0 + WIFI_VND_IE_ID_1 ## < Vendor ID element 1 + +let + WIFI_VENDOR_IE_ELEMENT_ID* {.importc, header: hdr.}: uint8 + +type + wifi_phy_mode_t* {.size: sizeof(cint).} = enum ## + ## @brief Operation PHY mode + ## + WIFI_PHY_MODE_LR, ## < PHY mode for Low Rate + WIFI_PHY_MODE_11B, ## < PHY mode for 11b + WIFI_PHY_MODE_11G, ## < PHY mode for 11g + WIFI_PHY_MODE_11A, ## < PHY mode for 11a + WIFI_PHY_MODE_HT20, ## < PHY mode for Bandwidth HT20 + WIFI_PHY_MODE_HT40, ## < PHY mode for Bandwidth HT40 + WIFI_PHY_MODE_HE20, ## < PHY mode for Bandwidth HE20 + WIFI_PHY_MODE_VHT20 ## < PHY mode for Bandwidth VHT20 + + +type + + vendor_ie_data_t* {.importc: "vendor_ie_data_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Vendor Information Element header + ## + ## The first bytes of the Information Element will match this header. Payload follows. + ## + element_id* {.importc: "element_id".}: uint8 ## < Should be set to WIFI_VENDOR_IE_ELEMENT_ID (0xDD) + length* {.importc: "length".}: uint8 ## < Length of all bytes in the element data following this field. Minimum 4. + vendor_oui* {.importc: "vendor_oui".}: array[3, uint8] ## + ## < Vendor identifier (OUI). + vendor_oui_type* {.importc: "vendor_oui_type".}: uint8 ## + ## < Vendor-specific OUI type. + payload* {.importc: "payload".}: UncheckedArray[uint8] + ## < Payload. Length is equal to value in 'length' field, minus 4. + + + wifi_promiscuous_pkt_type_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Promiscuous frame type + ## + ## Passed to promiscuous mode RX callback to indicate the type of parameter in the buffer. + ## + ## + WIFI_PKT_MGMT, ## < Management frame, indicates 'buf' argument is wifi_promiscuous_pkt_t + WIFI_PKT_CTRL, ## < Control frame, indicates 'buf' argument is wifi_promiscuous_pkt_t + WIFI_PKT_DATA, ## < Data frame, indicates 'buf' argument is wifi_promiscuous_pkt_t + WIFI_PKT_MISC ## < Other type, such as MIMO etc. 'buf' argument is wifi_promiscuous_pkt_t but the payload is zero length. const - WIFI_PROMIS_FILTER_MASK_ALL* = (0xFFFFFFFF) ## *< filter all packets - WIFI_PROMIS_FILTER_MASK_MGMT* = (1) ## *< filter the packets with type of WIFI_PKT_MGMT - WIFI_PROMIS_FILTER_MASK_CTRL* = (1 shl 1) ## *< filter the packets with type of WIFI_PKT_CTRL - WIFI_PROMIS_FILTER_MASK_DATA* = (1 shl 2) ## *< filter the packets with type of WIFI_PKT_DATA - WIFI_PROMIS_FILTER_MASK_MISC* = (1 shl 3) ## *< filter the packets with type of WIFI_PKT_MISC - WIFI_PROMIS_FILTER_MASK_DATA_MPDU* = (1 shl 4) ## *< filter the MPDU which is a kind of WIFI_PKT_DATA - WIFI_PROMIS_FILTER_MASK_DATA_AMPDU* = (1 shl 5) ## *< filter the AMPDU which is a kind of WIFI_PKT_DATA - WIFI_PROMIS_CTRL_FILTER_MASK_ALL* = (0xFF800000) ## *< filter all control packets - WIFI_PROMIS_CTRL_FILTER_MASK_WRAPPER* = (1 shl 23) ## *< filter the control packets with subtype of Control Wrapper - WIFI_PROMIS_CTRL_FILTER_MASK_BAR* = (1 shl 24) ## *< filter the control packets with subtype of Block Ack Request - WIFI_PROMIS_CTRL_FILTER_MASK_BA* = (1 shl 25) ## *< filter the control packets with subtype of Block Ack - WIFI_PROMIS_CTRL_FILTER_MASK_PSPOLL* = (1 shl 26) ## *< filter the control packets with subtype of PS-Poll - WIFI_PROMIS_CTRL_FILTER_MASK_RTS* = (1 shl 27) ## *< filter the control packets with subtype of RTS - WIFI_PROMIS_CTRL_FILTER_MASK_CTS* = (1 shl 28) ## *< filter the control packets with subtype of CTS - WIFI_PROMIS_CTRL_FILTER_MASK_ACK* = (1 shl 29) ## *< filter the control packets with subtype of ACK - WIFI_PROMIS_CTRL_FILTER_MASK_CFEND* = (1 shl 30) ## *< filter the control packets with subtype of CF-END - WIFI_PROMIS_CTRL_FILTER_MASK_CFENDACK* = (1 shl 31) ## *< filter the control packets with subtype of CF-END+CF-ACK - -## * @brief Mask for filtering different packet types in promiscuous mode. + WIFI_PROMIS_FILTER_MASK_ALL* = (0xFFFFFFFF) ## < Filter all packets + WIFI_PROMIS_FILTER_MASK_MGMT* = (1) ## < Filter the packets with type of WIFI_PKT_MGMT + WIFI_PROMIS_FILTER_MASK_CTRL* = (1 shl 1) ## < Filter the packets with type of WIFI_PKT_CTRL + WIFI_PROMIS_FILTER_MASK_DATA* = (1 shl 2) ## < Filter the packets with type of WIFI_PKT_DATA + WIFI_PROMIS_FILTER_MASK_MISC* = (1 shl 3) ## < Filter the packets with type of WIFI_PKT_MISC + WIFI_PROMIS_FILTER_MASK_DATA_MPDU* = (1 shl 4) ## < Filter the MPDU which is a kind of WIFI_PKT_DATA + WIFI_PROMIS_FILTER_MASK_DATA_AMPDU* = (1 shl 5) ## < Filter the AMPDU which is a kind of WIFI_PKT_DATA + WIFI_PROMIS_FILTER_MASK_FCSFAIL* = (1 shl 6) ## < Filter the FCS failed packets, do not open it in general + WIFI_PROMIS_CTRL_FILTER_MASK_ALL* = (0xFF800000) ## + ## < Filter all control packets + WIFI_PROMIS_CTRL_FILTER_MASK_WRAPPER* = (1 shl 23) ## + ## < Filter the control packets with subtype of Control Wrapper + WIFI_PROMIS_CTRL_FILTER_MASK_BAR* = (1 shl 24) ## < Filter the control packets with subtype of Block Ack Request + WIFI_PROMIS_CTRL_FILTER_MASK_BA* = (1 shl 25) ## < Filter the control packets with subtype of Block Ack + WIFI_PROMIS_CTRL_FILTER_MASK_PSPOLL* = (1 shl 26) ## + ## < Filter the control packets with subtype of PS-Poll + WIFI_PROMIS_CTRL_FILTER_MASK_RTS* = (1 shl 27) ## < Filter the control packets with subtype of RTS + WIFI_PROMIS_CTRL_FILTER_MASK_CTS* = (1 shl 28) ## < Filter the control packets with subtype of CTS + WIFI_PROMIS_CTRL_FILTER_MASK_ACK* = (1 shl 29) ## < Filter the control packets with subtype of ACK + WIFI_PROMIS_CTRL_FILTER_MASK_CFEND* = (1 shl 30) ## + ## < Filter the control packets with subtype of CF-END + WIFI_PROMIS_CTRL_FILTER_MASK_CFENDACK* = (1 shl 31) ## + ## < Filter the control packets with subtype of CF-END+CF-ACK type + wifi_promiscuous_filter_t* {.importc: "wifi_promiscuous_filter_t", - header: "esp_wifi_types.h", bycopy.} = object - filter_mask* {.importc: "filter_mask".}: uint32 ## *< OR of one or more filter values WIFI_PROMIS_FILTER_* + header: hdr, bycopy.} = object ## + ## + ## @brief Mask for filtering different packet types in promiscuous mode + ## + filter_mask* {.importc: "filter_mask".}: uint32 + ## < OR of one or more filter values WIFI_PROMIS_FILTER_* const - WIFI_EVENT_MASK_ALL* = (0xFFFFFFFF) ## *< mask all WiFi events - WIFI_EVENT_MASK_NONE* = (0) ## *< mask none of the WiFi events - WIFI_EVENT_MASK_AP_PROBEREQRECVED* = (BIT(0)) ## *< mask SYSTEM_EVENT_AP_PROBEREQRECVED event + WIFI_EVENT_MASK_ALL* = (0xFFFFFFFF) ## < Mask all Wi-Fi events + WIFI_EVENT_MASK_NONE* = (0) ## < Mask none of the Wi-Fi events + WIFI_EVENT_MASK_AP_PROBEREQRECVED* = (BIT(0)) ## < Mask SYSTEM_EVENT_AP_PROBEREQRECVED event -## * -## @brief Channel state information(CSI) configuration type +## +## @brief CSI data type ## ## @@ -386,13 +902,12 @@ type manu_scale* {.importc: "manu_scale".}: bool ## *< manually scale the CSI data by left shifting or automatically scale the CSI data. If set true, please set the shift bits. false: automatically. true: manually. Default false shift* {.importc: "shift".}: uint8 ## *< manually left shift bits of the scale of the CSI data. The range of the left shift bits is 0~15 + wifi_pkt_rx_ctrl_t* {.importc: "wifi_pkt_rx_ctrl_t", header: "esp_wifi_types.h", + bycopy.} = object + rssi* {.importc: "rssi", bitsize: 8.}: cint ## *< Received Signal Strength Indicator(RSSI) of packet. unit: dBm + rate* {.importc: "rate", bitsize: 5.}: cuint ## *< PHY rate encoding of the packet. Only valid for non HT(11bg) packet -## * -## @brief CSI data type -## -## -type wifi_csi_info_t* {.importc: "wifi_csi_info_t", header: "esp_wifi_types.h", bycopy.} = object rx_ctrl* {.importc: "rx_ctrl".}: wifi_pkt_rx_ctrl_t ## *< received packet radio metadata header of the CSI data mac* {.importc: "mac".}: array[6, uint8] ## *< source MAC address of the CSI data @@ -401,211 +916,1009 @@ type len* {.importc: "len".}: uint16 ## *< length of CSI data -## * -## @brief WiFi GPIO configuration for antenna selection -## -## +type + + wifi_ant_gpio_t* {.importc: "wifi_ant_gpio_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Wi-Fi GPIO configuration for antenna selection + ## + ## + gpio_select* {.importc: "gpio_select", bitsize: 1.}: uint8 ## + ## < Whether this GPIO is connected to external antenna switch + gpio_num* {.importc: "gpio_num", bitsize: 7.}: uint8 + ## < The GPIO number that connects to external antenna switch + + + wifi_ant_gpio_config_t* {.importc: "wifi_ant_gpio_config_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Wi-Fi GPIOs configuration for antenna selection + ## + ## + gpio_cfg* {.importc: "gpio_cfg".}: array[4, wifi_ant_gpio_t] + ## < The configurations of GPIOs that connect to external antenna switch + + + wifi_ant_mode_t* {.size: sizeof(cint).} = enum ## + ## @brief Wi-Fi antenna mode + ## + ## + WIFI_ANT_MODE_ANT0, ## < Enable Wi-Fi antenna 0 only + WIFI_ANT_MODE_ANT1, ## < Enable Wi-Fi antenna 1 only + WIFI_ANT_MODE_AUTO, ## < Enable Wi-Fi antenna 0 and 1, automatically select an antenna + WIFI_ANT_MODE_MAX ## < Invalid Wi-Fi enabled antenna + type - wifi_ant_gpio_t* {.importc: "wifi_ant_gpio_t", header: "esp_wifi_types.h", bycopy.} = object - gpio_select* {.importc: "gpio_select", bitsize: 1.}: uint8 ## *< Whether this GPIO is connected to external antenna switch - gpio_num* {.importc: "gpio_num", bitsize: 7.}: uint8 ## *< The GPIO number that connects to external antenna switch + wifi_ant_config_t* {.importc: "wifi_ant_config_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Wi-Fi antenna configuration + ## + ## + rx_ant_mode* {.importc: "rx_ant_mode".}: wifi_ant_mode_t ## + ## < Wi-Fi antenna mode for receiving + rx_ant_default* {.importc: "rx_ant_default".}: wifi_ant_t ## + ## < Default antenna mode for receiving, it's ignored if rx_ant_mode is not WIFI_ANT_MODE_AUTO + tx_ant_mode* {.importc: "tx_ant_mode".}: wifi_ant_mode_t ## + ## < Wi-Fi antenna mode for transmission, it can be set to WIFI_ANT_MODE_AUTO only if rx_ant_mode is set to WIFI_ANT_MODE_AUTO + enabled_ant0* {.importc: "enabled_ant0", bitsize: 4.}: uint8 ## + ## < Index (in antenna GPIO configuration) of enabled WIFI_ANT_MODE_ANT0 + enabled_ant1* {.importc: "enabled_ant1", bitsize: 4.}: uint8 + ## < Index (in antenna GPIO configuration) of enabled WIFI_ANT_MODE_ANT1 + + + wifi_action_rx_cb_t* = proc (hdr: ptr uint8; payload: ptr uint8; len: csize_t; + channel: uint8): cint {.cdecl.} ## + ## + ## @brief The Rx callback function of Action Tx operations + ## + ## @param hdr Pointer to the IEEE 802.11 Header structure + ## @param payload Pointer to the Payload following 802.11 Header + ## @param len Length of the Payload + ## @param channel Channel number the frame is received on + ## + ## + + wifi_action_tx_req_t* {.importc: "wifi_action_tx_req_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Action Frame Tx Request + ## + ifx* {.importc: "ifx".}: wifi_interface_t ## < Wi-Fi interface to send request to + dest_mac* {.importc: "dest_mac".}: array[6, uint8] ## + ## < Destination MAC address + `type`* {.importc: "type".}: wifi_action_tx_t ## < ACTION TX operation type + channel* {.importc: "channel".}: uint8 ## < Channel on which to perform ACTION TX Operation + wait_time_ms* {.importc: "wait_time_ms".}: uint32 ## + ## < Duration to wait for on target channel + no_ack* {.importc: "no_ack".}: bool ## < Indicates no ack required + rx_cb* {.importc: "rx_cb".}: wifi_action_rx_cb_t ## + ## < Rx Callback to receive action frames + op_id* {.importc: "op_id".}: uint8 ## < Unique Identifier for operation provided by wifi driver + data_len* {.importc: "data_len".}: uint32 ## < Length of the appended Data + data* {.importc: "data".}: UncheckedArray[uint8] + ## < Appended Data payload + + + wifi_roc_done_status_t* {.size: sizeof(cint).} = enum ## + ## Status codes for WIFI_EVENT_ROC_DONE evt + WIFI_ROC_DONE = 0, ## < ROC operation was completed successfully + WIFI_ROC_FAIL ## < ROC operation was cancelled -## * -## @brief WiFi GPIOs configuration for antenna selection -## -## type - wifi_ant_gpio_config_t* {.importc: "wifi_ant_gpio_config_t", - header: "esp_wifi_types.h", bycopy.} = object - gpio_cfg* {.importc: "gpio_cfg".}: array[4, wifi_ant_gpio_t] ## *< The configurations of GPIOs that connect to external antenna switch + wifi_action_roc_done_cb_t* = proc (context: uint32; op_id: uint8; + status: wifi_roc_done_status_t) {.cdecl.} ## + ## + ## @brief The callback function executed when ROC operation has ended + ## + ## @param context rxcb registered for the corresponding ROC operation + ## @param op_id ID of the corresponding ROC operation + ## @param status status code of the ROC operation denoted + ## + ## + + wifi_roc_req_t* {.importc: "wifi_roc_req_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Remain on Channel request + ## + ## + ## + ifx* {.importc: "ifx".}: wifi_interface_t ## < WiFi interface to send request to + `type`* {.importc: "type".}: wifi_roc_t ## < ROC operation type + channel* {.importc: "channel".}: uint8 ## < Channel on which to perform ROC Operation + sec_channel* {.importc: "sec_channel".}: wifi_second_chan_t ## + ## < Secondary channel + wait_time_ms* {.importc: "wait_time_ms".}: uint32 ## + ## < Duration to wait for on target channel + rx_cb* {.importc: "rx_cb".}: wifi_action_rx_cb_t ## + ## < Rx Callback to receive any response + op_id* {.importc: "op_id".}: uint8 ## < ID of this specific ROC operation provided by wifi driver + done_cb* {.importc: "done_cb".}: wifi_action_roc_done_cb_t + ## < Callback to function that will be called upon ROC done. If assigned, WIFI_EVENT_ROC_DONE event will not be posted + + + wifi_ftm_initiator_cfg_t* {.importc: "wifi_ftm_initiator_cfg_t", + header: hdr, bycopy.} = object ## + ## + ## @brief FTM Initiator configuration + ## + ## + resp_mac* {.importc: "resp_mac".}: array[6, uint8] ## + ## < MAC address of the FTM Responder + channel* {.importc: "channel".}: uint8 ## < Primary channel of the FTM Responder + frm_count* {.importc: "frm_count".}: uint8 ## < No. of FTM frames requested in terms of 4 or 8 bursts (allowed values - 0(No pref), 16, 24, 32, 64) + burst_period* {.importc: "burst_period".}: uint16 ## + ## < Requested period between FTM bursts in 100's of milliseconds (allowed values 0(No pref) - 100) + use_get_report_api* {.importc: "use_get_report_api".}: bool + ## < True - Using esp_wifi_ftm_get_report to get FTM report, False - Using ftm_report_data from + ## WIFI_EVENT_FTM_REPORT to get FTM report -## * -## @brief WiFi antenna mode -## -## + +const + ESP_WIFI_NAN_MAX_SVC_SUPPORTED* = 2 + ESP_WIFI_NAN_DATAPATH_MAX_PEERS* = 2 + ESP_WIFI_NDP_ROLE_INITIATOR* = 1 + ESP_WIFI_NDP_ROLE_RESPONDER* = 2 + ESP_WIFI_MAX_SVC_NAME_LEN* = 256 + ESP_WIFI_MAX_FILTER_LEN* = 256 + ESP_WIFI_MAX_SVC_INFO_LEN* = 64 + ESP_WIFI_MAX_FUP_SSI_LEN* = 2048 + ESP_WIFI_MAX_SVC_SSI_LEN* = 512 + ESP_WIFI_MAX_NEIGHBOR_REP_LEN* = 64 + WIFI_OUI_LEN* = 3 type - wifi_ant_mode_t* {.size: sizeof(cint).} = enum - WIFI_ANT_MODE_ANT0, ## *< Enable WiFi antenna 0 only - WIFI_ANT_MODE_ANT1, ## *< Enable WiFi antenna 1 only - WIFI_ANT_MODE_AUTO, ## *< Enable WiFi antenna 0 and 1, automatically select an antenna - WIFI_ANT_MODE_MAX ## *< Invalid WiFi enabled antenna + wifi_nan_svc_proto_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Protocol types in NAN service specific info attribute + ## + ## + WIFI_SVC_PROTO_RESERVED = 0, ## < Value 0 Reserved + WIFI_SVC_PROTO_BONJOUR = 1, ## < Bonjour Protocol + WIFI_SVC_PROTO_GENERIC = 2, ## < Generic Service Protocol + WIFI_SVC_PROTO_CSA_MATTER = 3, ## < CSA Matter specific protocol + WIFI_SVC_PROTO_MAX ## < Values 4-255 Reserved -## * -## @brief WiFi antenna configuration -## -## type - wifi_ant_config_t* {.importc: "wifi_ant_config_t", header: "esp_wifi_types.h", - bycopy.} = object - rx_ant_mode* {.importc: "rx_ant_mode".}: wifi_ant_mode_t ## *< WiFi antenna mode for receiving - rx_ant_default* {.importc: "rx_ant_default".}: wifi_ant_t ## *< Default antenna mode for receiving, it's ignored if rx_ant_mode is not WIFI_ANT_MODE_AUTO - tx_ant_mode* {.importc: "tx_ant_mode".}: wifi_ant_mode_t ## *< WiFi antenna mode for transmission, it can be set to WIFI_ANT_MODE_AUTO only if rx_ant_mode is set to WIFI_ANT_MODE_AUTO - enabled_ant0* {.importc: "enabled_ant0", bitsize: 4.}: uint8 ## *< Index (in antenna GPIO configuration) of enabled WIFI_ANT_MODE_ANT0 - enabled_ant1* {.importc: "enabled_ant1", bitsize: 4.}: uint8 ## *< Index (in antenna GPIO configuration) of enabled WIFI_ANT_MODE_ANT1 + wifi_nan_wfa_ssi_t* {.importc: "wifi_nan_wfa_ssi_t", + header: hdr, bycopy.} = object ## + ## + ## @brief WFA defined Protocol types in NAN service specific info attribute + ## + ## + wfa_oui* {.importc: "wfa_oui".}: array[WIFI_OUI_LEN, uint8] ## + ## < WFA OUI - 0x50, 0x6F, 0x9A + proto* {.importc: "proto".}: wifi_nan_svc_proto_t ## + ## < WFA defined protocol types + payload* {.importc: "payload".}: UncheckedArray[uint8] + ## < Service Info payload + + + wifi_nan_service_type_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief NAN Services types + ## + ## + NAN_PUBLISH_SOLICITED, ## < Send unicast Publish frame to Subscribers that match the requirement + NAN_PUBLISH_UNSOLICITED, ## < Send broadcast Publish frames in every Discovery Window(DW) + NAN_SUBSCRIBE_ACTIVE, ## < Send broadcast Subscribe frames in every DW + NAN_SUBSCRIBE_PASSIVE ## < Passively listens to Publish frames -## * -## @brief WiFi PHY rate encodings -## -## type - wifi_phy_rate_t* {.size: sizeof(cint).} = enum - WIFI_PHY_RATE_1M_L = 0x00000000, ## *< 1 Mbps with long preamble - WIFI_PHY_RATE_2M_L = 0x00000001, ## *< 2 Mbps with long preamble - WIFI_PHY_RATE_5M_L = 0x00000002, ## *< 5.5 Mbps with long preamble - WIFI_PHY_RATE_11M_L = 0x00000003, ## *< 11 Mbps with long preamble - WIFI_PHY_RATE_2M_S = 0x00000005, ## *< 2 Mbps with short preamble - WIFI_PHY_RATE_5M_S = 0x00000006, ## *< 5.5 Mbps with short preamble - WIFI_PHY_RATE_11M_S = 0x00000007, ## *< 11 Mbps with short preamble - WIFI_PHY_RATE_48M = 0x00000008, ## *< 48 Mbps - WIFI_PHY_RATE_24M = 0x00000009, ## *< 24 Mbps - WIFI_PHY_RATE_12M = 0x0000000A, ## *< 12 Mbps - WIFI_PHY_RATE_6M = 0x0000000B, ## *< 6 Mbps - WIFI_PHY_RATE_54M = 0x0000000C, ## *< 54 Mbps - WIFI_PHY_RATE_36M = 0x0000000D, ## *< 36 Mbps - WIFI_PHY_RATE_18M = 0x0000000E, ## *< 18 Mbps - WIFI_PHY_RATE_9M = 0x0000000F, ## *< 9 Mbps - WIFI_PHY_RATE_MCS0_LGI = 0x00000010, ## *< MCS0 with long GI, 6.5 Mbps for 20MHz, 13.5 Mbps for 40MHz - WIFI_PHY_RATE_MCS1_LGI = 0x00000011, ## *< MCS1 with long GI, 13 Mbps for 20MHz, 27 Mbps for 40MHz - WIFI_PHY_RATE_MCS2_LGI = 0x00000012, ## *< MCS2 with long GI, 19.5 Mbps for 20MHz, 40.5 Mbps for 40MHz - WIFI_PHY_RATE_MCS3_LGI = 0x00000013, ## *< MCS3 with long GI, 26 Mbps for 20MHz, 54 Mbps for 40MHz - WIFI_PHY_RATE_MCS4_LGI = 0x00000014, ## *< MCS4 with long GI, 39 Mbps for 20MHz, 81 Mbps for 40MHz - WIFI_PHY_RATE_MCS5_LGI = 0x00000015, ## *< MCS5 with long GI, 52 Mbps for 20MHz, 108 Mbps for 40MHz - WIFI_PHY_RATE_MCS6_LGI = 0x00000016, ## *< MCS6 with long GI, 58.5 Mbps for 20MHz, 121.5 Mbps for 40MHz - WIFI_PHY_RATE_MCS7_LGI = 0x00000017, ## *< MCS7 with long GI, 65 Mbps for 20MHz, 135 Mbps for 40MHz - WIFI_PHY_RATE_MCS0_SGI = 0x00000018, ## *< MCS0 with short GI, 7.2 Mbps for 20MHz, 15 Mbps for 40MHz - WIFI_PHY_RATE_MCS1_SGI = 0x00000019, ## *< MCS1 with short GI, 14.4 Mbps for 20MHz, 30 Mbps for 40MHz - WIFI_PHY_RATE_MCS2_SGI = 0x0000001A, ## *< MCS2 with short GI, 21.7 Mbps for 20MHz, 45 Mbps for 40MHz - WIFI_PHY_RATE_MCS3_SGI = 0x0000001B, ## *< MCS3 with short GI, 28.9 Mbps for 20MHz, 60 Mbps for 40MHz - WIFI_PHY_RATE_MCS4_SGI = 0x0000001C, ## *< MCS4 with short GI, 43.3 Mbps for 20MHz, 90 Mbps for 40MHz - WIFI_PHY_RATE_MCS5_SGI = 0x0000001D, ## *< MCS5 with short GI, 57.8 Mbps for 20MHz, 120 Mbps for 40MHz - WIFI_PHY_RATE_MCS6_SGI = 0x0000001E, ## *< MCS6 with short GI, 65 Mbps for 20MHz, 135 Mbps for 40MHz - WIFI_PHY_RATE_MCS7_SGI = 0x0000001F, ## *< MCS7 with short GI, 72.2 Mbps for 20MHz, 150 Mbps for 40MHz - WIFI_PHY_RATE_LORA_250K = 0x00000029, ## *< 250 Kbps - WIFI_PHY_RATE_LORA_500K = 0x0000002A, ## *< 500 Kbps - WIFI_PHY_RATE_MAX - - -## * WiFi event declarations - -type - wifi_event_t* {.size: sizeof(cint).} = enum - WIFI_EVENT_WIFI_READY = 0, ## *< ESP32 WiFi ready - WIFI_EVENT_SCAN_DONE, ## *< ESP32 finish scanning AP - WIFI_EVENT_STA_START, ## *< ESP32 station start - WIFI_EVENT_STA_STOP, ## *< ESP32 station stop - WIFI_EVENT_STA_CONNECTED, ## *< ESP32 station connected to AP - WIFI_EVENT_STA_DISCONNECTED, ## *< ESP32 station disconnected from AP - WIFI_EVENT_STA_AUTHMODE_CHANGE, ## *< the auth mode of AP connected by ESP32 station changed - WIFI_EVENT_STA_WPS_ER_SUCCESS, ## *< ESP32 station wps succeeds in enrollee mode - WIFI_EVENT_STA_WPS_ER_FAILED, ## *< ESP32 station wps fails in enrollee mode - WIFI_EVENT_STA_WPS_ER_TIMEOUT, ## *< ESP32 station wps timeout in enrollee mode - WIFI_EVENT_STA_WPS_ER_PIN, ## *< ESP32 station wps pin code in enrollee mode - WIFI_EVENT_STA_WPS_ER_PBC_OVERLAP, ## *< ESP32 station wps overlap in enrollee mode - WIFI_EVENT_AP_START, ## *< ESP32 soft-AP start - WIFI_EVENT_AP_STOP, ## *< ESP32 soft-AP stop - WIFI_EVENT_AP_STACONNECTED, ## *< a station connected to ESP32 soft-AP - WIFI_EVENT_AP_STADISCONNECTED, ## *< a station disconnected from ESP32 soft-AP - WIFI_EVENT_AP_PROBEREQRECVED ## *< Receive probe request packet in soft-AP interface - - -## * @cond * -## * @brief WiFi event base declaration -# ESP_EVENT_DECLARE_BASE(WIFI_EVENT) -var WIFI_EVENT* {.importc: "WIFI_EVENT", header: "esp_wifi_types.h".}: esp_event_base_t + wifi_nan_publish_cfg_t* {.importc: "wifi_nan_publish_cfg_t", + header: hdr, bycopy.} = object ## + ## + ## @brief NAN Publish service configuration parameters + ## + ## + service_name* {.importc: "service_name".}: array[ESP_WIFI_MAX_SVC_NAME_LEN, + char] ## < Service name identifier + `type`* {.importc: "type".}: wifi_nan_service_type_t ## + ## < Service type + matching_filter* {.importc: "matching_filter".}: array[ + ESP_WIFI_MAX_FILTER_LEN, char] ## < Comma separated filters for filtering services + svc_info* {.importc: "svc_info".}: array[ESP_WIFI_MAX_SVC_INFO_LEN, char] ## + ## < To be deprecated in next major release, use ssi instead + single_replied_event* {.importc: "single_replied_event", bitsize: 1.}: uint8 ## + ## < Give single Replied event or every time + datapath_reqd* {.importc: "datapath_reqd", bitsize: 1.}: uint8 ## + ## < NAN Datapath required for the service + fsd_reqd* {.importc: "fsd_reqd", bitsize: 1.}: uint8 ## + ## < Further Service Discovery(FSD) required + fsd_gas* {.importc: "fsd_gas", bitsize: 1.}: uint8 ## + ## < 0 - Follow-up used for FSD, 1 - GAS used for FSD + reserved* {.importc: "reserved", bitsize: 4.}: uint8 ## + ## < Reserved + ssi_len* {.importc: "ssi_len".}: uint16 ## < Length of service specific info, maximum allowed length - ESP_WIFI_MAX_SVC_SSI_LEN + ssi* {.importc: "ssi".}: ptr uint8 + ## < Service Specific Info of type wifi_nan_wfa_ssi_t for WFA defined protocols, otherwise proprietary and defined by Applications + + + wifi_nan_subscribe_cfg_t* {.importc: "wifi_nan_subscribe_cfg_t", + header: hdr, bycopy.} = object ## + ## + ## @brief NAN Subscribe service configuration parameters + ## + ## + service_name* {.importc: "service_name".}: array[ESP_WIFI_MAX_SVC_NAME_LEN, + char] ## < Service name identifier + `type`* {.importc: "type".}: wifi_nan_service_type_t ## + ## < Service type + matching_filter* {.importc: "matching_filter".}: array[ + ESP_WIFI_MAX_FILTER_LEN, char] ## < Comma separated filters for filtering services + svc_info* {.importc: "svc_info".}: array[ESP_WIFI_MAX_SVC_INFO_LEN, char] ## + ## < To be deprecated in next major release, use ssi instead + single_match_event* {.importc: "single_match_event", bitsize: 1.}: uint8 ## + ## < Give single Match event(per SSI update) or every time + datapath_reqd* {.importc: "datapath_reqd", bitsize: 1.}: uint8 ## + ## < NAN Datapath required for the service + fsd_reqd* {.importc: "fsd_reqd", bitsize: 1.}: uint8 ## + ## < Further Service Discovery(FSD) required + fsd_gas* {.importc: "fsd_gas", bitsize: 1.}: uint8 ## + ## < 0 - Follow-up used for FSD, 1 - GAS used for FSD + reserved* {.importc: "reserved", bitsize: 4.}: uint8 ## + ## < Reserved + ssi_len* {.importc: "ssi_len".}: uint16 ## < Length of service specific info, maximum allowed length - ESP_WIFI_MAX_SVC_SSI_LEN + ssi* {.importc: "ssi".}: ptr uint8 + ## < Service Specific Info of type wifi_nan_wfa_ssi_t for WFA defined protocols, otherwise proprietary and defined by Applications + + + wifi_nan_followup_params_t* {.importc: "wifi_nan_followup_params_t", + header: hdr, bycopy.} = object ## + ## + ## @brief NAN Follow-up parameters + ## + ## + inst_id* {.importc: "inst_id".}: uint8 ## < Own service instance id + peer_inst_id* {.importc: "peer_inst_id".}: uint8 ## + ## < Peer's service instance id + peer_mac* {.importc: "peer_mac".}: array[6, uint8] ## + ## < Peer's MAC address + svc_info* {.importc: "svc_info".}: array[ESP_WIFI_MAX_SVC_INFO_LEN, char] ## + ## < To be deprecated in next major release, use ssi instead + ssi_len* {.importc: "ssi_len".}: uint16 ## < Length of service specific info, maximum allowed length - ESP_WIFI_MAX_FUP_SSI_LEN + ssi* {.importc: "ssi".}: ptr uint8 + ## < Service Specific Info of type wifi_nan_wfa_ssi_t for WFA defined protocols, otherwise proprietary and defined by Applications + + + wifi_nan_datapath_req_t* {.importc: "wifi_nan_datapath_req_t", + header: hdr, bycopy.} = object ## + ## + ## @brief NAN Datapath Request parameters + ## + ## + pub_id* {.importc: "pub_id".}: uint8 ## < Publisher's service instance id + peer_mac* {.importc: "peer_mac".}: array[6, uint8] ## + ## < Peer's MAC address + confirm_required* {.importc: "confirm_required".}: bool + ## < NDP Confirm frame required + + + wifi_nan_datapath_resp_t* {.importc: "wifi_nan_datapath_resp_t", + header: hdr, bycopy.} = object ## + ## + ## @brief NAN Datapath Response parameters + ## + ## + accept* {.importc: "accept".}: bool ## < True - Accept incoming NDP, False - Reject it + ndp_id* {.importc: "ndp_id".}: uint8 ## < NAN Datapath Identifier + peer_mac* {.importc: "peer_mac".}: array[6, uint8] + ## < Peer's MAC address + + + wifi_nan_datapath_end_req_t* {.importc: "wifi_nan_datapath_end_req_t", + header: hdr, bycopy.} = object ## + ## + ## @brief NAN Datapath End parameters + ## + ## + ndp_id* {.importc: "ndp_id".}: uint8 ## < NAN Datapath Identifier + peer_mac* {.importc: "peer_mac".}: array[6, uint8] + ## < Peer's MAC address + + + wifi_phy_rate_t* {.importc: "wifi_phy_rate_t", header: hdr.} = cint ##\ + ## @brief Wi-Fi PHY rate encodings + ## + ## @note Rate Table: MCS Rate and Guard Interval Information + ## | MCS RATE | HT20 | HT40 | HE20 | VHT20 | + ## + ## |-----------------------------|-------------------------|-------------------------|-------------------------|-------------------------| + ## | WIFI_PHY_RATE_MCS0_LGI | 6.5 Mbps (800 ns) | 13.5 Mbps (800 ns) | 8.1 Mbps (1600 ns) | 6.5 Mbps (800 ns) | + ## | WIFI_PHY_RATE_MCS1_LGI | 13 Mbps (800 ns) | 27 Mbps (800 ns) | 16.3 Mbps (1600 ns) | 13 Mbps (800 ns) | + ## | WIFI_PHY_RATE_MCS2_LGI | 19.5 Mbps (800 ns) | 40.5 Mbps (800 ns) | 24.4 Mbps (1600 ns) | 19.5 Mbps (800 ns) | + ## | WIFI_PHY_RATE_MCS3_LGI | 26 Mbps (800 ns) | 54 Mbps (800 ns) | 32.5 Mbps (1600 ns) | 26 Mbps (800 ns) | + ## | WIFI_PHY_RATE_MCS4_LGI | 39 Mbps (800 ns) | 81 Mbps (800 ns) | 48.8 Mbps (1600 ns) | 39 Mbps (800 ns) | + ## | WIFI_PHY_RATE_MCS5_LGI | 52 Mbps (800 ns) | 108 Mbps (800 ns) | 65 Mbps (1600 ns) | 52 Mbps (800 ns) | + ## | WIFI_PHY_RATE_MCS6_LGI | 58.5 Mbps (800 ns) | 121.5 Mbps (800 ns) | 73.1 Mbps (1600 ns) | 58.5 Mbps (800 ns) | + ## | WIFI_PHY_RATE_MCS7_LGI | 65 Mbps (800 ns) | 135 Mbps (800 ns) | 81.3 Mbps (1600 ns) | 65 Mbps (800 ns) | + ## | WIFI_PHY_RATE_MCS8_LGI | | | 97.5 Mbps (1600 ns) | | + ## | WIFI_PHY_RATE_MCS9_LGI | | | 108.3 Mbps (1600 ns) | | + ## + ## @note + ## | MCS RATE | HT20 | HT40 | HE20 | VHT20 | + ## + ## |-----------------------------|-------------------------|-------------------------|-------------------------|-------------------------| + ## | WIFI_PHY_RATE_MCS0_SGI | 7.2 Mbps (400 ns) | 15 Mbps (400 ns) | 8.6 Mbps (800 ns) | 7.2 Mbps (400 ns) | + ## | WIFI_PHY_RATE_MCS1_SGI | 14.4 Mbps (400 ns) | 30 Mbps (400 ns) | 17.2 Mbps (800 ns) | 14.4 Mbps (400 ns) | + ## | WIFI_PHY_RATE_MCS2_SGI | 21.7 Mbps (400 ns) | 45 Mbps (400 ns) | 25.8 Mbps (800 ns) | 21.7 Mbps (400 ns) | + ## | WIFI_PHY_RATE_MCS3_SGI | 28.9 Mbps (400 ns) | 60 Mbps (400 ns) | 34.4 Mbps (800 ns) | 28.9 Mbps (400 ns) | + ## | WIFI_PHY_RATE_MCS4_SGI | 43.3 Mbps (400 ns) | 90 Mbps (400 ns) | 51.6 Mbps (800 ns) | 43.3 Mbps (400 ns) | + ## | WIFI_PHY_RATE_MCS5_SGI | 57.8 Mbps (400 ns) | 120 Mbps (400 ns) | 68.8 Mbps (800 ns) | 57.8 Mbps (400 ns) | + ## | WIFI_PHY_RATE_MCS6_SGI | 65 Mbps (400 ns) | 135 Mbps (400 ns) | 77.4 Mbps (800 ns) | 65 Mbps (400 ns) | + ## | WIFI_PHY_RATE_MCS7_SGI | 72.2 Mbps (400 ns) | 150 Mbps (400 ns) | 86 Mbps (800 ns) | 72.2 Mbps (400 ns) | + ## | WIFI_PHY_RATE_MCS8_SGI | | | 103.2 Mbps (800 ns) | | + ## | WIFI_PHY_RATE_MCS9_SGI | | | 114.7 Mbps (800 ns) | | + ## + ## + +let + WIFI_PHY_RATE_1M_L {.importc: "WIFI_PHY_RATE_1M_L", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_2M_L {.importc: "WIFI_PHY_RATE_2M_L", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_5M_L {.importc: "WIFI_PHY_RATE_5M_L", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_11M_L {.importc: "WIFI_PHY_RATE_11M_L", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_2M_S {.importc: "WIFI_PHY_RATE_2M_S", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_5M_S {.importc: "WIFI_PHY_RATE_5M_S", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_11M_S {.importc: "WIFI_PHY_RATE_11M_S", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_48M {.importc: "WIFI_PHY_RATE_48M", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_24M {.importc: "WIFI_PHY_RATE_24M", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_12M {.importc: "WIFI_PHY_RATE_12M", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_6M {.importc: "WIFI_PHY_RATE_6M", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_54M {.importc: "WIFI_PHY_RATE_54M", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_36M {.importc: "WIFI_PHY_RATE_36M", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_18M {.importc: "WIFI_PHY_RATE_18M", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_9M {.importc: "WIFI_PHY_RATE_9M", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS0_LGI {.importc: "WIFI_PHY_RATE_MCS0_LGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS1_LGI {.importc: "WIFI_PHY_RATE_MCS1_LGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS2_LGI {.importc: "WIFI_PHY_RATE_MCS2_LGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS3_LGI {.importc: "WIFI_PHY_RATE_MCS3_LGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS4_LGI {.importc: "WIFI_PHY_RATE_MCS4_LGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS5_LGI {.importc: "WIFI_PHY_RATE_MCS5_LGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS6_LGI {.importc: "WIFI_PHY_RATE_MCS6_LGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS7_LGI {.importc: "WIFI_PHY_RATE_MCS7_LGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS8_LGI {.importc: "WIFI_PHY_RATE_MCS8_LGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS9_LGI {.importc: "WIFI_PHY_RATE_MCS9_LGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS0_SGI {.importc: "WIFI_PHY_RATE_MCS0_SGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS1_SGI {.importc: "WIFI_PHY_RATE_MCS1_SGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS2_SGI {.importc: "WIFI_PHY_RATE_MCS2_SGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS3_SGI {.importc: "WIFI_PHY_RATE_MCS3_SGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS4_SGI {.importc: "WIFI_PHY_RATE_MCS4_SGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS5_SGI {.importc: "WIFI_PHY_RATE_MCS5_SGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS6_SGI {.importc: "WIFI_PHY_RATE_MCS6_SGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS7_SGI {.importc: "WIFI_PHY_RATE_MCS7_SGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS8_SGI {.importc: "WIFI_PHY_RATE_MCS8_SGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MCS9_SGI {.importc: "WIFI_PHY_RATE_MCS9_SGI", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_LORA_250K {.importc: "WIFI_PHY_RATE_LORA_250K", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_LORA_500K {.importc: "WIFI_PHY_RATE_LORA_500K", header: hdr.}: wifi_phy_rate_t + WIFI_PHY_RATE_MAX {.importc: "WIFI_PHY_RATE_MAX", header: hdr.}: wifi_phy_rate_t -## * @endcond * -## * Argument structure for WIFI_EVENT_SCAN_DONE event type - wifi_event_sta_scan_done_t* {.importc: "wifi_event_sta_scan_done_t", - header: "esp_wifi_types.h", bycopy.} = object - status* {.importc: "status".}: uint32 ## *< status of scanning APs: 0 — success, 1 - failure - number* {.importc: "number".}: uint8 ## *< number of scan results - scan_id* {.importc: "scan_id".}: uint8 ## *< scan sequence number, used for block scan + wifi_event_t* {.size: sizeof(cint).} = enum ## + ## @brief Wi-Fi event declarations + ## + WIFI_EVENT_WIFI_READY = 0, ## < Wi-Fi ready + WIFI_EVENT_SCAN_DONE, ## < Finished scanning AP + WIFI_EVENT_STA_START, ## < Station start + WIFI_EVENT_STA_STOP, ## < Station stop + WIFI_EVENT_STA_CONNECTED, ## < Station connected to AP + WIFI_EVENT_STA_DISCONNECTED, ## < Station disconnected from AP + WIFI_EVENT_STA_AUTHMODE_CHANGE, ## < The auth mode of AP connected by device's station changed + WIFI_EVENT_STA_WPS_ER_SUCCESS, ## < Station WPS succeeds in enrollee mode + WIFI_EVENT_STA_WPS_ER_FAILED, ## < Station WPS fails in enrollee mode + WIFI_EVENT_STA_WPS_ER_TIMEOUT, ## < Station WPS timeout in enrollee mode + WIFI_EVENT_STA_WPS_ER_PIN, ## < Station WPS pin code in enrollee mode + WIFI_EVENT_STA_WPS_ER_PBC_OVERLAP, ## < Station WPS overlap in enrollee mode + WIFI_EVENT_AP_START, ## < Soft-AP start + WIFI_EVENT_AP_STOP, ## < Soft-AP stop + WIFI_EVENT_AP_STACONNECTED, ## < A station connected to Soft-AP + WIFI_EVENT_AP_STADISCONNECTED, ## < A station disconnected from Soft-AP + WIFI_EVENT_AP_PROBEREQRECVED, ## < Receive probe request packet in soft-AP interface + WIFI_EVENT_FTM_REPORT, ## < Receive report of FTM procedure + ## Add next events after this only + WIFI_EVENT_STA_BSS_RSSI_LOW, ## < AP's RSSI crossed configured threshold + WIFI_EVENT_ACTION_TX_STATUS, ## < Status indication of Action Tx operation + WIFI_EVENT_ROC_DONE, ## < Remain-on-Channel operation complete + WIFI_EVENT_STA_BEACON_TIMEOUT, ## < Station beacon timeout + WIFI_EVENT_CONNECTIONLESS_MODULE_WAKE_INTERVAL_START, ## + ## < Connectionless module wake interval start + ## Add next events after this only + WIFI_EVENT_AP_WPS_RG_SUCCESS, ## < Soft-AP wps succeeds in registrar mode + WIFI_EVENT_AP_WPS_RG_FAILED, ## < Soft-AP wps fails in registrar mode + WIFI_EVENT_AP_WPS_RG_TIMEOUT, ## < Soft-AP wps timeout in registrar mode + WIFI_EVENT_AP_WPS_RG_PIN, ## < Soft-AP wps pin code in registrar mode + WIFI_EVENT_AP_WPS_RG_PBC_OVERLAP, ## < Soft-AP wps overlap in registrar mode + WIFI_EVENT_ITWT_SETUP, ## < iTWT setup + WIFI_EVENT_ITWT_TEARDOWN, ## < iTWT teardown + WIFI_EVENT_ITWT_PROBE, ## < iTWT probe + WIFI_EVENT_ITWT_SUSPEND, ## < iTWT suspend + WIFI_EVENT_TWT_WAKEUP, ## < TWT wakeup + WIFI_EVENT_BTWT_SETUP, ## < bTWT setup + WIFI_EVENT_BTWT_TEARDOWN, ## < bTWT teardown + WIFI_EVENT_NAN_STARTED, ## < NAN Discovery has started + WIFI_EVENT_NAN_STOPPED, ## < NAN Discovery has stopped + WIFI_EVENT_NAN_SVC_MATCH, ## < NAN Service Discovery match found + WIFI_EVENT_NAN_REPLIED, ## < Replied to a NAN peer with Service Discovery match + WIFI_EVENT_NAN_RECEIVE, ## < Received a Follow-up message + WIFI_EVENT_NDP_INDICATION, ## < Received NDP Request from a NAN Peer + WIFI_EVENT_NDP_CONFIRM, ## < NDP Confirm Indication + WIFI_EVENT_NDP_TERMINATED, ## < NAN Datapath terminated indication + WIFI_EVENT_HOME_CHANNEL_CHANGE, ## < Wi-Fi home channel change,doesn't occur when scanning + WIFI_EVENT_STA_NEIGHBOR_REP, ## < Received Neighbor Report response + WIFI_EVENT_AP_WRONG_PASSWORD, ## < a station tried to connect with wrong password + WIFI_EVENT_STA_BEACON_OFFSET_UNSTABLE, ## < Station sampled beacon offset unstable + WIFI_EVENT_DPP_URI_READY, ## < DPP URI is ready through Bootstrapping + WIFI_EVENT_DPP_CFG_RECVD, ## < Config received via DPP Authentication + WIFI_EVENT_DPP_FAILED, ## < DPP failed + WIFI_EVENT_MAX ## < Invalid Wi-Fi event ID + + +## @cond * +## @brief Wi-Fi event base declaration -## * Argument structure for WIFI_EVENT_STA_CONNECTED event +# ESP_EVENT_DECLARE_BASE(WIFI_EVENT) +let WIFI_EVENT* {.importc: "WIFI_EVENT", header: "esp_wifi_types.h".}: esp_event_base_t type + + wifi_event_sta_scan_done_t* {.importc: "wifi_event_sta_scan_done_t", + header: hdr, bycopy.} = object ## + ## @endcond * + ## + ## @brief Argument structure for WIFI_EVENT_SCAN_DONE event + ## + status* {.importc: "status".}: uint32 ## < Status of scanning APs: 0 — success, 1 - failure + number* {.importc: "number".}: uint8 ## < Number of scan results + scan_id* {.importc: "scan_id".}: uint8 + ## < Scan sequence number, used for block scan + + wifi_event_sta_connected_t* {.importc: "wifi_event_sta_connected_t", - header: "esp_wifi_types.h", bycopy.} = object - ssid* {.importc: "ssid".}: array[32, uint8] ## *< SSID of connected AP - ssid_len* {.importc: "ssid_len".}: uint8 ## *< SSID length of connected AP - bssid* {.importc: "bssid".}: array[6, uint8] ## *< BSSID of connected AP - channel* {.importc: "channel".}: uint8 ## *< channel of connected AP - authmode* {.importc: "authmode".}: wifi_auth_mode_t ## *< authentication mode used by AP + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_STA_CONNECTED event + ## + ssid* {.importc: "ssid".}: array[32, uint8] ## < SSID of connected AP + ssid_len* {.importc: "ssid_len".}: uint8 ## < SSID length of connected AP + bssid* {.importc: "bssid".}: array[6, uint8] ## < BSSID of connected AP + channel* {.importc: "channel".}: uint8 ## < Channel of connected AP + authmode* {.importc: "authmode".}: wifi_auth_mode_t ## + ## < Authentication mode used by the connection + aid* {.importc: "aid".}: uint16 + ## < Authentication id assigned by the connected AP -## * Argument structure for WIFI_EVENT_STA_DISCONNECTED event + wifi_event_sta_disconnected_t* {.importc: "wifi_event_sta_disconnected_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_STA_DISCONNECTED event + ## + ssid* {.importc: "ssid".}: array[32, uint8] ## < SSID of disconnected AP + ssid_len* {.importc: "ssid_len".}: uint8 ## < SSID length of disconnected AP + bssid* {.importc: "bssid".}: array[6, uint8] ## < BSSID of disconnected AP + reason* {.importc: "reason".}: uint8 ## < Disconnection reason + rssi* {.importc: "rssi".}: int8 + ## < Disconnection RSSI + + + wifi_event_sta_authmode_change_t* {.importc: "wifi_event_sta_authmode_change_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_STA_AUTHMODE_CHANGE event + ## + old_mode* {.importc: "old_mode".}: wifi_auth_mode_t ## + ## < Old auth mode of AP + new_mode* {.importc: "new_mode".}: wifi_auth_mode_t + ## < New auth mode of AP + + + wifi_event_sta_wps_er_pin_t* {.importc: "wifi_event_sta_wps_er_pin_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_STA_WPS_ER_PIN event + ## + pin_code* {.importc: "pin_code".}: array[8, uint8] + ## < PIN code of station in enrollee mode + + + wifi_event_sta_wps_fail_reason_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Argument structure for WIFI_EVENT_STA_WPS_ER_FAILED event + ## + WPS_FAIL_REASON_NORMAL = 0, ## < WPS normal fail reason + WPS_FAIL_REASON_RECV_M2D, ## < WPS receive M2D frame + WPS_FAIL_REASON_RECV_DEAUTH, ## < Recv deauth from AP while wps handshake + WPS_FAIL_REASON_MAX ## < Max WPS fail reason + +const + MAX_SSID_LEN* = 32 + MAX_PASSPHRASE_LEN* = 64 + MAX_WPS_AP_CRED* = 3 type - wifi_event_sta_disconnected_t* {.importc: "wifi_event_sta_disconnected_t", - header: "esp_wifi_types.h", bycopy.} = object - ssid* {.importc: "ssid".}: array[32, uint8] ## *< SSID of disconnected AP - ssid_len* {.importc: "ssid_len".}: uint8 ## *< SSID length of disconnected AP - bssid* {.importc: "bssid".}: array[6, uint8] ## *< BSSID of disconnected AP - reason* {.importc: "reason".}: uint8 ## *< reason of disconnection + INNER_C_STRUCT_esp_wifi_types_generic_1* {. + importc: "wifi_event_sta_wps_er_success_t::no_name", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_STA_WPS_ER_SUCCESS event + ## + ssid* {.importc: "ssid".}: array[MAX_SSID_LEN, uint8] ## + ## < SSID of AP + passphrase* {.importc: "passphrase".}: array[MAX_PASSPHRASE_LEN, uint8] + ## < Passphrase for the AP + + + wifi_event_sta_wps_er_success_t* {.importc: "wifi_event_sta_wps_er_success_t", + header: hdr, bycopy.} = object + ap_cred_cnt* {.importc: "ap_cred_cnt".}: uint8 ## + ## < Number of AP credentials received + ap_cred* {.importc: "ap_cred".}: array[MAX_WPS_AP_CRED, + INNER_C_STRUCT_esp_wifi_types_generic_1] + ## < All AP credentials received from WPS handshake + + + wifi_event_ap_staconnected_t* {.importc: "wifi_event_ap_staconnected_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_AP_STACONNECTED event + ## + mac* {.importc: "mac".}: array[6, uint8] ## < MAC address of the station connected to Soft-AP + aid* {.importc: "aid".}: uint8 ## < AID assigned by the Soft-AP to the connected station + is_mesh_child* {.importc: "is_mesh_child".}: bool + ## < Flag indicating whether the connected station is a mesh child + + + wifi_event_ap_stadisconnected_t* {.importc: "wifi_event_ap_stadisconnected_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_AP_STADISCONNECTED event + ## + mac* {.importc: "mac".}: array[6, uint8] ## < MAC address of the station disconnects from the soft-AP + aid* {.importc: "aid".}: uint8 ## < AID that the Soft-AP assigned to the disconnected station + is_mesh_child* {.importc: "is_mesh_child".}: bool ## + ## < Flag indicating whether the disconnected station is a mesh child + reason* {.importc: "reason".}: uint16 + ## < Disconnection reason + + + wifi_event_ap_probe_req_rx_t* {.importc: "wifi_event_ap_probe_req_rx_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_AP_PROBEREQRECVED event + ## + rssi* {.importc: "rssi".}: cint ## < Received probe request signal strength + mac* {.importc: "mac".}: array[6, uint8] + ## < MAC address of the station which send probe request + + + wifi_event_bss_rssi_low_t* {.importc: "wifi_event_bss_rssi_low_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_STA_BSS_RSSI_LOW event + ## + rssi* {.importc: "rssi".}: int32 + ## < RSSI value of bss + + + wifi_event_home_channel_change_t* {.importc: "wifi_event_home_channel_change_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_HOME_CHANNEL_CHANGE event + ## + old_chan* {.importc: "old_chan".}: uint8 ## < Old home channel of the device + old_snd* {.importc: "old_snd".}: wifi_second_chan_t ## + ## < Old second channel of the device + new_chan* {.importc: "new_chan".}: uint8 ## < New home channel of the device + new_snd* {.importc: "new_snd".}: wifi_second_chan_t + ## < New second channel of the device + + + wifi_ftm_status_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief FTM operation status types + ## + ## + 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 + FTM_STATUS_NO_VALID_MSMT, ## < FTM session did not result in any valid measurements + FTM_STATUS_USER_TERM ## < User triggered termination -## * Argument structure for WIFI_EVENT_STA_AUTHMODE_CHANGE event type - wifi_event_sta_authmode_change_t* {.importc: "wifi_event_sta_authmode_change_t", - header: "esp_wifi_types.h", bycopy.} = object - old_mode* {.importc: "old_mode".}: wifi_auth_mode_t ## *< the old auth mode of AP - new_mode* {.importc: "new_mode".}: wifi_auth_mode_t ## *< the new auth mode of AP + wifi_ftm_report_entry_t* {.importc: "wifi_ftm_report_entry_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Structure representing a report entry for Fine Timing Measurement (FTM) in Wi-Fi. + ## + ## This structure holds the information related to the FTM process between a Wi-Fi FTM Initiator + ## and a Wi-Fi FTM Responder. FTM is used for precise distance measurement by timing the exchange + ## of frames between devices. + ## + dlog_token* {.importc: "dlog_token".}: uint8 ## < Dialog Token of the FTM frame + rssi* {.importc: "rssi".}: int8 ## < RSSI of the FTM frame received + rtt* {.importc: "rtt".}: uint32 ## < Round Trip Time in pSec with a peer + t1* {.importc: "t1".}: uint64 ## < Time of departure of FTM frame from FTM Responder in pSec + t2* {.importc: "t2".}: uint64 ## < Time of arrival of FTM frame at FTM Initiator in pSec + t3* {.importc: "t3".}: uint64 ## < Time of departure of ACK from FTM Initiator in pSec + t4* {.importc: "t4".}: uint64 + ## < Time of arrival of ACK at FTM Responder in pSec + + + wifi_event_ftm_report_t* {.importc: "wifi_event_ftm_report_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_FTM_REPORT event + ## + peer_mac* {.importc: "peer_mac".}: array[6, uint8] ## + ## < MAC address of the FTM Peer + status* {.importc: "status".}: wifi_ftm_status_t ## + ## < Status of the FTM operation + rtt_raw* {.importc: "rtt_raw".}: uint32 ## < Raw average Round-Trip-Time with peer in Nano-Seconds + rtt_est* {.importc: "rtt_est".}: uint32 ## < Estimated Round-Trip-Time with peer in Nano-Seconds + dist_est* {.importc: "dist_est".}: uint32 ## < Estimated one-way distance in Centi-Meters + ftm_report_data* {.importc: "ftm_report_data".}: ptr wifi_ftm_report_entry_t ## + ## < Pointer to FTM Report, should be freed after use. Note: Highly recommended + ## to use API esp_wifi_ftm_get_report to get the report instead of using this + ftm_report_num_entries* {.importc: "ftm_report_num_entries".}: uint8 + ## < Number of entries in the FTM Report data -## * Argument structure for WIFI_EVENT_STA_WPS_ER_PIN event + +const + WIFI_STATIS_BUFFER* = (1 shl 0) ## < Buffer status + WIFI_STATIS_RXTX* = (1 shl 1) ## < RX/TX status + WIFI_STATIS_HW* = (1 shl 2) ## < Hardware status + WIFI_STATIS_DIAG* = (1 shl 3) ## < Diagnostic status + WIFI_STATIS_PS* = (1 shl 4) ## < Power save status + WIFI_STATIS_ALL* = (-1) ## < All status type - wifi_event_sta_wps_er_pin_t* {.importc: "wifi_event_sta_wps_er_pin_t", - header: "esp_wifi_types.h", bycopy.} = object - pin_code* {.importc: "pin_code".}: array[8, uint8] ## *< PIN code of station in enrollee mode + wifi_action_tx_status_type_t* {.size: sizeof(cint).} = enum ## + ## Status codes for WIFI_EVENT_ACTION_TX_STATUS evt + ## There will be back to back events in success case TX_DONE and TX_DURATION_COMPLETED + WIFI_ACTION_TX_DONE = 0, ## < ACTION_TX operation was completed successfully + WIFI_ACTION_TX_FAILED, ## < ACTION_TX operation failed during tx + WIFI_ACTION_TX_DURATION_COMPLETED, ## < ACTION_TX operation completed it's wait duration + WIFI_ACTION_TX_OP_CANCELLED ## < ACTION_TX operation was cancelled by application or higher priority operation -## * Argument structure for WIFI_EVENT_STA_WPS_ER_FAILED event type - wifi_event_sta_wps_fail_reason_t* {.size: sizeof(cint).} = 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_action_tx_status_t* {.importc: "wifi_event_action_tx_status_t", + header: hdr, bycopy.} = object ## + ## Argument structure for WIFI_EVENT_ACTION_TX_STATUS event + ifx* {.importc: "ifx".}: wifi_interface_t ## < WiFi interface to send request to + context* {.importc: "context".}: uint32 ## < Context to identify the request + status* {.importc: "status".}: wifi_action_tx_status_type_t ## + ## < Status of the operation + op_id* {.importc: "op_id".}: uint8 ## < ID of the corresponding operation that was provided during action tx request + channel* {.importc: "channel".}: uint8 + ## < Channel provided in tx request + + + wifi_event_roc_done_t* {.importc: "wifi_event_roc_done_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_ROC_DONE event + ## + context* {.importc: "context".}: uint32 ## < Context to identify the initiator of the request + status* {.importc: "status".}: wifi_roc_done_status_t ## + ## < ROC status + op_id* {.importc: "op_id".}: uint8 ## < ID of the corresponding ROC operation + channel* {.importc: "channel".}: uint8 + ## < Channel provided in tx request + + + wifi_event_ap_wps_rg_pin_t* {.importc: "wifi_event_ap_wps_rg_pin_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_AP_WPS_RG_PIN event + ## + pin_code* {.importc: "pin_code".}: array[8, uint8] + ## < PIN code of station in enrollee mode + + + wps_fail_reason_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief WPS fail reason + ## + WPS_AP_FAIL_REASON_NORMAL = 0, ## < WPS normal fail reason + WPS_AP_FAIL_REASON_CONFIG, ## < WPS failed due to incorrect config + WPS_AP_FAIL_REASON_AUTH, ## < WPS failed during auth + WPS_AP_FAIL_REASON_MAX ## < Max WPS fail reason -## * Argument structure for WIFI_EVENT_AP_STACONNECTED event type - wifi_event_ap_staconnected_t* {.importc: "wifi_event_ap_staconnected_t", - header: "esp_wifi_types.h", bycopy.} = object - mac* {.importc: "mac".}: array[6, uint8] ## *< MAC address of the station connected to ESP32 soft-AP - aid* {.importc: "aid".}: uint8 ## *< the aid that ESP32 soft-AP gives to the station connected to + wifi_event_ap_wps_rg_fail_reason_t* {.importc: "wifi_event_ap_wps_rg_fail_reason_t", + header: hdr, + bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_AP_WPS_RG_FAILED event + ## + reason* {.importc: "reason".}: wps_fail_reason_t ## + ## < WPS failure reason wps_fail_reason_t + peer_macaddr* {.importc: "peer_macaddr".}: array[6, uint8] + ## < Enrollee mac address + + + wifi_event_ap_wps_rg_success_t* {.importc: "wifi_event_ap_wps_rg_success_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_AP_WPS_RG_SUCCESS event + ## + peer_macaddr* {.importc: "peer_macaddr".}: array[6, uint8] + ## < Enrollee mac address + + + wifi_event_nan_svc_match_t* {.importc: "wifi_event_nan_svc_match_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_NAN_SVC_MATCH event + ## + subscribe_id* {.importc: "subscribe_id".}: uint8 ## + ## < Subscribe Service Identifier + publish_id* {.importc: "publish_id".}: uint8 ## < Publish Service Identifier + pub_if_mac* {.importc: "pub_if_mac".}: array[6, uint8] ## + ## < NAN Interface MAC of the Publisher + update_pub_id* {.importc: "update_pub_id".}: bool ## + ## < Indicates whether publisher's service ID needs to be updated + datapath_reqd* {.importc: "datapath_reqd", bitsize: 1.}: uint8 ## + ## < NAN Datapath required for the service + fsd_reqd* {.importc: "fsd_reqd", bitsize: 1.}: uint8 ## + ## < Further Service Discovery(FSD) required + fsd_gas* {.importc: "fsd_gas", bitsize: 1.}: uint8 ## + ## < 0 - Follow-up used for FSD, 1 - GAS used for FSD + reserved* {.importc: "reserved", bitsize: 5.}: uint8 ## + ## < Reserved + reserved_1* {.importc: "reserved_1".}: uint32 ## < Reserved + reserved_2* {.importc: "reserved_2".}: uint32 ## < Reserved + ssi_version* {.importc: "ssi_version".}: uint8 ## + ## < Indicates version of SSI in Publish instance, 0 if not available + ssi_len* {.importc: "ssi_len".}: uint16 ## < Length of service specific info + ssi* {.importc: "ssi".}: UncheckedArray[uint8] + ## < Service specific info of Publisher + + + wifi_event_nan_replied_t* {.importc: "wifi_event_nan_replied_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_NAN_REPLIED event + ## + publish_id* {.importc: "publish_id".}: uint8 ## < Publish Service Identifier + subscribe_id* {.importc: "subscribe_id".}: uint8 ## + ## < Subscribe Service Identifier + sub_if_mac* {.importc: "sub_if_mac".}: array[6, uint8] ## + ## < NAN Interface MAC of the Subscriber + reserved_1* {.importc: "reserved_1".}: uint32 ## < Reserved + reserved_2* {.importc: "reserved_2".}: uint32 ## < Reserved + ssi_len* {.importc: "ssi_len".}: uint16 ## < Length of service specific info + ssi* {.importc: "ssi".}: UncheckedArray[uint8] + ## < Service specific info of Subscriber + + + wifi_event_nan_receive_t* {.importc: "wifi_event_nan_receive_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_NAN_RECEIVE event + ## + inst_id* {.importc: "inst_id".}: uint8 ## < Our Service Identifier + peer_inst_id* {.importc: "peer_inst_id".}: uint8 ## + ## < Peer's Service Identifier + peer_if_mac* {.importc: "peer_if_mac".}: array[6, uint8] ## + ## < Peer's NAN Interface MAC + peer_svc_info* {.importc: "peer_svc_info".}: array[ + ESP_WIFI_MAX_SVC_INFO_LEN, uint8] ## < To be deprecated in next major release, use ssi instead + reserved_1* {.importc: "reserved_1".}: uint32 ## < Reserved + reserved_2* {.importc: "reserved_2".}: uint32 ## < Reserved + ssi_len* {.importc: "ssi_len".}: uint16 ## < Length of service specific info + ssi* {.importc: "ssi".}: UncheckedArray[uint8] + ## < Service specific info from Follow-up + + + wifi_event_ndp_indication_t* {.importc: "wifi_event_ndp_indication_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_NDP_INDICATION event + ## + publish_id* {.importc: "publish_id".}: uint8 ## < Publish Id for NAN Service + ndp_id* {.importc: "ndp_id".}: uint8 ## < NDP instance id + peer_nmi* {.importc: "peer_nmi".}: array[6, uint8] ## + ## < Peer's NAN Management Interface MAC + peer_ndi* {.importc: "peer_ndi".}: array[6, uint8] ## + ## < Peer's NAN Data Interface MAC + svc_info* {.importc: "svc_info".}: array[ESP_WIFI_MAX_SVC_INFO_LEN, uint8] ## + ## < To be deprecated in next major release, use ssi instead + reserved_1* {.importc: "reserved_1".}: uint32 ## < Reserved + reserved_2* {.importc: "reserved_2".}: uint32 ## < Reserved + ssi_len* {.importc: "ssi_len".}: uint16 ## < Length of service specific info + ssi* {.importc: "ssi".}: UncheckedArray[uint8] + ## < Service specific info from NDP/NDPE Attribute + + + wifi_event_ndp_confirm_t* {.importc: "wifi_event_ndp_confirm_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_NDP_CONFIRM event + ## + status* {.importc: "status".}: uint8 ## < NDP status code + ndp_id* {.importc: "ndp_id".}: uint8 ## < NDP instance id + peer_nmi* {.importc: "peer_nmi".}: array[6, uint8] ## + ## < Peer's NAN Management Interface MAC + peer_ndi* {.importc: "peer_ndi".}: array[6, uint8] ## + ## < Peer's NAN Data Interface MAC + own_ndi* {.importc: "own_ndi".}: array[6, uint8] ## + ## < Own NAN Data Interface MAC + svc_info* {.importc: "svc_info".}: array[ESP_WIFI_MAX_SVC_INFO_LEN, uint8] ## + ## < To be deprecated in next major release, use ssi instead + reserved_1* {.importc: "reserved_1".}: uint32 ## < Reserved + reserved_2* {.importc: "reserved_2".}: uint32 ## < Reserved + ssi_len* {.importc: "ssi_len".}: uint16 ## < Length of Service Specific Info + ssi* {.importc: "ssi".}: UncheckedArray[uint8] + ## < Service specific info from NDP/NDPE Attribute + + + wifi_event_ndp_terminated_t* {.importc: "wifi_event_ndp_terminated_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_NDP_TERMINATED event + ## + reason* {.importc: "reason".}: uint8 ## < Termination reason code + ndp_id* {.importc: "ndp_id".}: uint8 ## < NDP instance id + init_ndi* {.importc: "init_ndi".}: array[6, uint8] + ## < Initiator's NAN Data Interface MAC + + + wifi_event_neighbor_report_t* {.importc: "wifi_event_neighbor_report_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for WIFI_EVENT_STA_NEIGHBOR_REP event + ## + report* {.importc: "report".}: array[ESP_WIFI_MAX_NEIGHBOR_REP_LEN, uint8] ## + ## < Neighbor Report received from the AP (will be deprecated in next major release, use n_report instead) + report_len* {.importc: "report_len".}: uint16 ## < Length of the report + n_report* {.importc: "n_report".}: UncheckedArray[uint8] + ## < Neighbor Report received from the AP + + + wifi_event_ap_wrong_password_t* {.importc: "wifi_event_ap_wrong_password_t", + header: hdr, bycopy.} = object ## + ## Argument structure for WIFI_EVENT_AP_WRONG_PASSWORD event + mac* {.importc: "mac".}: array[6, uint8] + ## < MAC address of the station trying to connect to Soft-AP + + + wifi_tx_rate_config_t* {.importc: "wifi_tx_rate_config_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Argument structure for wifi_tx_rate_config + ## + phymode* {.importc: "phymode".}: wifi_phy_mode_t ## + ## < Phymode of specified interface + rate* {.importc: "rate".}: wifi_phy_rate_t ## < Rate of specified interface + ersu* {.importc: "ersu".}: bool ## < Using ERSU to send frame, ERSU is a transmission mode related to 802.11 ax. + ## ERSU is always used in long distance transmission, and its frame has lower rate compared with SU mode + dcm* {.importc: "dcm".}: bool + ## < Using dcm rate to send frame -## * Argument structure for WIFI_EVENT_AP_STADISCONNECTED event +const + WIFI_MAX_SUPPORT_COUNTRY_NUM* = 176 + +when defined(CONFIG_SOC_WIFI_SUPPORT_5G): + const + WIFI_MAX_REGULATORY_RULE_NUM* = 7 +else: + const + WIFI_MAX_REGULATORY_RULE_NUM* = 2 type - wifi_event_ap_stadisconnected_t* {.importc: "wifi_event_ap_stadisconnected_t", - header: "esp_wifi_types.h", bycopy.} = object - mac* {.importc: "mac".}: array[6, uint8] ## *< MAC address of the station disconnects to ESP32 soft-AP - aid* {.importc: "aid".}: uint8 ## *< the aid that ESP32 soft-AP gave to the station disconnects to + wifi_reg_rule_t* {.importc: "wifi_reg_rule_t", + header: hdr, bycopy.} = object ## + ## Argument structure for regulatory rule + start_channel* {.importc: "start_channel".}: uint8 ## + ## < start channel of regulatory rule + end_channel* {.importc: "end_channel".}: uint8 ## + ## < end channel of regulatory rule + max_bandwidth* {.importc: "max_bandwidth", bitsize: 3.}: uint16 ## + ## < max bandwidth(MHz) of regulatory rule, 1:20M, 2:40M, 3:80M, 4:160M + max_eirp* {.importc: "max_eirp", bitsize: 6.}: uint16 ## + ## < indicates the maximum Equivalent Isotropically Radiated Power (EIRP), typically measured in dBm + is_dfs* {.importc: "is_dfs", bitsize: 1.}: uint16 ## + ## < flag to identify dfs channel + reserved* {.importc: "reserved", bitsize: 6.}: uint16 + ## < reserved + + + wifi_regulatory_t* {.importc: "wifi_regulatory_t", + header: hdr, bycopy.} = object ## + ## Argument structure for regdomain + n_reg_rules* {.importc: "n_reg_rules".}: uint8 ## + ## < number of regulatory rules + reg_rules* {.importc: "reg_rules".}: array[WIFI_MAX_REGULATORY_RULE_NUM, + wifi_reg_rule_t] + ## < array of regulatory rules + + + wifi_regdomain_t* {.importc: "wifi_regdomain_t", + header: hdr, bycopy.} = object ## + ## Argument structure for regdomain + cn* {.importc: "cn".}: array[2, char] ## < country code string + regulatory_type* {.importc: "regulatory_type".}: uint8 + ## < regulatory type of country + + + wifi_tx_status_t* {.size: sizeof(cint).} = enum ## + ## + ## @brief Status of wifi sending data + ## + WIFI_SEND_SUCCESS = 0, ## < Sending Wi-Fi data successfully + WIFI_SEND_FAIL ## < Sending Wi-Fi data fail -## * Argument structure for WIFI_EVENT_AP_PROBEREQRECVED event type - wifi_event_ap_probe_req_rx_t* {.importc: "wifi_event_ap_probe_req_rx_t", - header: "esp_wifi_types.h", bycopy.} = object - rssi* {.importc: "rssi".}: cint ## *< Received probe request signal strength - mac* {.importc: "mac".}: array[6, uint8] ## *< MAC address of the station which send probe request + + wifi_tx_info_t* {.importc: "wifi_tx_info_t", + header: hdr, bycopy.} = object ## + ## + ## @brief Information of wifi sending data + ## + des_addr* {.importc: "des_addr".}: ptr uint8 ## < The address of the receive device + src_addr* {.importc: "src_addr".}: ptr uint8 ## < The address of the sending device + ifidx* {.importc: "ifidx".}: wifi_interface_t ## < Interface of sending 80211 tx data + data* {.importc: "data".}: ptr uint8 ## < The data for 80211 tx, start from the MAC header + data_len* {.importc: "data_len".}: uint8 ## < The frame body length for 80211 tx, excluding the MAC header + rate* {.importc: "rate".}: wifi_phy_rate_t ## < Data rate + tx_status* {.importc: "tx_status".}: wifi_tx_status_t + ## < Status of sending 80211 tx data + + + esp_80211_tx_info_t* = wifi_tx_info_t + + wifi_event_sta_beacon_offset_unstable_t* {. + importc: "wifi_event_sta_beacon_offset_unstable_t", + header: hdr, bycopy.} = object ## + ## Argument structure for WIFI_EVENT_STA_BEACON_OFFSET_UNSTABLE event + beacon_success_rate* {.importc: "beacon_success_rate".}: cfloat + ## < Received beacon success rate + + + wifi_event_dpp_uri_ready_t* {.importc: "wifi_event_dpp_uri_ready_t", + header: hdr, bycopy.} = object ## + ## Argument structure for WIFI_EVENT_DPP_URI_READY event + uri_data_len* {.importc: "uri_data_len".}: uint32 ## + ## < URI data length including null termination + uri* {.importc: "uri".}: UncheckedArray[char] + ## < URI data + + + wifi_event_dpp_config_received_t* {.importc: "wifi_event_dpp_config_received_t", + header: hdr, bycopy.} = object ## + ## Argument structure for WIFI_EVENT_DPP_CFG_RECVD event + wifi_cfg* {.importc: "wifi_cfg".}: wifi_config_t + ## < Received WIFI config in DPP + + + wifi_event_dpp_failed_t* {.importc: "wifi_event_dpp_failed_t", + header: hdr, bycopy.} = object ## + ## Argument structure for WIFI_EVENT_DPP_FAIL event + failure_reason* {.importc: "failure_reason".}: cint + ## < Failure reason diff --git a/src/nesper/esp/net/esp_wifi_types_old.nim b/src/nesper/esp/net/esp_wifi_types_old.nim new file mode 100644 index 00000000..1cad2b36 --- /dev/null +++ b/src/nesper/esp/net/esp_wifi_types_old.nim @@ -0,0 +1,610 @@ +## 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. + +import ../esp_event + +type + wifi_country_policy_t* {.size: sizeof(cint).} = enum + WIFI_COUNTRY_POLICY_AUTO, ## *< Country policy is auto, use the country info of AP to which the station is connected + WIFI_COUNTRY_POLICY_MANUAL ## *< Country policy is manual, always use the configured country info + + +## * @brief Structure describing WiFi country-based regional restrictions. + +type + wifi_country_t* {.importc: "wifi_country_t", header: "esp_wifi_types.h", bycopy.} = object + cc* {.importc: "cc".}: array[3, char] ## *< country code string + schan* {.importc: "schan".}: uint8 ## *< start channel + nchan* {.importc: "nchan".}: uint8 ## *< total channel number + max_tx_power* {.importc: "max_tx_power".}: int8 ## *< This field is used for getting WiFi maximum transmitting power, call esp_wifi_set_max_tx_power to set the maximum transmitting power. + policy* {.importc: "policy".}: wifi_country_policy_t ## *< country policy + + wifi_auth_mode_t* {.size: sizeof(cint).} = 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_MAX + wifi_err_reason_t* {.size: sizeof(cint).} = 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_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_second_chan_t* {.size: sizeof(cint).} = enum + WIFI_SECOND_CHAN_NONE = 0, ## *< the channel width is HT20 + WIFI_SECOND_CHAN_ABOVE, ## *< the channel width is HT40 and the secondary channel is above the primary channel + WIFI_SECOND_CHAN_BELOW ## *< the channel width is HT40 and the secondary channel is below the primary channel + wifi_scan_type_t* {.size: sizeof(cint).} = enum + WIFI_SCAN_TYPE_ACTIVE = 0, ## *< active scan + WIFI_SCAN_TYPE_PASSIVE ## *< passive scan + + + + + +## * @brief Range of active scan times per channel + +type + wifi_active_scan_time_t* {.importc: "wifi_active_scan_time_t", + header: "esp_wifi_types.h", bycopy.} = object + min* {.importc: "min".}: uint32 ## *< minimum active scan time per channel, units: millisecond + max* {.importc: "max".}: uint32 ## *< maximum active scan time per channel, units: millisecond, values above 1500ms may + ## cause station to disconnect from AP and are not recommended. + + +## * @brief Aggregate of active & passive scan time per channel + +type + wifi_scan_time_t* {.importc: "wifi_scan_time_t", header: "esp_wifi_types.h", bycopy, union.} = object + active* {.importc: "active".}: wifi_active_scan_time_t ## *< active scan time per channel, units: millisecond. + passive* {.importc: "passive".}: uint32 ## *< passive scan time per channel, units: millisecond, values above 1500ms may + ## cause station to disconnect from AP and are not recommended. + + +## * @brief Parameters for an SSID scan. + +type + wifi_scan_config_t* {.importc: "wifi_scan_config_t", header: "esp_wifi_types.h", + bycopy.} = object + ssid* {.importc: "ssid".}: ptr uint8 ## *< SSID of AP + bssid* {.importc: "bssid".}: ptr uint8 ## *< MAC address of AP + channel* {.importc: "channel".}: uint8 ## *< channel, scan the specific channel + show_hidden* {.importc: "show_hidden".}: bool ## *< enable to scan AP whose SSID is hidden + scan_type* {.importc: "scan_type".}: wifi_scan_type_t ## *< scan type, active or passive + scan_time* {.importc: "scan_time".}: wifi_scan_time_t ## *< scan time per channel + + wifi_cipher_type_t* {.size: sizeof(cint).} = enum + WIFI_CIPHER_TYPE_NONE = 0, ## *< the cipher type is none + WIFI_CIPHER_TYPE_WEP40, ## *< the cipher type is WEP40 + WIFI_CIPHER_TYPE_WEP104, ## *< the cipher type is WEP104 + WIFI_CIPHER_TYPE_TKIP, ## *< the cipher type is TKIP + WIFI_CIPHER_TYPE_CCMP, ## *< the cipher type is CCMP + WIFI_CIPHER_TYPE_TKIP_CCMP, ## *< the cipher type is TKIP and CCMP + WIFI_CIPHER_TYPE_AES_CMAC128, ## *< the cipher type is AES-CMAC-128 + WIFI_CIPHER_TYPE_UNKNOWN ## *< the cipher type is unknown + + +## * +## @brief WiFi antenna +## +## + +type + wifi_ant_t* {.size: sizeof(cint).} = enum + WIFI_ANT_ANT0, ## *< WiFi antenna 0 + WIFI_ANT_ANT1, ## *< WiFi antenna 1 + WIFI_ANT_MAX ## *< Invalid WiFi antenna + + +## * @brief Description of a WiFi AP + +type + wifi_ap_record_t* {.importc: "wifi_ap_record_t", header: "esp_wifi_types.h", bycopy.} = object + bssid* {.importc: "bssid".}: array[6, uint8] ## *< MAC address of AP + ssid* {.importc: "ssid".}: array[33, uint8] ## *< SSID of AP + primary* {.importc: "primary".}: uint8 ## *< channel of AP + second* {.importc: "second".}: wifi_second_chan_t ## *< secondary channel of AP + rssi* {.importc: "rssi".}: int8 ## *< signal strength of AP + authmode* {.importc: "authmode".}: wifi_auth_mode_t ## *< authmode of AP + pairwise_cipher* {.importc: "pairwise_cipher".}: wifi_cipher_type_t ## *< pairwise cipher of AP + group_cipher* {.importc: "group_cipher".}: wifi_cipher_type_t ## *< group cipher of AP + ant* {.importc: "ant".}: wifi_ant_t ## *< antenna used to receive beacon from AP + phy_11b* {.importc: "phy_11b", bitsize: 1.}: uint32 ## *< bit: 0 flag to identify if 11b mode is enabled or not + phy_11g* {.importc: "phy_11g", bitsize: 1.}: uint32 ## *< bit: 1 flag to identify if 11g mode is enabled or not + phy_11n* {.importc: "phy_11n", bitsize: 1.}: uint32 ## *< bit: 2 flag to identify if 11n mode is enabled or not + phy_lr* {.importc: "phy_lr", bitsize: 1.}: uint32 ## *< bit: 3 flag to identify if low rate is enabled or not + wps* {.importc: "wps", bitsize: 1.}: uint32 ## *< bit: 4 flag to identify if WPS is supported or not + reserved* {.importc: "reserved", bitsize: 27.}: uint32 ## *< bit: 5..31 reserved + country* {.importc: "country".}: wifi_country_t ## *< country information of AP + + wifi_scan_method_t* {.size: sizeof(cint).} = enum + WIFI_FAST_SCAN = 0, ## *< Do fast scan, scan will end after find SSID match AP + WIFI_ALL_CHANNEL_SCAN ## *< All channel scan, scan will end after scan all the channel + wifi_sort_method_t* {.size: sizeof(cint).} = enum + WIFI_CONNECT_AP_BY_SIGNAL = 0, ## *< Sort match AP in scan list by RSSI + WIFI_CONNECT_AP_BY_SECURITY ## *< Sort match AP in scan list by security mode + + + +## * @brief Structure describing parameters for a WiFi fast scan + +type + wifi_scan_threshold_t* {.importc: "wifi_scan_threshold_t", + header: "esp_wifi_types.h", bycopy.} = object + rssi* {.importc: "rssi".}: int8 ## *< The minimum rssi to accept in the fast scan mode + authmode* {.importc: "authmode".}: wifi_auth_mode_t ## *< The weakest authmode to accept in the fast scan mode + + wifi_ps_type_t* {.size: sizeof(cint).} = enum + WIFI_PS_NONE, ## *< No power save + WIFI_PS_MIN_MODEM, ## *< Minimum modem power saving. In this mode, station wakes up to receive beacon every DTIM period + WIFI_PS_MAX_MODEM ## *< Maximum modem power saving. In this mode, interval to receive beacons is determined by the listen_interval parameter in wifi_sta_config_t + + +const + WIFI_PROTOCOL_11B* = 1 + WIFI_PROTOCOL_11G* = 2 + WIFI_PROTOCOL_11N* = 4 + WIFI_PROTOCOL_LR* = 8 + +type + wifi_bandwidth_t* {.size: sizeof(cint).} = enum + WIFI_BW_HT20 = 1, ## Bandwidth is HT20 + WIFI_BW_HT40 ## Bandwidth is HT40 + + +## * Configuration structure for Protected Management Frame + +type + wifi_pmf_config_t* {.importc: "wifi_pmf_config_t", header: "esp_wifi_types.h", + bycopy.} = object + capable* {.importc: "capable".}: bool ## *< Advertizes support for Protected Management Frame. Device will prefer to connect in PMF mode if other device also advertizes PMF capability. + required* {.importc: "required".}: bool ## *< Advertizes that Protected Management Frame is required. Device will not associate to non-PMF capable devices. + + +## * @brief Soft-AP configuration settings for the ESP32 + +type + wifi_ap_config_t* {.importc: "wifi_ap_config_t", header: "esp_wifi_types.h", bycopy.} = object + ssid* {.importc: "ssid".}: array[32, uint8] ## *< SSID of ESP32 soft-AP. If ssid_len field is 0, this must be a Null terminated string. Otherwise, length is set according to ssid_len. + password* {.importc: "password".}: array[64, uint8] ## *< Password of ESP32 soft-AP. Null terminated string. + ssid_len* {.importc: "ssid_len".}: uint8 ## *< Optional length of SSID field. + channel* {.importc: "channel".}: uint8 ## *< Channel of ESP32 soft-AP + authmode* {.importc: "authmode".}: wifi_auth_mode_t ## *< Auth mode of ESP32 soft-AP. Do not support AUTH_WEP in soft-AP mode + ssid_hidden* {.importc: "ssid_hidden".}: uint8 ## *< Broadcast SSID or not, default 0, broadcast the SSID + max_connection* {.importc: "max_connection".}: uint8 ## *< Max number of stations allowed to connect in, default 4, max 10 + beacon_interval* {.importc: "beacon_interval".}: uint16 ## *< Beacon interval, 100 ~ 60000 ms, default 100 ms + + +## * @brief STA configuration settings for the ESP32 + +type + wifi_sta_config_t* {.importc: "wifi_sta_config_t", header: "esp_wifi_types.h", + bycopy.} = object + ssid* {.importc: "ssid".}: array[32, uint8] ## *< SSID of target AP. Null terminated string. + password* {.importc: "password".}: array[64, uint8] ## *< Password of target AP. Null terminated string. + scan_method* {.importc: "scan_method".}: wifi_scan_method_t ## *< do all channel scan or fast scan + bssid_set* {.importc: "bssid_set".}: bool ## *< whether set MAC address of target AP or not. Generally, station_config.bssid_set needs to be 0; and it needs to be 1 only when users need to check the MAC address of the AP. + bssid* {.importc: "bssid".}: array[6, uint8] ## *< MAC address of target AP + channel* {.importc: "channel".}: uint8 ## *< channel of target AP. Set to 1~13 to scan starting from the specified channel before connecting to AP. If the channel of AP is unknown, set it to 0. + listen_interval* {.importc: "listen_interval".}: uint16 ## *< Listen interval for ESP32 station to receive beacon when WIFI_PS_MAX_MODEM is set. Units: AP beacon intervals. Defaults to 3 if set to 0. + sort_method* {.importc: "sort_method".}: wifi_sort_method_t ## *< sort the connect AP in the list by rssi or security mode + threshold* {.importc: "threshold".}: wifi_scan_threshold_t ## *< When sort_method is set, only APs which have an auth mode that is more secure than the selected auth mode and a signal stronger than the minimum RSSI will be used. + pmf_cfg* {.importc: "pmf_cfg".}: wifi_pmf_config_t ## *< Configuration for Protected Management Frame. Will be advertized in RSN Capabilities in RSN IE. + + +## * @brief Configuration data for ESP32 AP or STA. +## +## The usage of this union (for ap or sta configuration) is determined by the accompanying +## interface argument passed to esp_wifi_set_config() or esp_wifi_get_config() +## +## + +type + wifi_config_t* {.importc: "wifi_config_t", header: "esp_wifi_types.h", bycopy, union.} = object + ap* {.importc: "ap".}: wifi_ap_config_t ## *< configuration of AP + sta* {.importc: "sta".}: wifi_sta_config_t ## *< configuration of STA + + +## * @brief Description of STA associated with AP + +type + wifi_sta_info_t* {.importc: "wifi_sta_info_t", header: "esp_wifi_types.h", bycopy.} = object + mac* {.importc: "mac".}: array[6, uint8] ## *< mac address + rssi* {.importc: "rssi".}: int8 ## *< current average rssi of sta connected + phy_11b* {.importc: "phy_11b", bitsize: 1.}: uint32 ## *< bit: 0 flag to identify if 11b mode is enabled or not + phy_11g* {.importc: "phy_11g", bitsize: 1.}: uint32 ## *< bit: 1 flag to identify if 11g mode is enabled or not + phy_11n* {.importc: "phy_11n", bitsize: 1.}: uint32 ## *< bit: 2 flag to identify if 11n mode is enabled or not + phy_lr* {.importc: "phy_lr", bitsize: 1.}: uint32 ## *< bit: 3 flag to identify if low rate is enabled or not + reserved* {.importc: "reserved", bitsize: 28.}: uint32 ## *< bit: 4..31 reserved + + +const + ESP_WIFI_MAX_CONN_NUM* = (10) ## *< max number of stations which can connect to ESP32 soft-AP + +## * @brief List of stations associated with the ESP32 Soft-AP + +type + wifi_sta_list_t* {.importc: "wifi_sta_list_t", header: "esp_wifi_types.h", bycopy.} = object + sta* {.importc: "sta".}: array[ESP_WIFI_MAX_CONN_NUM, wifi_sta_info_t] ## *< station list + num* {.importc: "num".}: cint ## *< number of stations in the list (other entries are invalid) + + wifi_storage_t* {.size: sizeof(cint).} = enum + WIFI_STORAGE_FLASH, ## *< all configuration will store in both memory and flash + WIFI_STORAGE_RAM ## *< all configuration will only store in the memory + + +## * +## @brief Vendor Information Element type +## +## Determines the frame type that the IE will be associated with. +## + +type + wifi_vendor_ie_type_t* {.size: sizeof(cint).} = enum + WIFI_VND_IE_TYPE_BEACON, WIFI_VND_IE_TYPE_PROBE_REQ, + WIFI_VND_IE_TYPE_PROBE_RESP, WIFI_VND_IE_TYPE_ASSOC_REQ, + WIFI_VND_IE_TYPE_ASSOC_RESP + + +## * +## @brief Vendor Information Element index +## +## Each IE type can have up to two associated vendor ID elements. +## + +type + wifi_vendor_ie_id_t* {.size: sizeof(cint).} = enum + WIFI_VND_IE_ID_0, WIFI_VND_IE_ID_1 + + +const + WIFI_VENDOR_IE_ELEMENT_ID* = 0x000000DD + +## * +## @brief Vendor Information Element header +## +## The first bytes of the Information Element will match this header. Payload follows. +## + +type + vendor_ie_data_t* {.importc: "vendor_ie_data_t", header: "esp_wifi_types.h", bycopy.} = object + element_id* {.importc: "element_id".}: uint8 ## *< Should be set to WIFI_VENDOR_IE_ELEMENT_ID (0xDD) + length* {.importc: "length".}: uint8 ## *< Length of all bytes in the element data following this field. Minimum 4. + vendor_oui* {.importc: "vendor_oui".}: array[3, uint8] ## *< Vendor identifier (OUI). + vendor_oui_type* {.importc: "vendor_oui_type".}: uint8 ## *< Vendor-specific OUI type. + payload* {.importc: "payload".}: UncheckedArray[uint8] ## *< Payload. Length is equal to value in 'length' field, minus 4. + + wifi_pkt_rx_ctrl_t* {.importc: "wifi_pkt_rx_ctrl_t", header: "esp_wifi_types.h", + bycopy.} = object + rssi* {.importc: "rssi", bitsize: 8.}: cint ## *< Received Signal Strength Indicator(RSSI) of packet. unit: dBm + rate* {.importc: "rate", bitsize: 5.}: cuint ## *< PHY rate encoding of the packet. Only valid for non HT(11bg) packet + r1* {.importc: "r1", bitsize: 1.}: cuint ## *< reserve + sig_mode* {.importc: "sig_mode", bitsize: 2.}: cuint ## *< 0: non HT(11bg) packet; 1: HT(11n) packet; 3: VHT(11ac) packet + r2* {.importc: "r2", bitsize: 16.}: cuint ## *< reserve + mcs* {.importc: "mcs", bitsize: 7.}: cuint ## *< Modulation Coding Scheme. If is HT(11n) packet, shows the modulation, range from 0 to 76(MSC0 ~ MCS76) + cwb* {.importc: "cwb", bitsize: 1.}: cuint ## *< Channel Bandwidth of the packet. 0: 20MHz; 1: 40MHz + r3* {.importc: "r3", bitsize: 16.}: cuint ## *< reserve + smoothing* {.importc: "smoothing", bitsize: 1.}: cuint ## *< reserve + not_sounding* {.importc: "not_sounding", bitsize: 1.}: cuint ## *< reserve + r4* {.importc: "r4", bitsize: 1.}: cuint ## *< reserve + aggregation* {.importc: "aggregation", bitsize: 1.}: cuint ## *< Aggregation. 0: MPDU packet; 1: AMPDU packet + stbc* {.importc: "stbc", bitsize: 2.}: cuint ## *< Space Time Block Code(STBC). 0: non STBC packet; 1: STBC packet + fec_coding* {.importc: "fec_coding", bitsize: 1.}: cuint ## *< Flag is set for 11n packets which are LDPC + sgi* {.importc: "sgi", bitsize: 1.}: cuint ## *< Short Guide Interval(SGI). 0: Long GI; 1: Short GI + noise_floor* {.importc: "noise_floor", bitsize: 8.}: cint ## *< noise floor of Radio Frequency Module(RF). unit: 0.25dBm + ampdu_cnt* {.importc: "ampdu_cnt", bitsize: 8.}: cuint ## *< ampdu cnt + channel* {.importc: "channel", bitsize: 4.}: cuint ## *< primary channel on which this packet is received + secondary_channel* {.importc: "secondary_channel", bitsize: 4.}: cuint ## *< secondary channel on which this packet is received. 0: none; 1: above; 2: below + r5* {.importc: "r5", bitsize: 8.}: cuint ## *< reserve + timestamp* {.importc: "timestamp", bitsize: 32.}: cuint ## *< timestamp. The local time when this packet is received. It is precise only if modem sleep or light sleep is not enabled. unit: microsecond + r6* {.importc: "r6", bitsize: 32.}: cuint ## *< reserve + r7* {.importc: "r7", bitsize: 31.}: cuint ## *< reserve + ant* {.importc: "ant", bitsize: 1.}: cuint ## *< antenna number from which this packet is received. 0: WiFi antenna 0; 1: WiFi antenna 1 + sig_len* {.importc: "sig_len", bitsize: 12.}: cuint ## *< length of packet including Frame Check Sequence(FCS) + r8* {.importc: "r8", bitsize: 12.}: cuint ## *< reserve + rx_state* {.importc: "rx_state", bitsize: 8.}: cuint ## *< state of the packet. 0: no error; others: error numbers which are not public + + wifi_promiscuous_pkt_type_t* {.size: sizeof(cint).} = enum + WIFI_PKT_MGMT, ## *< Management frame, indicates 'buf' argument is wifi_promiscuous_pkt_t + WIFI_PKT_CTRL, ## *< Control frame, indicates 'buf' argument is wifi_promiscuous_pkt_t + WIFI_PKT_DATA, ## *< Data frame, indiciates 'buf' argument is wifi_promiscuous_pkt_t + WIFI_PKT_MISC ## *< Other type, such as MIMO etc. 'buf' argument is wifi_promiscuous_pkt_t but the payload is zero length. + + +const + WIFI_PROMIS_FILTER_MASK_ALL* = (0xFFFFFFFF) ## *< filter all packets + WIFI_PROMIS_FILTER_MASK_MGMT* = (1) ## *< filter the packets with type of WIFI_PKT_MGMT + WIFI_PROMIS_FILTER_MASK_CTRL* = (1 shl 1) ## *< filter the packets with type of WIFI_PKT_CTRL + WIFI_PROMIS_FILTER_MASK_DATA* = (1 shl 2) ## *< filter the packets with type of WIFI_PKT_DATA + WIFI_PROMIS_FILTER_MASK_MISC* = (1 shl 3) ## *< filter the packets with type of WIFI_PKT_MISC + WIFI_PROMIS_FILTER_MASK_DATA_MPDU* = (1 shl 4) ## *< filter the MPDU which is a kind of WIFI_PKT_DATA + WIFI_PROMIS_FILTER_MASK_DATA_AMPDU* = (1 shl 5) ## *< filter the AMPDU which is a kind of WIFI_PKT_DATA + WIFI_PROMIS_CTRL_FILTER_MASK_ALL* = (0xFF800000) ## *< filter all control packets + WIFI_PROMIS_CTRL_FILTER_MASK_WRAPPER* = (1 shl 23) ## *< filter the control packets with subtype of Control Wrapper + WIFI_PROMIS_CTRL_FILTER_MASK_BAR* = (1 shl 24) ## *< filter the control packets with subtype of Block Ack Request + WIFI_PROMIS_CTRL_FILTER_MASK_BA* = (1 shl 25) ## *< filter the control packets with subtype of Block Ack + WIFI_PROMIS_CTRL_FILTER_MASK_PSPOLL* = (1 shl 26) ## *< filter the control packets with subtype of PS-Poll + WIFI_PROMIS_CTRL_FILTER_MASK_RTS* = (1 shl 27) ## *< filter the control packets with subtype of RTS + WIFI_PROMIS_CTRL_FILTER_MASK_CTS* = (1 shl 28) ## *< filter the control packets with subtype of CTS + WIFI_PROMIS_CTRL_FILTER_MASK_ACK* = (1 shl 29) ## *< filter the control packets with subtype of ACK + WIFI_PROMIS_CTRL_FILTER_MASK_CFEND* = (1 shl 30) ## *< filter the control packets with subtype of CF-END + WIFI_PROMIS_CTRL_FILTER_MASK_CFENDACK* = (1 shl 31) ## *< filter the control packets with subtype of CF-END+CF-ACK + +## * @brief Mask for filtering different packet types in promiscuous mode. + +type + wifi_promiscuous_filter_t* {.importc: "wifi_promiscuous_filter_t", + header: "esp_wifi_types.h", bycopy.} = object + filter_mask* {.importc: "filter_mask".}: uint32 ## *< OR of one or more filter values WIFI_PROMIS_FILTER_* + + +const + WIFI_EVENT_MASK_ALL* = (0xFFFFFFFF) ## *< mask all WiFi events + WIFI_EVENT_MASK_NONE* = (0) ## *< mask none of the WiFi events + WIFI_EVENT_MASK_AP_PROBEREQRECVED* = (BIT(0)) ## *< mask SYSTEM_EVENT_AP_PROBEREQRECVED event + +## * +## @brief Channel state information(CSI) configuration type +## +## + +type + wifi_csi_config_t* {.importc: "wifi_csi_config_t", header: "esp_wifi_types.h", + bycopy.} = object + lltf_en* {.importc: "lltf_en".}: bool ## *< enable to receive legacy long training field(lltf) data. Default enabled + htltf_en* {.importc: "htltf_en".}: bool ## *< enable to receive HT long training field(htltf) data. Default enabled + stbc_htltf2_en* {.importc: "stbc_htltf2_en".}: bool ## *< enable to receive space time block code HT long training field(stbc-htltf2) data. Default enabled + ltf_merge_en* {.importc: "ltf_merge_en".}: bool ## *< enable to generate htlft data by averaging lltf and ht_ltf data when receiving HT packet. Otherwise, use ht_ltf data directly. Default enabled + channel_filter_en* {.importc: "channel_filter_en".}: bool ## *< enable to turn on channel filter to smooth adjacent sub-carrier. Disable it to keep independence of adjacent sub-carrier. Default enabled + manu_scale* {.importc: "manu_scale".}: bool ## *< manually scale the CSI data by left shifting or automatically scale the CSI data. If set true, please set the shift bits. false: automatically. true: manually. Default false + shift* {.importc: "shift".}: uint8 ## *< manually left shift bits of the scale of the CSI data. The range of the left shift bits is 0~15 + + +## * +## @brief CSI data type +## +## + +type + wifi_csi_info_t* {.importc: "wifi_csi_info_t", header: "esp_wifi_types.h", bycopy.} = object + rx_ctrl* {.importc: "rx_ctrl".}: wifi_pkt_rx_ctrl_t ## *< received packet radio metadata header of the CSI data + mac* {.importc: "mac".}: array[6, uint8] ## *< source MAC address of the CSI data + first_word_invalid* {.importc: "first_word_invalid".}: bool ## *< first four bytes of the CSI data is invalid or not + buf* {.importc: "buf".}: ptr int8 ## *< buffer of CSI data + len* {.importc: "len".}: uint16 ## *< length of CSI data + + +## * +## @brief WiFi GPIO configuration for antenna selection +## +## + +type + wifi_ant_gpio_t* {.importc: "wifi_ant_gpio_t", header: "esp_wifi_types.h", bycopy.} = object + gpio_select* {.importc: "gpio_select", bitsize: 1.}: uint8 ## *< Whether this GPIO is connected to external antenna switch + gpio_num* {.importc: "gpio_num", bitsize: 7.}: uint8 ## *< The GPIO number that connects to external antenna switch + + +## * +## @brief WiFi GPIOs configuration for antenna selection +## +## + +type + wifi_ant_gpio_config_t* {.importc: "wifi_ant_gpio_config_t", + header: "esp_wifi_types.h", bycopy.} = object + gpio_cfg* {.importc: "gpio_cfg".}: array[4, wifi_ant_gpio_t] ## *< The configurations of GPIOs that connect to external antenna switch + + +## * +## @brief WiFi antenna mode +## +## + +type + wifi_ant_mode_t* {.size: sizeof(cint).} = enum + WIFI_ANT_MODE_ANT0, ## *< Enable WiFi antenna 0 only + WIFI_ANT_MODE_ANT1, ## *< Enable WiFi antenna 1 only + WIFI_ANT_MODE_AUTO, ## *< Enable WiFi antenna 0 and 1, automatically select an antenna + WIFI_ANT_MODE_MAX ## *< Invalid WiFi enabled antenna + + +## * +## @brief WiFi antenna configuration +## +## + +type + wifi_ant_config_t* {.importc: "wifi_ant_config_t", header: "esp_wifi_types.h", + bycopy.} = object + rx_ant_mode* {.importc: "rx_ant_mode".}: wifi_ant_mode_t ## *< WiFi antenna mode for receiving + rx_ant_default* {.importc: "rx_ant_default".}: wifi_ant_t ## *< Default antenna mode for receiving, it's ignored if rx_ant_mode is not WIFI_ANT_MODE_AUTO + tx_ant_mode* {.importc: "tx_ant_mode".}: wifi_ant_mode_t ## *< WiFi antenna mode for transmission, it can be set to WIFI_ANT_MODE_AUTO only if rx_ant_mode is set to WIFI_ANT_MODE_AUTO + enabled_ant0* {.importc: "enabled_ant0", bitsize: 4.}: uint8 ## *< Index (in antenna GPIO configuration) of enabled WIFI_ANT_MODE_ANT0 + enabled_ant1* {.importc: "enabled_ant1", bitsize: 4.}: uint8 ## *< Index (in antenna GPIO configuration) of enabled WIFI_ANT_MODE_ANT1 + + +## * +## @brief WiFi PHY rate encodings +## +## + +type + wifi_phy_rate_t* {.size: sizeof(cint).} = enum + WIFI_PHY_RATE_1M_L = 0x00000000, ## *< 1 Mbps with long preamble + WIFI_PHY_RATE_2M_L = 0x00000001, ## *< 2 Mbps with long preamble + WIFI_PHY_RATE_5M_L = 0x00000002, ## *< 5.5 Mbps with long preamble + WIFI_PHY_RATE_11M_L = 0x00000003, ## *< 11 Mbps with long preamble + WIFI_PHY_RATE_2M_S = 0x00000005, ## *< 2 Mbps with short preamble + WIFI_PHY_RATE_5M_S = 0x00000006, ## *< 5.5 Mbps with short preamble + WIFI_PHY_RATE_11M_S = 0x00000007, ## *< 11 Mbps with short preamble + WIFI_PHY_RATE_48M = 0x00000008, ## *< 48 Mbps + WIFI_PHY_RATE_24M = 0x00000009, ## *< 24 Mbps + WIFI_PHY_RATE_12M = 0x0000000A, ## *< 12 Mbps + WIFI_PHY_RATE_6M = 0x0000000B, ## *< 6 Mbps + WIFI_PHY_RATE_54M = 0x0000000C, ## *< 54 Mbps + WIFI_PHY_RATE_36M = 0x0000000D, ## *< 36 Mbps + WIFI_PHY_RATE_18M = 0x0000000E, ## *< 18 Mbps + WIFI_PHY_RATE_9M = 0x0000000F, ## *< 9 Mbps + WIFI_PHY_RATE_MCS0_LGI = 0x00000010, ## *< MCS0 with long GI, 6.5 Mbps for 20MHz, 13.5 Mbps for 40MHz + WIFI_PHY_RATE_MCS1_LGI = 0x00000011, ## *< MCS1 with long GI, 13 Mbps for 20MHz, 27 Mbps for 40MHz + WIFI_PHY_RATE_MCS2_LGI = 0x00000012, ## *< MCS2 with long GI, 19.5 Mbps for 20MHz, 40.5 Mbps for 40MHz + WIFI_PHY_RATE_MCS3_LGI = 0x00000013, ## *< MCS3 with long GI, 26 Mbps for 20MHz, 54 Mbps for 40MHz + WIFI_PHY_RATE_MCS4_LGI = 0x00000014, ## *< MCS4 with long GI, 39 Mbps for 20MHz, 81 Mbps for 40MHz + WIFI_PHY_RATE_MCS5_LGI = 0x00000015, ## *< MCS5 with long GI, 52 Mbps for 20MHz, 108 Mbps for 40MHz + WIFI_PHY_RATE_MCS6_LGI = 0x00000016, ## *< MCS6 with long GI, 58.5 Mbps for 20MHz, 121.5 Mbps for 40MHz + WIFI_PHY_RATE_MCS7_LGI = 0x00000017, ## *< MCS7 with long GI, 65 Mbps for 20MHz, 135 Mbps for 40MHz + WIFI_PHY_RATE_MCS0_SGI = 0x00000018, ## *< MCS0 with short GI, 7.2 Mbps for 20MHz, 15 Mbps for 40MHz + WIFI_PHY_RATE_MCS1_SGI = 0x00000019, ## *< MCS1 with short GI, 14.4 Mbps for 20MHz, 30 Mbps for 40MHz + WIFI_PHY_RATE_MCS2_SGI = 0x0000001A, ## *< MCS2 with short GI, 21.7 Mbps for 20MHz, 45 Mbps for 40MHz + WIFI_PHY_RATE_MCS3_SGI = 0x0000001B, ## *< MCS3 with short GI, 28.9 Mbps for 20MHz, 60 Mbps for 40MHz + WIFI_PHY_RATE_MCS4_SGI = 0x0000001C, ## *< MCS4 with short GI, 43.3 Mbps for 20MHz, 90 Mbps for 40MHz + WIFI_PHY_RATE_MCS5_SGI = 0x0000001D, ## *< MCS5 with short GI, 57.8 Mbps for 20MHz, 120 Mbps for 40MHz + WIFI_PHY_RATE_MCS6_SGI = 0x0000001E, ## *< MCS6 with short GI, 65 Mbps for 20MHz, 135 Mbps for 40MHz + WIFI_PHY_RATE_MCS7_SGI = 0x0000001F, ## *< MCS7 with short GI, 72.2 Mbps for 20MHz, 150 Mbps for 40MHz + WIFI_PHY_RATE_LORA_250K = 0x00000029, ## *< 250 Kbps + WIFI_PHY_RATE_LORA_500K = 0x0000002A, ## *< 500 Kbps + WIFI_PHY_RATE_MAX + + +## * WiFi event declarations + +type + wifi_event_t* {.size: sizeof(cint).} = enum + WIFI_EVENT_WIFI_READY = 0, ## *< ESP32 WiFi ready + WIFI_EVENT_SCAN_DONE, ## *< ESP32 finish scanning AP + WIFI_EVENT_STA_START, ## *< ESP32 station start + WIFI_EVENT_STA_STOP, ## *< ESP32 station stop + WIFI_EVENT_STA_CONNECTED, ## *< ESP32 station connected to AP + WIFI_EVENT_STA_DISCONNECTED, ## *< ESP32 station disconnected from AP + WIFI_EVENT_STA_AUTHMODE_CHANGE, ## *< the auth mode of AP connected by ESP32 station changed + WIFI_EVENT_STA_WPS_ER_SUCCESS, ## *< ESP32 station wps succeeds in enrollee mode + WIFI_EVENT_STA_WPS_ER_FAILED, ## *< ESP32 station wps fails in enrollee mode + WIFI_EVENT_STA_WPS_ER_TIMEOUT, ## *< ESP32 station wps timeout in enrollee mode + WIFI_EVENT_STA_WPS_ER_PIN, ## *< ESP32 station wps pin code in enrollee mode + WIFI_EVENT_STA_WPS_ER_PBC_OVERLAP, ## *< ESP32 station wps overlap in enrollee mode + WIFI_EVENT_AP_START, ## *< ESP32 soft-AP start + WIFI_EVENT_AP_STOP, ## *< ESP32 soft-AP stop + WIFI_EVENT_AP_STACONNECTED, ## *< a station connected to ESP32 soft-AP + WIFI_EVENT_AP_STADISCONNECTED, ## *< a station disconnected from ESP32 soft-AP + WIFI_EVENT_AP_PROBEREQRECVED ## *< Receive probe request packet in soft-AP interface + + +## * @cond * +## * @brief WiFi event base declaration + +# ESP_EVENT_DECLARE_BASE(WIFI_EVENT) +var WIFI_EVENT* {.importc: "WIFI_EVENT", header: "esp_wifi_types.h".}: esp_event_base_t + +## * @endcond * +## * Argument structure for WIFI_EVENT_SCAN_DONE event + +type + wifi_event_sta_scan_done_t* {.importc: "wifi_event_sta_scan_done_t", + header: "esp_wifi_types.h", bycopy.} = object + status* {.importc: "status".}: uint32 ## *< status of scanning APs: 0 — success, 1 - failure + number* {.importc: "number".}: uint8 ## *< number of scan results + scan_id* {.importc: "scan_id".}: uint8 ## *< scan sequence number, used for block scan + + +## * Argument structure for WIFI_EVENT_STA_CONNECTED event + +type + wifi_event_sta_connected_t* {.importc: "wifi_event_sta_connected_t", + header: "esp_wifi_types.h", bycopy.} = object + ssid* {.importc: "ssid".}: array[32, uint8] ## *< SSID of connected AP + ssid_len* {.importc: "ssid_len".}: uint8 ## *< SSID length of connected AP + bssid* {.importc: "bssid".}: array[6, uint8] ## *< BSSID of connected AP + channel* {.importc: "channel".}: uint8 ## *< channel of connected AP + authmode* {.importc: "authmode".}: wifi_auth_mode_t ## *< authentication mode used by AP + + +## * Argument structure for WIFI_EVENT_STA_DISCONNECTED event + +type + wifi_event_sta_disconnected_t* {.importc: "wifi_event_sta_disconnected_t", + header: "esp_wifi_types.h", bycopy.} = object + ssid* {.importc: "ssid".}: array[32, uint8] ## *< SSID of disconnected AP + ssid_len* {.importc: "ssid_len".}: uint8 ## *< SSID length of disconnected AP + bssid* {.importc: "bssid".}: array[6, uint8] ## *< BSSID of disconnected AP + reason* {.importc: "reason".}: uint8 ## *< reason of disconnection + + +## * Argument structure for WIFI_EVENT_STA_AUTHMODE_CHANGE event + +type + wifi_event_sta_authmode_change_t* {.importc: "wifi_event_sta_authmode_change_t", + header: "esp_wifi_types.h", bycopy.} = object + old_mode* {.importc: "old_mode".}: wifi_auth_mode_t ## *< the old auth mode of AP + new_mode* {.importc: "new_mode".}: wifi_auth_mode_t ## *< the new auth mode of AP + + +## * Argument structure for WIFI_EVENT_STA_WPS_ER_PIN event + +type + wifi_event_sta_wps_er_pin_t* {.importc: "wifi_event_sta_wps_er_pin_t", + header: "esp_wifi_types.h", bycopy.} = object + pin_code* {.importc: "pin_code".}: array[8, uint8] ## *< PIN code of station in enrollee mode + + +## * Argument structure for WIFI_EVENT_STA_WPS_ER_FAILED event + +type + wifi_event_sta_wps_fail_reason_t* {.size: sizeof(cint).} = 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 + + +## * Argument structure for WIFI_EVENT_AP_STACONNECTED event + +type + wifi_event_ap_staconnected_t* {.importc: "wifi_event_ap_staconnected_t", + header: "esp_wifi_types.h", bycopy.} = object + mac* {.importc: "mac".}: array[6, uint8] ## *< MAC address of the station connected to ESP32 soft-AP + aid* {.importc: "aid".}: uint8 ## *< the aid that ESP32 soft-AP gives to the station connected to + + +## * Argument structure for WIFI_EVENT_AP_STADISCONNECTED event + +type + wifi_event_ap_stadisconnected_t* {.importc: "wifi_event_ap_stadisconnected_t", + header: "esp_wifi_types.h", bycopy.} = object + mac* {.importc: "mac".}: array[6, uint8] ## *< MAC address of the station disconnects to ESP32 soft-AP + aid* {.importc: "aid".}: uint8 ## *< the aid that ESP32 soft-AP gave to the station disconnects to + + +## * Argument structure for WIFI_EVENT_AP_PROBEREQRECVED event + +type + wifi_event_ap_probe_req_rx_t* {.importc: "wifi_event_ap_probe_req_rx_t", + header: "esp_wifi_types.h", bycopy.} = object + rssi* {.importc: "rssi".}: cint ## *< Received probe request signal strength + mac* {.importc: "mac".}: array[6, uint8] ## *< MAC address of the station which send probe request + diff --git a/src/nesper/esp/net/tcpip_adapter.nim b/src/nesper/esp/net/tcpip_adapter.nim deleted file mode 100644 index 3e46fa8b..00000000 --- a/src/nesper/esp/net/tcpip_adapter.nim +++ /dev/null @@ -1,856 +0,0 @@ -## 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. - -import ../../consts -import ../../net_utils -import esp_wifi_types - -# import - # sys/queue, esp_wifi_types, sdkconfig - -# import - # lwip/ip_addr, dhcpserver/dhcpserver - -# type - # tcpip_adapter_dhcps_lease_t* = dhcps_lease_t - -# template IP2STR*(ipaddr: untyped): untyped = -# ip4_addr1_16(ipaddr) -# ip4_addr2_16(ipaddr) -# ip4_addr3_16(ipaddr) -# ip4_addr4_16(ipaddr) - -# const -# IPSTR* = "%d.%d.%d.%d" -# template IPV62STR*(ipaddr: untyped): untyped = -# IP6_ADDR_BLOCK1(addr((ipaddr))) -# IP6_ADDR_BLOCK2(addr((ipaddr))) -# IP6_ADDR_BLOCK3(addr((ipaddr))) -# IP6_ADDR_BLOCK4(addr((ipaddr))) -# IP6_ADDR_BLOCK5(addr((ipaddr))) -# IP6_ADDR_BLOCK6(addr((ipaddr))) -# IP6_ADDR_BLOCK7(addr((ipaddr))) -# IP6_ADDR_BLOCK8(addr((ipaddr))) - -# const - # IPV6STR* = "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" -## * @brief IPV4 IP address information -## -type - tcpip_adapter_ip_info_t* {.importc: "tcpip_adapter_ip_info_t", - header: "tcpip_adapter.h", bycopy.} = object - ip* {.importc: "ip".}: ip4_addr_t ## *< Interface IPV4 address - netmask* {.importc: "netmask".}: ip4_addr_t ## *< Interface IPV4 netmask - gw* {.importc: "gw".}: ip4_addr_t ## *< Interface IPV4 gateway address - -## * @brief IPV6 IP address information -## -type - tcpip_adapter_ip6_info_t* {.importc: "tcpip_adapter_ip6_info_t", - header: "tcpip_adapter.h", bycopy.} = object - ip* {.importc: "ip".}: ip6_addr_t ## *< Interface IPV6 address - -## * @brief IP address info of station connected to WLAN AP -## -## @note See also wifi_sta_info_t (MAC layer information only) -## -type - tcpip_adapter_sta_info_t* {.importc: "tcpip_adapter_sta_info_t", - header: "tcpip_adapter.h", bycopy.} = object - mac* {.importc: "mac".}: array[6, uint8] ## *< Station MAC address - ip* {.importc: "ip".}: ip4_addr_t ## *< Station assigned IP address - -## * @brief WLAN AP: Connected stations list -## -## Used to retrieve IP address information about connected stations. -## -type - tcpip_adapter_sta_list_t* {.importc: "tcpip_adapter_sta_list_t", - header: "tcpip_adapter.h", bycopy.} = object - sta* {.importc: "sta".}: array[ESP_WIFI_MAX_CONN_NUM, tcpip_adapter_sta_info_t] ## *< Connected stations - num* {.importc: "num".}: cint ## *< Number of connected stations - -const - ESP_ERR_TCPIP_ADAPTER_BASE* = 0x00005000 - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS* = ESP_ERR_TCPIP_ADAPTER_BASE + 0x00000001 - ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY* = ESP_ERR_TCPIP_ADAPTER_BASE + 0x00000002 - ESP_ERR_TCPIP_ADAPTER_DHCPC_START_FAILED* = ESP_ERR_TCPIP_ADAPTER_BASE + - 0x00000003 - ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STARTED* = ESP_ERR_TCPIP_ADAPTER_BASE + - 0x00000004 - ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED* = ESP_ERR_TCPIP_ADAPTER_BASE + - 0x00000005 - ESP_ERR_TCPIP_ADAPTER_NO_MEM* = ESP_ERR_TCPIP_ADAPTER_BASE + 0x00000006 - ESP_ERR_TCPIP_ADAPTER_DHCP_NOT_STOPPED* = ESP_ERR_TCPIP_ADAPTER_BASE + - 0x00000007 - -## @brief On-chip network interfaces - -type - tcpip_adapter_if_t* {.size: sizeof(cint).} = enum - TCPIP_ADAPTER_IF_STA = 0, ## *< Wi-Fi STA (station) interface - TCPIP_ADAPTER_IF_AP, ## *< Wi-Fi soft-AP interface - TCPIP_ADAPTER_IF_ETH, ## *< Ethernet interface - TCPIP_ADAPTER_IF_TEST, ## *< tcpip stack test interface - TCPIP_ADAPTER_IF_MAX - - -## * @brief Type of DNS server - -type - tcpip_adapter_dns_type_t* {.size: sizeof(cint).} = enum - TCPIP_ADAPTER_DNS_MAIN = 0, ## *< DNS main server address - TCPIP_ADAPTER_DNS_BACKUP, ## *< DNS backup server address (Wi-Fi STA and Ethernet only) - TCPIP_ADAPTER_DNS_FALLBACK, ## *< DNS fallback server address (Wi-Fi STA and Ethernet only) - TCPIP_ADAPTER_DNS_MAX - - -## * @brief DNS server info - -type - tcpip_adapter_dns_info_t* {.importc: "tcpip_adapter_dns_info_t", - header: "tcpip_adapter.h", bycopy.} = object - ip* {.importc: "ip".}: ip_addr_t ## *< IPV4 address of DNS server - - -## * @brief Status of DHCP client or DHCP server - -type - tcpip_adapter_dhcp_status_t* {.size: sizeof(cint).} = enum - TCPIP_ADAPTER_DHCP_INIT = 0, ## *< DHCP client/server is in initial state (not yet started) - TCPIP_ADAPTER_DHCP_STARTED, ## *< DHCP client/server has been started - TCPIP_ADAPTER_DHCP_STOPPED, ## *< DHCP client/server has been stopped - TCPIP_ADAPTER_DHCP_STATUS_MAX - - -## * @brief Mode for DHCP client or DHCP server option functions - -type - tcpip_adapter_dhcp_option_mode_t* {.size: sizeof(cint).} = enum - TCPIP_ADAPTER_OP_START = 0, TCPIP_ADAPTER_OP_SET, ## *< Set option - TCPIP_ADAPTER_OP_GET, ## *< Get option - TCPIP_ADAPTER_OP_MAX - - -## Deprecated name for tcpip_adapter_dhcp_option_mode_t, to remove after ESP-IDF V4.0 - -type - tcpip_adapter_option_mode_t* = tcpip_adapter_dhcp_option_mode_t - -## * @brief Supported options for DHCP client or DHCP server - -type - tcpip_adapter_dhcp_option_id_t* {.size: sizeof(cint).} = enum - TCPIP_ADAPTER_DOMAIN_NAME_SERVER = 6, ## *< Domain name server - TCPIP_ADAPTER_ROUTER_SOLICITATION_ADDRESS = 32, ## *< Solicitation router address - TCPIP_ADAPTER_REQUESTED_IP_ADDRESS = 50, ## *< Request specific IP address - TCPIP_ADAPTER_IP_ADDRESS_LEASE_TIME = 51, ## *< Request IP address lease time - TCPIP_ADAPTER_IP_REQUEST_RETRY_TIME = 52 ## *< Request IP address retry counter - - -## Deprecated name for tcpip_adapter_dhcp_option_id_t, to remove after ESP-IDF V4.0 - -type - tcpip_adapter_option_id_t* = tcpip_adapter_dhcp_option_id_t - -## * IP event declarations - -type - ip_event_t* {.size: sizeof(cint).} = enum - IP_EVENT_STA_GOT_IP, ## !< ESP32 station got IP from connected AP - IP_EVENT_STA_LOST_IP, ## !< ESP32 station lost IP and the IP is reset to 0 - IP_EVENT_AP_STAIPASSIGNED, ## !< ESP32 soft-AP assign an IP to a connected station - IP_EVENT_GOT_IP6, ## !< ESP32 station or ap or ethernet interface v6IP addr is preferred - IP_EVENT_ETH_GOT_IP ## !< ESP32 ethernet got IP from connected AP - - -## * @brief IP event base declaration - -# ESP_EVENT_DECLARE_BASE(IP_EVENT) -var IP_EVENT* {.importc: "IP_EVENT", header: "tcpip_adapter.h".}: esp_event_base_t -## * Event structure for IP_EVENT_STA_GOT_IP, IP_EVENT_ETH_GOT_IP events - -type - ip_event_got_ip_t* {.importc: "ip_event_got_ip_t", header: "tcpip_adapter.h", - bycopy.} = object - if_index* {.importc: "if_index".}: tcpip_adapter_if_t ## !< Interface for which the event is received - ip_info* {.importc: "ip_info".}: tcpip_adapter_ip_info_t ## !< IP address, netmask, gatway IP address - ip_changed* {.importc: "ip_changed".}: bool ## !< Whether the assigned IP has changed or not - - -## * Event structure for IP_EVENT_GOT_IP6 event - -type - ip_event_got_ip6_t* {.importc: "ip_event_got_ip6_t", header: "tcpip_adapter.h", - bycopy.} = object - if_index* {.importc: "if_index".}: tcpip_adapter_if_t ## !< Interface for which the event is received - ip6_info* {.importc: "ip6_info".}: tcpip_adapter_ip6_info_t ## !< IPv6 address of the interface - - -## * Event structure for IP_EVENT_AP_STAIPASSIGNED event - -type - ip_event_ap_staipassigned_t* {.importc: "ip_event_ap_staipassigned_t", - header: "tcpip_adapter.h", bycopy.} = object - ip* {.importc: "ip".}: ip4_addr_t ## !< IP address which was assigned to the station - - -## * -## @brief Initialize the underlying TCP/IP stack -## -## @note This function should be called exactly once from application code, when the application starts up. -## - -proc tcpip_adapter_init*() {.importc: "tcpip_adapter_init", - header: "tcpip_adapter.h".} -## * -## @brief Cause the TCP/IP stack to start the Ethernet interface with specified MAC and IP -## -## @note This function should be called after the Ethernet MAC hardware is initialized. In the default configuration, application code does not need to call this function - it is called automatically by the default handler for the SYSTEM_EVENT_ETH_START event. -## -## @param[in] mac Set MAC address of this interface -## @param[in] ip_info Set IP address of this interface -## @param[in] args extra args passed to tcpip_adapter -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - ESP_ERR_NO_MEM -## - -proc tcpip_adapter_eth_start*(mac: ptr uint8; - ip_info: ptr tcpip_adapter_ip_info_t; args: pointer): esp_err_t {. - importc: "tcpip_adapter_eth_start", header: "tcpip_adapter.h".} -## * -## @brief Cause the TCP/IP stack to start the Wi-Fi station interface with specified MAC and IP -## -## -## @note This function should be called after the Wi-Fi Station hardware is initialized. In the default configuration, application code does not need to call this function - it is called automatically by the default handler for the SYSTEM_EVENT_STA_START event. -## -## @param[in] mac Set MAC address of this interface -## @param[in] ip_info Set IP address of this interface -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - ESP_ERR_NO_MEM -## - -proc tcpip_adapter_sta_start*(mac: ptr uint8; ip_info: ptr tcpip_adapter_ip_info_t): esp_err_t {. - importc: "tcpip_adapter_sta_start", header: "tcpip_adapter.h".} -## * -## @brief Cause the TCP/IP stack to start the Wi-Fi AP interface with specified MAC and IP -## -## @note This function should be called after the Wi-Fi AP hardware is initialized. In the default configuration, application code does not need to call this function - it is called automatically by the default handler for the SYSTEM_EVENT_AP_START event. -## -## DHCP server will be started automatically when this function is called. -## -## @param[in] mac Set MAC address of this interface -## @param[in] ip_info Set IP address of this interface -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - ESP_ERR_NO_MEM -## - -proc tcpip_adapter_ap_start*(mac: ptr uint8; ip_info: ptr tcpip_adapter_ip_info_t): esp_err_t {. - importc: "tcpip_adapter_ap_start", header: "tcpip_adapter.h".} -## * -## @brief Cause the TCP/IP stack to stop a network interface -## -## Causes TCP/IP stack to clean up this interface. This includes stopping the DHCP server or client, if they are started. -## -## @note This API is called by the default Wi-Fi and Ethernet event handlers if the underlying network driver reports that the -## interface has stopped. -## -## @note To stop an interface from application code, call the network-specific API (esp_wifi_stop() or esp_eth_stop()). -## The driver layer will then send a stop event and the event handler should call this API. -## Otherwise, the driver and MAC layer will remain started. -## -## @param[in] tcpip_if Interface which will be stopped -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY -## - -proc tcpip_adapter_stop*(tcpip_if: tcpip_adapter_if_t): esp_err_t {. - importc: "tcpip_adapter_stop", header: "tcpip_adapter.h".} -## * -## @brief Cause the TCP/IP stack to bring up an interface -## -## @note This function is called automatically by the default event handlers for the Wi-Fi Station and Ethernet interfaces, -## in response to the SYSTEM_EVENT_STA_CONNECTED and SYSTEM_EVENT_ETH_CONNECTED events, respectively. -## -## @note This function is not normally used with Wi-Fi AP interface. If the AP interface is started, it is up. -## -## @param[in] tcpip_if Interface to bring up -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY -## - -proc tcpip_adapter_up*(tcpip_if: tcpip_adapter_if_t): esp_err_t {. - importc: "tcpip_adapter_up", header: "tcpip_adapter.h".} -## * -## @brief Cause the TCP/IP stack to shutdown an interface -## -## @note This function is called automatically by the default event handlers for the Wi-Fi Station and Ethernet interfaces, -## in response to the SYSTEM_EVENT_STA_DISCONNECTED and SYSTEM_EVENT_ETH_DISCONNECTED events, respectively. -## -## @note This function is not normally used with Wi-Fi AP interface. If the AP interface is stopped, it is down. -## -## @param[in] tcpip_if Interface to shutdown -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY -## - -proc tcpip_adapter_down*(tcpip_if: tcpip_adapter_if_t): esp_err_t {. - importc: "tcpip_adapter_down", header: "tcpip_adapter.h".} -## * -## @brief Get interface's IP address information -## -## If the interface is up, IP information is read directly from the TCP/IP stack. -## -## If the interface is down, IP information is read from a copy kept in the TCP/IP adapter -## library itself. -## -## @param[in] tcpip_if Interface to get IP information -## @param[out] ip_info If successful, IP information will be returned in this argument. -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - -proc tcpip_adapter_get_ip_info*(tcpip_if: tcpip_adapter_if_t; - ip_info: ptr tcpip_adapter_ip_info_t): esp_err_t {. - importc: "tcpip_adapter_get_ip_info", header: "tcpip_adapter.h".} -## * -## @brief Set interface's IP address information -## -## This function is mainly used to set a static IP on an interface. -## -## If the interface is up, the new IP information is set directly in the TCP/IP stack. -## -## The copy of IP information kept in the TCP/IP adapter library is also updated (this -## copy is returned if the IP is queried while the interface is still down.) -## -## @note DHCP client/server must be stopped before setting new IP information. -## -## @note Calling this interface for the Wi-Fi STA or Ethernet interfaces may generate a -## SYSTEM_EVENT_STA_GOT_IP or SYSTEM_EVENT_ETH_GOT_IP event. -## -## @param[in] tcpip_if Interface to set IP information -## @param[in] ip_info IP information to set on the specified interface -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - ESP_ERR_TCPIP_ADAPTER_DHCP_NOT_STOPPED If DHCP server or client is still running -## - -proc tcpip_adapter_set_ip_info*(tcpip_if: tcpip_adapter_if_t; - ip_info: ptr tcpip_adapter_ip_info_t): esp_err_t {. - importc: "tcpip_adapter_set_ip_info", header: "tcpip_adapter.h".} -## * -## @brief Set DNS Server information -## -## This function behaves differently for different interfaces: -## -## - For Wi-Fi Station interface and Ethernet interface, up to three types of DNS server can be set (in order of priority): -## - Main DNS Server (TCPIP_ADAPTER_DNS_MAIN) -## - Backup DNS Server (TCPIP_ADAPTER_DNS_BACKUP) -## - Fallback DNS Server (TCPIP_ADAPTER_DNS_FALLBACK) -## -## If DHCP client is enabled, main and backup DNS servers will be updated automatically from the DHCP lease if the relevant DHCP options are set. Fallback DNS Server is never updated from the DHCP lease and is designed to be set via this API. -## -## If DHCP client is disabled, all DNS server types can be set via this API only. -## -## - For Wi-Fi AP interface, the Main DNS Server setting is used by the DHCP server to provide a DNS Server option to DHCP clients (Wi-Fi stations). -## - The default Main DNS server is the IP of the Wi-Fi AP interface itself. -## - This function can override it by setting server type TCPIP_ADAPTER_DNS_MAIN. -## - Other DNS Server types are not supported for the Wi-Fi AP interface. -## -## @param[in] tcpip_if Interface to set DNS Server information -## @param[in] type Type of DNS Server to set: TCPIP_ADAPTER_DNS_MAIN, TCPIP_ADAPTER_DNS_BACKUP, TCPIP_ADAPTER_DNS_FALLBACK -## @param[in] dns DNS Server address to set -## -## @return -## - ESP_OK on success -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS invalid params -## - -proc tcpip_adapter_set_dns_info*(tcpip_if: tcpip_adapter_if_t; - `type`: tcpip_adapter_dns_type_t; - dns: ptr tcpip_adapter_dns_info_t): esp_err_t {. - importc: "tcpip_adapter_set_dns_info", header: "tcpip_adapter.h".} -## * -## @brief Get DNS Server information -## -## Return the currently configured DNS Server address for the specified interface and Server type. -## -## This may be result of a previous call to tcpip_adapter_set_dns_info(). If the interface's DHCP client is enabled, -## the Main or Backup DNS Server may be set by the current DHCP lease. -## -## @param[in] tcpip_if Interface to get DNS Server information -## @param[in] type Type of DNS Server to get: TCPIP_ADAPTER_DNS_MAIN, TCPIP_ADAPTER_DNS_BACKUP, TCPIP_ADAPTER_DNS_FALLBACK -## @param[out] dns DNS Server result is written here on success -## -## @return -## - ESP_OK on success -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS invalid params -## - -proc tcpip_adapter_get_dns_info*(tcpip_if: tcpip_adapter_if_t; - `type`: tcpip_adapter_dns_type_t; - dns: ptr tcpip_adapter_dns_info_t): esp_err_t {. - importc: "tcpip_adapter_get_dns_info", header: "tcpip_adapter.h".} -## * -## @brief Get interface's old IP information -## -## Returns an "old" IP address previously stored for the interface when the valid IP changed. -## -## If the IP lost timer has expired (meaning the interface was down for longer than the configured interval) -## then the old IP information will be zero. -## -## @param[in] tcpip_if Interface to get old IP information -## @param[out] ip_info If successful, IP information will be returned in this argument. -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - -proc tcpip_adapter_get_old_ip_info*(tcpip_if: tcpip_adapter_if_t; - ip_info: ptr tcpip_adapter_ip_info_t): esp_err_t {. - importc: "tcpip_adapter_get_old_ip_info", header: "tcpip_adapter.h".} -## * -## @brief Set interface old IP information -## -## This function is called from the DHCP client for the Wi-Fi STA and Ethernet interfaces, before a new IP is set. It is also called from the default handlers for the SYSTEM_EVENT_STA_CONNECTED and SYSTEM_EVENT_ETH_CONNECTED events. -## -## Calling this function stores the previously configured IP, which can be used to determine if the IP changes in the future. -## -## If the interface is disconnected or down for too long, the "IP lost timer" will expire (after the configured interval) and set the old IP information to zero. -## -## @param[in] tcpip_if Interface to set old IP information -## @param[in] ip_info Store the old IP information for the specified interface -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - -proc tcpip_adapter_set_old_ip_info*(tcpip_if: tcpip_adapter_if_t; - ip_info: ptr tcpip_adapter_ip_info_t): esp_err_t {. - importc: "tcpip_adapter_set_old_ip_info", header: "tcpip_adapter.h".} -## * -## @brief Create interface link-local IPv6 address -## -## Cause the TCP/IP stack to create a link-local IPv6 address for the specified interface. -## -## This function also registers a callback for the specified interface, so that if the link-local address becomes verified as the preferred address then a SYSTEM_EVENT_GOT_IP6 event will be sent. -## -## @param[in] tcpip_if Interface to create a link-local IPv6 address -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - -proc tcpip_adapter_create_ip6_linklocal*(tcpip_if: tcpip_adapter_if_t): esp_err_t {. - importc: "tcpip_adapter_create_ip6_linklocal", header: "tcpip_adapter.h".} -## * -## @brief Get interface link-local IPv6 address -## -## If the specified interface is up and a preferred link-local IPv6 address -## has been created for the interface, return a copy of it. -## -## @param[in] tcpip_if Interface to get link-local IPv6 address -## @param[out] if_ip6 IPv6 information will be returned in this argument if successful. -## -## @return -## - ESP_OK -## - ESP_FAIL If interface is down, does not have a link-local IPv6 address, or the link-local IPv6 address is not a preferred address. -## - -proc tcpip_adapter_get_ip6_linklocal*(tcpip_if: tcpip_adapter_if_t; - if_ip6: ptr ip6_addr_t): esp_err_t {. - importc: "tcpip_adapter_get_ip6_linklocal", header: "tcpip_adapter.h".} -## * -## @brief Get interface global IPv6 address -## -## If the specified interface is up and a preferred global IPv6 address -## has been created for the interface, return a copy of it. -## -## @param[in] tcpip_if Interface to get global IPv6 address -## @param[out] if_ip6 IPv6 information will be returned in this argument if successful. -## -## @return -## - ESP_OK -## - ESP_FAIL If interface is down, does not have a global IPv6 address, or the global IPv6 address is not a preferred address. -## - -proc tcpip_adapter_get_ip6_global*(tcpip_if: tcpip_adapter_if_t; - if_ip6: ptr ip6_addr_t): esp_err_t {. - importc: "tcpip_adapter_get_ip6_global", header: "tcpip_adapter.h".} -proc tcpip_adapter_get_mac*(tcpip_if: tcpip_adapter_if_t; mac: ptr uint8): esp_err_t {. - importc: "tcpip_adapter_get_mac", header: "tcpip_adapter.h".} -proc tcpip_adapter_set_mac*(tcpip_if: tcpip_adapter_if_t; mac: ptr uint8): esp_err_t {. - importc: "tcpip_adapter_set_mac", header: "tcpip_adapter.h".} - -## * -## @brief Get DHCP Server status -## -## @param[in] tcpip_if Interface to get status of DHCP server. -## @param[out] status If successful, the status of the DHCP server will be returned in this argument. -## -## @return -## - ESP_OK -## - -proc tcpip_adapter_dhcps_get_status*(tcpip_if: tcpip_adapter_if_t; - status: ptr tcpip_adapter_dhcp_status_t): esp_err_t {. - importc: "tcpip_adapter_dhcps_get_status", header: "tcpip_adapter.h".} -## * -## @brief Set or Get DHCP server option -## -## @param[in] opt_op TCPIP_ADAPTER_OP_SET to set an option, TCPIP_ADAPTER_OP_GET to get an option. -## @param[in] opt_id Option index to get or set, must be one of the supported enum values. -## @param[inout] opt_val Pointer to the option parameter. -## @param[in] opt_len Length of the option parameter. -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED -## - ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STARTED -## - -proc tcpip_adapter_dhcps_option*(opt_op: tcpip_adapter_dhcp_option_mode_t; - opt_id: tcpip_adapter_dhcp_option_id_t; - opt_val: pointer; opt_len: uint32): esp_err_t {. - importc: "tcpip_adapter_dhcps_option", header: "tcpip_adapter.h".} -## * -## @brief Start DHCP server -## -## @note Currently DHCP server is only supported on the Wi-Fi AP interface. -## -## @param[in] tcpip_if Interface to start DHCP server. Must be TCPIP_ADAPTER_IF_AP. -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STARTED -## - -proc tcpip_adapter_dhcps_start*(tcpip_if: tcpip_adapter_if_t): esp_err_t {. - importc: "tcpip_adapter_dhcps_start", header: "tcpip_adapter.h".} -## * -## @brief Stop DHCP server -## -## @note Currently DHCP server is only supported on the Wi-Fi AP interface. -## -## @param[in] tcpip_if Interface to stop DHCP server. Must be TCPIP_ADAPTER_IF_AP. -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED -## - ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY -## - -proc tcpip_adapter_dhcps_stop*(tcpip_if: tcpip_adapter_if_t): esp_err_t {. - importc: "tcpip_adapter_dhcps_stop", header: "tcpip_adapter.h".} -## * -## @brief Get DHCP client status -## -## @param[in] tcpip_if Interface to get status of DHCP client -## @param[out] status If successful, the status of DHCP client will be returned in this argument. -## -## @return -## - ESP_OK -## - -proc tcpip_adapter_dhcpc_get_status*(tcpip_if: tcpip_adapter_if_t; - status: ptr tcpip_adapter_dhcp_status_t): esp_err_t {. - importc: "tcpip_adapter_dhcpc_get_status", header: "tcpip_adapter.h".} -## * -## @brief Set or Get DHCP client's option -## -## @note This function is not yet implemented -## -## @param[in] opt_op TCPIP_ADAPTER_OP_SET to set an option, TCPIP_ADAPTER_OP_GET to get an option. -## @param[in] opt_id Option index to get or set, must be one of the supported enum values. -## @param[inout] opt_val Pointer to the option parameter. -## @param[in] opt_len Length of the option parameter. -## -## @return -## - ESP_ERR_NOT_SUPPORTED (not implemented) -## - -proc tcpip_adapter_dhcpc_option*(opt_op: tcpip_adapter_dhcp_option_mode_t; - opt_id: tcpip_adapter_dhcp_option_id_t; - opt_val: pointer; opt_len: uint32): esp_err_t {. - importc: "tcpip_adapter_dhcpc_option", header: "tcpip_adapter.h".} -## * -## @brief Start DHCP client -## -## @note DHCP Client is only supported for the Wi-Fi station and Ethernet interfaces. -## -## @note The default event handlers for the SYSTEM_EVENT_STA_CONNECTED and SYSTEM_EVENT_ETH_CONNECTED events call this function. -## -## @param[in] tcpip_if Interface to start the DHCP client -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STARTED -## - ESP_ERR_TCPIP_ADAPTER_DHCPC_START_FAILED -## - -proc tcpip_adapter_dhcpc_start*(tcpip_if: tcpip_adapter_if_t): esp_err_t {. - importc: "tcpip_adapter_dhcpc_start", header: "tcpip_adapter.h".} -## * -## @brief Stop DHCP client -## -## @note DHCP Client is only supported for the Wi-Fi station and Ethernet interfaces. -## -## @note Calling tcpip_adapter_stop() or tcpip_adapter_down() will also stop the DHCP Client if it is running. -## -## @param[in] tcpip_if Interface to stop the DHCP client -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED -## - ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY -## - -proc tcpip_adapter_dhcpc_stop*(tcpip_if: tcpip_adapter_if_t): esp_err_t {. - importc: "tcpip_adapter_dhcpc_stop", header: "tcpip_adapter.h".} -## * -## @brief Receive an Ethernet frame from the Ethernet interface -## -## This function will automatically be installed by esp_eth_init(). The Ethernet driver layer will then call this function to forward frames to the TCP/IP stack. -## -## @note Application code does not usually need to use this function directly. -## -## @param[in] buffer Received data -## @param[in] len Length of the data frame -## @param[in] eb Pointer to internal Wi-Fi buffer (ignored for Ethernet) -## -## @return -## - ESP_OK -## - -proc tcpip_adapter_eth_input*(buffer: pointer; len: uint16; eb: pointer): esp_err_t {. - importc: "tcpip_adapter_eth_input", header: "tcpip_adapter.h".} -## * -## @brief Receive an 802.11 data frame from the Wi-Fi Station interface -## -## This function should be installed by calling esp_wifi_reg_rxcb(). The Wi-Fi driver layer will then call this function to forward frames to the TCP/IP stack. -## -## @note Installation happens automatically in the default handler for the SYSTEM_EVENT_STA_CONNECTED event. -## -## @note Application code does not usually need to call this function directly. -## -## @param[in] buffer Received data -## @param[in] len Length of the data frame -## @param[in] eb Pointer to internal Wi-Fi buffer -## -## @return -## - ESP_OK -## - -proc tcpip_adapter_sta_input*(buffer: pointer; len: uint16; eb: pointer): esp_err_t {. - importc: "tcpip_adapter_sta_input", header: "tcpip_adapter.h".} -## * -## @brief Receive an 802.11 data frame from the Wi-Fi AP interface -## -## This function should be installed by calling esp_wifi_reg_rxcb(). The Wi-Fi driver layer will then call this function to forward frames to the TCP/IP stack. -## -## @note Installation happens automatically in the default handler for the SYSTEM_EVENT_AP_START event. -## -## @note Application code does not usually need to call this function directly. -## -## @param[in] buffer Received data -## @param[in] len Length of the data frame -## @param[in] eb Pointer to internal Wi-Fi buffer -## -## @return -## - ESP_OK -## - -proc tcpip_adapter_ap_input*(buffer: pointer; len: uint16; eb: pointer): esp_err_t {. - importc: "tcpip_adapter_ap_input", header: "tcpip_adapter.h".} -## * -## @brief Get network interface index -## -## Get network interface from TCP/IP implementation-specific interface pointer. -## -## @param[in] dev Implementation-specific TCP/IP stack interface pointer. -## -## @return -## - ESP_IF_WIFI_STA -## - ESP_IF_WIFI_AP -## - ESP_IF_ETH -## - ESP_IF_MAX - invalid parameter -## - -proc tcpip_adapter_get_esp_if*(dev: pointer): esp_interface_t {. - importc: "tcpip_adapter_get_esp_if", header: "tcpip_adapter.h".} -## * -## @brief Get IP information for stations connected to the Wi-Fi AP interface -## -## @param[in] wifi_sta_list Wi-Fi station info list, returned from esp_wifi_ap_get_sta_list() -## @param[out] tcpip_sta_list IP layer station info list, corresponding to MAC addresses provided in wifi_sta_list -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_NO_MEM -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - -proc tcpip_adapter_get_sta_list*(wifi_sta_list: ptr wifi_sta_list_t; - tcpip_sta_list: ptr tcpip_adapter_sta_list_t): esp_err_t {. - importc: "tcpip_adapter_get_sta_list", header: "tcpip_adapter.h".} -const - TCPIP_HOSTNAME_MAX_SIZE* = 32 - -## * -## @brief Set the hostname of an interface -## -## @param[in] tcpip_if Interface to set the hostname -## @param[in] hostname New hostname for the interface. Maximum length 32 bytes. -## -## @return -## - ESP_OK - success -## - ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY - interface status error -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS - parameter error -## - -proc tcpip_adapter_set_hostname*(tcpip_if: tcpip_adapter_if_t; hostname: cstring): esp_err_t {. - importc: "tcpip_adapter_set_hostname", header: "tcpip_adapter.h".} -## * -## @brief Get interface hostname. -## -## @param[in] tcpip_if Interface to get the hostname -## @param[out] hostname Returns a pointer to the hostname. May be NULL if no hostname is set. If set non-NULL, pointer remains valid (and string may change if the hostname changes). -## -## @return -## - ESP_OK - success -## - ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY - interface status error -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS - parameter error -## - -proc tcpip_adapter_get_hostname*(tcpip_if: tcpip_adapter_if_t; - hostname: cstringArray): esp_err_t {. - importc: "tcpip_adapter_get_hostname", header: "tcpip_adapter.h".} -## * -## @brief Get the TCP/IP stack-specific interface that is assigned to a given interface -## -## @note For lwIP, this returns a pointer to a netif structure. -## -## @param[in] tcpip_if Interface to get the implementation-specific interface -## @param[out] netif Pointer to the implementation-specific interface -## -## @return -## - ESP_OK - success -## - ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY - interface status error -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS - parameter error -## - -proc tcpip_adapter_get_netif*(tcpip_if: tcpip_adapter_if_t; netif: ptr pointer): esp_err_t {. - importc: "tcpip_adapter_get_netif", header: "tcpip_adapter.h".} -## * -## @brief Test if supplied interface is up or down -## -## @param[in] tcpip_if Interface to test up/down status -## -## @return -## - true - Interface is up -## - false - Interface is down -## - -proc tcpip_adapter_is_netif_up*(tcpip_if: tcpip_adapter_if_t): bool {. - importc: "tcpip_adapter_is_netif_up", header: "tcpip_adapter.h".} -## * -## @brief Cause the TCP/IP stack to start the test interface with specified MAC and IP. -## Test interface is used to exercise network stack with injected packets from SW. -## -## @param[in] mac Set MAC address of this interface -## @param[in] ip_info Set IP address of this interface -## -## @return -## - ESP_OK -## - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS -## - ESP_ERR_NO_MEM -## - -proc tcpip_adapter_test_start*(mac: ptr uint8; - ip_info: ptr tcpip_adapter_ip_info_t): esp_err_t {. - importc: "tcpip_adapter_test_start", header: "tcpip_adapter.h".} -## * -## @brief Install default event handlers for Ethernet interface -## @return -## - ESP_OK on success -## - one of the errors from esp_event on failure -## - -proc tcpip_adapter_set_default_eth_handlers*(): esp_err_t {. - importc: "tcpip_adapter_set_default_eth_handlers", header: "tcpip_adapter.h".} -## * -## @brief Uninstall default event handlers for Ethernet interface -## @return -## - ESP_OK on success -## - one of the errors from esp_event on failure -## - -proc tcpip_adapter_clear_default_eth_handlers*(): esp_err_t {. - importc: "tcpip_adapter_clear_default_eth_handlers", header: "tcpip_adapter.h".} -## * -## @brief Install default event handlers for Wi-Fi interfaces (station and AP) -## @return -## - ESP_OK on success -## - one of the errors from esp_event on failure -## - -proc tcpip_adapter_set_default_wifi_handlers*(): esp_err_t {. - importc: "tcpip_adapter_set_default_wifi_handlers", header: "tcpip_adapter.h".} -## * -## @brief Uninstall default event handlers for Wi-Fi interfaces (station and AP) -## @return -## - ESP_OK on success -## - one of the errors from esp_event on failure -## - -proc tcpip_adapter_clear_default_wifi_handlers*(): esp_err_t {. - importc: "tcpip_adapter_clear_default_wifi_handlers", - header: "tcpip_adapter.h".} -## * -## @brief Search nefit index through netif interface -## @param[in] tcpip_if Interface to search for netif index -## @return -## - netif_index on success -## - -1 if an invalid parameter is supplied -## - -proc tcpip_adapter_get_netif_index*(tcpip_if: tcpip_adapter_if_t): cint {. - importc: "tcpip_adapter_get_netif_index", header: "tcpip_adapter.h".} \ No newline at end of file diff --git a/src/nesper/events.nim b/src/nesper/events.nim index d4c5cbdb..a9ba4f27 100644 --- a/src/nesper/events.nim +++ b/src/nesper/events.nim @@ -21,60 +21,64 @@ type EventError* = object of OSError code*: esp_err_t -template eventRegister*[EVT]( - evt_id: EVT; - evt_handler: esp_event_handler_t; - evt_handler_arg: pointer = nil, +type + ## *< a number that identifies an event with respect to a base + EventHandlerCb*[T] = proc (event_handler_arg: T; + event_base: esp_event_base_t; + event_id: int32; + event_data: pointer) {.cdecl.} + EventHandlerInstance* = ptr esp_event_handler_instance_t + +proc eventRegister*[E; T]( + evt_base: esp_event_base_t, + evt_id: E, + evt_handler: EventHandlerCb[T], + evt_handler_arg: T = nil, + instance: EventHandlerInstance = nil ) = ## Register event with the default event loop. Understand WIFI & IP Events - let evt_base = - when typeof(EVT) is wifi_event_t: - WIFI_EVENT - elif typeof(EVT) is ip_event_t: - IP_EVENT - elif typeof(EVT) is eth_event_t: - ETH_EVENT - else: - {.fatal: "Uknown event type, don't know how to unregister automatically".} - + var handler: esp_event_handler_t = cast[esp_event_handler_t](evt_handler) let ret = - esp_event_handler_register( + esp_event_handler_instance_register( event_base = evt_base, - event_id = cint(evt_id), - event_handler = evt_handler, - event_handler_arg = evt_handler_arg) - - if ret != ESP_OK: - raise newEspError[EventError]("register: " & $esp_err_to_name(ret), ret) - -template eventRegister*[EVT]( - event_base: esp_event_base_t; - event_id: EVT; - event_handler: esp_event_handler_t; - event_handler_arg: pointer = nil - ) = - ## Register event for an event base on the default loop. - let ret = esp_event_handler_register( - event_base, - event_id.cint, - event_handler, - event_handler_arg) + event_id = int32(evt_id), + event_handler = handler, + event_handler_arg = cast[pointer](evt_handler_arg), + instance = instance) if ret != ESP_OK: raise newEspError[EventError]("register: " & $esp_err_to_name(ret), ret) -template eventRegisterWith*[EVT, TP]( +# template eventRegister*[E; T: pointer]( +# event_base: esp_event_base_t; +# event_id: E; +# event_handler: EventHandlerCb[T]; +# event_handler_arg: T = nil, +# instance: ptr esp_event_handler_instance_t = nil +# ) = +# ## Register event for an event base on the default loop. +# let handler: esp_event_handler_t = cast[esp_event_handler_t](event_handler) +# let ret = esp_event_handler_instance_register( +# event_base, +# event_id.cint, +# handler, +# event_handler_arg, +# instance = instance) +# if ret != ESP_OK: +# raise newEspError[EventError]("register: " & $esp_err_to_name(ret), ret) + +template eventRegisterWith*[E; TP]( event_loop: esp_event_loop_handle_t; event_base: esp_event_base_t; - event_id: EVT; + event_id: E; event_handler: TP; event_handler_arg: pointer = nil ) = ## Register event with a given event loop. let ret = esp_event_handler_register_with( event_loop, - event_base, cint(event_id), + event_base, int32(event_id), cast[esp_event_handler_t](event_handler), event_handler_arg) if ret != ESP_OK: @@ -89,7 +93,7 @@ template eventPost*[EVT]( ticks_to_wait: TickType_t = 1000 ) = ## Register event with a given event loop. - let ret = esp_event_post_to(evt_loop, evt_base, cint(evt_id), evt_data, csize_t(evt_data_size), ticks_to_wait) + let ret = esp_event_post_to(evt_loop, evt_base, int32(evt_id), evt_data, csize_t(evt_data_size), ticks_to_wait) if ret != ESP_OK: raise newEspError[EventError]("post: " & $esp_err_to_name(ret), ret) @@ -128,7 +132,7 @@ template eventUnregister*[EVT]( let ret = esp_event_handler_unregister( event_base = evt_base, - event_id = cint(evt_id), + event_id = int32(evt_id), event_handler = evt_handler) if ret != ESP_OK: @@ -144,7 +148,7 @@ template eventUnregister*( let ret = esp_event_handler_unregister( event_base = evt_base, event_base = IP_EVENT, - event_id = cint(evt_id), + event_id = int32(evt_id), event_handler = evt_handler) if ret != ESP_OK: diff --git a/src/nesper/general.nim b/src/nesper/general.nim index 3c6e0cdf..2d05f360 100644 --- a/src/nesper/general.nim +++ b/src/nesper/general.nim @@ -30,9 +30,6 @@ type proc esp_restart*() {.cdecl, importc: "esp_restart".} -proc vTaskDelete*( handle: any ) - {.cdecl, importc: "vTaskDelete", header: "".} - proc esp_err_to_name*(code: esp_err_t): cstring {.cdecl, importc: "esp_err_to_name", header: "freertos/FreeRTOS.h".} proc esp_err_to_name_r*(code: esp_err_t; buf: cstring; buflen: csize_t): cstring {.cdecl, diff --git a/src/nesper/ledcs.nim b/src/nesper/ledcs.nim index edcddd90..2be21112 100644 --- a/src/nesper/ledcs.nim +++ b/src/nesper/ledcs.nim @@ -42,6 +42,13 @@ proc repr*(c: ledc_channel_config_t): string = result &= "duty: " & $c.duty & ", " result &= "hpoint: " & $c.hpoint & ")" +var ledcInitialized*: bool = false + +proc initLedc*() = + if not ledcInitialized: + ledcInitialized = true + check: ledc_fade_func_install(0) + # Timer helpers proc newLedcTimer*( mode: ledc_mode_t, @@ -58,6 +65,8 @@ proc newLedcTimer*( result.cfg.clk_cfg = clkCfg result.cfg.deconfigure = false + # initLedc() + let ret = ledc_timer_config(addr result.cfg) if ret != ESP_OK: raise newEspError[LedcError]("ledc_timer_config failed (" & $esp_err_to_name(ret) & ")", ret) diff --git a/src/nesper/net_utils.nim b/src/nesper/net_utils.nim index 7d38b3b0..66a08f58 100644 --- a/src/nesper/net_utils.nim +++ b/src/nesper/net_utils.nim @@ -1,5 +1,6 @@ import strutils +import consts import general import esp/esp_system @@ -11,54 +12,12 @@ when not defined(ESP_IDF_V4_0): export esp_netif -## * This is the aligned version of ip4_addr_t, -## used as local variable, on the stack, etc. -type - ip4_addr_t* {.importc: "ip4_addr_t", header: "lwip/ip4_addr.h", bycopy.} = object - address* {.importc: "addr".}: uint32 - - ip6_addr_t* {.importc: "ip6_addr_t", header: "lwip/ip6_addr.h", bycopy.} = object - address* {.importc: "addr".}: array[4, uint32] - -type - lwip_ip_addr_type* {.size: sizeof(cint).} = enum ## * IPv4 - IPADDR_TYPE_V4 = 0, ## * IPv6 - IPADDR_TYPE_V6 = 6, ## * IPv4+IPv6 ("dual-stack") - IPADDR_TYPE_ANY = 46 - - -## #if LWIP_IPV4 && LWIP_IPV6 -## * -## @ingroup ipaddr -## A union struct for both IP version's addresses. -## ATTENTION: watch out for its size when adding IPv6 address scope! -## - -type - INNER_C_UNION_ip_addr_71* {.importc: "no_name", header: "lwip/ip_addr.h", bycopy, union.} = object - ip6* {.importc: "ip6".}: ip6_addr_t - ip4* {.importc: "ip4".}: ip4_addr_t - - ip_addr_t* {.importc: "ip_addr_t", header: "lwip/ip_addr.h", bycopy.} = object - u_addr* {.importc: "u_addr".}: INNER_C_UNION_ip_addr_71 ## * @ref lwip_ip_addr_type - `type`* {.importc: "type".}: uint8 - - -var ip_addr_any_type* {.importc: "ip_addr_any_type", header: "lwip/ip_addr.h".}: ip_addr_t - type esp_interface_t* {.size: sizeof(cint).} = enum ESP_IF_WIFI_STA = 0, ## *< ESP32 station interface ESP_IF_WIFI_AP, ## *< ESP32 soft-AP interface ESP_IF_ETH, ## *< ESP32 ethernet interface ESP_IF_MAX - wifi_mode_t* {.size: sizeof(cint).} = 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_interface_t* = esp_interface_t const WIFI_IF_STA* = ESP_IF_WIFI_STA @@ -87,6 +46,16 @@ when not defined(ESP_IDF_V4_0): proc toIpAddress*(ip: esp_ip6_addr_t): IpAddress = toIpAddress(ip.address) +when not defined(ESP_IDF_V4_0): + ## Convert a Nim IPv6 IpAddress to ESP-IDF esp_ip6_addr_t + proc toEspIp6Addr*(ip: IpAddress): esp_ip6_addr_t = + ## Ensures the input is IPv6 and maps bytes directly + doAssert ip.family == IpAddressFamily.IPv6 + var res: esp_ip6_addr_t + res.zone = 0'u8 + copyMem(res.address[0].unsafeAddr, ip.address_v6[0].unsafeAddr, 16) + return res + ## * Generate host name based on sdkconfig, optionally adding a portion of MAC address to it. proc generate_hostname*(hostname: string): string = var mac: array[6, uint8] @@ -110,3 +79,7 @@ proc generate_sensor_id*(): string = return sensor_id.join(":") +proc addIpv6ToNetif*(netif: ptr esp_netif_t; ip: IpAddress): esp_err_t = + ## Convert Nim IpAddress to esp_ip6_addr_t and add it to the interface + let espIp6 = toEspIp6Addr(ip) + result = esp_netif_add_ip6_address(netif, espIp6, preferred=true) diff --git a/src/nesper/networking/ioselectors_select_patch.nim b/src/nesper/networking/ioselectors_select_patch.nim new file mode 100644 index 00000000..315c858b --- /dev/null +++ b/src/nesper/networking/ioselectors_select_patch.nim @@ -0,0 +1,484 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2016 Eugene Kabanov +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# This module implements Posix and Windows select(). + +import std/[times, nativesockets] + +when defined(windows): + import std/winlean + when defined(gcc): + {.passl: "-lws2_32".} + elif defined(vcc): + {.passl: "ws2_32.lib".} + const platformHeaders = """#include + #include """ + const EAGAIN = WSAEWOULDBLOCK +else: + const platformHeaders = """#include + #include + #include + #include """ +type + FdSet {.importc: "fd_set", header: platformHeaders, pure, final.} = object +var + FD_SETSIZE {.importc: "FD_SETSIZE", header: platformHeaders.}: cint + +proc IOFD_SET(fd: SocketHandle, fdset: ptr FdSet) + {.cdecl, importc: "FD_SET", header: platformHeaders, inline.} +proc IOFD_CLR(fd: SocketHandle, fdset: ptr FdSet) + {.cdecl, importc: "FD_CLR", header: platformHeaders, inline.} +proc IOFD_ZERO(fdset: ptr FdSet) + {.cdecl, importc: "FD_ZERO", header: platformHeaders, inline.} + +when defined(windows): + proc IOFD_ISSET(fd: SocketHandle, fdset: ptr FdSet): cint + {.stdcall, importc: "FD_ISSET", header: platformHeaders, inline.} + proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr FdSet, + timeout: ptr Timeval): cint + {.stdcall, importc: "select", header: platformHeaders.} +else: + proc IOFD_ISSET(fd: SocketHandle, fdset: ptr FdSet): cint + {.cdecl, importc: "FD_ISSET", header: platformHeaders, inline.} + proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr FdSet, + timeout: ptr Timeval): cint + {.cdecl, importc: "select", header: platformHeaders.} + +when hasThreadSupport: + type + SelectorImpl[T] = object + rSet: FdSet + wSet: FdSet + eSet: FdSet + maxFD: int + fds: ptr SharedArray[SelectorKey[T]] + count*: int + lock: Lock + Selector*[T] = ptr SelectorImpl[T] +else: + type + SelectorImpl[T] = object + rSet: FdSet + wSet: FdSet + eSet: FdSet + maxFD: int + fds: seq[SelectorKey[T]] + count*: int + Selector*[T] = ref SelectorImpl[T] + +type + SelectEventImpl = object + rsock: SocketHandle + when not defined(nimIoselectorEventfd): + wsock: SocketHandle + SelectEvent* = ptr SelectEventImpl + +when hasThreadSupport: + template withSelectLock[T](s: Selector[T], body: untyped) = + acquire(s.lock) + {.locks: [s.lock].}: + try: + body + finally: + release(s.lock) +else: + template withSelectLock[T](s: Selector[T], body: untyped) = + body + +proc newSelector*[T](): Selector[T] = + when hasThreadSupport: + result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T]))) + result.fds = allocSharedArray[SelectorKey[T]](FD_SETSIZE) + initLock result.lock + else: + result = Selector[T]() + result.fds = newSeq[SelectorKey[T]](FD_SETSIZE) + + for i in 0 ..< FD_SETSIZE: + result.fds[i].ident = InvalidIdent + + IOFD_ZERO(addr result.rSet) + IOFD_ZERO(addr result.wSet) + IOFD_ZERO(addr result.eSet) + +proc close*[T](s: Selector[T]) = + when hasThreadSupport: + deallocSharedArray(s.fds) + deallocShared(cast[pointer](s)) + deinitLock(s.lock) + +when defined(windows): + proc newSelectEvent*(): SelectEvent = + var ssock = createNativeSocket() + var wsock = createNativeSocket() + var rsock: SocketHandle = INVALID_SOCKET + var saddr = Sockaddr_in() + + saddr.sin_family = winlean.AF_INET + saddr.sin_port = 0 + saddr.sin_addr.s_addr = INADDR_ANY + if bindAddr(ssock, cast[ptr SockAddr](addr(saddr)), + sizeof(saddr).SockLen) < 0'i32: + raiseIOSelectorsError(osLastError()) + + if winlean.listen(ssock, 1) != 0: + raiseIOSelectorsError(osLastError()) + + var namelen = sizeof(saddr).SockLen + if getsockname(ssock, cast[ptr SockAddr](addr(saddr)), + addr(namelen)) != 0'i32: + raiseIOSelectorsError(osLastError()) + + saddr.sin_addr.s_addr = 0x0100007F + if winlean.connect(wsock, cast[ptr SockAddr](addr(saddr)), + sizeof(saddr).SockLen) != 0: + raiseIOSelectorsError(osLastError()) + namelen = sizeof(saddr).SockLen + rsock = winlean.accept(ssock, cast[ptr SockAddr](addr(saddr)), + cast[ptr SockLen](addr(namelen))) + if rsock == SocketHandle(-1): + raiseIOSelectorsError(osLastError()) + + if winlean.closesocket(ssock) != 0: + raiseIOSelectorsError(osLastError()) + + var mode = clong(1) + if ioctlsocket(rsock, FIONBIO, addr(mode)) != 0: + raiseIOSelectorsError(osLastError()) + mode = clong(1) + if ioctlsocket(wsock, FIONBIO, addr(mode)) != 0: + raiseIOSelectorsError(osLastError()) + + result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl))) + result.rsock = rsock + result.wsock = wsock + + proc trigger*(ev: SelectEvent) = + var data: uint64 = 1 + if winlean.send(ev.wsock, cast[pointer](addr data), + cint(sizeof(uint64)), 0) != sizeof(uint64): + raiseIOSelectorsError(osLastError()) + + proc close*(ev: SelectEvent) = + let res1 = winlean.closesocket(ev.rsock) + let res2 = winlean.closesocket(ev.wsock) + deallocShared(cast[pointer](ev)) + if res1 != 0 or res2 != 0: + raiseIOSelectorsError(osLastError()) + +elif defined(nimIoselectorEventfd): + proc eventfd(fd: cint, flags: cint): cint {.importc: "eventfd", header: platformHeaders, inline.} + + proc newSelectEvent*(): SelectEvent = + when compiles(O_CLOEXEC): + let fdci = eventfd(0, O_CLOEXEC or O_NONBLOCK) + else: + let fdci = eventfd(0, 0) + if fdci == -1: + raiseIOSelectorsError(osLastError()) + result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl))) + result.rsock = SocketHandle(fdci) + + proc trigger*(ev: SelectEvent) = + var data: uint64 = 1 + if posix.write(ev.rsock.cint, addr data, sizeof(uint64)) == -1: + raiseIOSelectorsError(osLastError()) + + proc close*(ev: SelectEvent) = + let res = posix.close(ev.rsock.cint) + deallocShared(cast[pointer](ev)) + if res != 0: + raiseIOSelectorsError(osLastError()) + +else: + proc newSelectEvent*(): SelectEvent = + var fds: array[2, cint] + if posix.pipe(fds) != 0: + raiseIOSelectorsError(osLastError()) + setNonBlocking(fds[0]) + setNonBlocking(fds[1]) + result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl))) + result.rsock = SocketHandle(fds[0]) + result.wsock = SocketHandle(fds[1]) + + proc trigger*(ev: SelectEvent) = + var data: uint64 = 1 + if posix.write(cint(ev.wsock), addr data, sizeof(uint64)) != sizeof(uint64): + raiseIOSelectorsError(osLastError()) + + proc close*(ev: SelectEvent) = + let res1 = posix.close(cint(ev.rsock)) + let res2 = posix.close(cint(ev.wsock)) + deallocShared(cast[pointer](ev)) + if res1 != 0 or res2 != 0: + raiseIOSelectorsError(osLastError()) + +proc setSelectKey[T](s: Selector[T], fd: SocketHandle, events: set[Event], + data: T) = + var i = 0 + let fdi = int(fd) + while i < FD_SETSIZE: + if s.fds[i].ident == InvalidIdent: + var pkey = addr(s.fds[i]) + pkey.ident = fdi + pkey.events = events + pkey.data = data + break + inc(i) + if i >= FD_SETSIZE: + raiseIOSelectorsError("Maximum number of descriptors is exhausted!") + +proc getKey[T](s: Selector[T], fd: SocketHandle): ptr SelectorKey[T] = + var i = 0 + let fdi = int(fd) + while i < FD_SETSIZE: + if s.fds[i].ident == fdi: + result = addr(s.fds[i]) + break + inc(i) + doAssert(i < FD_SETSIZE, + "Descriptor [" & $int(fd) & "] is not registered in the queue!") + +proc delKey[T](s: Selector[T], fd: SocketHandle) = + var empty: T + var i = 0 + while i < FD_SETSIZE: + if s.fds[i].ident == fd.int: + s.fds[i].ident = InvalidIdent + s.fds[i].events = {} + s.fds[i].data = empty + break + inc(i) + doAssert(i < FD_SETSIZE, + "Descriptor [" & $int(fd) & "] is not registered in the queue!") + +proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle, + events: set[Event], data: T) = + when not defined(windows): + let fdi = int(fd) + s.withSelectLock(): + s.setSelectKey(fd, events, data) + when not defined(windows): + if fdi > s.maxFD: s.maxFD = fdi + if Event.Read in events: + IOFD_SET(fd, addr s.rSet) + inc(s.count) + if Event.Write in events: + IOFD_SET(fd, addr s.wSet) + IOFD_SET(fd, addr s.eSet) + inc(s.count) + +proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) = + when not defined(windows): + let fdi = int(ev.rsock) + s.withSelectLock(): + s.setSelectKey(ev.rsock, {Event.User}, data) + when not defined(windows): + if fdi > s.maxFD: s.maxFD = fdi + IOFD_SET(ev.rsock, addr s.rSet) + inc(s.count) + +proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle, + events: set[Event]) = + let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode, + Event.User, Event.Oneshot, Event.Error} + s.withSelectLock(): + var pkey = s.getKey(fd) + doAssert(pkey.events * maskEvents == {}) + if pkey.events != events: + if (Event.Read in pkey.events) and (Event.Read notin events): + IOFD_CLR(fd, addr s.rSet) + dec(s.count) + if (Event.Write in pkey.events) and (Event.Write notin events): + IOFD_CLR(fd, addr s.wSet) + IOFD_CLR(fd, addr s.eSet) + dec(s.count) + if (Event.Read notin pkey.events) and (Event.Read in events): + IOFD_SET(fd, addr s.rSet) + inc(s.count) + if (Event.Write notin pkey.events) and (Event.Write in events): + IOFD_SET(fd, addr s.wSet) + IOFD_SET(fd, addr s.eSet) + inc(s.count) + pkey.events = events + +proc unregister*[T](s: Selector[T], fd: SocketHandle|int) = + s.withSelectLock(): + let fd = fd.SocketHandle + var pkey = s.getKey(fd) + if Event.Read in pkey.events or Event.User in pkey.events: + IOFD_CLR(fd, addr s.rSet) + dec(s.count) + if Event.Write in pkey.events: + IOFD_CLR(fd, addr s.wSet) + IOFD_CLR(fd, addr s.eSet) + dec(s.count) + s.delKey(fd) + +proc unregister*[T](s: Selector[T], ev: SelectEvent) = + let fd = ev.rsock + s.withSelectLock(): + var pkey = s.getKey(fd) + IOFD_CLR(fd, addr s.rSet) + dec(s.count) + s.delKey(fd) + +proc selectInto*[T](s: Selector[T], timeout: int, + results: var openArray[ReadyKey]): int = + var tv = Timeval() + var ptv = addr tv + var rset, wset, eset: FdSet + + verifySelectParams(timeout) + + if timeout != -1: + when defined(genode) or defined(freertos) or defined(zephyr) or defined(nuttx): + tv.tv_sec = posix.Time(timeout div 1_000) + else: + tv.tv_sec = timeout.int32 div 1_000 + tv.tv_usec = (timeout.int32 %% 1_000) * 1_000 + else: + ptv = nil + + s.withSelectLock(): + rset = s.rSet + wset = s.wSet + eset = s.eSet + + var count = ioselect(cint(s.maxFD) + 1, addr(rset), addr(wset), + addr(eset), ptv) + if count < 0: + result = 0 + when defined(windows): + raiseIOSelectorsError(osLastError()) + else: + let err = osLastError() + if cint(err) != EINTR: + raiseIOSelectorsError(err) + elif count == 0: + result = 0 + else: + var rindex = 0 + var i = 0 + var k = 0 + + while (i < FD_SETSIZE) and (k < count): + if s.fds[i].ident != InvalidIdent: + var flag = false + var pkey = addr(s.fds[i]) + var rkey = ReadyKey(fd: int(pkey.ident), events: {}) + let fd = SocketHandle(pkey.ident) + if IOFD_ISSET(fd, addr rset) != 0: + if Event.User in pkey.events: + var data: uint64 = 0 + when defined(nimIoselectorEventfd): + let res = read(fd.cint, cast[pointer](addr(data)), + sizeof(uint64).cint) + else: + let res = recv(fd.cint, cast[pointer](addr(data)), + sizeof(uint64).cint, 0) + if res != sizeof(uint64): + let err = osLastError() + if cint(err) != EAGAIN: + raiseIOSelectorsError(err) + else: + inc(i) + inc(k) + continue + else: + flag = true + rkey.events = {Event.User} + else: + flag = true + rkey.events = {Event.Read} + if IOFD_ISSET(fd, addr wset) != 0: + rkey.events.incl(Event.Write) + if IOFD_ISSET(fd, addr eset) != 0: + rkey.events.incl(Event.Error) + flag = true + if flag: + results[rindex] = rkey + inc(rindex) + inc(k) + inc(i) + result = rindex + +proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey] = + result = newSeq[ReadyKey](FD_SETSIZE) + var count = selectInto(s, timeout, result) + result.setLen(count) + +proc flush*[T](s: Selector[T]) = discard + +template isEmpty*[T](s: Selector[T]): bool = + (s.count == 0) + +proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = + s.withSelectLock(): + result = false + + let fdi = int(fd) + for i in 0.. /proc/sys/net/ipv6/conf/$1/use_tempaddr +## echo 2 > /proc/sys/net/ipv6/conf/$1/accept_ra +## echo 1 > /proc/sys/net/ipv6/conf/all/forwarding +## +## ULA_PREFIX="fd12:1234:0E74" # Choose your own prefix!!! +## HOST_ULA="${ULA_PREFIX}::1/64" +## DEVICE_ULA="${ULA_PREFIX}::2/64" +## +## # Add the ULA address to the PPP interface +## ip -6 addr add $HOST_ULA dev $INTERFACE +## +## # Add a route to the device's ULA address +## ip -6 route add $DEVICE_ULA dev $INTERFACE +## +## # Optional: Add the ULA subnet to the routing table +## ip -6 route add ${ULA_PREFIX}::/64 dev $INTERFACE +## +## # Log the configuration +## logger "IPv6-up: Added ULA addresses - Host: $HOST_ULA, Device: $DEVICE_ULA on $INTERFACE" +## ``` +## +## Example of /etc/ppp/peers/esp32 config: +## +## ```sh +## # Serial device +## /dev/ttyACM0 +## # Baud rate (match your ESP32-S3 configuration) +## 115200 +## # Hardware flow control +## crtscts +## # Don't use modem control lines +## local +## # Don't require authentication from peer +## noauth +## # Set this side as client +## noipdefault +## defaultroute +## # Use peer's DNS +## usepeerdns +## # Enable debug (remove after testing) +## debug +## dump +## # Keep connection alive +## persist +## maxfail 0 +## +## # Optional: specific IP addresses if your ESP32-S3 assigns them +## # 192.168.1.100:192.168.1.101 +## +ipv6 +## ``` +## +## Then you can simply run `sudo pppd call esp32` + +const + TAG* = "pppos_dualcdc" + +# State +var + sNetif: ptr esp_netif_t + sEventGroup: EventGroupHandle_t + sPppItf: tinyusb_cdcacm_itf_t = TINYUSB_CDC_ACM_0 + sLogItf: tinyusb_cdcacm_itf_t = TINYUSB_CDC_ACM_1 + rxBuf = newSeq[uint8](CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1) + +const + GOT_IPV4 = 1 shl 0 + CONN_FAILED = 1 shl 1 + GOT_IPV6 = 1 shl 2 + CONNECT_BITS = GOT_IPV4 or GOT_IPV6 or CONN_FAILED + +# PPP transmit: push bytes to CDC PPP interface +proc pppTransmit(h: pointer; buffer: pointer; len: csize_t): esp_err_t {.cdecl.} = + # logi(TAG, "CDC TX: %d", len) + discard tinyusb_cdcacm_write_queue(sPppItf, cast[ptr uint8](buffer), len) + result = tinyusb_cdcacm_write_flush(sPppItf, 0'u32) + +var driverCfg: esp_netif_driver_ifconfig_t + +# CDC RX: feed data to esp_netif +proc onCdcRx(itf: cint; event: ptr cdcacm_event_t) {.cdecl.} = + # logi(TAG, "CDC RX: interface %d", itf) + if tinyusb_cdcacm_itf_t(itf) != sPppItf: + # logi(TAG, "CDC RX: interface %d not sPppItf", itf) + return + var rxSize: csize_t = 0 + let ret = tinyusb_cdcacm_read(tinyusb_cdcacm_itf_t(itf), addr rxBuf[0], CONFIG_TINYUSB_CDC_RX_BUFSIZE.csize_t, addr rxSize) + # logi(TAG, "CDC RX: interface %d returned %d rxBuf: %d", itf, ret, rxBuf.len()) + if ret == ESP_OK and rxSize > 0: + discard esp_netif_receive(sNetif, addr rxBuf[0], rxSize, nil) + +proc onLineState(itf: cint; event: ptr cdcacm_event_t) {.cdecl.} = + # logi(TAG, "Line state changed on itf %d", itf) + # Allow host tools (esptool) to reset via DTR/RTS on the console CDC + if tinyusb_cdcacm_itf_t(itf) == sLogItf: + let ls = event.line_state_changed_data + # logi(TAG, "DTR/RTS triggered on console rts: %s, dtr: %s", $ls.rts, $ls.dtr) + # Common esptool pattern: RTS asserted and DTR deasserted -> reset to enter flashing + if ls.rts and not ls.dtr: + # logi(TAG, "DTR/RTS trigger: restarting...") + delay(10.Millis) + esp_restart() + +# IP events handler: filter for our PPP netif and set bits +proc onIpEvent(arg: pointer; event_base: esp_event_base_t; event_id: int32; event_data: pointer) {.cdecl.} = + if event_base != IP_EVENT: return + case ip_event_t(event_id) + of IP_EVENT_PPP_GOT_IP: + let ev = cast[ptr ip_event_got_ip_t](event_data) + if ev.esp_netif == sNetif: + logi(TAG, "PPP IPv4: %s %s", esp_netif_get_desc(ev.esp_netif), $ev.ip_info.ip) + discard xEventGroupSetBits(sEventGroup, EventBits_t(GOT_IPV4)) + of IP_EVENT_GOT_IP6: + let ev6 = cast[ptr ip_event_got_ip6_t](event_data) + if ev6.esp_netif == sNetif: + logi(TAG, "PPP IPv6 acquired") + discard xEventGroupSetBits(sEventGroup, EventBits_t(GOT_IPV6)) + of IP_EVENT_PPP_LOST_IP: + logi(TAG, "PPP IPv6 lost") + discard xEventGroupSetBits(sEventGroup, EventBits_t(CONN_FAILED)) + else: + discard + +proc setupNetworking*() = + # Initialize esp-netif and default event loop + check: esp_netif_init() + check: esp_event_loop_create_default() + +proc initPppConnectDualCdc*(): esp_err_t = + ## Initializes USB CDC for PPPoS and a serial console + ## requires TinyUSB and incompatible with USB JTAG or OTG Modes + ## CDC0 is used for PPP data, CDC1 is used for console + ## + logi(TAG, "Starting dual-CDC PPPoS (PPP=CDC0, console=CDC1)") + + var tusbCfg: tinyusb_config_t + tusbCfg.external_phy = false + check: tinyusb_driver_install(addr tusbCfg) + + # CDC0 for PPP + var acmPpp: tinyusb_config_cdcacm_t + acmPpp.usb_dev = TINYUSB_USBDEV_0 + acmPpp.cdc_port = TINYUSB_CDC_ACM_0 + acmPpp.callback_rx = onCdcRx + acmPpp.callback_rx_wanted_char = nil + acmPpp.callback_line_state_changed = onLineState + acmPpp.callback_line_coding_changed = nil + check: tusb_cdc_acm_init(addr acmPpp) + + # CDC1 for logs/console + var acmLog: tinyusb_config_cdcacm_t + acmLog.usb_dev = TINYUSB_USBDEV_0 + acmLog.cdc_port = TINYUSB_CDC_ACM_1 + acmLog.callback_line_state_changed = onLineState + check: tusb_cdc_acm_init(addr acmLog) + check: esp_tusb_init_console(TINYUSB_CDC_ACM_1) + + # Event group and handler + sEventGroup = xEventGroupCreate() + if sEventGroup.isNil: return ESP_ERR_NO_MEM + check: esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID.cint, onIpEvent, nil) + + # Create PPP netif using inherent defaults and custom driver + logi(TAG, "Creating PPP netif...") + var baseCfg = ESP_NETIF_INHERENT_DEFAULT_PPP() + baseCfg.if_desc = "example_netif_ppp" + + driverCfg.handle = cast[pointer](1) + driverCfg.transmit = pppTransmit + + var netifCfg: esp_netif_config_t + netifCfg.base = addr baseCfg + netifCfg.driver = addr driverCfg + netifCfg.stack = ESP_NETIF_NETSTACK_DEFAULT_PPP + + sNetif = esp_netif_new(addr netifCfg) + doAssert sNetif != nil + esp_netif_action_start(sNetif, nil, 0, nil) + esp_netif_action_connected(sNetif, nil, 0, nil) + + logi(TAG, "Waiting for IP...") + let bits = xEventGroupWaitBits(sEventGroup, EventBits_t(CONNECT_BITS), pdFALSE, pdFALSE, portMAX_DELAY) + if (bits and EventBits_t(CONN_FAILED)) != 0: + loge(TAG, "PPP connect failed") + return ESP_FAIL + logi(TAG, "PPP connected") + return ESP_OK + +proc shutdownPppConnectDualCdc*() = + discard esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID.cint, onIpEvent) + if sNetif != nil: + esp_netif_action_disconnected(sNetif, nil, 0, nil) + esp_netif_action_stop(sNetif, nil, 0, nil) + esp_netif_destroy(sNetif) + sNetif = nil + if sEventGroup != nil: + vEventGroupDelete(sEventGroup) + sEventGroup = nil + discard esp_tusb_deinit_console(TINYUSB_CDC_ACM_1) + +proc pppInterface*(): ptr esp_netif_t = + ## Returns the PPP esp-netif created by initPppConnectDualCdc + sNetif diff --git a/src/nesper/ota_utils.nim b/src/nesper/ota_utils.nim index 0b88e3cc..9b8dde40 100644 --- a/src/nesper/ota_utils.nim +++ b/src/nesper/ota_utils.nim @@ -5,6 +5,9 @@ import options import consts import general import esp/storage/esp_ota_ops +import esp/storage/esp_app_format + +export esp_app_format const TAG = "OTAUTL" @@ -103,7 +106,7 @@ proc begin*(ota: OtaUpdateHandle) = TAG.loge("esp_ota_begin failed (%s)", repr(ota.update)) raise newEspError[OtaError]("Error ota begin: " & $esp_err_to_name(err), err) -proc checkImageHeader*(ota: OtaUpdateHandle, data: var string; version_check = true): +proc checkImageHeader*(ota: OtaUpdateHandle, data: string; version_check = true): tuple[status: OtaUpdateStatus, info: esp_app_desc_t] = var new_app_info: esp_app_desc_t @@ -145,7 +148,7 @@ proc checkImageHeader*(ota: OtaUpdateHandle, data: var string; version_check = t return (status: VersionNewer, info: new_app_info) -proc write*(ota: var OtaUpdateHandle, write_data: var string) = +proc write*(ota: var OtaUpdateHandle, write_data: string) = let err = esp_ota_write(ota.handle, addr write_data[0], write_data.len().csize_t) if err != ESP_OK: raise newEspError[OtaError]("Error ota write: " & $esp_err_to_name(err), err) diff --git a/src/nesper/servers/rpc/rpc_cli_utils.nim b/src/nesper/servers/rpc/rpc_cli_utils.nim index d77eeef6..73ab0654 100644 --- a/src/nesper/servers/rpc/rpc_cli_utils.nim +++ b/src/nesper/servers/rpc/rpc_cli_utils.nim @@ -48,7 +48,8 @@ type count: int delay: int jsonArg: string - ipAddr: string + host: string + ipaddr: IpAddress port: Port prettyPrint: bool quiet: bool @@ -65,7 +66,7 @@ proc rpcOptions*(p: var OptParser): RpcCli = result.delay = 0 result.quiet = false result.jsonArg = "" - result.ipAddr = "" + result.host = "" result.port = Port(5555) result.prettyPrint = false result.id = 1 @@ -79,7 +80,7 @@ proc rpcOptions*(p: var OptParser): RpcCli = of "count", "c": result.count = parseInt(val) of "ip", "i": - result.ipAddr = val + result.host = val of "port", "p": result.port = Port(parseInt(val)) of "stats", "s": @@ -92,7 +93,7 @@ proc rpcOptions*(p: var OptParser): RpcCli = echo "args: ", $result - if result.ipAddr == "": + if result.host == "": # no filename has been given, so we show the help raise newException(ValueError, "missing ip address") @@ -100,7 +101,7 @@ proc rpcOptions*(p: var OptParser): RpcCli = # Check IP address try: - discard parseIpAddress(result.ipAddr) + result.ipaddr = parseIpAddress(result.host) except CatchableError as err: echo "invalid IP address, check the --ip:$IP argument" raise err @@ -192,14 +193,18 @@ proc runRpc*(args: var RpcCli) = else: call = %* { "jsonrpc": "2.0", "id": 1 } - let m = parseJson(args.jsonArg) + let m = parseJson(args.jsonArg) + for (f,v) in m.pairs(): + call[f] = v - for (f,v) in m.pairs(): - call[f] = v + var client: Socket + if args.ipaddr.family == IpAddressFamily.IPv6: + client = newSocket(buffered=false, domain = AF_INET6) + else: + client = newSocket(buffered=false, domain = AF_INET) - let client: Socket = newSocket(buffered=false) - client.connect(args.ipAddr, args.port) - echo(yellow, "[connected to server ip addr: ", args.ipAddr,"]") + client.connect(args.host, args.port) + echo(yellow, "[connected to server ip addr: ", args.host,"]") echo(blue, "[call: ", $call, "]") for i in 1..args.count: diff --git a/src/nesper/servers/rpc/rpcsocket_json.nim b/src/nesper/servers/rpc/rpcsocket_json.nim index 823ca2bc..8d0d0572 100644 --- a/src/nesper/servers/rpc/rpcsocket_json.nim +++ b/src/nesper/servers/rpc/rpcsocket_json.nim @@ -4,8 +4,10 @@ import selectors import tables import posix -import ../../consts -import ../../general +when defined(freertos): + import ../../consts + import ../../general + import ../tcpsocket import router import json @@ -14,10 +16,10 @@ export tcpsocket, router const TAG = "socketrpc" -proc rpcMsgPackWriteHandler*(srv: TcpServerInfo[RpcRouter], result: ReadyKey, sourceClient: Socket, rt: RpcRouter) = +proc rpcJsonWriteHandler*(srv: TcpServerInfo[RpcRouter], result: ReadyKey, sourceClient: Socket, rt: RpcRouter) = raise newException(OSError, "the request to the OS failed") -proc rpcMsgPackReadHandler*(srv: TcpServerInfo[RpcRouter], result: ReadyKey, sourceClient: Socket, rt: RpcRouter) = +proc rpcJsonReadHandler*(srv: TcpServerInfo[RpcRouter], result: ReadyKey, sourceClient: Socket, rt: RpcRouter) = try: logd(TAG, "rpc server handler: router: %x", rt.buffer) @@ -40,13 +42,13 @@ proc rpcMsgPackReadHandler*(srv: TcpServerInfo[RpcRouter], result: ReadyKey, sou proc startRpcSocketServer*(port: Port; address="", router: var RpcRouter) = - logi(TAG, "starting json rpc server: buffer: %s", $router.buffer) + logi(TAG, "starting json rpc server: buffer: %s, port: %s", $router.buffer, $port) - startSocketServer[RpcRouter]( + startUdpSocketServer[RpcRouter]( port, address=address, - readHandler=rpcMsgPackReadHandler, - writeHandler=rpcMsgPackWriteHandler, + readHandler=rpcJsonReadHandler, + writeHandler=rpcJsonWriteHandler, data=router) diff --git a/src/nesper/servers/tcpsocket.nim b/src/nesper/servers/tcpsocket.nim index defc76a3..cb0c0202 100644 --- a/src/nesper/servers/tcpsocket.nim +++ b/src/nesper/servers/tcpsocket.nim @@ -6,12 +6,11 @@ import posix import ../consts import ../general -import ../tasks import ../timers +export consts, general, timers export net, selectors, tables, posix -export consts, general, timers const TAG = "socketrpc" @@ -37,11 +36,11 @@ template sendWrap*(socket: Socket, data: untyped) = proc sendChunks*(sourceClient: Socket, rmsg: string) = let rN = rmsg.len() - # logd(TAG,"rpc handler send client: %d bytes", rN) + logi(TAG,"rpc handler send client: %d bytes", rN) var i = 0 while i < rN: var j = min(i + MsgChunk, rN) - # logd(TAG,"rpc handler sending: i: %s j: %s ", $i, $j) + logi(TAG,"rpc handler sending: i: %s j: %s ", $i, $j) var sl = rmsg[i.. | || D- +[USB_DP] ------> | || D+ + | || GND + -- +``` + +### USB PHY pin mapping + +| | USB_DP | USB_DM | +| ------------ | ------ | ------ | +| ESP32-S2/S3 | GPIO20 | GPIO19 | +| ESP32-P4 2.0 | pin 50 | pin 49 | +| ESP32-P4 1.1 | GPIO27 | GPIO26 | + +> Note: On the ESP32-P4, the USB 2.0 PHY pins are dedicated to USB-OTG functionality and cannot be used as general-purpose GPIOs. diff --git a/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/CMakeLists.txt b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/CMakeLists.txt new file mode 100644 index 00000000..ac369a25 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/CMakeLists.txt @@ -0,0 +1,11 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +add_compile_options(-w) # Turn off warnings until esp_tinyusb is updated IEC-86 +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(tusb_composite) diff --git a/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/README.md b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/README.md new file mode 100644 index 00000000..a7a32c45 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/README.md @@ -0,0 +1,81 @@ +| Supported Targets | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | + +# TinyUSB Composite Device (MSC + Serial) Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +A USB device can provide multiple functions that are active simultaneously. Such multi-function devices are also known as composite devices. This example shows how to set up ESP chip to work as a USB Serial Device as well as MSC device (Storage media as SPI-Flash). + +As a USB stack, a TinyUSB component is used. + +## How to use example + +### Hardware Required + +Any ESP board that supports the USB-OTG peripheral. + +### Pin Assignment + +_Note:_ In case your board doesn't have micro-USB connector connected to USB-OTG peripheral, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below. + +See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments). + +Next, for Self-Powered Devices with VBUS monitoring, user must set ``self_powered`` to ``true`` and ``vbus_monitor_io`` to GPIO number (``VBUS_MONITORING_GPIO_NUM``) that will be used for VBUS monitoring. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +```bash +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +After the flashing you should see the output at idf monitor: + +``` +I (344) main_task: Calling app_main() +I (344) example_main: USB Composite initialization +W (354) TinyUSB: The device's configuration descriptor is not provided by user, using default. +W (364) TinyUSB: The device's string descriptor is not provided by user, using default. +W (374) TinyUSB: The device's device descriptor is not provided by user, using default. +I (384) tusb_desc: +┌─────────────────────────────────┐ +│ USB Device Descriptor Summary │ +├───────────────────┬─────────────┤ +│bDeviceClass │ 239 │ +├───────────────────┼─────────────┤ +│bDeviceSubClass │ 2 │ +├───────────────────┼─────────────┤ +│bDeviceProtocol │ 1 │ +├───────────────────┼─────────────┤ +│bMaxPacketSize0 │ 64 │ +├───────────────────┼─────────────┤ +│idVendor │ 0x303a │ +├───────────────────┼─────────────┤ +│idProduct │ 0x4001 │ +├───────────────────┼─────────────┤ +│bcdDevice │ 0x100 │ +├───────────────────┼─────────────┤ +│iManufacturer │ 0x1 │ +├───────────────────┼─────────────┤ +│iProduct │ 0x2 │ +├───────────────────┼─────────────┤ +│iSerialNumber │ 0x3 │ +├───────────────────┼─────────────┤ +│bNumConfigurations │ 0x1 │ +└───────────────────┴─────────────┘ +I (544) TinyUSB: TinyUSB Driver installed +I (554) example_main: Initializing storage... +I (554) example_main: Initializing wear levelling +I (584) example_main: USB Composite initialization DONE +I (584) main_task: Returned from app_main() +``` diff --git a/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/main/CMakeLists.txt b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/main/CMakeLists.txt new file mode 100644 index 00000000..b8dc619b --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS tusb_composite_main.c + INCLUDE_DIRS . + PRIV_REQUIRES fatfs wear_levelling esp_partition +) diff --git a/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/main/idf_component.yml b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/main/idf_component.yml new file mode 100644 index 00000000..06b047d6 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_tinyusb: "^1.2" + idf: "^5.0" diff --git a/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/main/tusb_composite_main.c b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/main/tusb_composite_main.c new file mode 100644 index 00000000..87474afb --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/main/tusb_composite_main.c @@ -0,0 +1,207 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#include "esp_partition.h" +#include "esp_check.h" +#include "tinyusb.h" +#include "tusb_msc_storage.h" +#include "tusb_cdc_acm.h" + +#define BASE_PATH "/usb" // base path to mount the partition + +static const char *TAG = "example_main"; +static uint8_t rx_buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1]; + +/** + * @brief Application Queue + */ +static QueueHandle_t app_queue; +typedef struct { + uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1]; // Data buffer + size_t buf_len; // Number of bytes received + uint8_t itf; // Index of CDC device interface +} app_message_t; + +/** + * @brief CDC device RX callback + * + * CDC device signals, that new data were received + * + * @param[in] itf CDC device index + * @param[in] event CDC event type + */ +void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event) +{ + /* initialization */ + size_t rx_size = 0; + + /* read */ + esp_err_t ret = tinyusb_cdcacm_read(itf, rx_buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size); + if (ret == ESP_OK) { + + app_message_t tx_msg = { + .buf_len = rx_size, + .itf = itf, + }; + + /* Copy received message to application queue buffer */ + memcpy(tx_msg.buf, rx_buf, rx_size); + xQueueSend(app_queue, &tx_msg, 0); + } else { + ESP_LOGE(TAG, "Read Error"); + } +} + +/** + * @brief CDC device line change callback + * + * CDC device signals, that the DTR, RTS states changed + * + * @param[in] itf CDC device index + * @param[in] event CDC event type + */ +void tinyusb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event) +{ + int dtr = event->line_state_changed_data.dtr; + int rts = event->line_state_changed_data.rts; + ESP_LOGI(TAG, "Line state changed on channel %d: DTR:%d, RTS:%d", itf, dtr, rts); +} + +static bool file_exists(const char *file_path) +{ + struct stat buffer; + return stat(file_path, &buffer) == 0; +} + +static void file_operations(void) +{ + const char *directory = "/usb/esp"; + const char *file_path = "/usb/esp/test.txt"; + + struct stat s = {0}; + bool directory_exists = stat(directory, &s) == 0; + if (!directory_exists) { + if (mkdir(directory, 0775) != 0) { + ESP_LOGE(TAG, "mkdir failed with errno: %s", strerror(errno)); + } + } + + if (!file_exists(file_path)) { + ESP_LOGI(TAG, "Creating file"); + FILE *f = fopen(file_path, "w"); + if (f == NULL) { + ESP_LOGE(TAG, "Failed to open file for writing"); + return; + } + fprintf(f, "Hello World!\n"); + fclose(f); + } + + FILE *f; + ESP_LOGI(TAG, "Reading file"); + f = fopen(file_path, "r"); + if (f == NULL) { + ESP_LOGE(TAG, "Failed to open file for reading"); + return; + } + char line[64]; + fgets(line, sizeof(line), f); + fclose(f); + // strip newline + char *pos = strchr(line, '\n'); + if (pos) { + *pos = '\0'; + } + ESP_LOGI(TAG, "Read from file: '%s'", line); +} + +static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle) +{ + ESP_LOGI(TAG, "Initializing wear levelling"); + + const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL); + if (data_partition == NULL) { + ESP_LOGE(TAG, "Failed to find FATFS partition. Check the partition table."); + return ESP_ERR_NOT_FOUND; + } + + return wl_mount(data_partition, wl_handle); +} + +void app_main(void) +{ + // Create FreeRTOS primitives + app_queue = xQueueCreate(5, sizeof(app_message_t)); + assert(app_queue); + app_message_t msg; + + ESP_LOGI(TAG, "Initializing storage..."); + + static wl_handle_t wl_handle = WL_INVALID_HANDLE; + ESP_ERROR_CHECK(storage_init_spiflash(&wl_handle)); + + const tinyusb_msc_spiflash_config_t config_spi = { + .wl_handle = wl_handle + }; + ESP_ERROR_CHECK(tinyusb_msc_storage_init_spiflash(&config_spi)); + ESP_ERROR_CHECK(tinyusb_msc_storage_mount(BASE_PATH)); + file_operations(); + + ESP_LOGI(TAG, "USB Composite initialization"); + const tinyusb_config_t tusb_cfg = { + .device_descriptor = NULL, + .string_descriptor = NULL, + .string_descriptor_count = 0, + .external_phy = false, +#if (TUD_OPT_HIGH_SPEED) + .fs_configuration_descriptor = NULL, + .hs_configuration_descriptor = NULL, + .qualifier_descriptor = NULL, +#else + .configuration_descriptor = NULL, +#endif // TUD_OPT_HIGH_SPEED + }; + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); + + tinyusb_config_cdcacm_t acm_cfg = { + .usb_dev = TINYUSB_USBDEV_0, + .cdc_port = TINYUSB_CDC_ACM_0, + .rx_unread_buf_sz = 64, + .callback_rx = &tinyusb_cdc_rx_callback, // the first way to register a callback + .callback_rx_wanted_char = NULL, + .callback_line_state_changed = NULL, + .callback_line_coding_changed = NULL + }; + + ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg)); + /* the second way to register a callback */ + ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback( + TINYUSB_CDC_ACM_0, + CDC_EVENT_LINE_STATE_CHANGED, + &tinyusb_cdc_line_state_changed_callback)); + + ESP_LOGI(TAG, "USB Composite initialization DONE"); + while (1) { + if (xQueueReceive(app_queue, &msg, portMAX_DELAY)) { + if (msg.buf_len) { + + /* Print received data*/ + ESP_LOGI(TAG, "Data from channel %d:", msg.itf); + ESP_LOG_BUFFER_HEXDUMP(TAG, msg.buf, msg.buf_len, ESP_LOG_INFO); + + /* write back */ + tinyusb_cdcacm_write_queue(msg.itf, msg.buf, msg.buf_len); + esp_err_t err = tinyusb_cdcacm_write_flush(msg.itf, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "CDC ACM write flush error: %s", esp_err_to_name(err)); + } + } + } + } +} diff --git a/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/partitions.csv b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/partitions.csv new file mode 100644 index 00000000..1c79321a --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/partitions.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1M, +storage, data, fat, , 1M, diff --git a/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/pytest_usb_device_composite.py b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/pytest_usb_device_composite.py new file mode 100644 index 00000000..2c42a923 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/pytest_usb_device_composite.py @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +from time import sleep + +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize +from serial import Serial +from serial.tools.list_ports import comports + + +@pytest.mark.temp_skip_ci(targets=['esp32s3'], reason='lack of runners with usb_device tag') +@pytest.mark.usb_device +@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32p4'], indirect=['target']) +def test_usb_composite_device_serial_example(dut: Dut) -> None: + dut.expect_exact('Hello World!') + dut.expect_exact('USB Composite initialization') + dut.expect_exact('USB Composite initialization DONE') + sleep(2) # Some time for the OS to enumerate our USB device + + # Find device with Espressif TinyUSB VID/PID + ports = comports() + for port, _, hwid in ports: + if '303A:4001' in hwid: + with Serial(port) as s: + s.write('text\r\n'.encode()) # Write dummy text to COM port + dut.expect_exact('Data from channel 0:') # Check ESP log + dut.expect_exact('|text..|') + res = s.readline() # Check COM echo + assert b'text\r\n' in res + return + + raise Exception('TinyUSB COM port not found') diff --git a/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/sdkconfig.defaults b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/sdkconfig.defaults new file mode 100644 index 00000000..087e74a6 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_composite_msc_serialdevice/sdkconfig.defaults @@ -0,0 +1,15 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_TINYUSB_MSC_ENABLED=y + +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_WL_SECTOR_SIZE_512=y +CONFIG_WL_SECTOR_MODE_PERF=y + +CONFIG_TINYUSB_CDC_ENABLED=y +# CONFIG_TINYUSB_DESC_USE_DEFAULT_PID is not set +CONFIG_TINYUSB_DESC_CUSTOM_PID=0x4001 diff --git a/tests/c_headers/idf-usb/device/tusb_console/CMakeLists.txt b/tests/c_headers/idf-usb/device/tusb_console/CMakeLists.txt new file mode 100644 index 00000000..c8353934 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_console/CMakeLists.txt @@ -0,0 +1,9 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +add_compile_options(-w) # Turn off warnings until esp_tinyusb is updated IEC-86 +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(tusb_console) diff --git a/tests/c_headers/idf-usb/device/tusb_console/README.md b/tests/c_headers/idf-usb/device/tusb_console/README.md new file mode 100644 index 00000000..aeccaab2 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_console/README.md @@ -0,0 +1,88 @@ +| Supported Targets | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | + +# TinyUSB Sample Descriptor + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example shows how to set up ESP chip to get log output via Serial Device connection. + +As a USB stack, a TinyUSB component is used. + +## How to use example + +### Hardware Required + +Any ESP boards that have USB-OTG supported. + +#### Pin Assignment + +See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments). + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +```bash +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Serial Connection + +After program's start and getting of the message of readiness (`Serial device is ready to connect`) you can connect to the board using any serial port terminal application (e.g. CoolTerm). + +Note: if you want to send data to the target see how to implement it via `tud_cdc_rx_cb` at the `tusb_serial_device` example. + +## Example Output + +After the flashing you should see the output at idf monitor: + +``` +I (288) example: USB initialization +I (288) tusb_desc: +┌─────────────────────────────────┐ +│ USB Device Descriptor Summary │ +├───────────────────┬─────────────┤ +│bDeviceClass │ 239 │ +├───────────────────┼─────────────┤ +│bDeviceSubClass │ 2 │ +├───────────────────┼─────────────┤ +│bDeviceProtocol │ 1 │ +├───────────────────┼─────────────┤ +│bMaxPacketSize0 │ 64 │ +├───────────────────┼─────────────┤ +│idVendor │ 0x303a │ +├───────────────────┼─────────────┤ +│idProduct │ 0x4001 │ +├───────────────────┼─────────────┤ +│bcdDevice │ 0x100 │ +├───────────────────┼─────────────┤ +│iManufacturer │ 0x1 │ +├───────────────────┼─────────────┤ +│iProduct │ 0x2 │ +├───────────────────┼─────────────┤ +│iSerialNumber │ 0x3 │ +├───────────────────┼─────────────┤ +│bNumConfigurations │ 0x1 │ +└───────────────────┴─────────────┘ +I (458) TinyUSB: TinyUSB Driver installed +I (468) example: USB initialization DONE +I (468) example: log -> UART +example: print -> stdout +example: print -> stderr +... + +``` + +Other log will be printed to USB: +``` +I (3478) example: log -> USB +example: print -> stdout +example: print -> stderr +``` diff --git a/tests/c_headers/idf-usb/device/tusb_console/main/CMakeLists.txt b/tests/c_headers/idf-usb/device/tusb_console/main/CMakeLists.txt new file mode 100644 index 00000000..f1ad99b5 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_console/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "tusb_console_main.c" + INCLUDE_DIRS .) diff --git a/tests/c_headers/idf-usb/device/tusb_console/main/idf_component.yml b/tests/c_headers/idf-usb/device/tusb_console/main/idf_component.yml new file mode 100644 index 00000000..ff67b5da --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_console/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_tinyusb: "^1" + idf: "^5.0" diff --git a/tests/c_headers/idf-usb/device/tusb_console/main/tusb_console_main.c b/tests/c_headers/idf-usb/device/tusb_console/main/tusb_console_main.c new file mode 100644 index 00000000..5b16591c --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_console/main/tusb_console_main.c @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +// DESCRIPTION: +// This example contains minimal code to make ESP32-S2 based device +// recognizable by USB-host devices as a USB Serial Device printing output from +// the application. + +#include +#include +#include +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "tinyusb.h" +#include "tusb_cdc_acm.h" +#include "tusb_console.h" +#include "sdkconfig.h" + +static const char *TAG = "example"; + +void app_main(void) +{ + /* Setting TinyUSB up */ + ESP_LOGI(TAG, "USB initialization"); + + const tinyusb_config_t tusb_cfg = { + .device_descriptor = NULL, + .string_descriptor = NULL, + .external_phy = false, // In the most cases you need to use a `false` value +#if (TUD_OPT_HIGH_SPEED) + .fs_configuration_descriptor = NULL, + .hs_configuration_descriptor = NULL, + .qualifier_descriptor = NULL, +#else + .configuration_descriptor = NULL, +#endif // TUD_OPT_HIGH_SPEED + }; + + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); + + tinyusb_config_cdcacm_t acm_cfg = { 0 }; // the configuration uses default values + ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg)); + + ESP_LOGI(TAG, "USB initialization DONE"); + while (1) { + ESP_LOGI(TAG, "log -> UART"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + fprintf(stdout, "example: print -> stdout\n"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + fprintf(stderr, "example: print -> stderr\n"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + + esp_tusb_init_console(TINYUSB_CDC_ACM_0); // log to usb + ESP_LOGI(TAG, "log -> USB"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + fprintf(stdout, "example: print -> stdout\n"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + fprintf(stderr, "example: print -> stderr\n"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + esp_tusb_deinit_console(TINYUSB_CDC_ACM_0); // log to uart + } +} diff --git a/tests/c_headers/idf-usb/device/tusb_console/pytest_usb_device_console.py b/tests/c_headers/idf-usb/device/tusb_console/pytest_usb_device_console.py new file mode 100644 index 00000000..8860dbff --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_console/pytest_usb_device_console.py @@ -0,0 +1,37 @@ +# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +from time import sleep + +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize +from serial import Serial +from serial.tools.list_ports import comports + + +@pytest.mark.temp_skip_ci(targets=['esp32s3'], reason='lack of runners with usb_device tag') +@pytest.mark.usb_device +@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32p4'], indirect=['target']) +def test_usb_device_console_example(dut: Dut) -> None: + dut.expect_exact('USB initialization DONE') + dut.expect_exact('example: log -> UART') + dut.expect_exact('example: print -> stdout') + dut.expect_exact('example: print -> stderr') + + # Find device with Espressif TinyUSB VID/PID + sleep(2) # Some time for the OS to enumerate our USB device + ports = comports() + for port, _, hwid in ports: + if '303A:4001' in hwid: + with Serial(port) as s: + # Assert TinyUSB output: Read 3 lines and check their content + serial_output = list() + serial_output.append(s.readline()) + serial_output.append(s.readline()) + serial_output.append(s.readline()) + assert any(b'example: log -> USB' in out for out in serial_output) + assert any(b'example: print -> stdout' in out for out in serial_output) + assert any(b'example: print -> stderr' in out for out in serial_output) + return + + raise Exception('TinyUSB COM port not found') diff --git a/tests/c_headers/idf-usb/device/tusb_console/sdkconfig.defaults b/tests/c_headers/idf-usb/device/tusb_console/sdkconfig.defaults new file mode 100644 index 00000000..afec5843 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_console/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_TINYUSB_CDC_ENABLED=y diff --git a/tests/c_headers/idf-usb/device/tusb_hid/CMakeLists.txt b/tests/c_headers/idf-usb/device/tusb_hid/CMakeLists.txt new file mode 100644 index 00000000..258a4dce --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_hid/CMakeLists.txt @@ -0,0 +1,10 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(tusb_hid) diff --git a/tests/c_headers/idf-usb/device/tusb_hid/README.md b/tests/c_headers/idf-usb/device/tusb_hid/README.md new file mode 100644 index 00000000..e7847425 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_hid/README.md @@ -0,0 +1,80 @@ +| Supported Targets | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | + +# TinyUSB Human Interface Device Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +Human interface devices (HID) are one of the most common USB devices, it is implemented in various devices such as keyboards, mice, game controllers, sensors and alphanumeric display devices. +In this example, we implement USB keyboard and mouse. +Upon connection to USB host (PC), the example application will sent 'key a/A pressed & released' events and move mouse in a square trajectory. To send these HID reports again, press the BOOT button, that is present on most ESP development boards (GPIO0). + +As a USB stack, a TinyUSB component is used. + +## How to use example + +### Hardware Required + +Any ESP board that have USB-OTG supported. + +#### Pin Assignment + +_Note:_ In case your board doesn't have micro-USB connector connected to USB-OTG peripheral, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below. + +See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments). + +Boot signal (GPIO0) is used to send HID reports to USB host. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +```bash +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +After the flashing you should see the output at idf monitor: + +``` +I (290) cpu_start: Starting scheduler on PRO CPU. +I (0) cpu_start: Starting scheduler on APP CPU. +I (310) example: USB initialization +I (310) tusb_desc: +┌─────────────────────────────────┐ +│ USB Device Descriptor Summary │ +├───────────────────┬─────────────┤ +│bDeviceClass │ 0 │ +├───────────────────┼─────────────┤ +│bDeviceSubClass │ 0 │ +├───────────────────┼─────────────┤ +│bDeviceProtocol │ 0 │ +├───────────────────┼─────────────┤ +│bMaxPacketSize0 │ 64 │ +├───────────────────┼─────────────┤ +│idVendor │ 0x303a │ +├───────────────────┼─────────────┤ +│idProduct │ 0x4004 │ +├───────────────────┼─────────────┤ +│bcdDevice │ 0x100 │ +├───────────────────┼─────────────┤ +│iManufacturer │ 0x1 │ +├───────────────────┼─────────────┤ +│iProduct │ 0x2 │ +├───────────────────┼─────────────┤ +│iSerialNumber │ 0x3 │ +├───────────────────┼─────────────┤ +│bNumConfigurations │ 0x1 │ +└───────────────────┴─────────────┘ +I (480) TinyUSB: TinyUSB Driver installed +I (480) example: USB initialization DONE +I (2490) example: Sending Keyboard report +I (3040) example: Sending Mouse report +``` diff --git a/tests/c_headers/idf-usb/device/tusb_hid/main/CMakeLists.txt b/tests/c_headers/idf-usb/device/tusb_hid/main/CMakeLists.txt new file mode 100644 index 00000000..ed6a4b2d --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_hid/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "tusb_hid_example_main.c" + INCLUDE_DIRS "." + PRIV_REQUIRES esp_driver_gpio + ) diff --git a/tests/c_headers/idf-usb/device/tusb_hid/main/idf_component.yml b/tests/c_headers/idf-usb/device/tusb_hid/main/idf_component.yml new file mode 100644 index 00000000..b60893c8 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_hid/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_tinyusb: "^1.1" + idf: "^5.0" diff --git a/tests/c_headers/idf-usb/device/tusb_hid/main/tusb_hid_example_main.c b/tests/c_headers/idf-usb/device/tusb_hid/main/tusb_hid_example_main.c new file mode 100644 index 00000000..6ac29716 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_hid/main/tusb_hid_example_main.c @@ -0,0 +1,194 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "tinyusb.h" +#include "class/hid/hid_device.h" +#include "driver/gpio.h" + +#define APP_BUTTON (GPIO_NUM_0) // Use BOOT signal by default +static const char *TAG = "example"; + +/************* TinyUSB descriptors ****************/ + +#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_HID * TUD_HID_DESC_LEN) + +/** + * @brief HID report descriptor + * + * In this example we implement Keyboard + Mouse HID device, + * so we must define both report descriptors + */ +const uint8_t hid_report_descriptor[] = { + TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(HID_ITF_PROTOCOL_KEYBOARD)), + TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(HID_ITF_PROTOCOL_MOUSE)) +}; + +/** + * @brief String descriptor + */ +const char* hid_string_descriptor[5] = { + // array of pointer to string descriptors + (char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + "123456", // 3: Serials, should use chip ID + "Example HID interface", // 4: HID +}; + +/** + * @brief Configuration descriptor + * + * This is a simple configuration descriptor that defines 1 configuration and 1 HID interface + */ +static const uint8_t hid_configuration_descriptor[] = { + // Configuration number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, 1, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + + // Interface number, string index, boot protocol, report descriptor len, EP In address, size & polling interval + TUD_HID_DESCRIPTOR(0, 4, false, sizeof(hid_report_descriptor), 0x81, 16, 10), +}; + +/********* TinyUSB HID callbacks ***************/ + +// Invoked when received GET HID REPORT DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) +{ + // We use only one interface and one HID report descriptor, so we can ignore parameter 'instance' + return hid_report_descriptor; +} + +// Invoked when received GET_REPORT control request +// Application must fill buffer report's content and return its length. +// Return zero will cause the stack to STALL request +uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) +{ + (void) instance; + (void) report_id; + (void) report_type; + (void) buffer; + (void) reqlen; + + return 0; +} + +// Invoked when received SET_REPORT control request or +// received data on OUT endpoint ( Report ID = 0, Type = 0 ) +void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) +{ +} + +/********* Application ***************/ + +typedef enum { + MOUSE_DIR_RIGHT, + MOUSE_DIR_DOWN, + MOUSE_DIR_LEFT, + MOUSE_DIR_UP, + MOUSE_DIR_MAX, +} mouse_dir_t; + +#define DISTANCE_MAX 125 +#define DELTA_SCALAR 5 + +static void mouse_draw_square_next_delta(int8_t *delta_x_ret, int8_t *delta_y_ret) +{ + static mouse_dir_t cur_dir = MOUSE_DIR_RIGHT; + static uint32_t distance = 0; + + // Calculate next delta + if (cur_dir == MOUSE_DIR_RIGHT) { + *delta_x_ret = DELTA_SCALAR; + *delta_y_ret = 0; + } else if (cur_dir == MOUSE_DIR_DOWN) { + *delta_x_ret = 0; + *delta_y_ret = DELTA_SCALAR; + } else if (cur_dir == MOUSE_DIR_LEFT) { + *delta_x_ret = -DELTA_SCALAR; + *delta_y_ret = 0; + } else if (cur_dir == MOUSE_DIR_UP) { + *delta_x_ret = 0; + *delta_y_ret = -DELTA_SCALAR; + } + + // Update cumulative distance for current direction + distance += DELTA_SCALAR; + // Check if we need to change direction + if (distance >= DISTANCE_MAX) { + distance = 0; + cur_dir++; + if (cur_dir == MOUSE_DIR_MAX) { + cur_dir = 0; + } + } +} + +static void app_send_hid_demo(void) +{ + // Keyboard output: Send key 'a/A' pressed and released + ESP_LOGI(TAG, "Sending Keyboard report"); + uint8_t keycode[6] = {HID_KEY_A}; + tud_hid_keyboard_report(HID_ITF_PROTOCOL_KEYBOARD, 0, keycode); + vTaskDelay(pdMS_TO_TICKS(50)); + tud_hid_keyboard_report(HID_ITF_PROTOCOL_KEYBOARD, 0, NULL); + + // Mouse output: Move mouse cursor in square trajectory + ESP_LOGI(TAG, "Sending Mouse report"); + int8_t delta_x; + int8_t delta_y; + for (int i = 0; i < (DISTANCE_MAX / DELTA_SCALAR) * 4; i++) { + // Get the next x and y delta in the draw square pattern + mouse_draw_square_next_delta(&delta_x, &delta_y); + tud_hid_mouse_report(HID_ITF_PROTOCOL_MOUSE, 0x00, delta_x, delta_y, 0, 0); + vTaskDelay(pdMS_TO_TICKS(20)); + } +} + +void app_main(void) +{ + // Initialize button that will trigger HID reports + const gpio_config_t boot_button_config = { + .pin_bit_mask = BIT64(APP_BUTTON), + .mode = GPIO_MODE_INPUT, + .intr_type = GPIO_INTR_DISABLE, + .pull_up_en = true, + .pull_down_en = false, + }; + ESP_ERROR_CHECK(gpio_config(&boot_button_config)); + + ESP_LOGI(TAG, "USB initialization"); + const tinyusb_config_t tusb_cfg = { + .device_descriptor = NULL, + .string_descriptor = hid_string_descriptor, + .string_descriptor_count = sizeof(hid_string_descriptor) / sizeof(hid_string_descriptor[0]), + .external_phy = false, +#if (TUD_OPT_HIGH_SPEED) + .fs_configuration_descriptor = hid_configuration_descriptor, // HID configuration descriptor for full-speed and high-speed are the same + .hs_configuration_descriptor = hid_configuration_descriptor, + .qualifier_descriptor = NULL, +#else + .configuration_descriptor = hid_configuration_descriptor, +#endif // TUD_OPT_HIGH_SPEED + }; + + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); + ESP_LOGI(TAG, "USB initialization DONE"); + + while (1) { + if (tud_mounted()) { + static bool send_hid_data = true; + if (send_hid_data) { + app_send_hid_demo(); + } + send_hid_data = !gpio_get_level(APP_BUTTON); + } + vTaskDelay(pdMS_TO_TICKS(100)); + } +} diff --git a/tests/c_headers/idf-usb/device/tusb_hid/pytest_usb_device_hid.py b/tests/c_headers/idf-usb/device/tusb_hid/pytest_usb_device_hid.py new file mode 100644 index 00000000..986b293f --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_hid/pytest_usb_device_hid.py @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.temp_skip_ci(targets=['esp32s3'], reason='lack of runners with usb_device tag') +@pytest.mark.usb_device +@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32p4'], indirect=['target']) +def test_usb_device_hid_example(dut: Dut) -> None: + dut.expect_exact('USB initialization DONE') + dut.expect_exact('Sending Keyboard report') + dut.expect_exact('Sending Mouse report') diff --git a/tests/c_headers/idf-usb/device/tusb_hid/sdkconfig.defaults b/tests/c_headers/idf-usb/device/tusb_hid/sdkconfig.defaults new file mode 100644 index 00000000..0dc3a98d --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_hid/sdkconfig.defaults @@ -0,0 +1,4 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_TINYUSB_HID_COUNT=1 diff --git a/tests/c_headers/idf-usb/device/tusb_midi/CMakeLists.txt b/tests/c_headers/idf-usb/device/tusb_midi/CMakeLists.txt new file mode 100644 index 00000000..91e90baa --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_midi/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(tusb_midi) diff --git a/tests/c_headers/idf-usb/device/tusb_midi/README.md b/tests/c_headers/idf-usb/device/tusb_midi/README.md new file mode 100644 index 00000000..7007230d --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_midi/README.md @@ -0,0 +1,83 @@ +| Supported Targets | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | + +# TinyUSB MIDI Device Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example shows how to set up ESP chip to work as a USB MIDI Device. +It outputs a MIDI note sequence via the native USB port. + +As a USB stack, a TinyUSB component is used. + +## How to use example + +### Hardware Required + +Any ESP board that have USB-OTG supported. + +#### Pin Assignment + +_Note:_ In case your board doesn't have micro-USB connector connected to USB-OTG peripheral, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below. + +See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments). + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +```bash +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## MIDI output + +You can use several programs on your computer to listen to the ESP's MIDI output depending on your operating system, e.g.: + +* Windows: `MIDI-OX` +* Linux: `qsynth` with `qjackctl` +* macOS: `SimpleSynth` + +## Example Output + +After the flashing you should see the output at idf monitor: + +``` +I (285) example: USB initialization +I (285) tusb_desc: +┌─────────────────────────────────┐ +│ USB Device Descriptor Summary │ +├───────────────────┬─────────────┤ +│bDeviceClass │ 0 │ +├───────────────────┼─────────────┤ +│bDeviceSubClass │ 0 │ +├───────────────────┼─────────────┤ +│bDeviceProtocol │ 0 │ +├───────────────────┼─────────────┤ +│bMaxPacketSize0 │ 64 │ +├───────────────────┼─────────────┤ +│idVendor │ 0x303a │ +├───────────────────┼─────────────┤ +│idProduct │ 0x4008 │ +├───────────────────┼─────────────┤ +│bcdDevice │ 0x100 │ +├───────────────────┼─────────────┤ +│iManufacturer │ 0x1 │ +├───────────────────┼─────────────┤ +│iProduct │ 0x2 │ +├───────────────────┼─────────────┤ +│iSerialNumber │ 0x3 │ +├───────────────────┼─────────────┤ +│bNumConfigurations │ 0x1 │ +└───────────────────┴─────────────┘ +I (455) TinyUSB: TinyUSB Driver installed +I (465) example: USB initialization DONE +``` + +Disconnect UART-to-USB port and connect the native USB port to a computer then the device should show up as a USB MIDI Device while outputting notes. diff --git a/tests/c_headers/idf-usb/device/tusb_midi/main/CMakeLists.txt b/tests/c_headers/idf-usb/device/tusb_midi/main/CMakeLists.txt new file mode 100644 index 00000000..01775bf6 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_midi/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "tusb_midi_main.c" + INCLUDE_DIRS "." + PRIV_REQUIRES esp_timer + ) diff --git a/tests/c_headers/idf-usb/device/tusb_midi/main/idf_component.yml b/tests/c_headers/idf-usb/device/tusb_midi/main/idf_component.yml new file mode 100644 index 00000000..b60893c8 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_midi/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_tinyusb: "^1.1" + idf: "^5.0" diff --git a/tests/c_headers/idf-usb/device/tusb_midi/main/tusb_midi_main.c b/tests/c_headers/idf-usb/device/tusb_midi/main/tusb_midi_main.c new file mode 100644 index 00000000..04fe850b --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_midi/main/tusb_midi_main.c @@ -0,0 +1,185 @@ +/* + * SPDX-FileCopyrightText: 2019 Ha Thach (tinyusb.org) + * + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: 2022-2024 Espressif Systems (Shanghai) CO LTD + */ + +#include +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "tinyusb.h" +#include "esp_timer.h" + +static const char *TAG = "example"; + +/** Helper defines **/ + +// Interface counter +enum interface_count { +#if CFG_TUD_MIDI + ITF_NUM_MIDI = 0, + ITF_NUM_MIDI_STREAMING, +#endif + ITF_COUNT +}; + +// USB Endpoint numbers +enum usb_endpoints { + // Available USB Endpoints: 5 IN/OUT EPs and 1 IN EP + EP_EMPTY = 0, +#if CFG_TUD_MIDI + EPNUM_MIDI, +#endif +}; + +/** TinyUSB descriptors **/ + +#define TUSB_DESCRIPTOR_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_MIDI * TUD_MIDI_DESC_LEN) + +/** + * @brief String descriptor + */ +static const char* s_str_desc[5] = { + // array of pointer to string descriptors + (char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + "123456", // 3: Serials, should use chip ID + "Example MIDI device", // 4: MIDI +}; + +/** + * @brief Configuration descriptor + * + * This is a simple configuration descriptor that defines 1 configuration and a MIDI interface + */ +static const uint8_t s_midi_cfg_desc[] = { + // Configuration number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_COUNT, 0, TUSB_DESCRIPTOR_TOTAL_LEN, 0, 100), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 4, EPNUM_MIDI, (0x80 | EPNUM_MIDI), 64), +}; + +#if (TUD_OPT_HIGH_SPEED) +/** + * @brief High Speed configuration descriptor + * + * This is a simple configuration descriptor that defines 1 configuration and a MIDI interface + */ +static const uint8_t s_midi_hs_cfg_desc[] = { + // Configuration number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_COUNT, 0, TUSB_DESCRIPTOR_TOTAL_LEN, 0, 100), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 4, EPNUM_MIDI, (0x80 | EPNUM_MIDI), 512), +}; +#endif // TUD_OPT_HIGH_SPEED + +static void midi_task_read_example(void *arg) +{ + // The MIDI interface always creates input and output port/jack descriptors + // regardless of these being used or not. Therefore incoming traffic should be read + // (possibly just discarded) to avoid the sender blocking in IO + uint8_t packet[4]; + bool read = false; + for (;;) { + vTaskDelay(1); + while (tud_midi_available()) { + read = tud_midi_packet_read(packet); + if (read) { + ESP_LOGI(TAG, "Read - Time (ms since boot): %lld, Data: %02hhX %02hhX %02hhX %02hhX", + esp_timer_get_time(), packet[0], packet[1], packet[2], packet[3]); + } + } + } +} + +// Basic MIDI Messages +#define NOTE_OFF 0x80 +#define NOTE_ON 0x90 + +static void periodic_midi_write_example_cb(void *arg) +{ + // Example melody stored as an array of note values + uint8_t const note_sequence[] = { + 74, 78, 81, 86, 90, 93, 98, 102, 57, 61, 66, 69, 73, 78, 81, 85, 88, 92, 97, 100, 97, 92, 88, 85, 81, 78, + 74, 69, 66, 62, 57, 62, 66, 69, 74, 78, 81, 86, 90, 93, 97, 102, 97, 93, 90, 85, 81, 78, 73, 68, 64, 61, + 56, 61, 64, 68, 74, 78, 81, 86, 90, 93, 98, 102 + }; + + static uint8_t const cable_num = 0; // MIDI jack associated with USB endpoint + static uint8_t const channel = 0; // 0 for channel 1 + static uint32_t note_pos = 0; + + // Previous positions in the note sequence. + int previous = note_pos - 1; + + // If we currently are at position 0, set the + // previous position to the last note in the sequence. + if (previous < 0) { + previous = sizeof(note_sequence) - 1; + } + + // Send Note On for current position at full velocity (127) on channel 1. + ESP_LOGI(TAG, "Writing MIDI data %d", note_sequence[note_pos]); + + if (tud_midi_mounted()) { + uint8_t note_on[3] = {NOTE_ON | channel, note_sequence[note_pos], 127}; + tud_midi_stream_write(cable_num, note_on, 3); + + // Send Note Off for previous note. + uint8_t note_off[3] = {NOTE_OFF | channel, note_sequence[previous], 0}; + tud_midi_stream_write(cable_num, note_off, 3); + } + + // Increment position + note_pos++; + + // If we are at the end of the sequence, start over. + if (note_pos >= sizeof(note_sequence)) { + note_pos = 0; + } +} + +void app_main(void) +{ + ESP_LOGI(TAG, "USB initialization"); + + tinyusb_config_t const tusb_cfg = { + .device_descriptor = NULL, // If device_descriptor is NULL, tinyusb_driver_install() will use Kconfig + .string_descriptor = s_str_desc, + .string_descriptor_count = sizeof(s_str_desc) / sizeof(s_str_desc[0]), + .external_phy = false, +#if (TUD_OPT_HIGH_SPEED) + .fs_configuration_descriptor = s_midi_cfg_desc, // HID configuration descriptor for full-speed and high-speed are the same + .hs_configuration_descriptor = s_midi_hs_cfg_desc, + .qualifier_descriptor = NULL, +#else + .configuration_descriptor = s_midi_cfg_desc, +#endif // TUD_OPT_HIGH_SPEED + }; + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); + + ESP_LOGI(TAG, "USB initialization DONE"); + + // Periodically send MIDI packets + int const tempo = 286; + const esp_timer_create_args_t periodic_midi_args = { + .callback = &periodic_midi_write_example_cb, + /* name is optional, but may help identify the timer when debugging */ + .name = "periodic_midi" + }; + + ESP_LOGI(TAG, "MIDI write task init"); + esp_timer_handle_t periodic_midi_timer; + ESP_ERROR_CHECK(esp_timer_create(&periodic_midi_args, &periodic_midi_timer)); + ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_midi_timer, tempo * 1000)); + + // Read received MIDI packets + ESP_LOGI(TAG, "MIDI read task init"); + xTaskCreate(midi_task_read_example, "midi_task_read_example", 4 * 1024, NULL, 5, NULL); +} diff --git a/tests/c_headers/idf-usb/device/tusb_midi/pytest_usb_device_midi.py b/tests/c_headers/idf-usb/device/tusb_midi/pytest_usb_device_midi.py new file mode 100644 index 00000000..9c6a5762 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_midi/pytest_usb_device_midi.py @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.temp_skip_ci(targets=['esp32s3'], reason='lack of runners with usb_device tag') +@pytest.mark.usb_device +@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32p4'], indirect=['target']) +def test_usb_device_midi_example(dut: Dut) -> None: + dut.expect_exact('USB initialization DONE') + dut.expect_exact('MIDI write task init') + dut.expect_exact('MIDI read task init') + dut.expect_exact('Writing MIDI data 74') diff --git a/tests/c_headers/idf-usb/device/tusb_midi/sdkconfig.defaults b/tests/c_headers/idf-usb/device/tusb_midi/sdkconfig.defaults new file mode 100644 index 00000000..4708b2ac --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_midi/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_TINYUSB_MIDI_COUNT=1 diff --git a/tests/c_headers/idf-usb/device/tusb_msc/CMakeLists.txt b/tests/c_headers/idf-usb/device/tusb_msc/CMakeLists.txt new file mode 100644 index 00000000..0863d423 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_msc/CMakeLists.txt @@ -0,0 +1,10 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(tusb_msc) diff --git a/tests/c_headers/idf-usb/device/tusb_msc/README.md b/tests/c_headers/idf-usb/device/tusb_msc/README.md new file mode 100644 index 00000000..134b2ecb --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_msc/README.md @@ -0,0 +1,203 @@ +| Supported Targets | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | + +# TinyUSB Mass Storage Device Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +Mass Storage Devices are one of the most common USB devices. It use Mass Storage Class (MSC) that allow access to their internal data storage. +This example contains code to make ESP based device recognizable by USB-hosts as a USB Mass Storage Device. +It either allows the embedded application i.e. example to access the partition or Host PC accesses the partition over USB MSC. +They can't be allowed to access the partition at the same time. + +This example supports storage media of two types: +1. SPI Flash +2. SD MMC Card + +Data is read/written from/to SPI Flash through wear-levelling APIs. Wear leveling is a technique that helps to distribute wear and tear among sectors more evenly without requiring any attention from the user. As a result, it helps in extending the life of each sector of the Flash memory. + +As a USB stack, a TinyUSB component is used. + +## How to use example + +### Scenarios +1. USB which accesses the ESP MSC Partition is unplugged initially and the board is powered-on. + - Result: Host PC can't access the partition over USB MSC. Application example can perform operations (read, write) on partition. +2. USB which accesses the ESP MSC Partition is already plugged-in at boot time. + - Result: Host PC recongnize it as removable device and can access the partition over USB MSC. Application example can't perform any operation on partition. +3. USB which accesses the ESP MSC Partition is plugged-in at boot-up. After boot-up, it is ejected on Host PC manually by user. + - Result: Host PC can't access the partition over USB MSC. Application example can perform operations (read, write) on partition. +4. USB which accesses the ESP MSC Partition is plugged-in at boot-up. It is then unplugged(removed) from Host PC manually by user. + - Result: The behaviour is different for bus-powered devices and self-powered devices + - (a) Bus-Powered devices - Both Host PC as well as application example can't access the partition over USB MSC. Here, the device will be Powered-off. + - (b) Self-Powered devices - Here, the device can be powered-on even after unplugging the device from Host PC. These behaviour can be further categorize in two ways: + - (i) Self-Powered Devices without VBUS monitoring - Both Host PC as well as application example can't access the partition over USB MSC. + - (ii) Self-Powered Devices with VBUS monitoring - Host PC can't access the partition over USB MSC. Application example can perform operations (read, write) on partition. Here, in ``tinyusb_config_t`` user must set ``self_powered`` to ``true`` and ``vbus_monitor_io`` to GPIO number (``VBUS_MONITORING_GPIO_NUM``) that will be used for VBUS monitoring. + +### Hardware Required + +1. If the storage media is SPI Flash, any ESP board that have USB-OTG is supported. +2. If the storage media is SD MMC Card, any ESP board with SD MMC card slot and an SD card is required. For Ex - ESP32-S3-USB-OTG + +### Pin Assignment + +_Note:_ In case your board doesn't have micro-USB connector connected to USB-OTG peripheral, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below. + +See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments). + +Next, for Self-Powered Devices with VBUS monitoring, user must set ``self_powered`` to ``true`` and ``vbus_monitor_io`` to GPIO number (``VBUS_MONITORING_GPIO_NUM``) that will be used for VBUS monitoring. + +### Additional Pin assignments for ESP32-S3 for accessing SD MMC Card + +On ESP32-S3, SDMMC peripheral is connected to GPIO pins using GPIO matrix. This allows arbitrary GPIOs to be used to connect an SD card. In this example, GPIOs can be configured in two ways: + +1. Using menuconfig: Run `idf.py menuconfig` in the project directory, open "USB DEV MSC Example Configuration" and select "SDMMC CARD" for "Storage Media Used". +2. In the source code: See the initialization of ``sdmmc_slot_config_t slot_config`` structure in the example code. + +The table below lists the default pin assignments. + +When using an ESP32-S3-USB-OTG board, this example runs without any extra modifications required. Only an SD card needs to be inserted into the slot. + +ESP32-S3 pin | SD card pin | Notes +--------------|-------------|------------ +GPIO36 | CLK | 10k pullup +GPIO35 | CMD | 10k pullup +GPIO37 | D0 | 10k pullup +GPIO38 | D1 | not used in 1-line SD mode; 10k pullup in 4-line mode +GPIO33 | D2 | not used in 1-line SD mode; 10k pullup in 4-line mode +GPIO34 | D3 | not used in 1-line SD mode, but card's D3 pin must have a 10k pullup + +By default, this example uses 4 line SD mode, utilizing 6 pins: CLK, CMD, D0 - D3. It is possible to use 1-line mode (CLK, CMD, D0) by changing "SD/MMC bus width" in the example configuration menu (see `CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_1`). + +Note that even if card's D3 line is not connected to the ESP chip, it still has to be pulled up, otherwise the card will go into SPI protocol mode. + +### Build and Flash + +1. By default, the example will compile to access SPI Flash as storage media. Here, SPI Flash Wear Levelling WL_SECTOR_SIZE is set to 512 and WL_SECTOR_MODE is set to PERF in Menuconfig. +2. In order to access SD MMC card as storage media, configuration has to be changed using `idf.py menuconfig`: + - i. Open "USB Dev MSC Example Configuration" and select "SDMMC CARD" for "Storage Media Used" + - ii. Open "SD/MMC bus width" and select between "4 lines (D0 - D3)" or "1 line (D0)" + - iii. Select the GPIO Pin numbers for SD Card Pin. + - iv. Save the configuration. + +Build the project and flash it to the board, then run monitor tool to view serial output: + +```bash +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +After the flashing you should see the output at idf monitor: + +``` +I (329) app_start: Starting scheduler on CPU0 +I (334) app_start: Starting scheduler on CPU1 +I (334) main_task: Started on CPU0 +I (344) main_task: Calling app_main() +I (344) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (354) example_main: Initializing storage... +I (364) example_main: Initializing wear levelling +I (374) example_main: Mount storage... +I (374) example_main: +ls command output: +.fseventsd +_pic.jpg +.__pic.jpg +README.MD +I (384) example_main: USB MSC initialization +I (384) tusb_desc: +┌─────────────────────────────────┐ +│ USB Device Descriptor Summary │ +├───────────────────┬─────────────┤ +│bDeviceClass │ 239 │ +├───────────────────┼─────────────┤ +│bDeviceSubClass │ 2 │ +├───────────────────┼─────────────┤ +│bDeviceProtocol │ 1 │ +├───────────────────┼─────────────┤ +│bMaxPacketSize0 │ 64 │ +├───────────────────┼─────────────┤ +│idVendor │ 0x303a │ +├───────────────────┼─────────────┤ +│idProduct │ 0x4002 │ +├───────────────────┼─────────────┤ +│bcdDevice │ 0x100 │ +├───────────────────┼─────────────┤ +│iManufacturer │ 0x1 │ +├───────────────────┼─────────────┤ +│iProduct │ 0x2 │ +├───────────────────┼─────────────┤ +│iSerialNumber │ 0x3 │ +├───────────────────┼─────────────┤ +│bNumConfigurations │ 0x1 │ +└───────────────────┴─────────────┘ +I (554) TinyUSB: TinyUSB Driver installed +I (564) example_main: USB MSC initialization DONE + +Type 'help' to get the list of commands. +Use UP/DOWN arrows to navigate through command history. +Press TAB when typing command name to auto-complete. +I (724) main_task: Returned from app_main() +esp32s3> +esp32s3> help +help + Print the list of registered commands + +read + read BASE_PATH/README.MD and print its contents + +write + create file BASE_PATH/README.MD if it does not exist + +size + show storage size and sector size + +expose + Expose Storage to Host + +status + Status of storage exposure over USB + +exit + exit from application + +esp32s3> +esp32s3> read +E (80054) example_main: storage exposed over USB. Application can't read from storage. +Command returned non-zero error code: 0xffffffff (ESP_FAIL) +esp32s3> write +E (83134) example_main: storage exposed over USB. Application can't write to storage. +Command returned non-zero error code: 0xffffffff (ESP_FAIL) +esp32s3> size +E (85354) example_main: storage exposed over USB. Application can't access storage +Command returned non-zero error code: 0xffffffff (ESP_FAIL) +esp32s3> status +storage exposed over USB: Yes +esp32s3> expose +E (108344) example_main: storage is already exposed +Command returned non-zero error code: 0xffffffff (ESP_FAIL) +esp32s3> +esp32s3> +esp32s3> read +Mass Storage Devices are one of the most common USB devices. It use Mass Storage Class (MSC) that allow access to their internal data storage. +In this example, ESP chip will be recognised by host (PC) as Mass Storage Device. +Upon connection to USB host (PC), the example application will initialize the storage module and then the storage will be seen as removable device on PC. +esp32s3> write +esp32s3> size +Storage Capacity 0MB +esp32s3> status +storage exposed over USB: No +esp32s3> expose +I (181224) example_main: Unmount storage... +esp32s3> status +storage exposed over USB: Yes +esp32s3> +esp32s3> +``` diff --git a/tests/c_headers/idf-usb/device/tusb_msc/main/CMakeLists.txt b/tests/c_headers/idf-usb/device/tusb_msc/main/CMakeLists.txt new file mode 100644 index 00000000..7c7438e1 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_msc/main/CMakeLists.txt @@ -0,0 +1,11 @@ +set(priv_requires fatfs console) + +if(CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH) + list(APPEND priv_requires wear_levelling esp_partition) +endif() + +idf_component_register( + SRCS "tusb_msc_main.c" + INCLUDE_DIRS . + PRIV_REQUIRES "${priv_requires}" +) diff --git a/tests/c_headers/idf-usb/device/tusb_msc/main/Kconfig.projbuild b/tests/c_headers/idf-usb/device/tusb_msc/main/Kconfig.projbuild new file mode 100644 index 00000000..a43bb71a --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_msc/main/Kconfig.projbuild @@ -0,0 +1,100 @@ +menu "TinyUSB MSC Example Configuration" + + orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps" + + choice EXAMPLE_STORAGE_MEDIA + prompt "Storage Media Used" + default EXAMPLE_STORAGE_MEDIA_SPIFLASH + help + Select the storage media that is exposed to USB host. + + config EXAMPLE_STORAGE_MEDIA_SPIFLASH + bool "SPI FLASH" + + config EXAMPLE_STORAGE_MEDIA_SDMMC + bool "SDMMC CARD" + depends on IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4 + endchoice + + if EXAMPLE_STORAGE_MEDIA_SDMMC + + choice EXAMPLE_SDMMC_BUS_WIDTH + prompt "SD/MMC bus width" + default EXAMPLE_SDMMC_BUS_WIDTH_4 + help + Select the bus width of SD or MMC interface. + Note that even if 1 line mode is used, D3 pin of the SD card must + have a pull-up resistor connected. Otherwise the card may enter + SPI mode, the only way to recover from which is to cycle power to the card. + + config EXAMPLE_SDMMC_BUS_WIDTH_4 + bool "4 lines (D0 - D3)" + + config EXAMPLE_SDMMC_BUS_WIDTH_1 + bool "1 line (D0)" + endchoice + + if SOC_SDMMC_USE_GPIO_MATRIX + + config EXAMPLE_PIN_CMD + int "CMD GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 35 if IDF_TARGET_ESP32S3 + default 44 if IDF_TARGET_ESP32P4 + + config EXAMPLE_PIN_CLK + int "CLK GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 36 if IDF_TARGET_ESP32S3 + default 43 if IDF_TARGET_ESP32P4 + + config EXAMPLE_PIN_D0 + int "D0 GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 37 if IDF_TARGET_ESP32S3 + default 39 if IDF_TARGET_ESP32P4 + + if EXAMPLE_SDMMC_BUS_WIDTH_4 + + config EXAMPLE_PIN_D1 + int "D1 GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 38 if IDF_TARGET_ESP32S3 + default 40 if IDF_TARGET_ESP32P4 + + config EXAMPLE_PIN_D2 + int "D2 GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 33 if IDF_TARGET_ESP32S3 + default 41 if IDF_TARGET_ESP32P4 + + config EXAMPLE_PIN_D3 + int "D3 GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 34 if IDF_TARGET_ESP32S3 + default 42 if IDF_TARGET_ESP32P4 + + endif # EXAMPLE_SDMMC_BUS_WIDTH_4 + + endif # SOC_SDMMC_USE_GPIO_MATRIX + + config EXAMPLE_SD_PWR_CTRL_LDO_INTERNAL_IO + depends on SOC_SDMMC_IO_POWER_EXTERNAL + bool "SDMMC powered by internal LDO" + default y + help + Enable when internal LDO of ESP32-P4 is used as a power supply for SD card. Specify the LDO IO. + Disable when external power supply is used as a power supply for SD card. + + config EXAMPLE_SD_PWR_CTRL_LDO_IO_ID + depends on SOC_SDMMC_IO_POWER_EXTERNAL && EXAMPLE_SD_PWR_CTRL_LDO_INTERNAL_IO + int "LDO IO" + default 4 if IDF_TARGET_ESP32P4 + help + There are several IOs in ESP32-P4 which are in "SD card IO" power domain. + Power has to be supplied to this power domain externally (from outside the chip) via one of the pins. + Based on the schematic, specify the LDO IO pin. + + endif # EXAMPLE_STORAGE_MEDIA_SDMMC + +endmenu diff --git a/tests/c_headers/idf-usb/device/tusb_msc/main/idf_component.yml b/tests/c_headers/idf-usb/device/tusb_msc/main/idf_component.yml new file mode 100644 index 00000000..1659a1eb --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_msc/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_tinyusb: "^1.4.2" + idf: "^5.0" diff --git a/tests/c_headers/idf-usb/device/tusb_msc/main/tusb_msc_main.c b/tests/c_headers/idf-usb/device/tusb_msc/main/tusb_msc_main.c new file mode 100644 index 00000000..ccea2ba6 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_msc/main/tusb_msc_main.c @@ -0,0 +1,491 @@ +/* + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +/* DESCRIPTION: + * This example contains code to make ESP32 based device recognizable by USB-hosts as a USB Mass Storage Device. + * It either allows the embedded application i.e. example to access the partition or Host PC accesses the partition over USB MSC. + * They can't be allowed to access the partition at the same time. + * For different scenarios and behaviour, Refer to README of this example. + */ + +#include +#include +#include +#include "sdkconfig.h" +#include "esp_console.h" +#include "esp_check.h" +#include "esp_partition.h" +#include "driver/gpio.h" +#include "tinyusb.h" +#include "tusb_msc_storage.h" +#ifdef CONFIG_EXAMPLE_STORAGE_MEDIA_SDMMC +#include "sdmmc_cmd.h" +#include "diskio_impl.h" +#include "diskio_sdmmc.h" +#if CONFIG_EXAMPLE_SD_PWR_CTRL_LDO_INTERNAL_IO +#include "sd_pwr_ctrl_by_on_chip_ldo.h" +#endif // CONFIG_EXAMPLE_SD_PWR_CTRL_LDO_INTERNAL_IO +#endif + +/* + * We warn if a secondary serial console is enabled. A secondary serial console is always output-only and + * hence not very useful for interactive console applications. If you encounter this warning, consider disabling + * the secondary serial console in menuconfig unless you know what you are doing. + */ +#if SOC_USB_SERIAL_JTAG_SUPPORTED +#if !CONFIG_ESP_CONSOLE_SECONDARY_NONE +#warning "A secondary serial console is not useful when using the console component. Please disable it in menuconfig." +#endif +#endif + +static const char *TAG = "example_main"; +static esp_console_repl_t *repl = NULL; + +static SemaphoreHandle_t _wait_console_smp = NULL; + +/* TinyUSB descriptors + ********************************************************************* */ +#define EPNUM_MSC 1 +#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN) + +enum { + ITF_NUM_MSC = 0, + ITF_NUM_TOTAL +}; + +enum { + EDPT_CTRL_OUT = 0x00, + EDPT_CTRL_IN = 0x80, + + EDPT_MSC_OUT = 0x01, + EDPT_MSC_IN = 0x81, +}; + +static tusb_desc_device_t descriptor_config = { + .bLength = sizeof(descriptor_config), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .idVendor = 0x303A, // This is Espressif VID. This needs to be changed according to Users / Customers + .idProduct = 0x4002, + .bcdDevice = 0x100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + .bNumConfigurations = 0x01 +}; + +static uint8_t const msc_fs_configuration_desc[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 64), +}; + +#if (TUD_OPT_HIGH_SPEED) +static const tusb_desc_device_qualifier_t device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0 +}; + +static uint8_t const msc_hs_configuration_desc[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 512), +}; +#endif // TUD_OPT_HIGH_SPEED + +static char const *string_desc_arr[] = { + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + "123456", // 3: Serials + "Example MSC", // 4. MSC +}; +/*********************************************************************** TinyUSB descriptors*/ + +#define BASE_PATH "/data" // base path to mount the partition + +#define PROMPT_STR CONFIG_IDF_TARGET +static int console_unmount(int argc, char **argv); +static int console_read(int argc, char **argv); +static int console_write(int argc, char **argv); +static int console_size(int argc, char **argv); +static int console_status(int argc, char **argv); +static int console_exit(int argc, char **argv); +const esp_console_cmd_t cmds[] = { + { + .command = "read", + .help = "read BASE_PATH/README.MD and print its contents", + .hint = NULL, + .func = &console_read, + }, + { + .command = "write", + .help = "create file BASE_PATH/README.MD if it does not exist", + .hint = NULL, + .func = &console_write, + }, + { + .command = "size", + .help = "show storage size and sector size", + .hint = NULL, + .func = &console_size, + }, + { + .command = "expose", + .help = "Expose Storage to Host", + .hint = NULL, + .func = &console_unmount, + }, + { + .command = "status", + .help = "Status of storage exposure over USB", + .hint = NULL, + .func = &console_status, + }, + { + .command = "exit", + .help = "exit from application", + .hint = NULL, + .func = &console_exit, + } +}; + +// mount the partition and show all the files in BASE_PATH +static void _mount(void) +{ + ESP_LOGI(TAG, "Mount storage..."); + ESP_ERROR_CHECK(tinyusb_msc_storage_mount(BASE_PATH)); + + // List all the files in this directory + ESP_LOGI(TAG, "\nls command output:"); + struct dirent *d; + DIR *dh = opendir(BASE_PATH); + if (!dh) { + if (errno == ENOENT) { + //If the directory is not found + ESP_LOGE(TAG, "Directory doesn't exist %s", BASE_PATH); + } else { + //If the directory is not readable then throw error and exit + ESP_LOGE(TAG, "Unable to read directory %s", BASE_PATH); + } + return; + } + //While the next entry is not readable we will print directory files + while ((d = readdir(dh)) != NULL) { + printf("%s\n", d->d_name); + } + return; +} + +// unmount storage +static int console_unmount(int argc, char **argv) +{ + if (tinyusb_msc_storage_in_use_by_usb_host()) { + ESP_LOGE(TAG, "storage is already exposed"); + return -1; + } + ESP_LOGI(TAG, "Unmount storage..."); + ESP_ERROR_CHECK(tinyusb_msc_storage_unmount()); + return 0; +} + +// read BASE_PATH/README.MD and print its contents +static int console_read(int argc, char **argv) +{ + if (tinyusb_msc_storage_in_use_by_usb_host()) { + ESP_LOGE(TAG, "storage exposed over USB. Application can't read from storage."); + return -1; + } + ESP_LOGD(TAG, "read from storage:"); + const char *filename = BASE_PATH "/README.MD"; + FILE *ptr = fopen(filename, "r"); + if (ptr == NULL) { + ESP_LOGE(TAG, "Filename not present - %s", filename); + return -1; + } + char buf[1024]; + while (fgets(buf, 1000, ptr) != NULL) { + printf("%s", buf); + } + fclose(ptr); + return 0; +} + +// create file BASE_PATH/README.MD if it does not exist +static int console_write(int argc, char **argv) +{ + if (tinyusb_msc_storage_in_use_by_usb_host()) { + ESP_LOGE(TAG, "storage exposed over USB. Application can't write to storage."); + return -1; + } + ESP_LOGD(TAG, "write to storage:"); + const char *filename = BASE_PATH "/README.MD"; + FILE *fd = fopen(filename, "r"); + if (!fd) { + ESP_LOGW(TAG, "README.MD doesn't exist yet, creating"); + fd = fopen(filename, "w"); + fprintf(fd, "Mass Storage Devices are one of the most common USB devices. It use Mass Storage Class (MSC) that allow access to their internal data storage.\n"); + fprintf(fd, "In this example, ESP chip will be recognised by host (PC) as Mass Storage Device.\n"); + fprintf(fd, "Upon connection to USB host (PC), the example application will initialize the storage module and then the storage will be seen as removable device on PC.\n"); + fclose(fd); + } + return 0; +} + +// Show storage size and sector size +static int console_size(int argc, char **argv) +{ + if (tinyusb_msc_storage_in_use_by_usb_host()) { + ESP_LOGE(TAG, "storage exposed over USB. Application can't access storage"); + return -1; + } + uint32_t sec_count = tinyusb_msc_storage_get_sector_count(); + uint32_t sec_size = tinyusb_msc_storage_get_sector_size(); + printf("Storage Capacity %lluMB\n", ((uint64_t) sec_count) * sec_size / (1024 * 1024)); + return 0; +} + +// Show storage status +static int console_status(int argc, char **argv) +{ + printf("storage exposed over USB: %s\n", tinyusb_msc_storage_in_use_by_usb_host() ? "Yes" : "No"); + return 0; +} + +// Exit from application +static int console_exit(int argc, char **argv) +{ + tinyusb_msc_unregister_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED); + tinyusb_msc_storage_deinit(); + tinyusb_driver_uninstall(); + + xSemaphoreGive(_wait_console_smp); + + printf("Application Exit\n"); + + return 0; +} + +// callback that is delivered when storage is mounted/unmounted by application. +static void storage_mount_changed_cb(tinyusb_msc_event_t *event) +{ + ESP_LOGI(TAG, "Storage mounted to application: %s", event->mount_changed_data.is_mounted ? "Yes" : "No"); +} + +#ifdef CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH +static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle) +{ + ESP_LOGI(TAG, "Initializing wear levelling"); + + const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL); + if (data_partition == NULL) { + ESP_LOGE(TAG, "Failed to find FATFS partition. Check the partition table."); + return ESP_ERR_NOT_FOUND; + } + + return wl_mount(data_partition, wl_handle); +} +#else // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH +static esp_err_t storage_init_sdmmc(sdmmc_card_t **card) +{ + esp_err_t ret = ESP_OK; + bool host_init = false; + sdmmc_card_t *sd_card; + + ESP_LOGI(TAG, "Initializing SDCard"); + + // By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz) + // For setting a specific frequency, use host.max_freq_khz (range 400kHz - 40MHz for SDMMC) + // Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000; + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + + // For SoCs where the SD power can be supplied both via an internal or external (e.g. on-board LDO) power supply. + // When using specific IO pins (which can be used for ultra high-speed SDMMC) to connect to the SD card + // and the internal LDO power supply, we need to initialize the power supply first. +#if CONFIG_EXAMPLE_SD_PWR_CTRL_LDO_INTERNAL_IO + sd_pwr_ctrl_ldo_config_t ldo_config = { + .ldo_chan_id = CONFIG_EXAMPLE_SD_PWR_CTRL_LDO_IO_ID, + }; + sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL; + + ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create a new on-chip LDO power control driver"); + return ret; + } + host.pwr_ctrl_handle = pwr_ctrl_handle; +#endif + + // This initializes the slot without card detect (CD) and write protect (WP) signals. + // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + + // For SD Card, set bus width to use +#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4 + slot_config.width = 4; +#else + slot_config.width = 1; +#endif // CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4 + + // On chips where the GPIOs used for SD card can be configured, set the user defined values +#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX + slot_config.clk = CONFIG_EXAMPLE_PIN_CLK; + slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD; + slot_config.d0 = CONFIG_EXAMPLE_PIN_D0; +#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4 + slot_config.d1 = CONFIG_EXAMPLE_PIN_D1; + slot_config.d2 = CONFIG_EXAMPLE_PIN_D2; + slot_config.d3 = CONFIG_EXAMPLE_PIN_D3; +#endif // CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4 + +#endif // CONFIG_SOC_SDMMC_USE_GPIO_MATRIX + + // Enable internal pullups on enabled pins. The internal pullups + // are insufficient however, please make sure 10k external pullups are + // connected on the bus. This is for debug / example purpose only. + slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; + + // not using ff_memalloc here, as allocation in internal RAM is preferred + sd_card = (sdmmc_card_t *)malloc(sizeof(sdmmc_card_t)); + ESP_GOTO_ON_FALSE(sd_card, ESP_ERR_NO_MEM, clean, TAG, "could not allocate new sdmmc_card_t"); + + ESP_GOTO_ON_ERROR((*host.init)(), clean, TAG, "Host Config Init fail"); + host_init = true; + + ESP_GOTO_ON_ERROR(sdmmc_host_init_slot(host.slot, (const sdmmc_slot_config_t *) &slot_config), + clean, TAG, "Host init slot fail"); + + while (sdmmc_card_init(&host, sd_card)) { + ESP_LOGE(TAG, "The detection pin of the slot is disconnected(Insert uSD card). Retrying..."); + vTaskDelay(pdMS_TO_TICKS(3000)); + } + + // Card has been initialized, print its properties + sdmmc_card_print_info(stdout, sd_card); + *card = sd_card; + + return ESP_OK; + +clean: + if (host_init) { + if (host.flags & SDMMC_HOST_FLAG_DEINIT_ARG) { + host.deinit_p(host.slot); + } else { + (*host.deinit)(); + } + } + if (sd_card) { + free(sd_card); + sd_card = NULL; + } +#if CONFIG_EXAMPLE_SD_PWR_CTRL_LDO_INTERNAL_IO + // We don't need to duplicate error here as all error messages are handled via sd_pwr_* call + sd_pwr_ctrl_del_on_chip_ldo(pwr_ctrl_handle); +#endif // CONFIG_EXAMPLE_SD_PWR_CTRL_LDO_INTERNAL_IO + return ret; +} +#endif // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH + +void app_main(void) +{ + ESP_LOGI(TAG, "Initializing storage..."); + + _wait_console_smp = xSemaphoreCreateBinary(); + if (_wait_console_smp == NULL) { + ESP_LOGE(TAG, "Failed to create semaphore"); + return; + } + +#ifdef CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH + static wl_handle_t wl_handle = WL_INVALID_HANDLE; + ESP_ERROR_CHECK(storage_init_spiflash(&wl_handle)); + + const tinyusb_msc_spiflash_config_t config_spi = { + .wl_handle = wl_handle, + .callback_mount_changed = storage_mount_changed_cb, /* First way to register the callback. This is while initializing the storage. */ + .mount_config.max_files = 5, + }; + ESP_ERROR_CHECK(tinyusb_msc_storage_init_spiflash(&config_spi)); + ESP_ERROR_CHECK(tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, storage_mount_changed_cb)); /* Other way to register the callback i.e. registering using separate API. If the callback had been already registered, it will be overwritten. */ +#else // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH + static sdmmc_card_t *card = NULL; + ESP_ERROR_CHECK(storage_init_sdmmc(&card)); + + const tinyusb_msc_sdmmc_config_t config_sdmmc = { + .card = card, + .callback_mount_changed = storage_mount_changed_cb, /* First way to register the callback. This is while initializing the storage. */ + .mount_config.max_files = 5, + }; + ESP_ERROR_CHECK(tinyusb_msc_storage_init_sdmmc(&config_sdmmc)); + ESP_ERROR_CHECK(tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, storage_mount_changed_cb)); /* Other way to register the callback i.e. registering using separate API. If the callback had been already registered, it will be overwritten. */ +#endif // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH + + //mounted in the app by default + _mount(); + + ESP_LOGI(TAG, "USB MSC initialization"); + const tinyusb_config_t tusb_cfg = { + .device_descriptor = &descriptor_config, + .string_descriptor = string_desc_arr, + .string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]), + .external_phy = false, +#if (TUD_OPT_HIGH_SPEED) + .fs_configuration_descriptor = msc_fs_configuration_desc, + .hs_configuration_descriptor = msc_hs_configuration_desc, + .qualifier_descriptor = &device_qualifier, +#else + .configuration_descriptor = msc_fs_configuration_desc, +#endif // TUD_OPT_HIGH_SPEED + }; + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); + ESP_LOGI(TAG, "USB MSC initialization DONE"); + + esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); + /* Prompt to be printed before each line. + * This can be customized, made dynamic, etc. + */ + repl_config.prompt = PROMPT_STR ">"; + repl_config.max_cmdline_length = 64; + + // Init console based on menuconfig settings +#if defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) || defined(CONFIG_ESP_CONSOLE_UART_CUSTOM) + esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl)); + + // USJ console can be set only on esp32p4, having separate USB PHYs for USB_OTG and USJ +#elif defined(CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG) && defined(CONFIG_IDF_TARGET_ESP32P4) + esp_console_dev_usb_serial_jtag_config_t hw_config = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_console_new_repl_usb_serial_jtag(&hw_config, &repl_config, &repl)); + +#else +#error Unsupported console type +#endif + + for (int count = 0; count < sizeof(cmds) / sizeof(esp_console_cmd_t); count++) { + ESP_ERROR_CHECK(esp_console_cmd_register(&cmds[count])); + } + + ESP_ERROR_CHECK(esp_console_start_repl(repl)); + + xSemaphoreTake(_wait_console_smp, portMAX_DELAY); + ESP_ERROR_CHECK(esp_console_stop_repl(repl)); + vSemaphoreDelete(_wait_console_smp); +} diff --git a/tests/c_headers/idf-usb/device/tusb_msc/partitions.csv b/tests/c_headers/idf-usb/device/tusb_msc/partitions.csv new file mode 100644 index 00000000..1c79321a --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_msc/partitions.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1M, +storage, data, fat, , 1M, diff --git a/tests/c_headers/idf-usb/device/tusb_msc/pytest_usb_device_msc.py b/tests/c_headers/idf-usb/device/tusb_msc/pytest_usb_device_msc.py new file mode 100644 index 00000000..3503b0e6 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_msc/pytest_usb_device_msc.py @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.temp_skip_ci(targets=['esp32s3'], reason='lack of runners with usb_device tag') +@pytest.mark.usb_device +@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32p4'], indirect=['target']) +def test_usb_device_msc_example(dut: Dut) -> None: + dut.expect('Mount storage') + dut.expect('TinyUSB Driver installed') + dut.expect('USB MSC initialization DONE') + dut.expect(dut.target + '>') + dut.write('status') + dut.expect('storage exposed over USB') diff --git a/tests/c_headers/idf-usb/device/tusb_msc/sdkconfig.ci b/tests/c_headers/idf-usb/device/tusb_msc/sdkconfig.ci new file mode 100644 index 00000000..e69de29b diff --git a/tests/c_headers/idf-usb/device/tusb_msc/sdkconfig.ci.esp32s3_sdmmc b/tests/c_headers/idf-usb/device/tusb_msc/sdkconfig.ci.esp32s3_sdmmc new file mode 100644 index 00000000..47f8df9f --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_msc/sdkconfig.ci.esp32s3_sdmmc @@ -0,0 +1,2 @@ +CONFIG_IDF_TARGET="esp32s3" +CONFIG_EXAMPLE_STORAGE_MEDIA_SDMMC=y diff --git a/tests/c_headers/idf-usb/device/tusb_msc/sdkconfig.defaults b/tests/c_headers/idf-usb/device/tusb_msc/sdkconfig.defaults new file mode 100644 index 00000000..cf5dd194 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_msc/sdkconfig.defaults @@ -0,0 +1,16 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_TINYUSB_MSC_ENABLED=y + +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_WL_SECTOR_SIZE_512=y +CONFIG_WL_SECTOR_MODE_PERF=y + +CONFIG_FATFS_LFN_HEAP=y + +# On chips with USB Serial JTAG, disable secondary console which does not make sense when using console component +CONFIG_ESP_CONSOLE_SECONDARY_NONE=y diff --git a/tests/c_headers/idf-usb/device/tusb_ncm/CMakeLists.txt b/tests/c_headers/idf-usb/device/tusb_ncm/CMakeLists.txt new file mode 100644 index 00000000..9772549e --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_ncm/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(tusb_ncm) diff --git a/tests/c_headers/idf-usb/device/tusb_ncm/README.md b/tests/c_headers/idf-usb/device/tusb_ncm/README.md new file mode 100644 index 00000000..66e6d6b2 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_ncm/README.md @@ -0,0 +1,103 @@ +| Supported Targets | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | + +# TinyUSB Network Control Model Device Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +Network Control Model (NCM) is a sub-class of Communication Device Class (CDC) USB Device for Ethernet-over-USB applications. + +In this example, we implemented the ESP development board to transmit WiFi data to the Linux or Windows host via USB, so that the host could access the Internet. + +As a USB stack, a TinyUSB component is used. + +## How to use example + +### Hardware Required + +Any ESP board that have USB-OTG supported. + +#### Pin Assignment + +_Note:_ In case your board doesn't have micro-USB connector connected to USB-OTG peripheral, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below. + +See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments). + +### Configure the project + +Open the project configuration menu (`idf.py menuconfig`). + +In the `Example Configuration` menu: + +* Set the Wi-Fi configuration. + * Set `WiFi SSID`. + * Set `WiFi Password`. + +### Build, Flash, and Run + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT build flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +After the flashing you should see the output at idf monitor: + +``` +I (399) main_task: Calling app_main() +I (429) USB_NCM: USB NCM device initialization +W (429) TinyUSB: The device's configuration descriptor is not provided by user, using default. +W (429) TinyUSB: The device's string descriptor is not provided by user, using default. +W (439) TinyUSB: The device's device descriptor is not provided by user, using default. +I (449) tusb_desc: +┌─────────────────────────────────┐ +│ USB Device Descriptor Summary │ +├───────────────────┬─────────────┤ +│bDeviceClass │ 239 │ +├───────────────────┼─────────────┤ +│bDeviceSubClass │ 2 │ +├───────────────────┼─────────────┤ +│bDeviceProtocol │ 1 │ +├───────────────────┼─────────────┤ +│bMaxPacketSize0 │ 64 │ +├───────────────────┼─────────────┤ +│idVendor │ 0x303a │ +├───────────────────┼─────────────┤ +│idProduct │ 0x4001 │ +├───────────────────┼─────────────┤ +│bcdDevice │ 0x100 │ +├───────────────────┼─────────────┤ +│iManufacturer │ 0x1 │ +├───────────────────┼─────────────┤ +│iProduct │ 0x2 │ +├───────────────────┼─────────────┤ +│iSerialNumber │ 0x3 │ +├───────────────────┼─────────────┤ +│bNumConfigurations │ 0x1 │ +└───────────────────┴─────────────┘ +I (619) TinyUSB: TinyUSB Driver installed +I (619) USB_NCM: WiFi initialization +I (619) pp: pp rom version: e7ae62f +I (629) net80211: net80211 rom version: e7ae62f +I (689) wifi_init: rx ba win: 6 +I (699) wifi_init: tcpip mbox: 32 +I (699) wifi_init: udp mbox: 6 +I (699) wifi_init: tcp mbox: 6 +I (709) wifi_init: tcp tx win: 5744 +I (709) wifi_init: tcp rx win: 5744 +I (719) wifi_init: tcp mss: 1440 +I (719) wifi_init: WiFi IRAM OP enabled +I (719) wifi_init: WiFi RX IRAM OP enabled +I (729) phy_init: phy_version 600,8dd0147,Mar 31 2023,16:34:12 +I (779) USB_NCM: USB NCM and WiFi initialized and started +I (779) main_task: Returned from app_main() +I (849) USB_NCM: WiFi STA connected +``` diff --git a/tests/c_headers/idf-usb/device/tusb_ncm/main/CMakeLists.txt b/tests/c_headers/idf-usb/device/tusb_ncm/main/CMakeLists.txt new file mode 100644 index 00000000..cb3fe664 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_ncm/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "tusb_ncm_main.c" + INCLUDE_DIRS "" + PRIV_REQUIRES esp_wifi nvs_flash + ) diff --git a/tests/c_headers/idf-usb/device/tusb_ncm/main/Kconfig.projbuild b/tests/c_headers/idf-usb/device/tusb_ncm/main/Kconfig.projbuild new file mode 100644 index 00000000..dd0b5e41 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_ncm/main/Kconfig.projbuild @@ -0,0 +1,15 @@ +menu "Example Configuration" + + config ESP_WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + + config ESP_WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + +endmenu diff --git a/tests/c_headers/idf-usb/device/tusb_ncm/main/idf_component.yml b/tests/c_headers/idf-usb/device/tusb_ncm/main/idf_component.yml new file mode 100644 index 00000000..2576181c --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_ncm/main/idf_component.yml @@ -0,0 +1,5 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_tinyusb: + version: "^1.3.0" + idf: "^5.0" diff --git a/tests/c_headers/idf-usb/device/tusb_ncm/main/tusb_ncm_main.c b/tests/c_headers/idf-usb/device/tusb_ncm/main/tusb_ncm_main.c new file mode 100644 index 00000000..79efe53a --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_ncm/main/tusb_ncm_main.c @@ -0,0 +1,126 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +/* DESCRIPTION: + * This example demonstrates using ESP32-S2/S3 as a USB network device. It initializes WiFi in station mode, + * connects and bridges the WiFi and USB networks, so the USB device acts as a standard network interface that + * acquires an IP address from the AP/router which the WiFi station connects to. + */ + +#include + +#include "esp_log.h" +#include "esp_event.h" +#include "esp_check.h" +#include "nvs_flash.h" +#include "esp_mac.h" + +#include "esp_wifi.h" +#include "esp_private/wifi.h" + +#include "tinyusb.h" +#include "tinyusb_net.h" + +static const char *TAG = "USB_NCM"; + +static esp_err_t usb_recv_callback(void *buffer, uint16_t len, void *ctx) +{ + bool *is_wifi_connected = ctx; + if (*is_wifi_connected) { + esp_wifi_internal_tx(ESP_IF_WIFI_STA, buffer, len); + } + return ESP_OK; +} + +static void wifi_pkt_free(void *eb, void *ctx) +{ + esp_wifi_internal_free_rx_buffer(eb); +} + +static esp_err_t pkt_wifi2usb(void *buffer, uint16_t len, void *eb) +{ + if (tinyusb_net_send_sync(buffer, len, eb, portMAX_DELAY) != ESP_OK) { + esp_wifi_internal_free_rx_buffer(eb); + } + return ESP_OK; +} + +static void wifi_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + bool *is_connected = arg; + + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + ESP_LOGI(TAG, "WiFi STA disconnected"); + *is_connected = false; + esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, NULL); + esp_wifi_connect(); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) { + ESP_LOGI(TAG, "WiFi STA connected"); + esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, pkt_wifi2usb); + *is_connected = true; + } +} + +static esp_err_t start_wifi(bool *is_connected) +{ + ESP_RETURN_ON_ERROR(esp_event_loop_create_default(), TAG, "Cannot initialize event loop"); + + wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_RETURN_ON_ERROR(esp_wifi_init(&wifi_cfg), TAG, "Failed to initialize WiFi library"); + ESP_RETURN_ON_ERROR(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, is_connected), + TAG, "Failed to register handler for wifi events"); + ESP_RETURN_ON_ERROR(esp_wifi_set_mode(WIFI_MODE_STA), TAG, "Failed to set WiFi station mode"); + ESP_RETURN_ON_ERROR(esp_wifi_start(), TAG, "Failed to start WiFi library"); + + wifi_config_t wifi_config = { + .sta = { + .ssid = CONFIG_ESP_WIFI_SSID, + .password = CONFIG_ESP_WIFI_PASSWORD, + }, + }; + + ESP_RETURN_ON_ERROR(esp_wifi_set_config(WIFI_IF_STA, &wifi_config), TAG, "Failed to set WiFi config"); + return esp_wifi_connect(); +} + +void app_main(void) +{ + static bool s_is_wifi_connected = false; // needs to be static as it's used after we exit app_main() + + /* Initialize NVS — it is used to store PHY calibration data */ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_LOGI(TAG, "USB NCM device initialization"); + const tinyusb_config_t tusb_cfg = { + .external_phy = false, + }; + ESP_GOTO_ON_ERROR(tinyusb_driver_install(&tusb_cfg), err, TAG, "Failed to install TinyUSB driver"); + + tinyusb_net_config_t net_config = { + .on_recv_callback = usb_recv_callback, + .free_tx_buffer = wifi_pkt_free, + .user_context = &s_is_wifi_connected + }; + esp_read_mac(net_config.mac_addr, ESP_MAC_WIFI_STA); + uint8_t *mac = net_config.mac_addr; + ESP_LOGI(TAG, "Network interface HW address: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + ESP_GOTO_ON_ERROR(tinyusb_net_init(TINYUSB_USBDEV_0, &net_config), err, TAG, "Failed to initialize TinyUSB NCM device class"); + + ESP_LOGI(TAG, "WiFi initialization"); + ESP_GOTO_ON_ERROR(start_wifi(&s_is_wifi_connected), err, TAG, "Failed to init and start WiFi"); + + ESP_LOGI(TAG, "USB NCM and WiFi initialized and started"); + return; + +err: + ESP_LOGE(TAG, "USB-WiFi bridge example failed!"); +} diff --git a/tests/c_headers/idf-usb/device/tusb_ncm/pytest_usb_device_ncm.py b/tests/c_headers/idf-usb/device/tusb_ncm/pytest_usb_device_ncm.py new file mode 100644 index 00000000..c3828531 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_ncm/pytest_usb_device_ncm.py @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import subprocess +import time + +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.temp_skip_ci(targets=['esp32s3'], reason='lack of runners with usb_device tag') +@pytest.mark.usb_device +@idf_parametrize('target', ['esp32s2', 'esp32s3'], indirect=['target']) +def test_usb_device_ncm_example(dut: Dut) -> None: + netif_mac = dut.expect( + r'Network interface HW address: ' + r'([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:' + r'[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})' + ) + netif_mac = netif_mac.group(1).decode('utf-8') + dut.expect_exact('USB NCM and WiFi initialized and started') + dut.expect_exact('Returned from app_main()') + time.sleep(2) # Wait 2s for the network interface to appear + out_bytes = subprocess.check_output('ifconfig', shell=True, timeout=5) + out_str = out_bytes.decode('utf-8') + print('expected network interface HW address: ', netif_mac) + print('ifconfig command output:\n', out_str) + if netif_mac in out_str: + print("NCM device's MAC address {} was found in system network interfaces".format(netif_mac)) + else: + raise AssertionError('NCM device not found in network interface list') diff --git a/tests/c_headers/idf-usb/device/tusb_ncm/sdkconfig.defaults b/tests/c_headers/idf-usb/device/tusb_ncm/sdkconfig.defaults new file mode 100644 index 00000000..65730e3e --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_ncm/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_TINYUSB_NET_MODE_NCM=y diff --git a/tests/c_headers/idf-usb/device/tusb_ncm/sdkconfig.defaults.esp32s3 b/tests/c_headers/idf-usb/device/tusb_ncm/sdkconfig.defaults.esp32s3 new file mode 100644 index 00000000..0776bebd --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_ncm/sdkconfig.defaults.esp32s3 @@ -0,0 +1,3 @@ +CONFIG_TINYUSB_TASK_AFFINITY_CPU0=y +CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK=y +CONFIG_TINYUSB_NET_MODE_NCM=y diff --git a/tests/c_headers/idf-usb/device/tusb_serial_device/CMakeLists.txt b/tests/c_headers/idf-usb/device/tusb_serial_device/CMakeLists.txt new file mode 100644 index 00000000..c2897f72 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_serial_device/CMakeLists.txt @@ -0,0 +1,9 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +add_compile_options(-w) # Turn off warnings until esp_tinyusb is updated IEC-86 +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(tusb_serial_device) diff --git a/tests/c_headers/idf-usb/device/tusb_serial_device/README.md b/tests/c_headers/idf-usb/device/tusb_serial_device/README.md new file mode 100644 index 00000000..60ef9a9b --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_serial_device/README.md @@ -0,0 +1,85 @@ +| Supported Targets | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | + +# TinyUSB Serial Device Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example shows how to set up ESP chip to work as a USB Serial Device. + +As a USB stack, a TinyUSB component is used. + +## How to use example + +This example can also be configured to act as double serial device. +Run `idf.py menuconfig` and in `Component config → TinyUSB Stack → Communication Device Class (CDC) → CDC Channel Count` select number of serial devices you want to implement. + +### Hardware Required + +Any ESP board that have USB-OTG supported. + +#### Pin Assignment + +_Note:_ In case your board doesn't have micro-USB connector connected to USB-OTG peripheral, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below. + +See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments). + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +```bash +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +After the flashing you should see this output (for single CDC channel): + +``` +I (285) example: USB initialization +I (285) tusb_desc: +┌─────────────────────────────────┐ +│ USB Device Descriptor Summary │ +├───────────────────┬─────────────┤ +│bDeviceClass │ 239 │ +├───────────────────┼─────────────┤ +│bDeviceSubClass │ 2 │ +├───────────────────┼─────────────┤ +│bDeviceProtocol │ 1 │ +├───────────────────┼─────────────┤ +│bMaxPacketSize0 │ 64 │ +├───────────────────┼─────────────┤ +│idVendor │ 0x303a │ +├───────────────────┼─────────────┤ +│idProduct │ 0x4001 │ +├───────────────────┼─────────────┤ +│bcdDevice │ 0x100 │ +├───────────────────┼─────────────┤ +│iManufacturer │ 0x1 │ +├───────────────────┼─────────────┤ +│iProduct │ 0x2 │ +├───────────────────┼─────────────┤ +│iSerialNumber │ 0x3 │ +├───────────────────┼─────────────┤ +│bNumConfigurations │ 0x1 │ +└───────────────────┴─────────────┘ +I (455) TinyUSB: TinyUSB Driver installed +I (465) example: USB initialization DONE +``` + +Connect to the serial port (e.g. on Linux, it should be `/dev/ttyACM0`) by any terminal application (e.g. `picocom /dev/ttyACM0`). +Now you can send data strings to the device, the device will echo back the same data string. + +The monitor tool will also print the communication process: + +``` +I (12438) example: Data from channel 0: +I (12438) example: 0x3ffbfea0 45 73 70 72 65 73 73 69 66 0d |Espressif.| +``` diff --git a/tests/c_headers/idf-usb/device/tusb_serial_device/main/CMakeLists.txt b/tests/c_headers/idf-usb/device/tusb_serial_device/main/CMakeLists.txt new file mode 100644 index 00000000..a3d8eb60 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_serial_device/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "tusb_serial_device_main.c" + INCLUDE_DIRS .) diff --git a/tests/c_headers/idf-usb/device/tusb_serial_device/main/idf_component.yml b/tests/c_headers/idf-usb/device/tusb_serial_device/main/idf_component.yml new file mode 100644 index 00000000..ff67b5da --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_serial_device/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_tinyusb: "^1" + idf: "^5.0" diff --git a/tests/c_headers/idf-usb/device/tusb_serial_device/main/tusb_serial_device_main.c b/tests/c_headers/idf-usb/device/tusb_serial_device/main/tusb_serial_device_main.c new file mode 100644 index 00000000..cc111189 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_serial_device/main/tusb_serial_device_main.c @@ -0,0 +1,140 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "tinyusb.h" +#include "tusb_cdc_acm.h" +#include "sdkconfig.h" + +static const char *TAG = "example"; +static uint8_t rx_buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1]; + +/** + * @brief Application Queue + */ +static QueueHandle_t app_queue; +typedef struct { + uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1]; // Data buffer + size_t buf_len; // Number of bytes received + uint8_t itf; // Index of CDC device interface +} app_message_t; + +/** + * @brief CDC device RX callback + * + * CDC device signals, that new data were received + * + * @param[in] itf CDC device index + * @param[in] event CDC event type + */ +void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event) +{ + /* initialization */ + size_t rx_size = 0; + + /* read */ + esp_err_t ret = tinyusb_cdcacm_read(itf, rx_buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size); + if (ret == ESP_OK) { + + app_message_t tx_msg = { + .buf_len = rx_size, + .itf = itf, + }; + + memcpy(tx_msg.buf, rx_buf, rx_size); + xQueueSend(app_queue, &tx_msg, 0); + } else { + ESP_LOGE(TAG, "Read Error"); + } +} + +/** + * @brief CDC device line change callback + * + * CDC device signals, that the DTR, RTS states changed + * + * @param[in] itf CDC device index + * @param[in] event CDC event type + */ +void tinyusb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event) +{ + int dtr = event->line_state_changed_data.dtr; + int rts = event->line_state_changed_data.rts; + ESP_LOGI(TAG, "Line state changed on channel %d: DTR:%d, RTS:%d", itf, dtr, rts); +} + +void app_main(void) +{ + // Create FreeRTOS primitives + app_queue = xQueueCreate(5, sizeof(app_message_t)); + assert(app_queue); + app_message_t msg; + + ESP_LOGI(TAG, "USB initialization"); + const tinyusb_config_t tusb_cfg = { + .device_descriptor = NULL, + .string_descriptor = NULL, + .external_phy = false, +#if (TUD_OPT_HIGH_SPEED) + .fs_configuration_descriptor = NULL, + .hs_configuration_descriptor = NULL, + .qualifier_descriptor = NULL, +#else + .configuration_descriptor = NULL, +#endif // TUD_OPT_HIGH_SPEED + }; + + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); + + tinyusb_config_cdcacm_t acm_cfg = { + .usb_dev = TINYUSB_USBDEV_0, + .cdc_port = TINYUSB_CDC_ACM_0, + .rx_unread_buf_sz = 64, + .callback_rx = &tinyusb_cdc_rx_callback, // the first way to register a callback + .callback_rx_wanted_char = NULL, + .callback_line_state_changed = NULL, + .callback_line_coding_changed = NULL + }; + + ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg)); + /* the second way to register a callback */ + ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback( + TINYUSB_CDC_ACM_0, + CDC_EVENT_LINE_STATE_CHANGED, + &tinyusb_cdc_line_state_changed_callback)); + +#if (CONFIG_TINYUSB_CDC_COUNT > 1) + acm_cfg.cdc_port = TINYUSB_CDC_ACM_1; + ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg)); + ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback( + TINYUSB_CDC_ACM_1, + CDC_EVENT_LINE_STATE_CHANGED, + &tinyusb_cdc_line_state_changed_callback)); +#endif + + ESP_LOGI(TAG, "USB initialization DONE"); + while (1) { + if (xQueueReceive(app_queue, &msg, portMAX_DELAY)) { + if (msg.buf_len) { + + /* Print received data*/ + ESP_LOGI(TAG, "Data from channel %d:", msg.itf); + ESP_LOG_BUFFER_HEXDUMP(TAG, msg.buf, msg.buf_len, ESP_LOG_INFO); + + /* write back */ + tinyusb_cdcacm_write_queue(msg.itf, msg.buf, msg.buf_len); + esp_err_t err = tinyusb_cdcacm_write_flush(msg.itf, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "CDC ACM write flush error: %s", esp_err_to_name(err)); + } + } + } + } +} diff --git a/tests/c_headers/idf-usb/device/tusb_serial_device/pytest_usb_device_serial.py b/tests/c_headers/idf-usb/device/tusb_serial_device/pytest_usb_device_serial.py new file mode 100644 index 00000000..f1dd71ef --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_serial_device/pytest_usb_device_serial.py @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +from time import sleep + +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize +from serial import Serial +from serial.tools.list_ports import comports + + +@pytest.mark.temp_skip_ci(targets=['esp32s3'], reason='lack of runners with usb_device tag') +@pytest.mark.usb_device +@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32p4'], indirect=['target']) +def test_usb_device_serial_example(dut: Dut) -> None: + dut.expect_exact('USB initialization DONE') + sleep(2) # Some time for the OS to enumerate our USB device + + # Find device with Espressif TinyUSB VID/PID + ports = comports() + for port, _, hwid in ports: + if '303A:4001' in hwid: + with Serial(port) as s: + s.write('text\r\n'.encode()) # Write dummy text to COM port + dut.expect_exact('Data from channel 0:') # Check ESP log + dut.expect_exact('|text..|') + res = s.readline() # Check COM echo + assert b'text\r\n' in res + return + + raise Exception('TinyUSB COM port not found') diff --git a/tests/c_headers/idf-usb/device/tusb_serial_device/sdkconfig.ci b/tests/c_headers/idf-usb/device/tusb_serial_device/sdkconfig.ci new file mode 100644 index 00000000..e69de29b diff --git a/tests/c_headers/idf-usb/device/tusb_serial_device/sdkconfig.ci.dualchannel b/tests/c_headers/idf-usb/device/tusb_serial_device/sdkconfig.ci.dualchannel new file mode 100644 index 00000000..909dd48c --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_serial_device/sdkconfig.ci.dualchannel @@ -0,0 +1 @@ +CONFIG_TINYUSB_CDC_COUNT=2 diff --git a/tests/c_headers/idf-usb/device/tusb_serial_device/sdkconfig.defaults b/tests/c_headers/idf-usb/device/tusb_serial_device/sdkconfig.defaults new file mode 100644 index 00000000..afec5843 --- /dev/null +++ b/tests/c_headers/idf-usb/device/tusb_serial_device/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_TINYUSB_CDC_ENABLED=y diff --git a/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/CMakeLists.txt b/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/CMakeLists.txt new file mode 100644 index 00000000..dd45c278 --- /dev/null +++ b/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/CMakeLists.txt @@ -0,0 +1,10 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(cdc_acm_host) diff --git a/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/README.md b/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/README.md new file mode 100644 index 00000000..504c16e5 --- /dev/null +++ b/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/README.md @@ -0,0 +1,57 @@ +| Supported Targets | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | + +# USB CDC-ACM Host Driver Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example shows how to use the CDC-ACM Host Driver to allow an ESP chip to communicate with a USB CDC-ACM device. + +## How to use example + +### Hardware Required + +Two development boards with USB-OTG support. One will act as USB host and the other as USB device. + +#### Pin Assignment + +Follow instruction in [examples/usb/README.md](../../../README.md) for specific hardware setup. + +### Build and Flash + +1. Build and flash [tusb_serial_device example](../../../device/tusb_serial_device) to USB device board. +2. Build this project and flash it to the USB host board, then run monitor tool to view serial output: + +```bash +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +After the flashing you should see the output at idf monitor: + +``` +... +I (256) USB-CDC: USB Host installed +I (256) USB-CDC: Opening CDC ACM device 0x303A:0x4001 +... +Device descriptor is printed here +... +I (1666) USB-CDC: Data received +I (1666) USB-CDC: 0x3ffc4c20 41 54 0d |AT.| +I (2666) USB-CDC: Data received +I (2666) USB-CDC: 0x3ffc4c20 41 54 2b 47 53 4e 0d |AT+GSN.| +I (3666) USB-CDC: Setting up line coding +I (3666) USB-CDC: Line Get: Rate: 115200, Stop bits: 0, Parity: 0, Databits: 8 +I (3666) USB-CDC: Line Set: Rate: 9600, Stop bits: 1, Parity: 1, Databits: 7 +I (3666) USB-CDC: Line Get: Rate: 9600, Stop bits: 1, Parity: 1, Databits: 7 +I (3676) Example finished successfully! +... + +``` diff --git a/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/main/CMakeLists.txt b/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/main/CMakeLists.txt new file mode 100644 index 00000000..3b0d7035 --- /dev/null +++ b/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "usb_cdc_example_main.c" + INCLUDE_DIRS "." + PRIV_REQUIRES usb + ) diff --git a/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/main/idf_component.yml b/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/main/idf_component.yml new file mode 100644 index 00000000..7865f535 --- /dev/null +++ b/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + usb_host_cdc_acm: "2.*" + idf: ">=4.4" diff --git a/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c b/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c new file mode 100644 index 00000000..f8c548e0 --- /dev/null +++ b/tests/c_headers/idf-usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c @@ -0,0 +1,179 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include +#include +#include "esp_system.h" +#include "esp_log.h" +#include "esp_err.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +#include "usb/usb_host.h" +#include "usb/cdc_acm_host.h" + +#define EXAMPLE_USB_HOST_PRIORITY (20) +#define EXAMPLE_USB_DEVICE_VID (0x303A) +#define EXAMPLE_USB_DEVICE_PID (0x4001) // 0x303A:0x4001 (TinyUSB CDC device) +#define EXAMPLE_USB_DEVICE_DUAL_PID (0x4002) // 0x303A:0x4002 (TinyUSB Dual CDC device) +#define EXAMPLE_TX_STRING ("CDC test string!") +#define EXAMPLE_TX_TIMEOUT_MS (1000) + +static const char *TAG = "USB-CDC"; +static SemaphoreHandle_t device_disconnected_sem; + +/** + * @brief Data received callback + * + * @param[in] data Pointer to received data + * @param[in] data_len Length of received data in bytes + * @param[in] arg Argument we passed to the device open function + * @return + * true: We have processed the received data + * false: We expect more data + */ +static bool handle_rx(const uint8_t *data, size_t data_len, void *arg) +{ + ESP_LOGI(TAG, "Data received"); + ESP_LOG_BUFFER_HEXDUMP(TAG, data, data_len, ESP_LOG_INFO); + return true; +} + +/** + * @brief Device event callback + * + * Apart from handling device disconnection it doesn't do anything useful + * + * @param[in] event Device event type and data + * @param[in] user_ctx Argument we passed to the device open function + */ +static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) +{ + switch (event->type) { + case CDC_ACM_HOST_ERROR: + ESP_LOGE(TAG, "CDC-ACM error has occurred, err_no = %i", event->data.error); + break; + case CDC_ACM_HOST_DEVICE_DISCONNECTED: + ESP_LOGI(TAG, "Device suddenly disconnected"); + ESP_ERROR_CHECK(cdc_acm_host_close(event->data.cdc_hdl)); + xSemaphoreGive(device_disconnected_sem); + break; + case CDC_ACM_HOST_SERIAL_STATE: + ESP_LOGI(TAG, "Serial state notif 0x%04X", event->data.serial_state.val); + break; + case CDC_ACM_HOST_NETWORK_CONNECTION: + default: + ESP_LOGW(TAG, "Unsupported CDC event: %i", event->type); + break; + } +} + +/** + * @brief USB Host library handling task + * + * @param arg Unused + */ +static void usb_lib_task(void *arg) +{ + while (1) { + // Start handling system events + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + ESP_ERROR_CHECK(usb_host_device_free_all()); + } + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + ESP_LOGI(TAG, "USB: All devices freed"); + // Continue handling USB events to allow device reconnection + } + } +} + +/** + * @brief Main application + * + * Here we open a USB CDC device and send some data to it + */ +void app_main(void) +{ + device_disconnected_sem = xSemaphoreCreateBinary(); + assert(device_disconnected_sem); + + // Install USB Host driver. Should only be called once in entire application + ESP_LOGI(TAG, "Installing USB Host"); + const usb_host_config_t host_config = { + .skip_phy_setup = false, + .intr_flags = ESP_INTR_FLAG_LEVEL1, + }; + ESP_ERROR_CHECK(usb_host_install(&host_config)); + + // Create a task that will handle USB library events + BaseType_t task_created = xTaskCreate(usb_lib_task, "usb_lib", 4096, xTaskGetCurrentTaskHandle(), EXAMPLE_USB_HOST_PRIORITY, NULL); + assert(task_created == pdTRUE); + + ESP_LOGI(TAG, "Installing CDC-ACM driver"); + ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); + + const cdc_acm_host_device_config_t dev_config = { + .connection_timeout_ms = 1000, + .out_buffer_size = 512, + .in_buffer_size = 512, + .user_arg = NULL, + .event_cb = handle_event, + .data_cb = handle_rx + }; + + while (true) { + cdc_acm_dev_hdl_t cdc_dev = NULL; + + // Open USB device from tusb_serial_device example example. Either single or dual port configuration. + ESP_LOGI(TAG, "Opening CDC ACM device 0x%04X:0x%04X...", EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID); + esp_err_t err = cdc_acm_host_open(EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID, 0, &dev_config, &cdc_dev); + if (ESP_OK != err) { + ESP_LOGI(TAG, "Opening CDC ACM device 0x%04X:0x%04X...", EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_DUAL_PID); + err = cdc_acm_host_open(EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_DUAL_PID, 0, &dev_config, &cdc_dev); + if (ESP_OK != err) { + ESP_LOGI(TAG, "Failed to open device"); + continue; + } + } + cdc_acm_host_desc_print(cdc_dev); + vTaskDelay(pdMS_TO_TICKS(100)); + + // Test sending and receiving: responses are handled in handle_rx callback + ESP_ERROR_CHECK(cdc_acm_host_data_tx_blocking(cdc_dev, (const uint8_t *)EXAMPLE_TX_STRING, strlen(EXAMPLE_TX_STRING), EXAMPLE_TX_TIMEOUT_MS)); + vTaskDelay(pdMS_TO_TICKS(100)); + + // Test Line Coding commands: Get current line coding, change it 9600 7N1 and read again + ESP_LOGI(TAG, "Setting up line coding"); + + cdc_acm_line_coding_t line_coding; + ESP_ERROR_CHECK(cdc_acm_host_line_coding_get(cdc_dev, &line_coding)); + ESP_LOGI(TAG, "Line Get: Rate: %"PRIu32", Stop bits: %"PRIu8", Parity: %"PRIu8", Databits: %"PRIu8"", + line_coding.dwDTERate, line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); + + line_coding.dwDTERate = 9600; + line_coding.bDataBits = 7; + line_coding.bParityType = 1; + line_coding.bCharFormat = 1; + ESP_ERROR_CHECK(cdc_acm_host_line_coding_set(cdc_dev, &line_coding)); + ESP_LOGI(TAG, "Line Set: Rate: %"PRIu32", Stop bits: %"PRIu8", Parity: %"PRIu8", Databits: %"PRIu8"", + line_coding.dwDTERate, line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); + + ESP_ERROR_CHECK(cdc_acm_host_line_coding_get(cdc_dev, &line_coding)); + ESP_LOGI(TAG, "Line Get: Rate: %"PRIu32", Stop bits: %"PRIu8", Parity: %"PRIu8", Databits: %"PRIu8"", + line_coding.dwDTERate, line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); + + ESP_ERROR_CHECK(cdc_acm_host_set_control_line_state(cdc_dev, true, false)); + + // We are done. Wait for device disconnection and start over + ESP_LOGI(TAG, "Example finished successfully! You can reconnect the device to run again."); + xSemaphoreTake(device_disconnected_sem, portMAX_DELAY); + } +} diff --git a/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/CMakeLists.txt b/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/CMakeLists.txt new file mode 100644 index 00000000..a4d34e3f --- /dev/null +++ b/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/CMakeLists.txt @@ -0,0 +1,10 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(cdc_acm_vcp) diff --git a/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/README.md b/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/README.md new file mode 100644 index 00000000..2883ef06 --- /dev/null +++ b/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/README.md @@ -0,0 +1,41 @@ +| Supported Targets | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | + +# USB CDC-ACM Virtual COM Port example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example shows how to extend CDC-ACM driver for Virtual Communication Port (VCP) devices, such as CP210x, FTDI FT23x or CH34x devices. + +The drivers are fetched from [ESP Component Registry](https://components.espressif.com/) together with VCP service that automatically loads correct driver for plugged-in device. + +## How to use example + +1. Connect your USB<->UART converter to ESP board, the device will be automatically enumerated and correct driver will be loaded +2. Change baudrate and other line coding parameters in [cdc_acm_vcp_example_main.cpp](main/cdc_acm_vcp_example_main.cpp) to match your needs +3. Now you can use the usual CDC-ACM API to control the device and send data. Data are received in `handle_rx` callback +4. Try disconnecting and then reconnecting of the USB device to experiment with USB hotplugging + +### Hardware Required + +* Development board with USB-OTG support +* A USB cable for Power supply and programming +* Silicon Labs CP210x, FTDI FT23x or CP34x USB to UART converter + +#### Pin Assignment + +Follow instruction in [examples/usb/README.md](../../../README.md) for specific hardware setup. + +### Build and Flash + +Build this project and flash it to the USB host board, then run monitor tool to view serial output: + +```bash +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. diff --git a/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/main/CMakeLists.txt b/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/main/CMakeLists.txt new file mode 100644 index 00000000..e52dfe2b --- /dev/null +++ b/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "cdc_acm_vcp_example_main.cpp" + INCLUDE_DIRS "." + PRIV_REQUIRES usb + ) diff --git a/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp_example_main.cpp b/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp_example_main.cpp new file mode 100644 index 00000000..32d2fee7 --- /dev/null +++ b/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp_example_main.cpp @@ -0,0 +1,177 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include + +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +#include "usb/cdc_acm_host.h" +#include "usb/vcp_ch34x.hpp" +#include "usb/vcp_cp210x.hpp" +#include "usb/vcp_ftdi.hpp" +#include "usb/vcp.hpp" +#include "usb/usb_host.h" + +using namespace esp_usb; + +// Change these values to match your needs +#define EXAMPLE_BAUDRATE (115200) +#define EXAMPLE_STOP_BITS (0) // 0: 1 stopbit, 1: 1.5 stopbits, 2: 2 stopbits +#define EXAMPLE_PARITY (0) // 0: None, 1: Odd, 2: Even, 3: Mark, 4: Space +#define EXAMPLE_DATA_BITS (8) + +namespace { +static const char *TAG = "VCP example"; +static SemaphoreHandle_t device_disconnected_sem; + +/** + * @brief Data received callback + * + * Just pass received data to stdout + * + * @param[in] data Pointer to received data + * @param[in] data_len Length of received data in bytes + * @param[in] arg Argument we passed to the device open function + * @return + * true: We have processed the received data + * false: We expect more data + */ +static bool handle_rx(const uint8_t *data, size_t data_len, void *arg) +{ + printf("%.*s", data_len, data); + return true; +} + +/** + * @brief Device event callback + * + * Apart from handling device disconnection it doesn't do anything useful + * + * @param[in] event Device event type and data + * @param[in] user_ctx Argument we passed to the device open function + */ +static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) +{ + switch (event->type) { + case CDC_ACM_HOST_ERROR: + ESP_LOGE(TAG, "CDC-ACM error has occurred, err_no = %d", event->data.error); + break; + case CDC_ACM_HOST_DEVICE_DISCONNECTED: + ESP_LOGI(TAG, "Device suddenly disconnected"); + xSemaphoreGive(device_disconnected_sem); + break; + case CDC_ACM_HOST_SERIAL_STATE: + ESP_LOGI(TAG, "Serial state notif 0x%04X", event->data.serial_state.val); + break; + case CDC_ACM_HOST_NETWORK_CONNECTION: + default: break; + } +} + +/** + * @brief USB Host library handling task + * + * @param arg Unused + */ +static void usb_lib_task(void *arg) +{ + while (1) { + // Start handling system events + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + ESP_ERROR_CHECK(usb_host_device_free_all()); + } + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + ESP_LOGI(TAG, "USB: All devices freed"); + // Continue handling USB events to allow device reconnection + } + } +} +} + +/** + * @brief Main application + * + * This function shows how you can use Virtual COM Port drivers + */ +extern "C" void app_main(void) +{ + device_disconnected_sem = xSemaphoreCreateBinary(); + assert(device_disconnected_sem); + + // Install USB Host driver. Should only be called once in entire application + ESP_LOGI(TAG, "Installing USB Host"); + usb_host_config_t host_config = {}; + host_config.skip_phy_setup = false; + host_config.intr_flags = ESP_INTR_FLAG_LEVEL1; + ESP_ERROR_CHECK(usb_host_install(&host_config)); + + // Create a task that will handle USB library events + BaseType_t task_created = xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, 10, NULL); + assert(task_created == pdTRUE); + + ESP_LOGI(TAG, "Installing CDC-ACM driver"); + ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); + + // Register VCP drivers to VCP service + VCP::register_driver(); + VCP::register_driver(); + VCP::register_driver(); + + // Do everything else in a loop, so we can demonstrate USB device reconnections + while (true) { + const cdc_acm_host_device_config_t dev_config = { + .connection_timeout_ms = 5000, // 5 seconds, enough time to plug the device in or experiment with timeout + .out_buffer_size = 512, + .in_buffer_size = 512, + .event_cb = handle_event, + .data_cb = handle_rx, + .user_arg = NULL, + }; + + // You don't need to know the device's VID and PID. Just plug in any device and the VCP service will load correct (already registered) driver for the device + ESP_LOGI(TAG, "Opening any VCP device..."); + auto vcp = std::unique_ptr(VCP::open(&dev_config)); + + if (vcp == nullptr) { + ESP_LOGI(TAG, "Failed to open VCP device"); + continue; + } + vTaskDelay(10); + + ESP_LOGI(TAG, "Setting up line coding"); + cdc_acm_line_coding_t line_coding = { + .dwDTERate = EXAMPLE_BAUDRATE, + .bCharFormat = EXAMPLE_STOP_BITS, + .bParityType = EXAMPLE_PARITY, + .bDataBits = EXAMPLE_DATA_BITS, + }; + ESP_ERROR_CHECK(vcp->line_coding_set(&line_coding)); + + /* + Now the USB-to-UART converter is configured and receiving data. + You can use standard CDC-ACM API to interact with it. E.g. + + ESP_ERROR_CHECK(vcp->set_control_line_state(false, true)); + ESP_ERROR_CHECK(vcp->tx_blocking((uint8_t *)"Test string", 12)); + */ + + // Send some dummy data + ESP_LOGI(TAG, "Sending data through CdcAcmDevice"); + uint8_t data[] = "test_string"; + ESP_ERROR_CHECK(vcp->tx_blocking(data, sizeof(data))); + ESP_ERROR_CHECK(vcp->set_control_line_state(true, true)); + + // We are done. Wait for device disconnection and start over + ESP_LOGI(TAG, "Done. You can reconnect the VCP device to run again."); + xSemaphoreTake(device_disconnected_sem, portMAX_DELAY); + } +} diff --git a/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/main/idf_component.yml b/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/main/idf_component.yml new file mode 100644 index 00000000..d8f31492 --- /dev/null +++ b/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/main/idf_component.yml @@ -0,0 +1,7 @@ +## IDF Component Manager Manifest File +dependencies: + usb_host_ch34x_vcp: "^2" + usb_host_cp210x_vcp: "^2" + usb_host_ftdi_vcp: "^2" + usb_host_vcp: "^1" + idf: ">=5.1.0" diff --git a/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/sdkconfig.defaults b/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/sdkconfig.defaults new file mode 100644 index 00000000..a3e635c9 --- /dev/null +++ b/tests/c_headers/idf-usb/host/cdc/cdc_acm_vcp/sdkconfig.defaults @@ -0,0 +1,5 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_USB_HOST_HUBS_SUPPORTED=y diff --git a/tests/c_headers/idf-usb/host/hid/CMakeLists.txt b/tests/c_headers/idf-usb/host/hid/CMakeLists.txt new file mode 100644 index 00000000..ad1d49fc --- /dev/null +++ b/tests/c_headers/idf-usb/host/hid/CMakeLists.txt @@ -0,0 +1,10 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +get_filename_component(ProjectId ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" ProjectId ${ProjectId}) +project(${ProjectId}) diff --git a/tests/c_headers/idf-usb/host/hid/README.md b/tests/c_headers/idf-usb/host/hid/README.md new file mode 100644 index 00000000..9c52850e --- /dev/null +++ b/tests/c_headers/idf-usb/host/hid/README.md @@ -0,0 +1,61 @@ +| Supported Targets | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | + +# USB HID Class example +This example implements a basic USB Host HID Class Driver, and demonstrates how to use the driver to communicate with USB HID devices (such as Keyboard and Mouse or both) on the ESP32-S2/S3. Currently, the example only supports the HID boot protocol which should be present on most USB Mouse and Keyboards. The example will continuously scan for the connection of any HID Mouse or Keyboard, and attempt to fetch HID reports from those devices once connected. To quit the example (and shut down the HID driver), users can GPIO0 to low (i.e., pressing the "Boot" button on most ESP dev kits). + + +### Hardware Required +* Development board with USB-OTG support +* A USB cable for Power supply and programming +* USB OTG Cable + +### Common Pin Assignments + +Follow instructions in [examples/usb/README.md](../../README.md) for specific hardware setup. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +The example serial output will be the following: + +``` +I (198) example: HID HOST example +I (598) example: Interface number 0, protocol Mouse +I (598) example: Interface number 1, protocol Keyboard + +Mouse +X: 000883 Y: 000058 |o| | +Keyboard +qwertyuiop[]\asdfghjkl;'zxcvbnm,./ +Mouse +X: 000883 Y: 000058 | |o| +``` + +Where every keyboard key printed as char symbol if it is possible and a Hex value for any other key. + +#### Keyboard input data +Keyboard input data starts with the word "Keyboard" and every pressed key is printed to the serial debug. +Left or right Shift modifier is also supported. + +``` +Keyboard +Hello, ESP32 USB HID Keyboard is here! +``` + +#### Mouse input data +Mouse input data starts with the word "Mouse" and has the following structure. +``` +Mouse +X: -00343 Y: 000183 | |o| + | | | | + | | | +- Right mouse button pressed status ("o" - pressed, " " - not pressed) + | | +--- Left mouse button pressed status ("o" - pressed, " " - not pressed) + | +---------- Y relative coordinate of the cursor + +----------------------- X relative coordinate of the cursor +``` diff --git a/tests/c_headers/idf-usb/host/hid/main/CMakeLists.txt b/tests/c_headers/idf-usb/host/hid/main/CMakeLists.txt new file mode 100644 index 00000000..0bf44fb7 --- /dev/null +++ b/tests/c_headers/idf-usb/host/hid/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "hid_host_example.c" + INCLUDE_DIRS "." + PRIV_REQUIRES usb esp_driver_gpio + ) diff --git a/tests/c_headers/idf-usb/host/hid/main/hid_host_example.c b/tests/c_headers/idf-usb/host/hid/main/hid_host_example.c new file mode 100644 index 00000000..fde9f95d --- /dev/null +++ b/tests/c_headers/idf-usb/host/hid/main/hid_host_example.c @@ -0,0 +1,616 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "freertos/queue.h" +#include "esp_err.h" +#include "esp_log.h" +#include "usb/usb_host.h" +#include "errno.h" +#include "driver/gpio.h" + +#include "usb/hid_host.h" +#include "usb/hid_usage_keyboard.h" +#include "usb/hid_usage_mouse.h" + +/* GPIO Pin number for quit from example logic */ +#define APP_QUIT_PIN GPIO_NUM_0 + +static const char *TAG = "example"; + +QueueHandle_t app_event_queue = NULL; + +/** + * @brief APP event group + * + * Application logic can be different. There is a one among other ways to distinguish the + * event by application event group. + * In this example we have two event groups: + * APP_EVENT - General event, which is APP_QUIT_PIN press event (Generally, it is IO0). + * APP_EVENT_HID_HOST - HID Host Driver event, such as device connection/disconnection or input report. + */ +typedef enum { + APP_EVENT = 0, + APP_EVENT_HID_HOST +} app_event_group_t; + +/** + * @brief APP event queue + * + * This event is used for delivering the HID Host event from callback to a task. + */ +typedef struct { + app_event_group_t event_group; + /* HID Host - Device related info */ + struct { + hid_host_device_handle_t handle; + hid_host_driver_event_t event; + void *arg; + } hid_host_device; +} app_event_queue_t; + +/** + * @brief HID Protocol string names + */ +static const char *hid_proto_name_str[] = { + "NONE", + "KEYBOARD", + "MOUSE" +}; + +/** + * @brief Key event + */ +typedef struct { + enum key_state { + KEY_STATE_PRESSED = 0x00, + KEY_STATE_RELEASED = 0x01 + } state; + uint8_t modifier; + uint8_t key_code; +} key_event_t; + +/* Main char symbol for ENTER key */ +#define KEYBOARD_ENTER_MAIN_CHAR '\r' +/* When set to 1 pressing ENTER will be extending with LineFeed during serial debug output */ +#define KEYBOARD_ENTER_LF_EXTEND 1 + +/** + * @brief Scancode to ascii table + */ +const uint8_t keycode2ascii [57][2] = { + {0, 0}, /* HID_KEY_NO_PRESS */ + {0, 0}, /* HID_KEY_ROLLOVER */ + {0, 0}, /* HID_KEY_POST_FAIL */ + {0, 0}, /* HID_KEY_ERROR_UNDEFINED */ + {'a', 'A'}, /* HID_KEY_A */ + {'b', 'B'}, /* HID_KEY_B */ + {'c', 'C'}, /* HID_KEY_C */ + {'d', 'D'}, /* HID_KEY_D */ + {'e', 'E'}, /* HID_KEY_E */ + {'f', 'F'}, /* HID_KEY_F */ + {'g', 'G'}, /* HID_KEY_G */ + {'h', 'H'}, /* HID_KEY_H */ + {'i', 'I'}, /* HID_KEY_I */ + {'j', 'J'}, /* HID_KEY_J */ + {'k', 'K'}, /* HID_KEY_K */ + {'l', 'L'}, /* HID_KEY_L */ + {'m', 'M'}, /* HID_KEY_M */ + {'n', 'N'}, /* HID_KEY_N */ + {'o', 'O'}, /* HID_KEY_O */ + {'p', 'P'}, /* HID_KEY_P */ + {'q', 'Q'}, /* HID_KEY_Q */ + {'r', 'R'}, /* HID_KEY_R */ + {'s', 'S'}, /* HID_KEY_S */ + {'t', 'T'}, /* HID_KEY_T */ + {'u', 'U'}, /* HID_KEY_U */ + {'v', 'V'}, /* HID_KEY_V */ + {'w', 'W'}, /* HID_KEY_W */ + {'x', 'X'}, /* HID_KEY_X */ + {'y', 'Y'}, /* HID_KEY_Y */ + {'z', 'Z'}, /* HID_KEY_Z */ + {'1', '!'}, /* HID_KEY_1 */ + {'2', '@'}, /* HID_KEY_2 */ + {'3', '#'}, /* HID_KEY_3 */ + {'4', '$'}, /* HID_KEY_4 */ + {'5', '%'}, /* HID_KEY_5 */ + {'6', '^'}, /* HID_KEY_6 */ + {'7', '&'}, /* HID_KEY_7 */ + {'8', '*'}, /* HID_KEY_8 */ + {'9', '('}, /* HID_KEY_9 */ + {'0', ')'}, /* HID_KEY_0 */ + {KEYBOARD_ENTER_MAIN_CHAR, KEYBOARD_ENTER_MAIN_CHAR}, /* HID_KEY_ENTER */ + {0, 0}, /* HID_KEY_ESC */ + {'\b', 0}, /* HID_KEY_DEL */ + {0, 0}, /* HID_KEY_TAB */ + {' ', ' '}, /* HID_KEY_SPACE */ + {'-', '_'}, /* HID_KEY_MINUS */ + {'=', '+'}, /* HID_KEY_EQUAL */ + {'[', '{'}, /* HID_KEY_OPEN_BRACKET */ + {']', '}'}, /* HID_KEY_CLOSE_BRACKET */ + {'\\', '|'}, /* HID_KEY_BACK_SLASH */ + {'\\', '|'}, /* HID_KEY_SHARP */ // HOTFIX: for NonUS Keyboards repeat HID_KEY_BACK_SLASH + {';', ':'}, /* HID_KEY_COLON */ + {'\'', '"'}, /* HID_KEY_QUOTE */ + {'`', '~'}, /* HID_KEY_TILDE */ + {',', '<'}, /* HID_KEY_LESS */ + {'.', '>'}, /* HID_KEY_GREATER */ + {'/', '?'} /* HID_KEY_SLASH */ +}; + +/** + * @brief Makes new line depending on report output protocol type + * + * @param[in] proto Current protocol to output + */ +static void hid_print_new_device_report_header(hid_protocol_t proto) +{ + static hid_protocol_t prev_proto_output = -1; + + if (prev_proto_output != proto) { + prev_proto_output = proto; + printf("\r\n"); + if (proto == HID_PROTOCOL_MOUSE) { + printf("Mouse\r\n"); + } else if (proto == HID_PROTOCOL_KEYBOARD) { + printf("Keyboard\r\n"); + } else { + printf("Generic\r\n"); + } + fflush(stdout); + } +} + +/** + * @brief HID Keyboard modifier verification for capitalization application (right or left shift) + * + * @param[in] modifier + * @return true Modifier was pressed (left or right shift) + * @return false Modifier was not pressed (left or right shift) + * + */ +static inline bool hid_keyboard_is_modifier_shift(uint8_t modifier) +{ + if (((modifier & HID_LEFT_SHIFT) == HID_LEFT_SHIFT) || + ((modifier & HID_RIGHT_SHIFT) == HID_RIGHT_SHIFT)) { + return true; + } + return false; +} + +/** + * @brief HID Keyboard get char symbol from key code + * + * @param[in] modifier Keyboard modifier data + * @param[in] key_code Keyboard key code + * @param[in] key_char Pointer to key char data + * + * @return true Key scancode converted successfully + * @return false Key scancode unknown + */ +static inline bool hid_keyboard_get_char(uint8_t modifier, + uint8_t key_code, + unsigned char *key_char) +{ + uint8_t mod = (hid_keyboard_is_modifier_shift(modifier)) ? 1 : 0; + + if ((key_code >= HID_KEY_A) && (key_code <= HID_KEY_SLASH)) { + *key_char = keycode2ascii[key_code][mod]; + } else { + // All other key pressed + return false; + } + + return true; +} + +/** + * @brief HID Keyboard print char symbol + * + * @param[in] key_char Keyboard char to stdout + */ +static inline void hid_keyboard_print_char(unsigned int key_char) +{ + if (!!key_char) { + putchar(key_char); +#if (KEYBOARD_ENTER_LF_EXTEND) + if (KEYBOARD_ENTER_MAIN_CHAR == key_char) { + putchar('\n'); + } +#endif // KEYBOARD_ENTER_LF_EXTEND + fflush(stdout); + } +} + +/** + * @brief Key Event. Key event with the key code, state and modifier. + * + * @param[in] key_event Pointer to Key Event structure + * + */ +static void key_event_callback(key_event_t *key_event) +{ + unsigned char key_char; + + hid_print_new_device_report_header(HID_PROTOCOL_KEYBOARD); + + if (KEY_STATE_PRESSED == key_event->state) { + if (hid_keyboard_get_char(key_event->modifier, + key_event->key_code, &key_char)) { + + hid_keyboard_print_char(key_char); + + } + } +} + +/** + * @brief Key buffer scan code search. + * + * @param[in] src Pointer to source buffer where to search + * @param[in] key Key scancode to search + * @param[in] length Size of the source buffer + */ +static inline bool key_found(const uint8_t *const src, + uint8_t key, + unsigned int length) +{ + for (unsigned int i = 0; i < length; i++) { + if (src[i] == key) { + return true; + } + } + return false; +} + +/** + * @brief USB HID Host Keyboard Interface report callback handler + * + * @param[in] data Pointer to input report data buffer + * @param[in] length Length of input report data buffer + */ +static void hid_host_keyboard_report_callback(const uint8_t *const data, const int length) +{ + hid_keyboard_input_report_boot_t *kb_report = (hid_keyboard_input_report_boot_t *)data; + + if (length < sizeof(hid_keyboard_input_report_boot_t)) { + return; + } + + static uint8_t prev_keys[HID_KEYBOARD_KEY_MAX] = { 0 }; + key_event_t key_event; + + for (int i = 0; i < HID_KEYBOARD_KEY_MAX; i++) { + + // key has been released verification + if (prev_keys[i] > HID_KEY_ERROR_UNDEFINED && + !key_found(kb_report->key, prev_keys[i], HID_KEYBOARD_KEY_MAX)) { + key_event.key_code = prev_keys[i]; + key_event.modifier = 0; + key_event.state = KEY_STATE_RELEASED; + key_event_callback(&key_event); + } + + // key has been pressed verification + if (kb_report->key[i] > HID_KEY_ERROR_UNDEFINED && + !key_found(prev_keys, kb_report->key[i], HID_KEYBOARD_KEY_MAX)) { + key_event.key_code = kb_report->key[i]; + key_event.modifier = kb_report->modifier.val; + key_event.state = KEY_STATE_PRESSED; + key_event_callback(&key_event); + } + } + + memcpy(prev_keys, &kb_report->key, HID_KEYBOARD_KEY_MAX); +} + +/** + * @brief USB HID Host Mouse Interface report callback handler + * + * @param[in] data Pointer to input report data buffer + * @param[in] length Length of input report data buffer + */ +static void hid_host_mouse_report_callback(const uint8_t *const data, const int length) +{ + hid_mouse_input_report_boot_t *mouse_report = (hid_mouse_input_report_boot_t *)data; + + if (length < sizeof(hid_mouse_input_report_boot_t)) { + return; + } + + static int x_pos = 0; + static int y_pos = 0; + + // Calculate absolute position from displacement + x_pos += mouse_report->x_displacement; + y_pos += mouse_report->y_displacement; + + hid_print_new_device_report_header(HID_PROTOCOL_MOUSE); + + printf("X: %06d\tY: %06d\t|%c|%c|\r", + x_pos, y_pos, + (mouse_report->buttons.button1 ? 'o' : ' '), + (mouse_report->buttons.button2 ? 'o' : ' ')); + fflush(stdout); +} + +/** + * @brief USB HID Host Generic Interface report callback handler + * + * 'generic' means anything else than mouse or keyboard + * + * @param[in] data Pointer to input report data buffer + * @param[in] length Length of input report data buffer + */ +static void hid_host_generic_report_callback(const uint8_t *const data, const int length) +{ + hid_print_new_device_report_header(HID_PROTOCOL_NONE); + for (int i = 0; i < length; i++) { + printf("%02X", data[i]); + } + putchar('\r'); +} + +/** + * @brief USB HID Host interface callback + * + * @param[in] hid_device_handle HID Device handle + * @param[in] event HID Host interface event + * @param[in] arg Pointer to arguments, does not used + */ +void hid_host_interface_callback(hid_host_device_handle_t hid_device_handle, + const hid_host_interface_event_t event, + void *arg) +{ + uint8_t data[64] = { 0 }; + size_t data_length = 0; + hid_host_dev_params_t dev_params; + ESP_ERROR_CHECK(hid_host_device_get_params(hid_device_handle, &dev_params)); + + switch (event) { + case HID_HOST_INTERFACE_EVENT_INPUT_REPORT: + ESP_ERROR_CHECK(hid_host_device_get_raw_input_report_data(hid_device_handle, + data, + 64, + &data_length)); + + if (HID_SUBCLASS_BOOT_INTERFACE == dev_params.sub_class) { + if (HID_PROTOCOL_KEYBOARD == dev_params.proto) { + hid_host_keyboard_report_callback(data, data_length); + } else if (HID_PROTOCOL_MOUSE == dev_params.proto) { + hid_host_mouse_report_callback(data, data_length); + } + } else { + hid_host_generic_report_callback(data, data_length); + } + + break; + case HID_HOST_INTERFACE_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "HID Device, protocol '%s' DISCONNECTED", + hid_proto_name_str[dev_params.proto]); + ESP_ERROR_CHECK(hid_host_device_close(hid_device_handle)); + break; + case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR: + ESP_LOGI(TAG, "HID Device, protocol '%s' TRANSFER_ERROR", + hid_proto_name_str[dev_params.proto]); + break; + default: + ESP_LOGE(TAG, "HID Device, protocol '%s' Unhandled event", + hid_proto_name_str[dev_params.proto]); + break; + } +} + +/** + * @brief USB HID Host Device event + * + * @param[in] hid_device_handle HID Device handle + * @param[in] event HID Host Device event + * @param[in] arg Pointer to arguments, does not used + */ +void hid_host_device_event(hid_host_device_handle_t hid_device_handle, + const hid_host_driver_event_t event, + void *arg) +{ + hid_host_dev_params_t dev_params; + ESP_ERROR_CHECK(hid_host_device_get_params(hid_device_handle, &dev_params)); + + switch (event) { + case HID_HOST_DRIVER_EVENT_CONNECTED: + ESP_LOGI(TAG, "HID Device, protocol '%s' CONNECTED", + hid_proto_name_str[dev_params.proto]); + + const hid_host_device_config_t dev_config = { + .callback = hid_host_interface_callback, + .callback_arg = NULL + }; + + ESP_ERROR_CHECK(hid_host_device_open(hid_device_handle, &dev_config)); + if (HID_SUBCLASS_BOOT_INTERFACE == dev_params.sub_class) { + ESP_ERROR_CHECK(hid_class_request_set_protocol(hid_device_handle, HID_REPORT_PROTOCOL_BOOT)); + if (HID_PROTOCOL_KEYBOARD == dev_params.proto) { + ESP_ERROR_CHECK(hid_class_request_set_idle(hid_device_handle, 0, 0)); + } + } + ESP_ERROR_CHECK(hid_host_device_start(hid_device_handle)); + break; + default: + break; + } +} + +/** + * @brief Start USB Host install and handle common USB host library events while app pin not low + * + * @param[in] arg Not used + */ +static void usb_lib_task(void *arg) +{ + const usb_host_config_t host_config = { + .skip_phy_setup = false, + .intr_flags = ESP_INTR_FLAG_LEVEL1, + }; + + ESP_ERROR_CHECK(usb_host_install(&host_config)); + xTaskNotifyGive(arg); + + while (true) { + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + // In this example, there is only one client registered + // So, once we deregister the client, this call must succeed with ESP_OK + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + ESP_ERROR_CHECK(usb_host_device_free_all()); + break; + } + } + + ESP_LOGI(TAG, "USB shutdown"); + // Clean up USB Host + vTaskDelay(10); // Short delay to allow clients clean-up + ESP_ERROR_CHECK(usb_host_uninstall()); + vTaskDelete(NULL); +} + +/** + * @brief BOOT button pressed callback + * + * Signal application to exit the HID Host task + * + * @param[in] arg Unused + */ +static void gpio_isr_cb(void *arg) +{ + BaseType_t xTaskWoken = pdFALSE; + const app_event_queue_t evt_queue = { + .event_group = APP_EVENT, + }; + + if (app_event_queue) { + xQueueSendFromISR(app_event_queue, &evt_queue, &xTaskWoken); + } + + if (xTaskWoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +/** + * @brief HID Host Device callback + * + * Puts new HID Device event to the queue + * + * @param[in] hid_device_handle HID Device handle + * @param[in] event HID Device event + * @param[in] arg Not used + */ +void hid_host_device_callback(hid_host_device_handle_t hid_device_handle, + const hid_host_driver_event_t event, + void *arg) +{ + const app_event_queue_t evt_queue = { + .event_group = APP_EVENT_HID_HOST, + // HID Host Device related info + .hid_host_device.handle = hid_device_handle, + .hid_host_device.event = event, + .hid_host_device.arg = arg + }; + + if (app_event_queue) { + xQueueSend(app_event_queue, &evt_queue, 0); + } +} + +void app_main(void) +{ + BaseType_t task_created; + app_event_queue_t evt_queue; + ESP_LOGI(TAG, "HID Host example"); + + // Init BOOT button: Pressing the button simulates app request to exit + // It will disconnect the USB device and uninstall the HID driver and USB Host Lib + const gpio_config_t input_pin = { + .pin_bit_mask = BIT64(APP_QUIT_PIN), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + .intr_type = GPIO_INTR_NEGEDGE, + }; + ESP_ERROR_CHECK(gpio_config(&input_pin)); + ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1)); + ESP_ERROR_CHECK(gpio_isr_handler_add(APP_QUIT_PIN, gpio_isr_cb, NULL)); + + /* + * Create usb_lib_task to: + * - initialize USB Host library + * - Handle USB Host events while APP pin in in HIGH state + */ + task_created = xTaskCreatePinnedToCore(usb_lib_task, + "usb_events", + 4096, + xTaskGetCurrentTaskHandle(), + 2, NULL, 0); + assert(task_created == pdTRUE); + + // Wait for notification from usb_lib_task to proceed + ulTaskNotifyTake(false, 1000); + + /* + * HID host driver configuration + * - create background task for handling low level event inside the HID driver + * - provide the device callback to get new HID Device connection event + */ + const hid_host_driver_config_t hid_host_driver_config = { + .create_background_task = true, + .task_priority = 5, + .stack_size = 4096, + .core_id = 0, + .callback = hid_host_device_callback, + .callback_arg = NULL + }; + + ESP_ERROR_CHECK(hid_host_install(&hid_host_driver_config)); + + // Create queue + app_event_queue = xQueueCreate(10, sizeof(app_event_queue_t)); + + ESP_LOGI(TAG, "Waiting for HID Device to be connected"); + + while (1) { + // Wait queue + if (xQueueReceive(app_event_queue, &evt_queue, portMAX_DELAY)) { + if (APP_EVENT == evt_queue.event_group) { + // User pressed button + usb_host_lib_info_t lib_info; + ESP_ERROR_CHECK(usb_host_lib_info(&lib_info)); + if (lib_info.num_devices == 0) { + // End while cycle + break; + } else { + ESP_LOGW(TAG, "To shutdown example, remove all USB devices and press button again."); + // Keep polling + } + } + + if (APP_EVENT_HID_HOST == evt_queue.event_group) { + hid_host_device_event(evt_queue.hid_host_device.handle, + evt_queue.hid_host_device.event, + evt_queue.hid_host_device.arg); + } + } + } + + ESP_LOGI(TAG, "HID Driver uninstall"); + ESP_ERROR_CHECK(hid_host_uninstall()); + gpio_isr_handler_remove(APP_QUIT_PIN); + xQueueReset(app_event_queue); + vQueueDelete(app_event_queue); +} diff --git a/tests/c_headers/idf-usb/host/hid/main/idf_component.yml b/tests/c_headers/idf-usb/host/hid/main/idf_component.yml new file mode 100644 index 00000000..0c3620e3 --- /dev/null +++ b/tests/c_headers/idf-usb/host/hid/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + idf: ">=4.4" + usb_host_hid: "^1.0.1" diff --git a/tests/c_headers/idf-usb/host/hid/sdkconfig.defaults b/tests/c_headers/idf-usb/host/hid/sdkconfig.defaults new file mode 100644 index 00000000..bf1b553b --- /dev/null +++ b/tests/c_headers/idf-usb/host/hid/sdkconfig.defaults @@ -0,0 +1,4 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_USB_HOST_HUBS_SUPPORTED=y diff --git a/tests/c_headers/idf-usb/host/msc/CMakeLists.txt b/tests/c_headers/idf-usb/host/msc/CMakeLists.txt new file mode 100644 index 00000000..f00f0d98 --- /dev/null +++ b/tests/c_headers/idf-usb/host/msc/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(usb-msc) diff --git a/tests/c_headers/idf-usb/host/msc/README.md b/tests/c_headers/idf-usb/host/msc/README.md new file mode 100644 index 00000000..46a8cc00 --- /dev/null +++ b/tests/c_headers/idf-usb/host/msc/README.md @@ -0,0 +1,139 @@ +| Supported Targets | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | + +# USB Mass Storage Class example + +## Overview + +This example demonstrates usage of the MSC (Mass Storage Class) to access storage on a USB flash drive. Upon connection of the flash drive, it is mounted to the Virtual filesystem. The following example operations are then performed: + +1. Print device info (capacity, sectors size, and count...). +2. List all folders and files in the root directory of the USB flash drive. +3. Create `esp` subdirectory (if not present already), as well as a `test.txt` file. +4. Run read/write benchmarks by transferring 1 MB of data to a `dummy` file. + +> Note: This example currently supports only FAT-formatted drives. Other file systems, such as exFAT or NTFS, are not compatible with this example. Please ensure that your USB drive is formatted as FAT to avoid compatibility issues. + +### USB Reconnections + +The example is run in a loop so that it can demonstrate USB connection and reconnection handling. If you want to deinitialize the entire USB Host Stack, you can short GPIO0 to GND. GPIO0 is usually mapped to a BOOT button, thus pressing the button will deinitialize the stack. + +### Hardware Required + +* Development board with USB-OTG support +* A USB cable for power supply and programming +* A USB flash drive + +### USB Host Limitations + +#### ESP32-S2 & ESP32-S3 +The USB OTG peripheral on ESP32-S2 and ESP32-S3 in host mode supports **up to 8 bidirectional endpoints**. Each of these endpoints can be configured as either IN or OUT. + +Since each USB Mass Storage Class (MSC) device typically requires **3 endpoints** (Control, BULK IN, and BULK OUT), and USB hubs also consume endpoints (Control, INTERRUPT IN), this limits the theoretical maximum number of connected MSC devices to **2**. + +#### ESP32-P4 +ESP32-P4 features a more advanced USB 2.0 OTG controller with **up to 16 bidirectional endpoints** in host mode. Given the same endpoint requirements per device, the theoretical maximum number of connected MSC devices is **4**. + +### Example Limitations +The number of simultaneously connected MSC devices is now **determined by `CONFIG_FATFS_VOLUME_COUNT`**. This ensures consistency with the FAT filesystem configuration and prevents misalignment between the MSC device count and the available FAT volumes. + +By default, `CONFIG_FATFS_VOLUME_COUNT` is set to **2**, meaning that up to **2 MSC devices** can be connected at the same time. If your application requires more devices, you can increase `CONFIG_FATFS_VOLUME_COUNT` accordingly, while keeping in mind **endpoint and memory constraints**. + +#### How to configure `CONFIG_FATFS_VOLUME_COUNT` in menuconfig: +To change the maximum number of MSC devices, adjust `CONFIG_FATFS_VOLUME_COUNT` in **menuconfig**: +idf.py menuconfig → Component config → FAT Filesystem support → Number of FATFS volumes +Set this value to the desired number of MSC devices, ensuring it aligns with USB endpoint availability. + +### Common Pin Assignments + +Follow instructions in [examples/usb/README.md](../../README.md) for specific hardware setup. + +Additionally, GPIO0 can be shorted to ground in order to deinitialize the USB stack. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +``` +... +I (323) example: Waiting for USB flash drive to be connected +I (3353) example: MSC device connected (usb_addr=3) +*** Device descriptor *** +bLength 18 +bDescriptorType 1 +bcdUSB 2.00 +bDeviceClass 0x0 +bDeviceSubClass 0x0 +bDeviceProtocol 0x0 +bMaxPacketSize0 64 +idVendor 0xabcd +idProduct 0x1234 +bcdDevice 1.00 +iManufacturer 1 +iProduct 2 +iSerialNumber 3 +bNumConfigurations 1 +*** Configuration descriptor *** +bLength 9 +bDescriptorType 2 +wTotalLength 32 +bNumInterfaces 1 +bConfigurationValue 1 +iConfiguration 0 +bmAttributes 0x80 +bMaxPower 100mA + *** Interface descriptor *** + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 0x8 + bInterfaceSubClass 0x6 + bInterfaceProtocol 0x50 + iInterface 0 + *** Endpoint descriptor *** + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x1 EP 1 OUT + bmAttributes 0x2 BULK + wMaxPacketSize 512 + bInterval 0 + *** Endpoint descriptor *** + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 0x2 BULK + wMaxPacketSize 512 + bInterval 0 +Device info: + Capacity: 3839 MB + Sector size: 512 + Sector count: 7864319 + PID: 0x1234 + VID: 0xABCD + iProduct: UDisk + iManufacturer: General + iSerialNumber: +I (3763) example: ls command output for all connected devices: +I (3763) example: Listing contents of /usb0 +/usb0/SYSTEM~1 +/usb0/ESP +I (3773) example: Reading file +I (3773) example: Read from file '/usb0/esp/test.txt': 'Hello World!' +I (3803) example: Writing to file /usb0/esp/dummy +I (3943) example: Write speed 7.16 MiB/s +I (3953) example: Reading from file /usb0/esp/dummy +I (4093) example: Read speed 7.16 MiB/s +I (4103) example: Example finished, you can disconnect the USB flash drive (or connect another USB flash drive) +``` diff --git a/tests/c_headers/idf-usb/host/msc/main/CMakeLists.txt b/tests/c_headers/idf-usb/host/msc/main/CMakeLists.txt new file mode 100644 index 00000000..cf7d18b3 --- /dev/null +++ b/tests/c_headers/idf-usb/host/msc/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "msc_example_main.c" + INCLUDE_DIRS "" + PRIV_REQUIRES usb fatfs esp_driver_gpio esp_timer + ) diff --git a/tests/c_headers/idf-usb/host/msc/main/idf_component.yml b/tests/c_headers/idf-usb/host/msc/main/idf_component.yml new file mode 100644 index 00000000..198a93bb --- /dev/null +++ b/tests/c_headers/idf-usb/host/msc/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + idf: ">=4.4" + usb_host_msc: "^1.1.1" diff --git a/tests/c_headers/idf-usb/host/msc/main/msc_example_main.c b/tests/c_headers/idf-usb/host/msc/main/msc_example_main.c new file mode 100644 index 00000000..690fb82d --- /dev/null +++ b/tests/c_headers/idf-usb/host/msc/main/msc_example_main.c @@ -0,0 +1,560 @@ +/* + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "esp_timer.h" +#include "esp_err.h" +#include "esp_log.h" +#include "usb/usb_host.h" +#include "usb/msc_host_vfs.h" +#include "ffconf.h" +#include "errno.h" +#include "driver/gpio.h" + +static const char *TAG = "example"; + +#define MNT_PATH "/usb" // Base mount path prefix, devices will be mounted as /usb0, /usb1, /usb2... +#define APP_QUIT_PIN GPIO_NUM_0 // BOOT button on most boards +#define BUFFER_SIZE 4096 // The read/write performance can be improved with larger buffer for the cost of RAM, 4kB is enough for most usecases +#define MAX_MSC_DEVICES CONFIG_FATFS_VOLUME_COUNT /*!< Maximum number of simultaneously connected MSC (Mass Storage Class) devices. + Adjust this value based on available system resources and expected use cases. */ + +/** + * @brief MSC Device Entry + * + * This structure holds information about a connected MSC device, + * including the USB address, MSC device handle, VFS handle, and assigned mount point. + */ +typedef struct { + uint8_t usb_addr; /*!< USB device address */ + msc_host_device_handle_t msc_device; /*!< Handle of the MSC device */ + msc_host_vfs_handle_t vfs_handle; /*!< VFS handle assigned to the MSC device */ +} msc_dev_entry_t; + +static msc_dev_entry_t *msc_devices[MAX_MSC_DEVICES] = {0}; + +/** + * @brief Application Queue and its messages ID + */ +static QueueHandle_t app_queue; +typedef struct { + enum { + APP_QUIT, // Signals request to exit the application + APP_DEVICE_CONNECTED, // USB device connect event + APP_DEVICE_DISCONNECTED, // USB device disconnect event + } id; + union { + uint8_t new_dev_address; // Address of new USB device for APP_DEVICE_CONNECTED event + msc_host_device_handle_t device_handle; // Handle of removed USB device for APP_DEVICE_DISCONNECTED event + } data; +} app_message_t; + +/** + * @brief Find a free slot in the device table. + * + * @return Index of the free slot, or -1 if no free slot is available. + */ +static inline int find_free_slot(void) +{ + for (int i = 0; i < MAX_MSC_DEVICES; i++) { + if (msc_devices[i] == NULL) { + return i; + } + } + return -1; +} + +/** + * @brief Allocates a new MSC device entry and mounts it to VFS. + * + * This function finds a free slot for a new MSC device, allocates memory for the device entry, + * installs the MSC device, and mounts it to the virtual file system (VFS). + * + * If any step fails, the function ensures proper cleanup of allocated resources before returning an error. + * + * @param[in] msg Message containing the address of the new USB device. + * @param[out] out_slot Pointer to store the allocated slot index on success. + * + * @return + * - ESP_OK on success. + * - ESP_ERR_NOT_FOUND if no free slot is available. + * - ESP_ERR_NO_MEM if memory allocation fails. + * - Other esp_err_t codes if device installation or VFS registration fails. + */ +static esp_err_t allocate_new_msc_device(const app_message_t *msg, int *out_slot) +{ + int slot = find_free_slot(); + if (slot < 0) { + ESP_LOGW(TAG, "No free slots for new MSC device (max %d)", MAX_MSC_DEVICES); + return ESP_ERR_NOT_FOUND; + } + + msc_devices[slot] = calloc(1, sizeof(msc_dev_entry_t)); + if (!msc_devices[slot]) { + ESP_LOGE(TAG, "Failed to allocate memory for new MSC device entry"); + return ESP_ERR_NO_MEM; + } + esp_err_t err = msc_host_install_device(msg->data.new_dev_address, &msc_devices[slot]->msc_device); + if (err != ESP_OK) { + ESP_LOGE(TAG, "msc_host_install_device failed: %s", esp_err_to_name(err)); + free(msc_devices[slot]); + msc_devices[slot] = NULL; + return err; + } + + msc_devices[slot]->usb_addr = msg->data.new_dev_address; + + const esp_vfs_fat_mount_config_t mount_config = { + .format_if_mount_failed = false, + .max_files = 3, + .allocation_unit_size = 8192, + }; + + char mount_path[16]; + snprintf(mount_path, sizeof(mount_path), MNT_PATH "%d", slot); + + err = msc_host_vfs_register(msc_devices[slot]->msc_device, mount_path, &mount_config, &msc_devices[slot]->vfs_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "msc_host_vfs_register failed: %s", esp_err_to_name(err)); + ESP_ERROR_CHECK(msc_host_uninstall_device(msc_devices[slot]->msc_device)); + free(msc_devices[slot]); + msc_devices[slot] = NULL; + return err; + } + + *out_slot = slot; + return ESP_OK; +} + +/** + * @brief Find a slot by MSC device handle. + * + * This function searches for the slot corresponding to a given MSC device handle. + * + * @param handle MSC device handle to search for. + * @return Index of the slot if found, otherwise -1. + */ +static int find_slot_by_handle(msc_host_device_handle_t handle) +{ + for (int i = 0; i < MAX_MSC_DEVICES; i++) { + if (msc_devices[i] && msc_devices[i]->msc_device == handle) { + return i; + } + } + return -1; +} + +/** + * @brief Free resources associated with a specific MSC device by slot index. + * + * This function releases all resources associated with a device identified by its slot index. + * It unmounts the VFS, uninstalls the MSC device, and frees the allocated memory. + * + * @param slot Index of the MSC device in the device array. + */ +static void free_msc_device(int slot) +{ + if (slot < 0 || slot >= MAX_MSC_DEVICES || !msc_devices[slot]) { + ESP_LOGE(TAG, "Invalid slot index for MSC device deallocation"); + return; + } + + if (msc_devices[slot]->vfs_handle) { + ESP_ERROR_CHECK(msc_host_vfs_unregister(msc_devices[slot]->vfs_handle)); + } + if (msc_devices[slot]->msc_device) { + ESP_ERROR_CHECK(msc_host_uninstall_device(msc_devices[slot]->msc_device)); + } + + free(msc_devices[slot]); + msc_devices[slot] = NULL; +} + +/** + * @brief Free all connected MSC devices. + * + * Iterates over all allocated MSC devices, unmounts them from VFS, and frees their memory. + */ +static void free_all_msc_devices(void) +{ + for (int i = 0; i < MAX_MSC_DEVICES; i++) { + if (msc_devices[i]) { + free_msc_device(i); + } + } +} + +/** + * @brief BOOT button pressed callback + * + * Signal application to exit the main task + * + * @param[in] arg Unused + */ +static void gpio_cb(void *arg) +{ + BaseType_t xTaskWoken = pdFALSE; + app_message_t message = { + .id = APP_QUIT, + }; + + if (app_queue) { + xQueueSendFromISR(app_queue, &message, &xTaskWoken); + } + + if (xTaskWoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +/** + * @brief Find a USB addr by MSC device handle. + * + * @param handle MSC device handle + * @return USB addr, or -1 if not found. + */ +static inline int8_t find_usb_addr_by_handle(msc_host_device_handle_t handle) +{ + for (int8_t i = 0; i < MAX_MSC_DEVICES; i++) { + if (msc_devices[i] && msc_devices[i]->msc_device == handle) { + return msc_devices[i]->usb_addr; + } + } + return -1; +} + +/** + * @brief MSC driver callback + * + * Signal device connection/disconnection to the main task + * + * @param[in] event MSC event + * @param[in] arg MSC event data + */ +static void msc_event_cb(const msc_host_event_t *event, void *arg) +{ + if (event->event == MSC_DEVICE_CONNECTED) { + ESP_LOGI(TAG, "MSC device connected (usb_addr=%d)", event->device.address); + app_message_t message = { + .id = APP_DEVICE_CONNECTED, + .data.new_dev_address = event->device.address, + }; + xQueueSend(app_queue, &message, portMAX_DELAY); + } else if (event->event == MSC_DEVICE_DISCONNECTED) { + int usb_addr = find_usb_addr_by_handle(event->device.handle); + if (usb_addr >= 0) { + ESP_LOGI(TAG, "MSC device disconnected (usb_addr=%d)", usb_addr); + } else { + ESP_LOGW(TAG, "MSC device disconnected, but failed to retrieve USB address"); + } + app_message_t message = { + .id = APP_DEVICE_DISCONNECTED, + .data.device_handle = event->device.handle, + }; + xQueueSend(app_queue, &message, portMAX_DELAY); + } +} + +/** + * @brief Print information about the connected MSC device. + * + * This function prints detailed information about the connected USB MSC device, + * such as capacity, sector size, PID, VID, and string descriptors. + * + * @param[in] info Pointer to MSC device information structure. + */ +static void print_device_info(msc_host_device_info_t *info) +{ + const size_t megabyte = 1024 * 1024; + uint64_t capacity = ((uint64_t)info->sector_size * info->sector_count) / megabyte; + + printf("Device info:\n"); + printf("\t Capacity: %llu MB\n", capacity); + printf("\t Sector size: %"PRIu32"\n", info->sector_size); + printf("\t Sector count: %"PRIu32"\n", info->sector_count); + printf("\t PID: 0x%04X \n", info->idProduct); + printf("\t VID: 0x%04X \n", info->idVendor); +#ifndef CONFIG_NEWLIB_NANO_FORMAT + wprintf(L"\t iProduct: %S \n", info->iProduct); + wprintf(L"\t iManufacturer: %S \n", info->iManufacturer); + wprintf(L"\t iSerialNumber: %S \n", info->iSerialNumber); +#endif +} + +/** + * @brief Perform basic file operations on the mounted USB storage device. + * + * This function demonstrates basic file I/O operations: + * - Create a directory `/usb/esp` if it does not exist. + * - Create a file `test.txt` in the directory with sample content if it does not exist. + * - Read the content of the `test.txt` file and print it to the console. + * + * @param[in] slot Index of the mounted USB device (0 to MAX_MSC_DEVICES-1). + */ +static void file_operations(int slot) +{ + char directory[32]; + char file_path[32]; + snprintf(directory, sizeof(directory), MNT_PATH "%d/esp", slot); + snprintf(file_path, sizeof(file_path), MNT_PATH "%d/esp/test.txt", slot); + + // Create /usb/esp directory + struct stat s = {0}; + bool directory_exists = stat(directory, &s) == 0; + if (!directory_exists) { + if (mkdir(directory, 0775) != 0) { + ESP_LOGE(TAG, "mkdir failed with errno: %s", strerror(errno)); + } + } + + // Create /usb/esp/test.txt file, if it doesn't exist + if (stat(file_path, &s) != 0) { + ESP_LOGI(TAG, "Creating file"); + FILE *f = fopen(file_path, "w"); + if (f == NULL) { + ESP_LOGE(TAG, "Failed to open file for writing"); + return; + } + fprintf(f, "Hello World!\n"); + fclose(f); + } + + // Read back the file + FILE *f; + ESP_LOGI(TAG, "Reading file"); + f = fopen(file_path, "r"); + if (f == NULL) { + ESP_LOGE(TAG, "Failed to open file for reading"); + return; + } + char line[64]; + fgets(line, sizeof(line), f); + fclose(f); + // strip newline + char *pos = strchr(line, '\n'); + if (pos) { + *pos = '\0'; + } + ESP_LOGI(TAG, "Read from file '%s': '%s'", file_path, line); +} + +/** + * @brief Perform sequential write and read speed test on the mounted USB storage device. + * + * This function performs: + * - A write speed test by writing a series of 4KB blocks to a test file. + * - A read speed test by reading the same number of 4KB blocks from the file. + * + * The results (in MiB/s) are printed to the console. + * + * @param[in] slot Index of the mounted USB device (0 to MAX_MSC_DEVICES-1). + */ +static void speed_test(int slot) +{ +#define ITERATIONS 256 // 256 * 4kb = 1MB + int64_t test_start, test_end; + char test_file[32]; + snprintf(test_file, sizeof(test_file), MNT_PATH "%d/esp/dummy", slot); + + FILE *f = fopen(test_file, "wb+"); + if (f == NULL) { + ESP_LOGE(TAG, "Failed to open file for writing"); + return; + } + // Set larger buffer for this file. It results in larger and more effective USB transfers + setvbuf(f, NULL, _IOFBF, BUFFER_SIZE); + + // Allocate application buffer used for read/write + uint8_t *data = malloc(BUFFER_SIZE); + assert(data); + + ESP_LOGI(TAG, "Writing to file %s", test_file); + test_start = esp_timer_get_time(); + for (int i = 0; i < ITERATIONS; i++) { + if (fwrite(data, BUFFER_SIZE, 1, f) == 0) { + ESP_LOGE(TAG, "Write error"); + fclose(f); + free(data); + return; + } + } + test_end = esp_timer_get_time(); + ESP_LOGI(TAG, "Write speed %1.2f MiB/s", (BUFFER_SIZE * ITERATIONS) / (float)(test_end - test_start)); + rewind(f); + + ESP_LOGI(TAG, "Reading from file %s", test_file); + test_start = esp_timer_get_time(); + for (int i = 0; i < ITERATIONS; i++) { + if (0 == fread(data, BUFFER_SIZE, 1, f)) { + ESP_LOGE(TAG, "Read error"); + fclose(f); + free(data); + return; + } + } + test_end = esp_timer_get_time(); + ESP_LOGI(TAG, "Read speed %1.2f MiB/s", (BUFFER_SIZE * ITERATIONS) / (float)(test_end - test_start)); + + fclose(f); + free(data); +} + +/** + * @brief USB task + * + * Install USB Host Library and MSC driver. + * Handle USB Host Library events + * + * @param[in] args Unused + */ +static void usb_task(void *args) +{ + const usb_host_config_t host_config = { .intr_flags = ESP_INTR_FLAG_LEVEL1 }; + ESP_ERROR_CHECK(usb_host_install(&host_config)); + + const msc_host_driver_config_t msc_config = { + .create_backround_task = true, + .task_priority = 5, + .stack_size = 4096, + .callback = msc_event_cb, + }; + ESP_ERROR_CHECK(msc_host_install(&msc_config)); + + bool has_clients = true; + while (true) { + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + + // Release devices once all clients has deregistered + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + has_clients = false; + if (usb_host_device_free_all() == ESP_OK) { + break; + }; + } + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE && !has_clients) { + break; + } + } + + vTaskDelay(10); // Give clients some time to uninstall + ESP_LOGI(TAG, "Deinitializing USB"); + ESP_ERROR_CHECK(usb_host_uninstall()); + vTaskDelete(NULL); +} + +/** + * @brief List contents of the root directories of all mounted USB storage devices. + * + * This function iterates over all mounted MSC devices and lists the contents + * of their root directories. It is intended for debugging and demonstration purposes. + * + * For each connected and mounted device, the function attempts to open the root directory + * `/usb` and print the names of all files and directories contained within. + * + * If opening the directory fails, an error is logged. + */ +static inline void show_list_files_all_devices(void) +{ + ESP_LOGI(TAG, "ls command output for all connected devices:"); + for (int i = 0; i < MAX_MSC_DEVICES; i++) { + if (msc_devices[i]) { + char mount_path[16]; + snprintf(mount_path, sizeof(mount_path), MNT_PATH "%d", i); + + ESP_LOGI(TAG, "Listing contents of %s", mount_path); + struct dirent *d; + DIR *dh = opendir(mount_path); + if (!dh) { + ESP_LOGE(TAG, "Failed to open directory: %s", mount_path); + continue; + } + + while ((d = readdir(dh)) != NULL) { + printf("%s/%s\n", mount_path, d->d_name); + } + closedir(dh); + } + } +} + +void app_main(void) +{ + // Create FreeRTOS primitives + app_queue = xQueueCreate(5, sizeof(app_message_t)); + assert(app_queue); + + BaseType_t task_created = xTaskCreate(usb_task, "usb_task", 4096, NULL, 2, NULL); + assert(task_created); + + // Init BOOT button: Pressing the button simulates app request to exit + // It will disconnect the USB device and uninstall the MSC drivers and USB Host Lib + const gpio_config_t input_pin = { + .pin_bit_mask = BIT64(APP_QUIT_PIN), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + .intr_type = GPIO_INTR_NEGEDGE, + }; + ESP_ERROR_CHECK(gpio_config(&input_pin)); + ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1)); + ESP_ERROR_CHECK(gpio_isr_handler_add(APP_QUIT_PIN, gpio_cb, NULL)); + + ESP_LOGI(TAG, "Waiting for USB flash drive to be connected"); + + // Perform all example operations in a loop to allow USB reconnections + while (1) { + app_message_t msg; + xQueueReceive(app_queue, &msg, portMAX_DELAY); + + if (msg.id == APP_DEVICE_CONNECTED) { + int slot; + esp_err_t res = allocate_new_msc_device(&msg, &slot); + if (res != ESP_OK) { + continue; + } + // 2. Print information about the connected disk + msc_host_device_info_t info; + ESP_ERROR_CHECK(msc_host_get_device_info(msc_devices[slot]->msc_device, &info)); + msc_host_print_descriptors(msc_devices[slot]->msc_device); + print_device_info(&info); + + // 3. List all the files in root directory from all connected msc devices + show_list_files_all_devices(); + + // 4. The disk is mounted to Virtual File System, perform some basic demo file operation + file_operations(slot); + // 5. Perform speed test + speed_test(slot); + + ESP_LOGI(TAG, "Example finished, you can disconnect the USB flash drive (or connect another USB flash drive)"); + } + if (msg.id == APP_DEVICE_DISCONNECTED) { + int slot = find_slot_by_handle(msg.data.device_handle); + if (slot >= 0) { + free_msc_device(slot); + } + } + if (msg.id == APP_QUIT) { + free_all_msc_devices(); + msc_host_uninstall(); + break; + } + } + + ESP_LOGI(TAG, "Done"); + gpio_isr_handler_remove(APP_QUIT_PIN); + gpio_uninstall_isr_service(); + vQueueDelete(app_queue); +} diff --git a/tests/c_headers/idf-usb/host/msc/pytest_usb_host_msc.py b/tests/c_headers/idf-usb/host/msc/pytest_usb_host_msc.py new file mode 100644 index 00000000..5a4fda39 --- /dev/null +++ b/tests/c_headers/idf-usb/host/msc/pytest_usb_host_msc.py @@ -0,0 +1,45 @@ +# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.temp_skip_ci(targets=['esp32s2'], reason='lack of runners with usb_host_flash_disk tag') +@pytest.mark.usb_host_flash_disk +@idf_parametrize( + 'config,target', + [('default', 'esp32s2'), ('default', 'esp32s3'), ('default', 'esp32p4'), ('esp32p4_psram', 'esp32p4')], + indirect=['config', 'target'], +) +def test_usb_host_msc_example(dut: Dut) -> None: + # Check whether the USB-DWC DMA capable memory is allocated in PSRAM + usb_dwc_in_psram = bool(dut.app.sdkconfig.get('USB_HOST_DWC_DMA_CAP_MEMORY_IN_PSRAM')) + + # Get wMaxPacketSize to get USB device speed + max_packet_size = int(dut.expect(r'wMaxPacketSize (\d{2,3})')[1].decode()) + + # Check result of file_operations() + dut.expect(r"example: Read from file '/usb[0-9]/esp/test.txt': 'Hello World!'") + + # Check result of speed_test() + write_throughput = float(dut.expect(r'example: Write speed ([0-9]*[.]?[0-9]+) MiB')[1].decode()) + read_throughput = float(dut.expect(r'example: Read speed ([0-9]*[.]?[0-9]+) MiB')[1].decode()) + + # Set write and read throughput limits + if max_packet_size == 512: # wMaxPacketSize = 512 for HS + write_throughput_limit = 3.5 if usb_dwc_in_psram else 4.9 + read_throughput_limit = 9.9 if usb_dwc_in_psram else 11.5 + else: # wMaxPacketSize = 64 for FS + write_throughput_limit = 0.9 + read_throughput_limit = 1.0 + + # Evaluate the speed measurements + if write_throughput > write_throughput_limit: + print('Write throughput put OK') + else: + print('write throughput too slow!') + if read_throughput > read_throughput_limit: + print('Read throughput put OK') + else: + print('Read throughput too slow!') diff --git a/tests/c_headers/idf-usb/host/msc/sdkconfig.ci b/tests/c_headers/idf-usb/host/msc/sdkconfig.ci new file mode 100644 index 00000000..e69de29b diff --git a/tests/c_headers/idf-usb/host/msc/sdkconfig.ci.esp32p4_psram b/tests/c_headers/idf-usb/host/msc/sdkconfig.ci.esp32p4_psram new file mode 100644 index 00000000..fa9e86ed --- /dev/null +++ b/tests/c_headers/idf-usb/host/msc/sdkconfig.ci.esp32p4_psram @@ -0,0 +1,11 @@ +CONFIG_IDF_TARGET="esp32p4" + +# Enable experimental features, to set PSRAM frequency to 200 MHz +CONFIG_IDF_EXPERIMENTAL_FEATURES=y + +# Enable PSRAM and set PSRAM frequency +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y + +# Allocate USB_DWC DMA capable memory in PSRAM +CONFIG_USB_HOST_DWC_DMA_CAP_MEMORY_IN_PSRAM=y diff --git a/tests/c_headers/idf-usb/host/msc/sdkconfig.defaults b/tests/c_headers/idf-usb/host/msc/sdkconfig.defaults new file mode 100644 index 00000000..bf1b553b --- /dev/null +++ b/tests/c_headers/idf-usb/host/msc/sdkconfig.defaults @@ -0,0 +1,4 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_USB_HOST_HUBS_SUPPORTED=y diff --git a/tests/c_headers/idf-usb/host/usb_host_lib/CMakeLists.txt b/tests/c_headers/idf-usb/host/usb_host_lib/CMakeLists.txt new file mode 100644 index 00000000..153b20d5 --- /dev/null +++ b/tests/c_headers/idf-usb/host/usb_host_lib/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(usb_host_lib_example) diff --git a/tests/c_headers/idf-usb/host/usb_host_lib/README.md b/tests/c_headers/idf-usb/host/usb_host_lib/README.md new file mode 100644 index 00000000..e7a2028e --- /dev/null +++ b/tests/c_headers/idf-usb/host/usb_host_lib/README.md @@ -0,0 +1,193 @@ +| Supported Targets | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | + +# USB Host Library Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example demonstrates the basic usage of the [USB Host Library API](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_host.html) by implementing a pseudo class driver and a Host Library task. The example does the following: + +1. Install Host Library and register a client +2. Waits for a device connection +3. Prints the device's information (such as device/configuration/string descriptors) +4. Waits for the device to disconnect +5. Repeats steps 2 to 4 until a user pressess a button, which quits the `app` +6. If the button has been pressed, while a USB device is still connected, the user will be prompted to remove the device and push the button again to quit the `app` +7. Deregister the client, uninstall the Host Library and quit the `app` + +The example demonstrates the following aspects of the USB Host Library API: + +- How to use the Library API to: + - Install and uninstall the USB Host Library + - Run the library event handler function and usb host library task + - How to handle library events +- How to use the Client API from a client task to: + - Register and deregister a client of the USB Host Library + - Run the client event handler functions + - How to handle client events via various callbacks + - Open and close a device + - Get a device's descriptors + +## How to use example + +### Hardware Required + +* Development board with USB-OTG support. +* Follow instruction in [examples/usb/README.md](../../README.md) for specific hardware setup. + +### Configure the project + +``` +idf.py menuconfig +``` + +* The USB Host Stack has a maximum supported transfer size for control transfer during device enumeration. This size is specified via the USB_HOST_CONTROL_TRANSFER_MAX_SIZE configuration option and has a default value of 256 bytes. Therefore, if devices with length config/string descriptors are used, users may want to increase the size of this configuration. +* Push button GPIO selection + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +``` +I (305) main_task: Started on CPU0 +I (315) main_task: Calling app_main() +I (315) USB host lib: USB host library example +I (315) gpio: GPIO[0]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:2 +I (325) USB host lib: Installing USB Host Library +I (365) CLASS: Registering Client +I (745) CLASS: Opening device at address 1 +I (745) CLASS: Getting device information +I (745) CLASS: Full speed +I (745) CLASS: bConfigurationValue 1 +I (745) CLASS: Getting device descriptor +*** Device descriptor *** +bLength 18 +bDescriptorType 1 +bcdUSB 2.00 +bDeviceClass 0xef +bDeviceSubClass 0x2 +bDeviceProtocol 0x1 +bMaxPacketSize0 64 +idVendor 0x303a +idProduct 0x1001 +bcdDevice 1.00 +iManufacturer 1 +iProduct 2 +iSerialNumber 3 +bNumConfigurations 1 +I (775) CLASS: Getting config descriptor +*** Configuration descriptor *** +bLength 9 +bDescriptorType 2 +wTotalLength 98 +bNumInterfaces 3 +bConfigurationValue 1 +iConfiguration 0 +bmAttributes 0xc0 +bMaxPower 500mA + *** Interface descriptor *** + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 0x0 + iInterface 0 + *** Endpoint descriptor *** + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 0x3 INT + wMaxPacketSize 64 + bInterval 1 + *** Interface descriptor *** + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 0x0 + iInterface 0 + *** Endpoint descriptor *** + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x1 EP 1 OUT + bmAttributes 0x2 BULK + wMaxPacketSize 64 + bInterval 1 + *** Endpoint descriptor *** + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 0x2 BULK + wMaxPacketSize 64 + bInterval 1 + *** Interface descriptor *** + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 0x1 + iInterface 0 + *** Endpoint descriptor *** + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x2 EP 2 OUT + bmAttributes 0x2 BULK + wMaxPacketSize 64 + bInterval 1 + *** Endpoint descriptor *** + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 0x2 BULK + wMaxPacketSize 64 + bInterval 1 +I (855) CLASS: Getting Manufacturer string descriptor +Espressif +I (855) CLASS: Getting Product string descriptor +USB JTAG/serial debug unit +I (865) CLASS: Getting Serial Number string descriptor +7C:DF:A1:E0:10:50 +W (2855) USB host lib: To shutdown example, remove all USB devices and press button again. +E (6135) USBH: Device 1 gone +I (9545) CLASS: Deregistering Client +I (9545) USB host lib: No more clients +I (9545) USB host lib: All devices marked as free +I (9545) USB host lib: No more clients and devices +I (9645) USB host lib: End of the example +I (9645) main_task: Returned from app_main() +``` + +## Troubleshooting + +To obtain more debug, users should set the [log level](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/system/log.html) to debug via menuconfig. + +### Failing Enumeration + +``` +I (262) cpu_start: Starting scheduler on PRO CPU. +I (268) DAEMON: Installing USB Host Library +I (298) CLASS: Registering Client +E (2748) HUB: Short string desc corrupt +E (2748) HUB: Stage failed: CHECK_SHORT_MANU_STR_DESC +``` + +The log output demonstrates a device that has failed. The Hub Driver will output some error logs indicating which stage of enumeration has failed. + +### Blank String Descriptors + +The current USB Host Library will automatically cache the Manufacturer, Product, and Serial Number string descriptors of the device during enumeration. However, when fetching the string descriptors, the USB Host Library will only fetch those strings descriptors of they of LANGID code 0x0409 (i.e., English - United States). Therefore, if the example does not print a particular descriptor, it is likely that the string descriptor was not cached during enumeration. diff --git a/tests/c_headers/idf-usb/host/usb_host_lib/main/CMakeLists.txt b/tests/c_headers/idf-usb/host/usb_host_lib/main/CMakeLists.txt new file mode 100644 index 00000000..8e8817d5 --- /dev/null +++ b/tests/c_headers/idf-usb/host/usb_host_lib/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "usb_host_lib_main.c" "class_driver.c" + INCLUDE_DIRS "." + PRIV_REQUIRES usb esp_driver_gpio + ) diff --git a/tests/c_headers/idf-usb/host/usb_host_lib/main/Kconfig.projbuild b/tests/c_headers/idf-usb/host/usb_host_lib/main/Kconfig.projbuild new file mode 100644 index 00000000..680b0cc3 --- /dev/null +++ b/tests/c_headers/idf-usb/host/usb_host_lib/main/Kconfig.projbuild @@ -0,0 +1,12 @@ +menu "Example Configuration" + + orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps" + + config APP_QUIT_PIN + int "APP Quit button GPIO pin" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 0 + help + GPIO pin number to be used as APP_QUIT button. + +endmenu diff --git a/tests/c_headers/idf-usb/host/usb_host_lib/main/class_driver.c b/tests/c_headers/idf-usb/host/usb_host_lib/main/class_driver.c new file mode 100644 index 00000000..5a89e40d --- /dev/null +++ b/tests/c_headers/idf-usb/host/usb_host_lib/main/class_driver.c @@ -0,0 +1,279 @@ +/* + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "usb/usb_host.h" + +#define CLIENT_NUM_EVENT_MSG 5 + +typedef enum { + ACTION_OPEN_DEV = (1 << 0), + ACTION_GET_DEV_INFO = (1 << 1), + ACTION_GET_DEV_DESC = (1 << 2), + ACTION_GET_CONFIG_DESC = (1 << 3), + ACTION_GET_STR_DESC = (1 << 4), + ACTION_CLOSE_DEV = (1 << 5), +} action_t; + +#define DEV_MAX_COUNT 128 + +typedef struct { + usb_host_client_handle_t client_hdl; + uint8_t dev_addr; + usb_device_handle_t dev_hdl; + action_t actions; +} usb_device_t; + +typedef struct { + struct { + union { + struct { + uint8_t unhandled_devices: 1; /**< Device has unhandled devices */ + uint8_t shutdown: 1; /**< */ + uint8_t reserved6: 6; /**< Reserved */ + }; + uint8_t val; /**< Class drivers' flags value */ + } flags; /**< Class drivers' flags */ + usb_device_t device[DEV_MAX_COUNT]; /**< Class drivers' static array of devices */ + } mux_protected; /**< Mutex protected members. Must be protected by the Class mux_lock when accessed */ + + struct { + usb_host_client_handle_t client_hdl; + SemaphoreHandle_t mux_lock; /**< Mutex for protected members */ + } constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */ +} class_driver_t; + +static const char *TAG = "CLASS"; +static class_driver_t *s_driver_obj; + +static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg) +{ + class_driver_t *driver_obj = (class_driver_t *)arg; + switch (event_msg->event) { + case USB_HOST_CLIENT_EVENT_NEW_DEV: + // Save the device address + xSemaphoreTake(driver_obj->constant.mux_lock, portMAX_DELAY); + driver_obj->mux_protected.device[event_msg->new_dev.address].dev_addr = event_msg->new_dev.address; + driver_obj->mux_protected.device[event_msg->new_dev.address].dev_hdl = NULL; + // Open the device next + driver_obj->mux_protected.device[event_msg->new_dev.address].actions |= ACTION_OPEN_DEV; + // Set flag + driver_obj->mux_protected.flags.unhandled_devices = 1; + xSemaphoreGive(driver_obj->constant.mux_lock); + break; + case USB_HOST_CLIENT_EVENT_DEV_GONE: + // Cancel any other actions and close the device next + xSemaphoreTake(driver_obj->constant.mux_lock, portMAX_DELAY); + for (uint8_t i = 0; i < DEV_MAX_COUNT; i++) { + if (driver_obj->mux_protected.device[i].dev_hdl == event_msg->dev_gone.dev_hdl) { + driver_obj->mux_protected.device[i].actions = ACTION_CLOSE_DEV; + // Set flag + driver_obj->mux_protected.flags.unhandled_devices = 1; + } + } + xSemaphoreGive(driver_obj->constant.mux_lock); + break; + default: + // Should never occur + abort(); + } +} + +static void action_open_dev(usb_device_t *device_obj) +{ + assert(device_obj->dev_addr != 0); + ESP_LOGI(TAG, "Opening device at address %d", device_obj->dev_addr); + ESP_ERROR_CHECK(usb_host_device_open(device_obj->client_hdl, device_obj->dev_addr, &device_obj->dev_hdl)); + // Get the device's information next + device_obj->actions |= ACTION_GET_DEV_INFO; +} + +static void action_get_info(usb_device_t *device_obj) +{ + assert(device_obj->dev_hdl != NULL); + ESP_LOGI(TAG, "Getting device information"); + usb_device_info_t dev_info; + ESP_ERROR_CHECK(usb_host_device_info(device_obj->dev_hdl, &dev_info)); + ESP_LOGI(TAG, "\t%s speed", (char *[]) { + "Low", "Full", "High" + }[dev_info.speed]); + ESP_LOGI(TAG, "\tParent info:"); + if (dev_info.parent.dev_hdl) { + usb_device_info_t parent_dev_info; + ESP_ERROR_CHECK(usb_host_device_info(dev_info.parent.dev_hdl, &parent_dev_info)); + ESP_LOGI(TAG, "\t\tBus addr: %d", parent_dev_info.dev_addr); + ESP_LOGI(TAG, "\t\tPort: %d", dev_info.parent.port_num); + + } else { + ESP_LOGI(TAG, "\t\tPort: ROOT"); + } + ESP_LOGI(TAG, "\tbConfigurationValue %d", dev_info.bConfigurationValue); + // Get the device descriptor next + device_obj->actions |= ACTION_GET_DEV_DESC; +} + +static void action_get_dev_desc(usb_device_t *device_obj) +{ + assert(device_obj->dev_hdl != NULL); + ESP_LOGI(TAG, "Getting device descriptor"); + const usb_device_desc_t *dev_desc; + ESP_ERROR_CHECK(usb_host_get_device_descriptor(device_obj->dev_hdl, &dev_desc)); + usb_print_device_descriptor(dev_desc); + // Get the device's config descriptor next + device_obj->actions |= ACTION_GET_CONFIG_DESC; +} + +static void action_get_config_desc(usb_device_t *device_obj) +{ + assert(device_obj->dev_hdl != NULL); + ESP_LOGI(TAG, "Getting config descriptor"); + const usb_config_desc_t *config_desc; + ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(device_obj->dev_hdl, &config_desc)); + usb_print_config_descriptor(config_desc, NULL); + // Get the device's string descriptors next + device_obj->actions |= ACTION_GET_STR_DESC; +} + +static void action_get_str_desc(usb_device_t *device_obj) +{ + assert(device_obj->dev_hdl != NULL); + usb_device_info_t dev_info; + ESP_ERROR_CHECK(usb_host_device_info(device_obj->dev_hdl, &dev_info)); + if (dev_info.str_desc_manufacturer) { + ESP_LOGI(TAG, "Getting Manufacturer string descriptor"); + usb_print_string_descriptor(dev_info.str_desc_manufacturer); + } + if (dev_info.str_desc_product) { + ESP_LOGI(TAG, "Getting Product string descriptor"); + usb_print_string_descriptor(dev_info.str_desc_product); + } + if (dev_info.str_desc_serial_num) { + ESP_LOGI(TAG, "Getting Serial Number string descriptor"); + usb_print_string_descriptor(dev_info.str_desc_serial_num); + } +} + +static void action_close_dev(usb_device_t *device_obj) +{ + ESP_ERROR_CHECK(usb_host_device_close(device_obj->client_hdl, device_obj->dev_hdl)); + device_obj->dev_hdl = NULL; + device_obj->dev_addr = 0; +} + +static void class_driver_device_handle(usb_device_t *device_obj) +{ + uint8_t actions = device_obj->actions; + device_obj->actions = 0; + + while (actions) { + if (actions & ACTION_OPEN_DEV) { + action_open_dev(device_obj); + } + if (actions & ACTION_GET_DEV_INFO) { + action_get_info(device_obj); + } + if (actions & ACTION_GET_DEV_DESC) { + action_get_dev_desc(device_obj); + } + if (actions & ACTION_GET_CONFIG_DESC) { + action_get_config_desc(device_obj); + } + if (actions & ACTION_GET_STR_DESC) { + action_get_str_desc(device_obj); + } + if (actions & ACTION_CLOSE_DEV) { + action_close_dev(device_obj); + } + + actions = device_obj->actions; + device_obj->actions = 0; + } +} + +void class_driver_task(void *arg) +{ + class_driver_t driver_obj = {0}; + usb_host_client_handle_t class_driver_client_hdl = NULL; + + ESP_LOGI(TAG, "Registering Client"); + + SemaphoreHandle_t mux_lock = xSemaphoreCreateMutex(); + if (mux_lock == NULL) { + ESP_LOGE(TAG, "Unable to create class driver mutex"); + vTaskSuspend(NULL); + return; + } + + usb_host_client_config_t client_config = { + .is_synchronous = false, //Synchronous clients currently not supported. Set this to false + .max_num_event_msg = CLIENT_NUM_EVENT_MSG, + .async = { + .client_event_callback = client_event_cb, + .callback_arg = (void *) &driver_obj, + }, + }; + ESP_ERROR_CHECK(usb_host_client_register(&client_config, &class_driver_client_hdl)); + + driver_obj.constant.mux_lock = mux_lock; + driver_obj.constant.client_hdl = class_driver_client_hdl; + + for (uint8_t i = 0; i < DEV_MAX_COUNT; i++) { + driver_obj.mux_protected.device[i].client_hdl = class_driver_client_hdl; + } + + s_driver_obj = &driver_obj; + + while (1) { + // Driver has unhandled devices, handle all devices first + if (driver_obj.mux_protected.flags.unhandled_devices) { + xSemaphoreTake(driver_obj.constant.mux_lock, portMAX_DELAY); + for (uint8_t i = 0; i < DEV_MAX_COUNT; i++) { + if (driver_obj.mux_protected.device[i].actions) { + class_driver_device_handle(&driver_obj.mux_protected.device[i]); + } + } + driver_obj.mux_protected.flags.unhandled_devices = 0; + xSemaphoreGive(driver_obj.constant.mux_lock); + } else { + // Driver is active, handle client events + if (driver_obj.mux_protected.flags.shutdown == 0) { + usb_host_client_handle_events(class_driver_client_hdl, portMAX_DELAY); + } else { + // Shutdown the driver + break; + } + } + } + + ESP_LOGI(TAG, "Deregistering Class Client"); + ESP_ERROR_CHECK(usb_host_client_deregister(class_driver_client_hdl)); + if (mux_lock != NULL) { + vSemaphoreDelete(mux_lock); + } + vTaskSuspend(NULL); +} + +void class_driver_client_deregister(void) +{ + // Mark all opened devices + xSemaphoreTake(s_driver_obj->constant.mux_lock, portMAX_DELAY); + for (uint8_t i = 0; i < DEV_MAX_COUNT; i++) { + if (s_driver_obj->mux_protected.device[i].dev_hdl != NULL) { + // Mark device to close + s_driver_obj->mux_protected.device[i].actions |= ACTION_CLOSE_DEV; + // Set flag + s_driver_obj->mux_protected.flags.unhandled_devices = 1; + } + } + s_driver_obj->mux_protected.flags.shutdown = 1; + xSemaphoreGive(s_driver_obj->constant.mux_lock); + + // Unblock, exit the loop and proceed to deregister client + ESP_ERROR_CHECK(usb_host_client_unblock(s_driver_obj->constant.client_hdl)); +} diff --git a/tests/c_headers/idf-usb/host/usb_host_lib/main/usb_host_lib_main.c b/tests/c_headers/idf-usb/host/usb_host_lib/main/usb_host_lib_main.c new file mode 100644 index 00000000..8c804344 --- /dev/null +++ b/tests/c_headers/idf-usb/host/usb_host_lib/main/usb_host_lib_main.c @@ -0,0 +1,226 @@ +/* + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/event_groups.h" +#include "esp_log.h" +#include "esp_intr_alloc.h" +#include "usb/usb_host.h" +#include "driver/gpio.h" + +#define HOST_LIB_TASK_PRIORITY 2 +#define CLASS_TASK_PRIORITY 3 +#define APP_QUIT_PIN CONFIG_APP_QUIT_PIN + +#ifdef CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK +#define ENABLE_ENUM_FILTER_CALLBACK +#endif // CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK + +extern void class_driver_task(void *arg); +extern void class_driver_client_deregister(void); + +static const char *TAG = "USB host lib"; + +QueueHandle_t app_event_queue = NULL; + +/** + * @brief APP event group + * + * APP_EVENT - General event, which is APP_QUIT_PIN press event in this example. + */ +typedef enum { + APP_EVENT = 0, +} app_event_group_t; + +/** + * @brief APP event queue + * + * This event is used for delivering events from callback to a task. + */ +typedef struct { + app_event_group_t event_group; +} app_event_queue_t; + +/** + * @brief BOOT button pressed callback + * + * Signal application to exit the Host lib task + * + * @param[in] arg Unused + */ +static void gpio_cb(void *arg) +{ + const app_event_queue_t evt_queue = { + .event_group = APP_EVENT, + }; + + BaseType_t xTaskWoken = pdFALSE; + + if (app_event_queue) { + xQueueSendFromISR(app_event_queue, &evt_queue, &xTaskWoken); + } + + if (xTaskWoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +/** + * @brief Set configuration callback + * + * Set the USB device configuration during the enumeration process, must be enabled in the menuconfig + + * @note bConfigurationValue starts at index 1 + * + * @param[in] dev_desc device descriptor of the USB device currently being enumerated + * @param[out] bConfigurationValue configuration descriptor index, that will be user for enumeration + * + * @return bool + * - true: USB device will be enumerated + * - false: USB device will not be enumerated + */ +#ifdef ENABLE_ENUM_FILTER_CALLBACK +static bool set_config_cb(const usb_device_desc_t *dev_desc, uint8_t *bConfigurationValue) +{ + // If the USB device has more than one configuration, set the second configuration + if (dev_desc->bNumConfigurations > 1) { + *bConfigurationValue = 2; + } else { + *bConfigurationValue = 1; + } + + // Return true to enumerate the USB device + return true; +} +#endif // ENABLE_ENUM_FILTER_CALLBACK + +/** + * @brief Start USB Host install and handle common USB host library events while app pin not low + * + * @param[in] arg Not used + */ +static void usb_host_lib_task(void *arg) +{ + ESP_LOGI(TAG, "Installing USB Host Library"); + usb_host_config_t host_config = { + .skip_phy_setup = false, + .intr_flags = ESP_INTR_FLAG_LEVEL1, +# ifdef ENABLE_ENUM_FILTER_CALLBACK + .enum_filter_cb = set_config_cb, +# endif // ENABLE_ENUM_FILTER_CALLBACK + .peripheral_map = BIT0, + }; + ESP_ERROR_CHECK(usb_host_install(&host_config)); + ESP_LOGI(TAG, "USB Host installed with peripheral map 0x%x", host_config.peripheral_map); + + //Signalize the app_main, the USB host library has been installed + xTaskNotifyGive(arg); + + bool has_clients = true; + bool has_devices = false; + while (has_clients) { + uint32_t event_flags; + ESP_ERROR_CHECK(usb_host_lib_handle_events(portMAX_DELAY, &event_flags)); + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + ESP_LOGI(TAG, "Get FLAGS_NO_CLIENTS"); + if (ESP_OK == usb_host_device_free_all()) { + ESP_LOGI(TAG, "All devices marked as free, no need to wait FLAGS_ALL_FREE event"); + has_clients = false; + } else { + ESP_LOGI(TAG, "Wait for the FLAGS_ALL_FREE"); + has_devices = true; + } + } + if (has_devices && event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + ESP_LOGI(TAG, "Get FLAGS_ALL_FREE"); + has_clients = false; + } + } + ESP_LOGI(TAG, "No more clients and devices, uninstall USB Host library"); + + //Uninstall the USB Host Library + ESP_ERROR_CHECK(usb_host_uninstall()); + vTaskSuspend(NULL); +} + +void app_main(void) +{ + ESP_LOGI(TAG, "USB host library example"); + + // Init BOOT button: Pressing the button simulates app request to exit + // It will uninstall the class driver and USB Host Lib + const gpio_config_t input_pin = { + .pin_bit_mask = BIT64(APP_QUIT_PIN), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + .intr_type = GPIO_INTR_NEGEDGE, + }; + ESP_ERROR_CHECK(gpio_config(&input_pin)); + ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1)); + ESP_ERROR_CHECK(gpio_isr_handler_add(APP_QUIT_PIN, gpio_cb, NULL)); + + app_event_queue = xQueueCreate(10, sizeof(app_event_queue_t)); + app_event_queue_t evt_queue; + + TaskHandle_t host_lib_task_hdl, class_driver_task_hdl; + + // Create usb host lib task + BaseType_t task_created; + task_created = xTaskCreatePinnedToCore(usb_host_lib_task, + "usb_host", + 4096, + xTaskGetCurrentTaskHandle(), + HOST_LIB_TASK_PRIORITY, + &host_lib_task_hdl, + 0); + assert(task_created == pdTRUE); + + // Wait until the USB host library is installed + ulTaskNotifyTake(false, 1000); + + // Create class driver task + task_created = xTaskCreatePinnedToCore(class_driver_task, + "class", + 5 * 1024, + NULL, + CLASS_TASK_PRIORITY, + &class_driver_task_hdl, + 0); + assert(task_created == pdTRUE); + // Add a short delay to let the tasks run + vTaskDelay(10); + + while (1) { + if (xQueueReceive(app_event_queue, &evt_queue, portMAX_DELAY)) { + if (APP_EVENT == evt_queue.event_group) { + // User pressed button + usb_host_lib_info_t lib_info; + ESP_ERROR_CHECK(usb_host_lib_info(&lib_info)); + if (lib_info.num_devices != 0) { + ESP_LOGW(TAG, "Shutdown with attached devices."); + } + // End while cycle + break; + } + } + } + + // Deregister client + class_driver_client_deregister(); + vTaskDelay(10); + + // Delete the tasks + vTaskDelete(class_driver_task_hdl); + vTaskDelete(host_lib_task_hdl); + + // Delete interrupt and queue + gpio_isr_handler_remove(APP_QUIT_PIN); + xQueueReset(app_event_queue); + vQueueDelete(app_event_queue); + ESP_LOGI(TAG, "End of the example"); +} diff --git a/tests/c_headers/idf-usb/host/usb_host_lib/pytest_usb_host_lib.py b/tests/c_headers/idf-usb/host/usb_host_lib/pytest_usb_host_lib.py new file mode 100644 index 00000000..b3907df7 --- /dev/null +++ b/tests/c_headers/idf-usb/host/usb_host_lib/pytest_usb_host_lib.py @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.temp_skip_ci(targets=['esp32s2'], reason='lack of runners with usb_host_flash_disk tag') +@pytest.mark.usb_host_flash_disk +@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32p4'], indirect=['target']) +def test_usb_host_lib_example(dut: Dut) -> None: + # register client + dut.expect_exact('CLASS: Registering Client') + + # expect device descriptor + dut.expect_exact('CLASS: Getting device descriptor') + + # confirm device descriptor + dut.expect_exact('*** Device descriptor ***') + + # expect configuration descriptor + dut.expect_exact('CLASS: Getting config descriptor') + + # confirm configuration descriptor + dut.expect_exact('*** Configuration descriptor ***') diff --git a/tests/c_headers/idf-usb/host/usb_host_lib/sdkconfig.defaults b/tests/c_headers/idf-usb/host/usb_host_lib/sdkconfig.defaults new file mode 100644 index 00000000..bf1b553b --- /dev/null +++ b/tests/c_headers/idf-usb/host/usb_host_lib/sdkconfig.defaults @@ -0,0 +1,4 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_USB_HOST_HUBS_SUPPORTED=y diff --git a/tests/c_headers/idf-usb/host/uvc/CMakeLists.txt b/tests/c_headers/idf-usb/host/uvc/CMakeLists.txt new file mode 100644 index 00000000..b54c6c14 --- /dev/null +++ b/tests/c_headers/idf-usb/host/uvc/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(usb-uvc) diff --git a/tests/c_headers/idf-usb/host/uvc/README.md b/tests/c_headers/idf-usb/host/uvc/README.md new file mode 100644 index 00000000..fb408d0d --- /dev/null +++ b/tests/c_headers/idf-usb/host/uvc/README.md @@ -0,0 +1,96 @@ +| Supported Targets | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | + +# USB Camera Example + +## Overview + +This example shows basic usage of [USB UVC host driver](https://components.espressif.com/components/espressif/usb_host_uvc). The example waits for USB camera connection, it then configures the camera and starts streaming video frames until the device is removed. + +More advanced examples can be found in [UVC host driver repository](https://github.com/espressif/esp-usb/tree/master/host/class/uvc/usb_host_uvc/examples). + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +## How to Use Example + +### Hardware Required + +* Development board with USB-OTG support and external PSRAM +* Exposed USB host connector +* USB camera + +Running this example on an **ESP module without external PSRAM will fail on initialization**. Please select your PSRAM configuration in menuconfig `Component config->ESP PSRAM`. If you manually disable PSRAM, the required frame buffers might not fit into DRAM (especially on ESP32-S2). If you absolutely must not use PSRAM, consider the following for minimizing RAM usage: +* Select smaller resolution (eg. 320x240) +* Select fewer frame buffers (eg. 2) +* Select smaller and fewer URBs (eg. 2x 2kB) + +Follow instructions in [examples/usb/README.md](../../README.md) for specific hardware setup. + +### Frame format + +You can select frame format in [main.c](./main/main.c) file in `stream_config` variable. Default is set to very common 640x480@15FPS, MJPEG encoded. + +This example will print supported formats on device connection - this can be disabled in menuconfig ` Component config → USB HOST UVC`. The frame format can also be changed in runtime by calling `uvc_host_stream_format_select()` function. + +### Build and Flash + +Build the project and flash it to the board, then run the monitor tool to view the serial output: + +Run `idf.py set-target esp32xx` to set target chip. + +Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for all the steps to configure and use the ESP-IDF to build projects. + +## Known limitations + +On targets with Full Speed USB peripheral the MPS (maximum packet size) is limited to 512 bytes, the ESP32-S2/S3 is capable of reading data at approximately 0.5 MB/s. When connected to Full Speed USB host, cameras usually provide resolution no larger than 640x480 pixels. The following two formats are the most commonly supported at Full Speed : +* 320x240 30 FPS MJPEG +* 640x480 15 FPS MJPEG + +## Tested cameras +* Logitech C980 +* Logitech C270 +* Logitech C170 +* Logitech Brio 100 +* CANYON CNE-CWC2 +* Trust WebCam +* Anker Powerconf C200 +* Microsoft LifeCam HD-6000 +* Asus Webcam C3 + +## Example Output + +Output with USB camera already connected at program start: +``` +... +I (1460) main_task: Calling app_main() +I (1460) UVC example: Installing USB Host +I (1500) UVC example: Installing UVC driver +I (1500) UVC example: Opening UVC device 0x0000:0x0000 640x480@15.0FPS... +I (1500) main_task: Returned from app_main() +*** Configuration descriptor *** + +... +Here is rest of Configuration descriptor +... + +I (3140) UVC example: UVC Device OPENED! +I (3240) UVC example: Stream start. Iteration 0 +I (4410) UVC example: New frame! Len: 13392 +I (4470) UVC example: New frame! Len: 13392 +I (4530) UVC example: New frame! Len: 13392 + +... +More frames received +... + +I (8310) UVC example: New frame! Len: 18512 +I (8310) UVC example: Stream stop +I (10440) UVC example: Stream start. Iteration 1 + +... +Streaming continues here +``` diff --git a/tests/c_headers/idf-usb/host/uvc/main/CMakeLists.txt b/tests/c_headers/idf-usb/host/uvc/main/CMakeLists.txt new file mode 100644 index 00000000..f91cf7ce --- /dev/null +++ b/tests/c_headers/idf-usb/host/uvc/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register(SRCS "main.c" + PRIV_REQUIRES + usb + esp_psram # Required for CONFIG_SPIRAM + ) diff --git a/tests/c_headers/idf-usb/host/uvc/main/idf_component.yml b/tests/c_headers/idf-usb/host/uvc/main/idf_component.yml new file mode 100644 index 00000000..75713163 --- /dev/null +++ b/tests/c_headers/idf-usb/host/uvc/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + idf: ">=5.0" + usb_host_uvc: "^2.2.0" diff --git a/tests/c_headers/idf-usb/host/uvc/main/main.c b/tests/c_headers/idf-usb/host/uvc/main/main.c new file mode 100644 index 00000000..60edd732 --- /dev/null +++ b/tests/c_headers/idf-usb/host/uvc/main/main.c @@ -0,0 +1,233 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "sdkconfig.h" +#include "esp_system.h" +#include "esp_log.h" +#include "esp_err.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" + +#include "usb/usb_host.h" +#include "usb/uvc_host.h" + +#define EXAMPLE_USB_HOST_PRIORITY (15) +#define EXAMPLE_RECORDING_LENGTH_S (5) // This example will enable the stream, run it for EXAMPLE_RECORDING_LENGTH_S and then stop it + +// Private function prototypes +static bool frame_callback(const uvc_host_frame_t *frame, void *user_ctx); +static void stream_callback(const uvc_host_stream_event_data_t *event, void *user_ctx); + +// Private variables +static QueueHandle_t rx_frames_queue; +static bool dev_connected = false; +static const char *TAG = "UVC example"; + +/** + * @brief Configuration of the UVC stream + * + * Update the vs_format to match your camera's capabilities. + * The example will try to open the camera with the specified resolution and FPS. + */ +static const uvc_host_stream_config_t stream_config = { + .event_cb = stream_callback, + .frame_cb = frame_callback, + .user_ctx = &rx_frames_queue, + .usb = { + .vid = UVC_HOST_ANY_VID, // Set to 0 to match any VID + .pid = UVC_HOST_ANY_PID, // Set to 0 to match any PID + .uvc_stream_index = 0, /* Index of UVC function you want to use. Set to 0 to use first available UVC function. + Setting this to >= 1 will only work if the camera has multiple UVC functions (eg. multiple image sensors in one USB device) */ + }, + .vs_format = { + // Note: For Full-Speed targets, only limited set of formats might be supported by the camera. + // Check the camera's Configuration descriptor for supported formats and resolutions. +#if CONFIG_SPIRAM + .h_res = 640, + .v_res = 480, +#else + .h_res = 320, + .v_res = 240, +#endif + .fps = 15, + .format = UVC_VS_FORMAT_MJPEG, + }, + .advanced = { + .frame_size = 0, /* == 0: Use dwMaxVideoFrameSize from format negotiation result (might be too large) + >= 0: Use user provide frame size -> can save RAM, but occasionally a frame can overflow */ +#if CONFIG_SPIRAM + .number_of_frame_buffers = 3, // Use triple buffering scheme if SPIRAM is available + .number_of_urbs = 3, // 3x 10kB URBs is usually enough, even for higher resolutions + .urb_size = 10 * 1024, // Larger values result in less frequent interrupts at the cost of memory consumption + .frame_heap_caps = MALLOC_CAP_SPIRAM, // Use SPIRAM for frame buffers +#else + .number_of_frame_buffers = 2, // Use double buffering scheme if SPIRAM is not available + .number_of_urbs = 2, // 2x 2kB URBs for memory constrained applications + .urb_size = 2 * 1024, // Larger values result in less frequent interrupts at the cost of memory consumption +#endif + }, +}; + +static bool frame_callback(const uvc_host_frame_t *frame, void *user_ctx) +{ + assert(frame); + assert(user_ctx); + QueueHandle_t frame_q = *((QueueHandle_t *)user_ctx); + + // Send the received frame to queue for further processing + ESP_LOGD(TAG, "Frame callback! data len: %d", frame->data_len); + BaseType_t result = xQueueSendToBack(frame_q, &frame, 0); + if (pdPASS != result) { + ESP_LOGW(TAG, "Queue full, losing frame"); // This should never happen + return true; // We will not process this frame, return it immediately + } + return false; // We only passed the frame to Queue, so we must return false and call uvc_host_frame_return() later +} + +static void stream_callback(const uvc_host_stream_event_data_t *event, void *user_ctx) +{ + switch (event->type) { + case UVC_HOST_TRANSFER_ERROR: + ESP_LOGE(TAG, "USB error has occurred, err_no = %i", event->transfer_error.error); + break; + case UVC_HOST_DEVICE_DISCONNECTED: + ESP_LOGI(TAG, "Device suddenly disconnected"); + dev_connected = false; + ESP_ERROR_CHECK(uvc_host_stream_close(event->device_disconnected.stream_hdl)); + break; + case UVC_HOST_FRAME_BUFFER_OVERFLOW: + // The Frame was discarded because it exceeded the available frame buffer size. + // To resolve this, increase the `frame_size` parameter in `uvc_host_stream_config_t.advanced` to allocate a larger buffer. + ESP_LOGW(TAG, "Frame buffer overflow"); + break; + case UVC_HOST_FRAME_BUFFER_UNDERFLOW: + // The Frame was discarded because no available buffer was free for storage. + // To resolve this, either optimize your processing speed or increase the `number_of_frame_buffers` parameter in + // `uvc_host_stream_config_t.advanced` to allocate additional buffers. + ESP_LOGW(TAG, "Frame buffer underflow"); + break; + default: + abort(); + break; + } +} + +static void usb_lib_task(void *arg) +{ + while (1) { + // Start handling system events + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + usb_host_device_free_all(); + } + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + ESP_LOGI(TAG, "USB: All devices freed"); + // Continue handling USB events to allow device reconnection + } + } +} + +static void frame_handling_task(void *arg) +{ + const uvc_host_stream_config_t *stream_config = (const uvc_host_stream_config_t *)arg; + QueueHandle_t frame_q = *((QueueHandle_t *)(stream_config->user_ctx)); + + // This is the main program loop, it tries to open the UVC device and start streaming + while (true) { + uvc_host_stream_hdl_t uvc_stream = NULL; + + ESP_LOGI(TAG, "Opening UVC device 0x%04X:0x%04X %dx%d@%2.1fFPS...", + stream_config->usb.vid, stream_config->usb.pid, stream_config->vs_format.h_res, stream_config->vs_format.v_res, stream_config->vs_format.fps); + esp_err_t err = uvc_host_stream_open(stream_config, pdMS_TO_TICKS(5000), &uvc_stream); + if (ESP_OK != err) { + ESP_LOGI(TAG, "Failed to open device"); + vTaskDelay(pdMS_TO_TICKS(5000)); + continue; + } + dev_connected = true; + ESP_LOGI(TAG, "UVC Device OPENED!"); + vTaskDelay(pdMS_TO_TICKS(100)); + unsigned count = 0; + + // This is the main processing loop. It enables the stream for EXAMPLE_RECORDING_LENGTH_S seconds and then closes it + while (true) { + ESP_LOGI(TAG, "Stream start. Iteration %u", count); + count++; + uvc_host_stream_start(uvc_stream); + TickType_t timeout_ticks = pdMS_TO_TICKS(EXAMPLE_RECORDING_LENGTH_S * 1000); + TimeOut_t stream_timeout; + vTaskSetTimeOutState(&stream_timeout); + + do { + uvc_host_frame_t *frame; + if (xQueueReceive(frame_q, &frame, pdMS_TO_TICKS(5000)) == pdPASS) { + ESP_LOGI(TAG, "New frame! Len: %d", frame->data_len); + + // Process the frame data here + // For example, you can: + // - Save the frame to a file + // - Send the frame over the network + // - Decode the frame (typically MJPEG encoded) + // - Display the frame on a screen + // - etc. + + uvc_host_frame_return(uvc_stream, frame); + } else { + ESP_LOGW(TAG, "Stream: Frame not received on time"); + break; + } + } while (xTaskCheckForTimeOut(&stream_timeout, &timeout_ticks) == pdFALSE); + + if (dev_connected) { + // Stop and wait 2 seconds + ESP_LOGI(TAG, "Stream stop"); + uvc_host_stream_stop(uvc_stream); + vTaskDelay(pdMS_TO_TICKS(2000)); + } else { + ESP_LOGI(TAG, "device disconnected, breaking"); + break; + } + } + } +} + +/** + * @brief Main application + */ +void app_main(void) +{ + rx_frames_queue = xQueueCreate(3, sizeof(uvc_host_frame_t *)); + assert(rx_frames_queue); + + // Install USB Host driver. Should only be called once in entire application + ESP_LOGI(TAG, "Installing USB Host"); + const usb_host_config_t host_config = { + .skip_phy_setup = false, + .intr_flags = ESP_INTR_FLAG_LEVEL1, + }; + ESP_ERROR_CHECK(usb_host_install(&host_config)); + + // Create a task that will handle USB library events + BaseType_t task_created = xTaskCreatePinnedToCore(usb_lib_task, "usb_lib", 4096, NULL, EXAMPLE_USB_HOST_PRIORITY, NULL, tskNO_AFFINITY); + assert(task_created == pdTRUE); + + ESP_LOGI(TAG, "Installing UVC driver"); + const uvc_host_driver_config_t uvc_driver_config = { + .driver_task_stack_size = 4 * 1024, + .driver_task_priority = EXAMPLE_USB_HOST_PRIORITY + 1, + .xCoreID = tskNO_AFFINITY, + .create_background_task = true, + }; + ESP_ERROR_CHECK(uvc_host_install(&uvc_driver_config)); + + task_created = xTaskCreatePinnedToCore(frame_handling_task, "frame_hdl", 4096, (void *)&stream_config, EXAMPLE_USB_HOST_PRIORITY - 2, NULL, tskNO_AFFINITY); + assert(task_created == pdTRUE); +} diff --git a/tests/c_headers/idf-usb/host/uvc/sdkconfig.defaults b/tests/c_headers/idf-usb/host/uvc/sdkconfig.defaults new file mode 100644 index 00000000..1248dbd1 --- /dev/null +++ b/tests/c_headers/idf-usb/host/uvc/sdkconfig.defaults @@ -0,0 +1,13 @@ +# +# SPIRAM +# +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=150000 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y + +# +# USB +# +CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE=3000 +CONFIG_USB_HOST_HW_BUFFER_BIAS_IN=y +CONFIG_PRINTF_UVC_CONFIGURATION_DESCRIPTOR=y diff --git a/tests/c_headers/idf-usb/host/uvc/sdkconfig.defaults.esp32p4 b/tests/c_headers/idf-usb/host/uvc/sdkconfig.defaults.esp32p4 new file mode 100644 index 00000000..b700e907 --- /dev/null +++ b/tests/c_headers/idf-usb/host/uvc/sdkconfig.defaults.esp32p4 @@ -0,0 +1,2 @@ +CONFIG_IDF_EXPERIMENTAL_FEATURES=y +CONFIG_SPIRAM_SPEED_200M=y diff --git a/tests/c_headers/idf-usb/host/uvc/sdkconfig.defaults.esp32s2 b/tests/c_headers/idf-usb/host/uvc/sdkconfig.defaults.esp32s2 new file mode 100644 index 00000000..864fd356 --- /dev/null +++ b/tests/c_headers/idf-usb/host/uvc/sdkconfig.defaults.esp32s2 @@ -0,0 +1,9 @@ +# +# SPIRAM +# +CONFIG_SPIRAM_SPEED_80M=y + +# +# SYSTEM +# +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y diff --git a/tests/c_headers/idf-usb/host/uvc/sdkconfig.defaults.esp32s3 b/tests/c_headers/idf-usb/host/uvc/sdkconfig.defaults.esp32s3 new file mode 100644 index 00000000..864fd356 --- /dev/null +++ b/tests/c_headers/idf-usb/host/uvc/sdkconfig.defaults.esp32s3 @@ -0,0 +1,9 @@ +# +# SPIRAM +# +CONFIG_SPIRAM_SPEED_80M=y + +# +# SYSTEM +# +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y diff --git a/tests/c_headers/mock/esp_log.h b/tests/c_headers/mock/esp_log.h index 8afe8abd..c4df3a68 100644 --- a/tests/c_headers/mock/esp_log.h +++ b/tests/c_headers/mock/esp_log.h @@ -2,5 +2,6 @@ #include #define ESP_LOGI(fmt, args...) printf(fmt, args); +#define ESP_LOGD(fmt, args...) printf(fmt, args); // #define esp_err_to_name(id) ("ERROR" ## id) diff --git a/tests/c_headers/protocol_examples_common/CMakeLists.txt b/tests/c_headers/protocol_examples_common/CMakeLists.txt new file mode 100644 index 00000000..8d0501a9 --- /dev/null +++ b/tests/c_headers/protocol_examples_common/CMakeLists.txt @@ -0,0 +1,52 @@ +idf_build_get_property(target IDF_TARGET) + +if(${target} STREQUAL "linux") + # Header only library for linux + + idf_component_register(INCLUDE_DIRS include + SRCS protocol_examples_utils.c) + return() +endif() + +set(srcs "stdin_out.c" + "addr_from_stdin.c" + "connect.c" + "wifi_connect.c" + "protocol_examples_utils.c") + +if(CONFIG_EXAMPLE_PROVIDE_WIFI_CONSOLE_CMD) + list(APPEND srcs "console_cmd.c") +endif() + +if(CONFIG_EXAMPLE_CONNECT_ETHERNET) + list(APPEND srcs "eth_connect.c") +endif() + +if(CONFIG_EXAMPLE_CONNECT_THREAD) + list(APPEND srcs "thread_connect.c") +endif() + +if(CONFIG_EXAMPLE_CONNECT_PPP) + list(APPEND srcs "ppp_connect.c") +endif() + + +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "include" + PRIV_REQUIRES esp_netif esp_driver_gpio esp_driver_uart esp_wifi vfs console esp_eth openthread) + +if(CONFIG_EXAMPLE_PROVIDE_WIFI_CONSOLE_CMD) + idf_component_optional_requires(PRIVATE console) +endif() + +if(CONFIG_EXAMPLE_CONNECT_ETHERNET) + idf_component_optional_requires(PUBLIC esp_eth) +endif() + +if(CONFIG_EXAMPLE_CONNECT_THREAD) + idf_component_optional_requires(PRIVATE openthread) +endif() + +if(CONFIG_EXAMPLE_CONNECT_PPP) + idf_component_optional_requires(PRIVATE esp_tinyusb espressif__esp_tinyusb) +endif() diff --git a/tests/c_headers/protocol_examples_common/Kconfig.projbuild b/tests/c_headers/protocol_examples_common/Kconfig.projbuild new file mode 100644 index 00000000..f9ccbfb5 --- /dev/null +++ b/tests/c_headers/protocol_examples_common/Kconfig.projbuild @@ -0,0 +1,504 @@ +menu "Example Connection Configuration" + + orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps" + + config EXAMPLE_CONNECT_WIFI + bool "connect using WiFi interface" + depends on !IDF_TARGET_LINUX && (SOC_WIFI_SUPPORTED || ESP_WIFI_REMOTE_ENABLED || ESP_HOST_WIFI_ENABLED) + default y if SOC_WIFI_SUPPORTED + help + Protocol examples can use Wi-Fi, Ethernet and/or Thread to connect to the network. + Choose this option to connect with WiFi + + if EXAMPLE_CONNECT_WIFI + config EXAMPLE_WIFI_SSID_PWD_FROM_STDIN + bool "Get ssid and password from stdin" + default n + help + Give the WiFi SSID and password from stdin. + + config EXAMPLE_PROVIDE_WIFI_CONSOLE_CMD + depends on !EXAMPLE_WIFI_SSID_PWD_FROM_STDIN + bool "Provide wifi connect commands" + default y + help + Provide wifi connect commands for esp_console. + Please use `example_register_wifi_connect_commands` to register them. + + config EXAMPLE_WIFI_SSID + depends on !EXAMPLE_WIFI_SSID_PWD_FROM_STDIN + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + + config EXAMPLE_WIFI_PASSWORD + depends on !EXAMPLE_WIFI_SSID_PWD_FROM_STDIN + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + Can be left blank if the network has no security set. + + config EXAMPLE_WIFI_CONN_MAX_RETRY + int "Maximum retry" + default 6 + help + Set the Maximum retry to avoid station reconnecting to the AP unlimited, + in case the AP is really inexistent. + + choice EXAMPLE_WIFI_SCAN_METHOD + prompt "WiFi Scan Method" + default EXAMPLE_WIFI_SCAN_METHOD_ALL_CHANNEL + help + WiFi scan method: + + If "Fast" is selected, scan will end after find SSID match AP. + + If "All Channel" is selected, scan will end after scan all the channel. + + config EXAMPLE_WIFI_SCAN_METHOD_FAST + bool "Fast" + config EXAMPLE_WIFI_SCAN_METHOD_ALL_CHANNEL + bool "All Channel" + endchoice + + menu "WiFi Scan threshold" + config EXAMPLE_WIFI_SCAN_RSSI_THRESHOLD + int "WiFi minimum rssi" + range -127 0 + + default -127 + help + The minimum rssi to accept in the scan mode. + + choice EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD + prompt "WiFi Scan auth mode threshold" + default EXAMPLE_WIFI_AUTH_OPEN + help + The weakest authmode to accept in the scan mode. + + config EXAMPLE_WIFI_AUTH_OPEN + bool "OPEN" + config EXAMPLE_WIFI_AUTH_WEP + bool "WEP" + config EXAMPLE_WIFI_AUTH_WPA_PSK + bool "WPA PSK" + config EXAMPLE_WIFI_AUTH_WPA2_PSK + bool "WPA2 PSK" + config EXAMPLE_WIFI_AUTH_WPA_WPA2_PSK + bool "WPA WPA2 PSK" + config EXAMPLE_WIFI_AUTH_WPA2_ENTERPRISE + bool "WPA2 ENTERPRISE" + config EXAMPLE_WIFI_AUTH_WPA3_PSK + bool "WPA3 PSK" + config EXAMPLE_WIFI_AUTH_WPA2_WPA3_PSK + bool "WPA2 WPA3 PSK" + config EXAMPLE_WIFI_AUTH_WAPI_PSK + bool "WAPI PSK" + endchoice + endmenu + + choice EXAMPLE_WIFI_CONNECT_AP_SORT_METHOD + prompt "WiFi Connect AP Sort Method" + default EXAMPLE_WIFI_CONNECT_AP_BY_SIGNAL + help + WiFi connect AP sort method: + + If "Signal" is selected, Sort matched APs in scan list by RSSI. + + If "Security" is selected, Sort matched APs in scan list by security mode. + + config EXAMPLE_WIFI_CONNECT_AP_BY_SIGNAL + bool "Signal" + config EXAMPLE_WIFI_CONNECT_AP_BY_SECURITY + bool "Security" + endchoice + endif + + config EXAMPLE_CONNECT_ETHERNET + bool "connect using Ethernet interface" + depends on !IDF_TARGET_LINUX + default y if !EXAMPLE_CONNECT_WIFI && !EXAMPLE_CONNECT_THREAD + help + Protocol examples can use Wi-Fi, Ethernet and/or Thread to connect to the network. + Choose this option to connect with Ethernet + + if EXAMPLE_CONNECT_ETHERNET + config EXAMPLE_ETHERNET_EMAC_TASK_STACK_SIZE + int "emac_rx task stack size" + default 2048 + help + This set stack size for emac_rx task + + config EXAMPLE_USE_SPI_ETHERNET + bool + + choice EXAMPLE_ETHERNET_TYPE + prompt "Ethernet Type" + default EXAMPLE_USE_INTERNAL_ETHERNET if SOC_EMAC_SUPPORTED + default EXAMPLE_USE_W5500 + help + Select which kind of Ethernet will be used in the example. + + config EXAMPLE_USE_INTERNAL_ETHERNET + depends on SOC_EMAC_SUPPORTED + select ETH_USE_ESP32_EMAC + bool "Internal EMAC" + help + Select internal Ethernet MAC controller. + + config EXAMPLE_USE_DM9051 + bool "DM9051 Module" + select EXAMPLE_USE_SPI_ETHERNET + select ETH_USE_SPI_ETHERNET + select ETH_SPI_ETHERNET_DM9051 + help + Select external SPI-Ethernet module. + + config EXAMPLE_USE_W5500 + bool "W5500 Module" + select EXAMPLE_USE_SPI_ETHERNET + select ETH_USE_SPI_ETHERNET + select ETH_SPI_ETHERNET_W5500 + help + Select external SPI-Ethernet module (W5500). + + config EXAMPLE_USE_OPENETH + bool "OpenCores Ethernet MAC (EXPERIMENTAL)" + select ETH_USE_OPENETH + help + When this option is enabled, the example is built with support for + OpenCores Ethernet MAC, which allows testing the example in QEMU. + Note that this option is used for internal testing purposes, and + not officially supported. Examples built with this option enabled + will not run on a real ESP32 chip. + + endchoice # EXAMPLE_ETHERNET_TYPE + + if EXAMPLE_USE_INTERNAL_ETHERNET + choice EXAMPLE_ETH_PHY_MODEL + prompt "Ethernet PHY Device" + default EXAMPLE_ETH_PHY_IP101 + help + Select the Ethernet PHY device to use in the example. + + config EXAMPLE_ETH_PHY_GENERIC + bool "Generic 802.3 PHY" + help + Any Ethernet PHY chip compliant with IEEE 802.3 can be used. However, while + basic functionality should always work, some specific features might be limited, + even if the PHY meets IEEE 802.3 standard. A typical example is loopback + functionality, where certain PHYs may require setting a specific speed mode to + operate correctly. + + config EXAMPLE_ETH_PHY_IP101 + bool "IP101" + help + IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver. + Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it. + + config EXAMPLE_ETH_PHY_RTL8201 + bool "RTL8201/SR8201" + help + RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX. + Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it. + + config EXAMPLE_ETH_PHY_LAN87XX + bool "LAN87xx" + help + Below chips are supported: + LAN8710A is a small footprint MII/RMII 10/100 Ethernet Transceiver with HP Auto-MDIX and + flexPWR® Technology. + LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support. + LAN8740A/LAN8741A is a small footprint MII/RMII 10/100 Energy Efficient Ethernet Transceiver + with HP Auto-MDIX and flexPWR® Technology. + LAN8742A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX and + flexPWR® Technology. + Goto https://www.microchip.com for more information about them. + + config EXAMPLE_ETH_PHY_DP83848 + bool "DP83848" + help + DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver. + Goto http://www.ti.com/product/DP83848J for more information about it. + + config EXAMPLE_ETH_PHY_KSZ80XX + bool "KSZ80xx" + help + With the KSZ80xx series, Microchip offers single-chip 10BASE-T/100BASE-TX + Ethernet Physical Layer Transceivers (PHY). + The following chips are supported: KSZ8001, KSZ8021, KSZ8031, KSZ8041, + KSZ8051, KSZ8061, KSZ8081, KSZ8091 + Goto https://www.microchip.com for more information about them. + endchoice + + config EXAMPLE_ETH_MDC_GPIO + int "SMI MDC GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 23 if IDF_TARGET_ESP32 + default 31 if IDF_TARGET_ESP32P4 + help + Set the GPIO number used by SMI MDC. + + config EXAMPLE_ETH_MDIO_GPIO + int "SMI MDIO GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 18 if IDF_TARGET_ESP32 + default 52 if IDF_TARGET_ESP32P4 + help + Set the GPIO number used by SMI MDIO. + endif + + if EXAMPLE_USE_SPI_ETHERNET + config EXAMPLE_ETH_SPI_HOST + int "SPI Host Number" + range 0 2 + default 1 + help + Set the SPI host used to communicate with the SPI Ethernet Controller. + + config EXAMPLE_ETH_SPI_SCLK_GPIO + int "SPI SCLK GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 14 + help + Set the GPIO number used by SPI SCLK. + + config EXAMPLE_ETH_SPI_MOSI_GPIO + int "SPI MOSI GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 13 + help + Set the GPIO number used by SPI MOSI. + + config EXAMPLE_ETH_SPI_MISO_GPIO + int "SPI MISO GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 12 + help + Set the GPIO number used by SPI MISO. + + config EXAMPLE_ETH_SPI_CS_GPIO + int "SPI CS GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 15 + help + Set the GPIO number used by SPI CS. + + config EXAMPLE_ETH_SPI_CLOCK_MHZ + int "SPI clock speed (MHz)" + range 5 80 + default 36 + help + Set the clock speed (MHz) of SPI interface. + + config EXAMPLE_ETH_SPI_INT_GPIO + int "Interrupt GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 4 + help + Set the GPIO number used by the SPI Ethernet module interrupt line. + endif # EXAMPLE_USE_SPI_ETHERNET + + config EXAMPLE_ETH_PHY_RST_GPIO + int "PHY Reset GPIO number" + range -1 ENV_GPIO_OUT_RANGE_MAX + default 51 if IDF_TARGET_ESP32P4 + default 5 + help + Set the GPIO number used to reset PHY chip. + Set to -1 to disable PHY chip hardware reset. + + config EXAMPLE_ETH_PHY_ADDR + int "PHY Address" + range 0 31 if EXAMPLE_USE_INTERNAL_ETHERNET + default 1 + help + Set PHY address according your board schematic. + endif # EXAMPLE_CONNECT_ETHERNET + + config EXAMPLE_CONNECT_PPP + bool "connect using Point to Point interface" + select LWIP_PPP_SUPPORT + help + Protocol examples can use PPP connection over serial line. + Choose this option to connect to the ppp server running + on your laptop over a serial line (either UART or USB ACM) + + if EXAMPLE_CONNECT_PPP + choice EXAMPLE_CONNECT_PPP_DEVICE + prompt "Choose PPP device" + default EXAMPLE_CONNECT_PPP_DEVICE_USB + help + Select which peripheral to use to connect to the PPP server. + + config EXAMPLE_CONNECT_PPP_DEVICE_USB + bool "USB" + depends on SOC_USB_OTG_SUPPORTED + select TINYUSB_CDC_ENABLED + help + Use USB ACM device. + + config EXAMPLE_CONNECT_PPP_DEVICE_UART + bool "UART" + help + Use UART. + + endchoice + + menu "UART Configuration" + depends on EXAMPLE_CONNECT_PPP_DEVICE_UART + config EXAMPLE_CONNECT_UART_TX_PIN + int "TXD Pin Number" + default 4 + range 0 31 + help + Pin number of UART TX. + + config EXAMPLE_CONNECT_UART_RX_PIN + int "RXD Pin Number" + default 5 + range 0 31 + help + Pin number of UART RX. + + config EXAMPLE_CONNECT_UART_BAUDRATE + int "UART Baudrate" + default 115200 + range 9600 3000000 + help + Baudrate of the UART device + + endmenu + + config EXAMPLE_PPP_CONN_MAX_RETRY + int "Maximum retry" + default 6 + help + Set the Maximum retry to avoid station reconnecting if the pppd + is not available + + endif # EXAMPLE_CONNECT_PPP + + config EXAMPLE_CONNECT_THREAD + bool "Connect using Thread interface" + depends on !IDF_TARGET_LINUX && OPENTHREAD_ENABLED + default y if SOC_IEEE802154_SUPPORTED + select EXAMPLE_CONNECT_IPV6 + help + Protocol examples can use Wi-Fi, Ethernet and/or Thread to connect to the network. + Choose this option to connect with Thread. + The operational active dataset of the Thread network can be configured in openthread + component at '->Components->OpenThread->Thread Core Features->Thread Operational Dataset' + + if EXAMPLE_CONNECT_THREAD + config EXAMPLE_THREAD_TASK_STACK_SIZE + int "Example Thread task stack size" + default 8192 + help + Thread task stack size + + menu "Radio Spinel Options" + depends on OPENTHREAD_RADIO_SPINEL_UART || OPENTHREAD_RADIO_SPINEL_SPI + + config EXAMPLE_THREAD_UART_RX_PIN + depends on OPENTHREAD_RADIO_SPINEL_UART + int "Uart Rx Pin" + default 17 + + config EXAMPLE_THREAD_UART_TX_PIN + depends on OPENTHREAD_RADIO_SPINEL_UART + int "Uart Tx pin" + default 18 + + config EXAMPLE_THREAD_UART_BAUD + depends on OPENTHREAD_RADIO_SPINEL_UART + int "Uart baud rate" + default 460800 + + config EXAMPLE_THREAD_UART_PORT + depends on OPENTHREAD_RADIO_SPINEL_UART + int "Uart port" + default 1 + + config EXAMPLE_THREAD_SPI_CS_PIN + depends on OPENTHREAD_RADIO_SPINEL_SPI + int "SPI CS Pin" + default 10 + + config EXAMPLE_THREAD_SPI_SCLK_PIN + depends on OPENTHREAD_RADIO_SPINEL_SPI + int "SPI SCLK Pin" + default 12 + + config EXAMPLE_THREAD_SPI_MISO_PIN + depends on OPENTHREAD_RADIO_SPINEL_SPI + int "SPI MISO Pin" + default 13 + + config EXAMPLE_THREAD_SPI_MOSI_PIN + depends on OPENTHREAD_RADIO_SPINEL_SPI + int "SPI MOSI Pin" + default 11 + + config EXAMPLE_THREAD_SPI_INTR_PIN + depends on OPENTHREAD_RADIO_SPINEL_SPI + int "SPI Interrupt Pin" + default 8 + endmenu + + endif + + config EXAMPLE_CONNECT_IPV4 + bool + depends on LWIP_IPV4 + default n if EXAMPLE_CONNECT_THREAD + default y + + config EXAMPLE_CONNECT_IPV6 + depends on EXAMPLE_CONNECT_WIFI || EXAMPLE_CONNECT_ETHERNET || EXAMPLE_CONNECT_PPP || EXAMPLE_CONNECT_THREAD + bool "Obtain IPv6 address" + default y + select LWIP_IPV6 + select LWIP_PPP_ENABLE_IPV6 if EXAMPLE_CONNECT_PPP + help + By default, examples will wait until IPv4 and IPv6 local link addresses are obtained. + Disable this option if the network does not support IPv6. + Choose the preferred IPv6 address type if the connection code should wait until other than + the local link address gets assigned. + Consider enabling IPv6 stateless address autoconfiguration (SLAAC) in the LWIP component. + + if EXAMPLE_CONNECT_IPV6 + choice EXAMPLE_CONNECT_PREFERRED_IPV6 + prompt "Preferred IPv6 Type" + default EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK + help + Select which kind of IPv6 address the connect logic waits for. + + config EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK + bool "Local Link Address" + help + Blocks until Local link address assigned. + + config EXAMPLE_CONNECT_IPV6_PREF_GLOBAL + bool "Global Address" + help + Blocks until Global address assigned. + + config EXAMPLE_CONNECT_IPV6_PREF_SITE_LOCAL + bool "Site Local Address" + help + Blocks until Site link address assigned. + + config EXAMPLE_CONNECT_IPV6_PREF_UNIQUE_LOCAL + bool "Unique Local Link Address" + help + Blocks until Unique local address assigned. + + endchoice + + endif + + +endmenu diff --git a/tests/c_headers/protocol_examples_common/README.md b/tests/c_headers/protocol_examples_common/README.md new file mode 100644 index 00000000..00111dbf --- /dev/null +++ b/tests/c_headers/protocol_examples_common/README.md @@ -0,0 +1,66 @@ +# protocol_example_connect + +This component implements the most common connection methods for ESP32 boards. It should be used mainly in examples of ESP-IDF to demonstrate functionality of network protocols and other libraries, that need the connection step as a prerequisite. + +## How to use this component + +Choose the preferred interface (WiFi, Ethernet, Thread, PPPoS) to connect to the network and configure the interface. + +It is possible to enable multiple interfaces simultaneously making the connection phase to block until all the chosen interfaces acquire IP addresses. +It is also possible to disable all interfaces, skipping the connection phase altogether. + +### WiFi + +Choose WiFi connection method (for chipsets that support it) and configure basic WiFi connection properties: +* WiFi SSID +* WiFI password +* Maximum connection retry (connection would be aborted if it doesn't succeed after specified number of retries) +* WiFi scan method (including RSSI and authorization mode threshold) + + + +### Ethernet + +Choose Ethernet connection if your board supports it. The most common settings is using Espressif Ethernet Kit, which is also the recommended HW for this selection. You can also select an SPI ethernet device (if your chipset doesn't support internal EMAC or if you prefer). It is also possible to use OpenCores Ethernet MAC if you're running the example under QEMU. + +### Thread + +Choose Thread connection if your board supports IEEE802.15.4 native radio or works with [OpenThread RCP](../../openthread/ot_rcp/README.md). You can configure the Thread network at menuconfig '->Components->OpenThread->Thread Core Features->Thread Operational Dataset'. + +If the Thread end-device joins a Thread network with a Thread Border Router that has the NAT64 feature enabled, the end-device can access the Internet with the standard DNS APIs after configuring the following properties: +* Enable DNS64 client ('->Components->OpenThread->Thread Core Features->Enable DNS64 client') +* Enable custom DNS external resolve Hook ('->Components->LWIP->Hooks->DNS external resolve Hook->Custom implementation') + +### PPP + +Point to point connection method creates a simple IP tunnel to the counterpart device (running PPP server), typically a Linux machine with pppd service. We currently support only PPP over Serial (using UART or USB CDC). This is useful for simple testing of networking layers, but with some additional configuration on the server side, we could simulate standard model of internet connectivity. The PPP server could be also represented by a cellular modem device with pre-configured connectivity and already switched to PPP mode (this setup is not very flexible though, so we suggest using a standard modem library implementing commands and modes, e.g. [esp_modem](https://components.espressif.com/component/espressif/esp_modem) ). + +> [!Note] +> Note that if you choose USB device, you have to manually add a dependency on `esp_tinyusb` component. This step is necessary to keep the `protocol_example_connect` component simple and dependency free. Please run this command from your project location to add the dependency: +> ```bash +> idf.py add-dependency espressif/esp_tinyusb^1 +> ``` + +#### Setup a PPP server + +Connect the board using UART or USB and note the device name, which would be typically: +* `/dev/ttyACMx` for USB devices +* `/dev/ttyUSBx` for UART devices + +Run the pppd server: + +```bash +sudo pppd /dev/ttyACM0 115200 192.168.11.1:192.168.11.2 ms-dns 8.8.8.8 modem local noauth debug nocrtscts nodetach +ipv6 +``` + +Please update the parameters with the correct serial device, baud rate, IP addresses, DNS server, use `+ipv6` if `EXAMPLE_CONNECT_IPV6=y`. + +#### Connection to outside + +In order to access other network endpoints, we have to configure some IP/translation rules. The easiest method is to setup a masquerade of the PPPD created interface (`ppp0`) to your default networking interface (`${ETH0}`). Here is an example of such rule: + +```bash +sudo iptables -t nat -A POSTROUTING -o ${ETH0} -j MASQUERADE +sudo iptables -A FORWARD -i ${ETH0} -o ppp0 -m state --state RELATED,ESTABLISHED -j ACCEPT +sudo iptables -A FORWARD -i ppp0 -o ${ETH0} -j ACCEPT +``` diff --git a/tests/c_headers/protocol_examples_common/addr_from_stdin.c b/tests/c_headers/protocol_examples_common/addr_from_stdin.c new file mode 100644 index 00000000..e4c94b1a --- /dev/null +++ b/tests/c_headers/protocol_examples_common/addr_from_stdin.c @@ -0,0 +1,69 @@ +#include +#include "esp_system.h" +#include "esp_log.h" +#include "esp_netif.h" +#include "protocol_examples_common.h" + +#include "lwip/sockets.h" +#include +#include + +#define HOST_IP_SIZE 128 + +esp_err_t get_addr_from_stdin(int port, int sock_type, int *ip_protocol, int *addr_family, struct sockaddr_storage *dest_addr) +{ + char host_ip[HOST_IP_SIZE]; + int len; + static bool already_init = false; + + // this function could be called multiple times -> make sure UART init runs only once + if (!already_init) { + example_configure_stdin_stdout(); + already_init = true; + } + + // ignore empty or LF only string (could receive from DUT class) + do { + fgets(host_ip, HOST_IP_SIZE, stdin); + len = strlen(host_ip); + } while (len<=1 && host_ip[0] == '\n'); + host_ip[len - 1] = '\0'; + + struct addrinfo hints, *addr_list, *cur; + memset( &hints, 0, sizeof( hints ) ); + + // run getaddrinfo() to decide on the IP protocol + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = sock_type; + hints.ai_protocol = IPPROTO_TCP; + if( getaddrinfo( host_ip, NULL, &hints, &addr_list ) != 0 ) { + return ESP_FAIL; + } + for( cur = addr_list; cur != NULL; cur = cur->ai_next ) { + memcpy(dest_addr, cur->ai_addr, sizeof(*dest_addr)); +#if CONFIG_EXAMPLE_CONNECT_IPV4 + if (cur->ai_family == AF_INET) { + *ip_protocol = IPPROTO_IP; + *addr_family = AF_INET; + // add port number and return on first IPv4 match + ((struct sockaddr_in*)dest_addr)->sin_port = htons(port); + freeaddrinfo( addr_list ); + return ESP_OK; + } +#endif // IPV4 +#if CONFIG_EXAMPLE_CONNECT_IPV6 + if (cur->ai_family == AF_INET6) { + *ip_protocol = IPPROTO_IPV6; + *addr_family = AF_INET6; + // add port and interface number and return on first IPv6 match + ((struct sockaddr_in6*)dest_addr)->sin6_port = htons(port); + ((struct sockaddr_in6*)dest_addr)->sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE); + freeaddrinfo( addr_list ); + return ESP_OK; + } +#endif // IPV6 + } + // no match found + freeaddrinfo( addr_list ); + return ESP_FAIL; +} diff --git a/tests/c_headers/protocol_examples_common/connect.c b/tests/c_headers/protocol_examples_common/connect.c new file mode 100644 index 00000000..65fa2b85 --- /dev/null +++ b/tests/c_headers/protocol_examples_common/connect.c @@ -0,0 +1,148 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include "protocol_examples_common.h" +#include "example_common_private.h" +#include "sdkconfig.h" +#include "esp_event.h" +#include "esp_wifi.h" +#include "esp_wifi_default.h" +#include "esp_log.h" +#include "esp_netif.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "lwip/err.h" +#include "lwip/sys.h" + +static const char *TAG = "example_common"; + +#if CONFIG_EXAMPLE_CONNECT_IPV6 +/* types of ipv6 addresses to be displayed on ipv6 events */ +const char *example_ipv6_addr_types_to_str[6] = { + "ESP_IP6_ADDR_IS_UNKNOWN", + "ESP_IP6_ADDR_IS_GLOBAL", + "ESP_IP6_ADDR_IS_LINK_LOCAL", + "ESP_IP6_ADDR_IS_SITE_LOCAL", + "ESP_IP6_ADDR_IS_UNIQUE_LOCAL", + "ESP_IP6_ADDR_IS_IPV4_MAPPED_IPV6" +}; +#endif + +/** + * @brief Checks the netif description if it contains specified prefix. + * All netifs created within common connect component are prefixed with the module TAG, + * so it returns true if the specified netif is owned by this module + */ +bool example_is_our_netif(const char *prefix, esp_netif_t *netif) +{ + return strncmp(prefix, esp_netif_get_desc(netif), strlen(prefix) - 1) == 0; +} + +static bool netif_desc_matches_with(esp_netif_t *netif, void *ctx) +{ + return strcmp(ctx, esp_netif_get_desc(netif)) == 0; +} + +esp_netif_t *get_example_netif_from_desc(const char *desc) +{ + return esp_netif_find_if(netif_desc_matches_with, (void*)desc); +} + +static esp_err_t print_all_ips_tcpip(void* ctx) +{ + const char *prefix = ctx; + // iterate over active interfaces, and print out IPs of "our" netifs + esp_netif_t *netif = NULL; + while ((netif = esp_netif_next_unsafe(netif)) != NULL) { + if (example_is_our_netif(prefix, netif)) { + ESP_LOGI(TAG, "Connected to %s", esp_netif_get_desc(netif)); +#if CONFIG_EXAMPLE_CONNECT_IPV4 + esp_netif_ip_info_t ip; + ESP_ERROR_CHECK(esp_netif_get_ip_info(netif, &ip)); + + ESP_LOGI(TAG, "- IPv4 address: " IPSTR ",", IP2STR(&ip.ip)); +#endif +#if CONFIG_EXAMPLE_CONNECT_IPV6 + esp_ip6_addr_t ip6[MAX_IP6_ADDRS_PER_NETIF]; + int ip6_addrs = esp_netif_get_all_ip6(netif, ip6); + for (int j = 0; j < ip6_addrs; ++j) { + esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&(ip6[j])); + ESP_LOGI(TAG, "- IPv6 address: " IPV6STR ", type: %s", IPV62STR(ip6[j]), example_ipv6_addr_types_to_str[ipv6_type]); + } +#endif + } + } + return ESP_OK; +} + +void example_print_all_netif_ips(const char *prefix) +{ + // Print all IPs in TCPIP context to avoid potential races of removing/adding netifs when iterating over the list + esp_netif_tcpip_exec(print_all_ips_tcpip, (void*) prefix); +} + + +esp_err_t example_connect(void) +{ +#if CONFIG_EXAMPLE_CONNECT_ETHERNET + if (example_ethernet_connect() != ESP_OK) { + return ESP_FAIL; + } + ESP_ERROR_CHECK(esp_register_shutdown_handler(&example_ethernet_shutdown)); +#endif +#if CONFIG_EXAMPLE_CONNECT_WIFI + if (example_wifi_connect() != ESP_OK) { + return ESP_FAIL; + } + ESP_ERROR_CHECK(esp_register_shutdown_handler(&example_wifi_shutdown)); +#endif +#if CONFIG_EXAMPLE_CONNECT_THREAD + if (example_thread_connect() != ESP_OK) { + return ESP_FAIL; + } + ESP_ERROR_CHECK(esp_register_shutdown_handler(&example_thread_shutdown)); +#endif +#if CONFIG_EXAMPLE_CONNECT_PPP + if (example_ppp_connect() != ESP_OK) { + return ESP_FAIL; + } + ESP_ERROR_CHECK(esp_register_shutdown_handler(&example_ppp_shutdown)); +#endif + +#if CONFIG_EXAMPLE_CONNECT_ETHERNET + example_print_all_netif_ips(EXAMPLE_NETIF_DESC_ETH); +#endif + +#if CONFIG_EXAMPLE_CONNECT_WIFI + example_print_all_netif_ips(EXAMPLE_NETIF_DESC_STA); +#endif + +#if CONFIG_EXAMPLE_CONNECT_THREAD + example_print_all_netif_ips(EXAMPLE_NETIF_DESC_THREAD); +#endif + +#if CONFIG_EXAMPLE_CONNECT_PPP + example_print_all_netif_ips(EXAMPLE_NETIF_DESC_PPP); +#endif + + return ESP_OK; +} + + +esp_err_t example_disconnect(void) +{ +#if CONFIG_EXAMPLE_CONNECT_ETHERNET + example_ethernet_shutdown(); + ESP_ERROR_CHECK(esp_unregister_shutdown_handler(&example_ethernet_shutdown)); +#endif +#if CONFIG_EXAMPLE_CONNECT_WIFI + example_wifi_shutdown(); + ESP_ERROR_CHECK(esp_unregister_shutdown_handler(&example_wifi_shutdown)); +#endif + return ESP_OK; +} diff --git a/tests/c_headers/protocol_examples_common/console_cmd.c b/tests/c_headers/protocol_examples_common/console_cmd.c new file mode 100644 index 00000000..342a92c7 --- /dev/null +++ b/tests/c_headers/protocol_examples_common/console_cmd.c @@ -0,0 +1,87 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + + +#include +#include "protocol_examples_common.h" +#include "example_common_private.h" +#include "esp_wifi.h" +#include "esp_log.h" +#include "esp_console.h" +#include "argtable3/argtable3.h" + + +static const char *TAG = "example_console"; + +typedef struct { + struct arg_str *ssid; + struct arg_str *password; + struct arg_int *channel; + struct arg_end *end; +} wifi_connect_args_t; +static wifi_connect_args_t connect_args; + +static int cmd_do_wifi_connect(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &connect_args); + + if (nerrors != 0) { + arg_print_errors(stderr, connect_args.end, argv[0]); + return 1; + } + + wifi_config_t wifi_config = { + .sta = { + .scan_method = WIFI_ALL_CHANNEL_SCAN, + .sort_method = WIFI_CONNECT_AP_BY_SIGNAL, + }, + }; + if (connect_args.channel->count > 0) { + wifi_config.sta.channel = (uint8_t)(connect_args.channel->ival[0]); + } + const char *ssid = connect_args.ssid->sval[0]; + const char *pass = connect_args.password->sval[0]; + strlcpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); + if (pass) { + strlcpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password)); + } + example_wifi_sta_do_connect(wifi_config, false); + return 0; +} + +static int cmd_do_wifi_disconnect(int argc, char **argv) +{ + example_wifi_sta_do_disconnect(); + return 0; +} + +void example_register_wifi_connect_commands(void) +{ + ESP_LOGI(TAG, "Registering WiFi connect commands."); + example_wifi_start(); + + connect_args.ssid = arg_str1(NULL, NULL, "", "SSID of AP"); + connect_args.password = arg_str0(NULL, NULL, "", "password of AP"); + connect_args.channel = arg_int0("n", "channel", "", "channel of AP"); + connect_args.end = arg_end(2); + const esp_console_cmd_t wifi_connect_cmd = { + .command = "wifi_connect", + .help = "WiFi is station mode, join specified soft-AP", + .hint = NULL, + .func = &cmd_do_wifi_connect, + .argtable = &connect_args + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&wifi_connect_cmd) ); + + + const esp_console_cmd_t wifi_disconnect_cmd = { + .command = "wifi_disconnect", + .help = "Do wifi disconnect", + .hint = NULL, + .func = &cmd_do_wifi_disconnect, + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&wifi_disconnect_cmd) ); +} diff --git a/tests/c_headers/protocol_examples_common/eth_connect.c b/tests/c_headers/protocol_examples_common/eth_connect.c new file mode 100644 index 00000000..ad480690 --- /dev/null +++ b/tests/c_headers/protocol_examples_common/eth_connect.c @@ -0,0 +1,245 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include "protocol_examples_common.h" +#include "example_common_private.h" +#include "esp_event.h" +#include "esp_eth.h" +#if CONFIG_ETH_USE_SPI_ETHERNET +#include "driver/spi_master.h" +#endif // CONFIG_ETH_USE_SPI_ETHERNET +#include "esp_log.h" +#include "esp_mac.h" +#include "driver/gpio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + + +static const char *TAG = "ethernet_connect"; +static SemaphoreHandle_t s_semph_get_ip_addrs = NULL; +#if CONFIG_EXAMPLE_CONNECT_IPV6 +static SemaphoreHandle_t s_semph_get_ip6_addrs = NULL; +#endif + +static esp_netif_t *eth_start(void); +static void eth_stop(void); + + +/** Event handler for Ethernet events */ + +static void eth_on_got_ip(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + if (!example_is_our_netif(EXAMPLE_NETIF_DESC_ETH, event->esp_netif)) { + return; + } + ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s\" address: " IPSTR, esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip)); + xSemaphoreGive(s_semph_get_ip_addrs); +} + +#if CONFIG_EXAMPLE_CONNECT_IPV6 + +static void eth_on_got_ipv6(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data; + if (!example_is_our_netif(EXAMPLE_NETIF_DESC_ETH, event->esp_netif)) { + return; + } + esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip); + ESP_LOGI(TAG, "Got IPv6 event: Interface \"%s\" address: " IPV6STR ", type: %s", esp_netif_get_desc(event->esp_netif), + IPV62STR(event->ip6_info.ip), example_ipv6_addr_types_to_str[ipv6_type]); + if (ipv6_type == EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE) { + xSemaphoreGive(s_semph_get_ip6_addrs); + } +} + +static void on_eth_event(void *esp_netif, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + switch (event_id) { + case ETHERNET_EVENT_CONNECTED: + ESP_LOGI(TAG, "Ethernet Link Up"); + ESP_ERROR_CHECK(esp_netif_create_ip6_linklocal(esp_netif)); + break; + default: + break; + } +} + +#endif // CONFIG_EXAMPLE_CONNECT_IPV6 + +static esp_eth_handle_t s_eth_handle = NULL; +static esp_eth_mac_t *s_mac = NULL; +static esp_eth_phy_t *s_phy = NULL; +static esp_eth_netif_glue_handle_t s_eth_glue = NULL; + +static esp_netif_t *eth_start(void) +{ + esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH(); + // Warning: the interface desc is used in tests to capture actual connection details (IP, gw, mask) + esp_netif_config.if_desc = EXAMPLE_NETIF_DESC_ETH; + esp_netif_config.route_prio = 64; + esp_netif_config_t netif_config = { + .base = &esp_netif_config, + .stack = ESP_NETIF_NETSTACK_DEFAULT_ETH + }; + esp_netif_t *netif = esp_netif_new(&netif_config); + assert(netif); + + eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + mac_config.rx_task_stack_size = CONFIG_EXAMPLE_ETHERNET_EMAC_TASK_STACK_SIZE; + eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); + phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR; + phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO; +#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET + eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG(); + esp32_emac_config.smi_gpio.mdc_num = CONFIG_EXAMPLE_ETH_MDC_GPIO; + esp32_emac_config.smi_gpio.mdio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO; + s_mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); +#if CONFIG_EXAMPLE_ETH_PHY_GENERIC + s_phy = esp_eth_phy_new_generic(&phy_config); +#elif CONFIG_EXAMPLE_ETH_PHY_IP101 + s_phy = esp_eth_phy_new_ip101(&phy_config); +#elif CONFIG_EXAMPLE_ETH_PHY_RTL8201 + s_phy = esp_eth_phy_new_rtl8201(&phy_config); +#elif CONFIG_EXAMPLE_ETH_PHY_LAN87XX + s_phy = esp_eth_phy_new_lan87xx(&phy_config); +#elif CONFIG_EXAMPLE_ETH_PHY_DP83848 + s_phy = esp_eth_phy_new_dp83848(&phy_config); +#elif CONFIG_EXAMPLE_ETH_PHY_KSZ80XX + s_phy = esp_eth_phy_new_ksz80xx(&phy_config); +#endif +#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET + gpio_install_isr_service(0); + spi_bus_config_t buscfg = { + .miso_io_num = CONFIG_EXAMPLE_ETH_SPI_MISO_GPIO, + .mosi_io_num = CONFIG_EXAMPLE_ETH_SPI_MOSI_GPIO, + .sclk_io_num = CONFIG_EXAMPLE_ETH_SPI_SCLK_GPIO, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + }; + ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO)); + spi_device_interface_config_t spi_devcfg = { + .mode = 0, + .clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000, + .spics_io_num = CONFIG_EXAMPLE_ETH_SPI_CS_GPIO, + .queue_size = 20 + }; +#if CONFIG_EXAMPLE_USE_DM9051 + /* dm9051 ethernet driver is based on spi driver */ + eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &spi_devcfg); + dm9051_config.int_gpio_num = CONFIG_EXAMPLE_ETH_SPI_INT_GPIO; + s_mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config); + s_phy = esp_eth_phy_new_dm9051(&phy_config); +#elif CONFIG_EXAMPLE_USE_W5500 + /* w5500 ethernet driver is based on spi driver */ + eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &spi_devcfg); + w5500_config.int_gpio_num = CONFIG_EXAMPLE_ETH_SPI_INT_GPIO; + s_mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config); + s_phy = esp_eth_phy_new_w5500(&phy_config); +#endif +#elif CONFIG_EXAMPLE_USE_OPENETH + phy_config.autonego_timeout_ms = 100; + s_mac = esp_eth_mac_new_openeth(&mac_config); + s_phy = esp_eth_phy_new_dp83848(&phy_config); +#endif + + // Install Ethernet driver + esp_eth_config_t config = ETH_DEFAULT_CONFIG(s_mac, s_phy); + ESP_ERROR_CHECK(esp_eth_driver_install(&config, &s_eth_handle)); + +#if CONFIG_EXAMPLE_USE_SPI_ETHERNET + /* The SPI Ethernet module might doesn't have a burned factory MAC address, we cat to set it manually. + We set the ESP_MAC_ETH mac address as the default, if you want to use ESP_MAC_EFUSE_CUSTOM mac address, please enable the + configuration: `ESP_MAC_USE_CUSTOM_MAC_AS_BASE_MAC` + */ + uint8_t eth_mac[6] = {0}; + ESP_ERROR_CHECK(esp_read_mac(eth_mac, ESP_MAC_ETH)); + ESP_ERROR_CHECK(esp_eth_ioctl(s_eth_handle, ETH_CMD_S_MAC_ADDR, eth_mac)); +#endif // CONFIG_EXAMPLE_USE_SPI_ETHERNET + + // combine driver with netif + s_eth_glue = esp_eth_new_netif_glue(s_eth_handle); + esp_netif_attach(netif, s_eth_glue); + + // Register user defined event handlers + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, ð_on_got_ip, NULL)); +#ifdef CONFIG_EXAMPLE_CONNECT_IPV6 + ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_CONNECTED, &on_eth_event, netif)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, ð_on_got_ipv6, NULL)); +#endif + + esp_eth_start(s_eth_handle); + return netif; +} + +static void eth_stop(void) +{ + esp_netif_t *eth_netif = get_example_netif_from_desc(EXAMPLE_NETIF_DESC_ETH); + ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, ð_on_got_ip)); +#if CONFIG_EXAMPLE_CONNECT_IPV6 + ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, ð_on_got_ipv6)); + ESP_ERROR_CHECK(esp_event_handler_unregister(ETH_EVENT, ETHERNET_EVENT_CONNECTED, &on_eth_event)); +#endif + ESP_ERROR_CHECK(esp_eth_stop(s_eth_handle)); + ESP_ERROR_CHECK(esp_eth_del_netif_glue(s_eth_glue)); + ESP_ERROR_CHECK(esp_eth_driver_uninstall(s_eth_handle)); + s_eth_handle = NULL; + ESP_ERROR_CHECK(s_phy->del(s_phy)); + ESP_ERROR_CHECK(s_mac->del(s_mac)); + + esp_netif_destroy(eth_netif); +} + +esp_eth_handle_t get_example_eth_handle(void) +{ + return s_eth_handle; +} + +/* tear down connection, release resources */ +void example_ethernet_shutdown(void) +{ + if (s_semph_get_ip_addrs == NULL) { + return; + } + vSemaphoreDelete(s_semph_get_ip_addrs); + s_semph_get_ip_addrs = NULL; +#if CONFIG_EXAMPLE_CONNECT_IPV6 + vSemaphoreDelete(s_semph_get_ip6_addrs); + s_semph_get_ip6_addrs = NULL; +#endif + eth_stop(); +} + +esp_err_t example_ethernet_connect(void) +{ +#if CONFIG_EXAMPLE_CONNECT_IPV4 + s_semph_get_ip_addrs = xSemaphoreCreateBinary(); + if (s_semph_get_ip_addrs == NULL) { + return ESP_ERR_NO_MEM; + } +#endif +#if CONFIG_EXAMPLE_CONNECT_IPV6 + s_semph_get_ip6_addrs = xSemaphoreCreateBinary(); + if (s_semph_get_ip6_addrs == NULL) { + vSemaphoreDelete(s_semph_get_ip_addrs); + return ESP_ERR_NO_MEM; + } +#endif + eth_start(); + ESP_LOGI(TAG, "Waiting for IP(s)."); +#if CONFIG_EXAMPLE_CONNECT_IPV4 + xSemaphoreTake(s_semph_get_ip_addrs, portMAX_DELAY); +#endif +#if CONFIG_EXAMPLE_CONNECT_IPV6 + xSemaphoreTake(s_semph_get_ip6_addrs, portMAX_DELAY); +#endif + return ESP_OK; +} diff --git a/tests/c_headers/protocol_examples_common/include/addr_from_stdin.h b/tests/c_headers/protocol_examples_common/include/addr_from_stdin.h new file mode 100644 index 00000000..827f8c25 --- /dev/null +++ b/tests/c_headers/protocol_examples_common/include/addr_from_stdin.h @@ -0,0 +1,44 @@ +/* Common utilities for socket address input interface: + The API get_addr_from_stdin() is mainly used by socket client examples which read IP address from stdin (if configured). + This option is typically used in the CI, but could be enabled in the project configuration. + In that case this component is used to receive a string that is evaluated and processed to output + socket structures to open a connectio + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. + */ + +#pragma once + +#include "lwip/sys.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Read and evaluate IP address from stdin + * + * This API reads stdin and parses the input address using getaddrinfo() + * to fill in struct sockaddr_storage (for both IPv4 and IPv6) used to open + * a socket. IP protocol is guessed from the IP address string. + * + * @param[in] port port number of expected connection + * @param[in] sock_type expected protocol: SOCK_STREAM or SOCK_DGRAM + * @param[out] ip_protocol resultant IP protocol: IPPROTO_IP or IPPROTO_IP6 + * @param[out] addr_family resultant address family: AF_INET or AF_INET6 + * @param[out] dest_addr sockaddr_storage structure (for both IPv4 and IPv6) + * @return ESP_OK on success, ESP_FAIL otherwise + */ +esp_err_t get_addr_from_stdin(int port, int sock_type, + int *ip_protocol, + int *addr_family, + struct sockaddr_storage *dest_addr); + +#ifdef __cplusplus +} +#endif diff --git a/tests/c_headers/protocol_examples_common/include/example_common_private.h b/tests/c_headers/protocol_examples_common/include/example_common_private.h new file mode 100644 index 00000000..ad6ef537 --- /dev/null +++ b/tests/c_headers/protocol_examples_common/include/example_common_private.h @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* Private Functions of protocol example common */ + +#pragma once + +#include "esp_err.h" +#include "esp_wifi.h" +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if CONFIG_EXAMPLE_CONNECT_IPV6 +#define MAX_IP6_ADDRS_PER_NETIF (5) + +#if defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK) +#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_LINK_LOCAL +#elif defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_GLOBAL) +#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_GLOBAL +#elif defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_SITE_LOCAL) +#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_SITE_LOCAL +#elif defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_UNIQUE_LOCAL) +#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_UNIQUE_LOCAL +#endif // if-elif CONFIG_EXAMPLE_CONNECT_IPV6_PREF_... + +#endif + + +#if CONFIG_EXAMPLE_CONNECT_IPV6 +extern const char *example_ipv6_addr_types_to_str[6]; +#endif + +void example_wifi_start(void); +void example_wifi_stop(void); +esp_err_t example_wifi_sta_do_connect(wifi_config_t wifi_config, bool wait); +esp_err_t example_wifi_sta_do_disconnect(void); +bool example_is_our_netif(const char *prefix, esp_netif_t *netif); +void example_print_all_netif_ips(const char *prefix); +void example_wifi_shutdown(void); +esp_err_t example_wifi_connect(void); +void example_ethernet_shutdown(void); +esp_err_t example_ethernet_connect(void); +void example_thread_shutdown(void); +esp_err_t example_thread_connect(void); +esp_err_t example_ppp_connect(void); +void example_ppp_start(void); +void example_ppp_shutdown(void); + + + +#ifdef __cplusplus +} +#endif diff --git a/tests/c_headers/protocol_examples_common/include/protocol_examples_common.h b/tests/c_headers/protocol_examples_common/include/protocol_examples_common.h new file mode 100644 index 00000000..54e2c07a --- /dev/null +++ b/tests/c_headers/protocol_examples_common/include/protocol_examples_common.h @@ -0,0 +1,154 @@ +/* Common functions for protocol examples, to establish Wi-Fi or Ethernet connection. + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. + */ + +#pragma once + +#include "sdkconfig.h" +#include "esp_err.h" +#if !CONFIG_IDF_TARGET_LINUX +#include "esp_netif.h" +#if CONFIG_EXAMPLE_CONNECT_ETHERNET +#include "esp_eth.h" +#endif +#endif // !CONFIG_IDF_TARGET_LINUX + +#ifdef __cplusplus +extern "C" { +#endif + +#if !CONFIG_IDF_TARGET_LINUX +#if CONFIG_EXAMPLE_CONNECT_WIFI +#define EXAMPLE_NETIF_DESC_STA "example_netif_sta" +#endif + +#if CONFIG_EXAMPLE_CONNECT_ETHERNET +#define EXAMPLE_NETIF_DESC_ETH "example_netif_eth" +#endif + +#if CONFIG_EXAMPLE_CONNECT_THREAD +#define EXAMPLE_NETIF_DESC_THREAD "example_netif_thread" +#endif + +#if CONFIG_EXAMPLE_CONNECT_PPP +#define EXAMPLE_NETIF_DESC_PPP "example_netif_ppp" +#endif + +#if CONFIG_EXAMPLE_WIFI_SCAN_METHOD_FAST +#define EXAMPLE_WIFI_SCAN_METHOD WIFI_FAST_SCAN +#elif CONFIG_EXAMPLE_WIFI_SCAN_METHOD_ALL_CHANNEL +#define EXAMPLE_WIFI_SCAN_METHOD WIFI_ALL_CHANNEL_SCAN +#endif + +#if CONFIG_EXAMPLE_WIFI_CONNECT_AP_BY_SIGNAL +#define EXAMPLE_WIFI_CONNECT_AP_SORT_METHOD WIFI_CONNECT_AP_BY_SIGNAL +#elif CONFIG_EXAMPLE_WIFI_CONNECT_AP_BY_SECURITY +#define EXAMPLE_WIFI_CONNECT_AP_SORT_METHOD WIFI_CONNECT_AP_BY_SECURITY +#endif + +#if CONFIG_EXAMPLE_WIFI_AUTH_OPEN +#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN +#elif CONFIG_EXAMPLE_WIFI_AUTH_WEP +#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP +#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA_PSK +#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK +#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA2_PSK +#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK +#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA_WPA2_PSK +#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK +#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA2_ENTERPRISE +#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_ENTERPRISE +#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA3_PSK +#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK +#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA2_WPA3_PSK +#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK +#elif CONFIG_EXAMPLE_WIFI_AUTH_WAPI_PSK +#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK +#endif + +/* Example default interface, prefer the ethernet one if running in example-test (CI) configuration */ +#if CONFIG_EXAMPLE_CONNECT_ETHERNET +#define EXAMPLE_INTERFACE get_example_netif_from_desc(EXAMPLE_NETIF_DESC_ETH) +#define get_example_netif() get_example_netif_from_desc(EXAMPLE_NETIF_DESC_ETH) +#elif CONFIG_EXAMPLE_CONNECT_WIFI +#define EXAMPLE_INTERFACE get_example_netif_from_desc(EXAMPLE_NETIF_DESC_STA) +#define get_example_netif() get_example_netif_from_desc(EXAMPLE_NETIF_DESC_STA) +#elif CONFIG_EXAMPLE_CONNECT_THREAD +#define EXAMPLE_INTERFACE get_example_netif_from_desc(EXAMPLE_NETIF_DESC_THREAD) +#define get_example_netif() get_example_netif_from_desc(EXAMPLE_NETIF_DESC_THREAD) +#elif CONFIG_EXAMPLE_CONNECT_PPP +#define EXAMPLE_INTERFACE get_example_netif_from_desc(EXAMPLE_NETIF_DESC_PPP) +#define get_example_netif() get_example_netif_from_desc(EXAMPLE_NETIF_DESC_PPP) +#endif + +/** + * @brief Configure Wi-Fi or Ethernet, connect, wait for IP + * + * This all-in-one helper function is used in protocols examples to + * reduce the amount of boilerplate in the example. + * + * It is not intended to be used in real world applications. + * See examples under examples/wifi/getting_started/ and examples/ethernet/ + * for more complete Wi-Fi or Ethernet initialization code. + * + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + * + * @return ESP_OK on successful connection + */ +esp_err_t example_connect(void); + +/** + * Counterpart to example_connect, de-initializes Wi-Fi or Ethernet + */ +esp_err_t example_disconnect(void); + +/** + * @brief Configure stdin and stdout to use blocking I/O + * + * This helper function is used in ASIO examples. It wraps installing the + * UART driver and configuring VFS layer to use UART driver for console I/O. + */ +esp_err_t example_configure_stdin_stdout(void); + +/** + * @brief Returns esp-netif pointer created by example_connect() described by + * the supplied desc field + * + * @param desc Textual interface of created network interface, for example "sta" + * indicate default WiFi station, "eth" default Ethernet interface. + * + */ +esp_netif_t *get_example_netif_from_desc(const char *desc); + +#if CONFIG_EXAMPLE_PROVIDE_WIFI_CONSOLE_CMD +/** + * @brief Register wifi connect commands + * + * Provide a simple wifi_connect command in esp_console. + * This function can be used after esp_console is initialized. + */ +void example_register_wifi_connect_commands(void); +#endif + +#if CONFIG_EXAMPLE_CONNECT_ETHERNET +/** + * @brief Get the example Ethernet driver handle + * + * @return esp_eth_handle_t + */ +esp_eth_handle_t get_example_eth_handle(void); +#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET + +#else +static inline esp_err_t example_connect(void) {return ESP_OK;} +#endif // !CONFIG_IDF_TARGET_LINUX + +#ifdef __cplusplus +} +#endif diff --git a/tests/c_headers/protocol_examples_common/include/protocol_examples_thread_config.h b/tests/c_headers/protocol_examples_common/include/protocol_examples_thread_config.h new file mode 100644 index 00000000..532f828b --- /dev/null +++ b/tests/c_headers/protocol_examples_common/include/protocol_examples_thread_config.h @@ -0,0 +1,115 @@ +/* + * Thread configurations for protocol examples + * + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#pragma once + +#include + +#include + +#ifdef CONFIG_OPENTHREAD_RADIO_NATIVE +#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ + { \ + .radio_mode = RADIO_MODE_NATIVE, \ + } + +#elif defined(CONFIG_OPENTHREAD_RADIO_SPINEL_UART) +#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ + { \ + .radio_mode = RADIO_MODE_UART_RCP, \ + .radio_uart_config = \ + { \ + .port = CONFIG_EXAMPLE_THREAD_UART_PORT, \ + .uart_config = \ + { \ + .baud_rate = CONFIG_EXAMPLE_THREAD_UART_BAUD, \ + .data_bits = UART_DATA_8_BITS, \ + .parity = UART_PARITY_DISABLE, \ + .stop_bits = UART_STOP_BITS_1, \ + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \ + .rx_flow_ctrl_thresh = 0, \ + .source_clk = UART_SCLK_DEFAULT, \ + }, \ + .rx_pin = CONFIG_EXAMPLE_THREAD_UART_RX_PIN, \ + .tx_pin = CONFIG_EXAMPLE_THREAD_UART_TX_PIN, \ + }, \ + } +#elif defined(CONFIG_OPENTHREAD_RADIO_SPINEL_SPI) +#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ + { \ + .radio_mode = RADIO_MODE_SPI_RCP, \ + .radio_spi_config = \ + { \ + .host_device = SPI2_HOST, \ + .dma_channel = 2, \ + .spi_interface = \ + { \ + .mosi_io_num = CONFIG_EXAMPLE_THREAD_SPI_MOSI_PIN, \ + .miso_io_num = CONFIG_EXAMPLE_THREAD_SPI_MISO_PIN, \ + .sclk_io_num = CONFIG_EXAMPLE_THREAD_SPI_SCLK_PIN, \ + .quadwp_io_num = -1, \ + .quadhd_io_num = -1, \ + }, \ + .spi_device = \ + { \ + .cs_ena_pretrans = 2, \ + .input_delay_ns = 100, \ + .mode = 0, \ + .clock_speed_hz = 2500 * 1000, \ + .spics_io_num = CONFIG_EXAMPLE_THREAD_SPI_CS_PIN, \ + .queue_size = 5, \ + }, \ + .intr_pin = CONFIG_EXAMPLE_THREAD_SPI_INTR_PIN, \ + }, \ + } +#else +#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ + { \ + .radio_mode = RADIO_MODE_TREL, \ + } +#endif + +#if CONFIG_OPENTHREAD_CONSOLE_TYPE_UART +#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ + { \ + .host_connection_mode = HOST_CONNECTION_MODE_CLI_UART, \ + .host_uart_config = \ + { \ + .port = 0, \ + .uart_config = \ + { \ + .baud_rate = 115200, \ + .data_bits = UART_DATA_8_BITS, \ + .parity = UART_PARITY_DISABLE, \ + .stop_bits = UART_STOP_BITS_1, \ + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \ + .rx_flow_ctrl_thresh = 0, \ + .source_clk = UART_SCLK_DEFAULT, \ + }, \ + .rx_pin = UART_PIN_NO_CHANGE, \ + .tx_pin = UART_PIN_NO_CHANGE, \ + }, \ + } +#elif CONFIG_OPENTHREAD_CONSOLE_TYPE_USB_SERIAL_JTAG +#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ + { \ + .host_connection_mode = HOST_CONNECTION_MODE_CLI_USB, \ + .host_usb_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(), \ + } +#else +#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ + { \ + .host_connection_mode = HOST_CONNECTION_MODE_NONE, \ + } +#endif + +#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \ + { \ + .storage_partition_name = "nvs", \ + .netif_queue_size = 10, \ + .task_queue_size = 10, \ + } diff --git a/tests/c_headers/protocol_examples_common/include/protocol_examples_utils.h b/tests/c_headers/protocol_examples_common/include/protocol_examples_utils.h new file mode 100644 index 00000000..79031171 --- /dev/null +++ b/tests/c_headers/protocol_examples_common/include/protocol_examples_utils.h @@ -0,0 +1,49 @@ +/* + * Utility functions for protocol examples + * + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @brief Encode an URI + * + * @param dest a destination memory location + * @param src the source string + * @param len the length of the source string + * @return uint32_t the count of escaped characters + * + * @note Please allocate the destination buffer keeping in mind that encoding a + * special character will take up 3 bytes (for '%' and two hex digits). + * In the worst-case scenario, the destination buffer will have to be 3 times + * that of the source string. + */ +uint32_t example_uri_encode(char *dest, const char *src, size_t len); + +/** + * @brief Decode an URI + * + * @param dest a destination memory location + * @param src the source string + * @param len the length of the source string + * + * @note Please allocate the destination buffer keeping in mind that a decoded + * special character will take up 2 less bytes than its encoded form. + * In the worst-case scenario, the destination buffer will have to be + * the same size that of the source string. + */ +void example_uri_decode(char *dest, const char *src, size_t len); + +#ifdef __cplusplus +} +#endif diff --git a/tests/c_headers/protocol_examples_common/ppp_connect.c b/tests/c_headers/protocol_examples_common/ppp_connect.c new file mode 100644 index 00000000..34c0aaf9 --- /dev/null +++ b/tests/c_headers/protocol_examples_common/ppp_connect.c @@ -0,0 +1,258 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include "sdkconfig.h" +#include "protocol_examples_common.h" +#include "example_common_private.h" + +#include "esp_log.h" +#include "esp_netif.h" +#include "esp_netif_ppp.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB +#include "tinyusb.h" +#include "tusb_cdc_acm.h" + +static int s_itf; +static uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE]; + +#else // DEVICE is UART + +#include "driver/uart.h" +#define BUF_SIZE (1024) +static bool s_stop_task = false; + +#endif // CONNECT_PPP_DEVICE + + +static const char *TAG = "example_connect_ppp"; +static int s_retry_num = 0; +static EventGroupHandle_t s_event_group = NULL; +static esp_netif_t *s_netif; +static const int GOT_IPV4 = BIT0; +static const int CONNECTION_FAILED = BIT1; +#if CONFIG_EXAMPLE_CONNECT_IPV6 +static const int GOT_IPV6 = BIT2; +#define CONNECT_BITS (GOT_IPV4|GOT_IPV6|CONNECTION_FAILED) +#else +#define CONNECT_BITS (GOT_IPV4|CONNECTION_FAILED) +#endif + +static esp_err_t transmit(void *h, void *buffer, size_t len) +{ + ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, len, ESP_LOG_VERBOSE); +#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB + tinyusb_cdcacm_write_queue(s_itf, buffer, len); + tinyusb_cdcacm_write_flush(s_itf, 0); +#else // DEVICE_UART + uart_write_bytes(UART_NUM_1, buffer, len); +#endif // CONNECT_PPP_DEVICE + return ESP_OK; +} + +static esp_netif_driver_ifconfig_t driver_cfg = { + .handle = (void *)1, // singleton driver, just to != NULL + .transmit = transmit, +}; +const esp_netif_driver_ifconfig_t *ppp_driver_cfg = &driver_cfg; + +static void on_ip_event(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + + if (event_id == IP_EVENT_PPP_GOT_IP) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + if (!example_is_our_netif(EXAMPLE_NETIF_DESC_PPP, event->esp_netif)) { + return; + } + esp_netif_t *netif = event->esp_netif; + esp_netif_dns_info_t dns_info; + ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s\" address: " IPSTR, esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip)); + esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info); + ESP_LOGI(TAG, "Main DNS server : " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + xEventGroupSetBits(s_event_group, GOT_IPV4); +#if CONFIG_EXAMPLE_CONNECT_IPV6 + } else if (event_id == IP_EVENT_GOT_IP6) { + ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data; + if (!example_is_our_netif(EXAMPLE_NETIF_DESC_PPP, event->esp_netif)) { + return; + } + esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip); + ESP_LOGI(TAG, "Got IPv6 event: Interface \"%s\" address: " IPV6STR ", type: %s", esp_netif_get_desc(event->esp_netif), + IPV62STR(event->ip6_info.ip), example_ipv6_addr_types_to_str[ipv6_type]); + if (ipv6_type == EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE) { + xEventGroupSetBits(s_event_group, GOT_IPV6); + } +#endif + } else if (event_id == IP_EVENT_PPP_LOST_IP) { + ESP_LOGI(TAG, "Disconnect from PPP Server"); + s_retry_num++; + if (s_retry_num > CONFIG_EXAMPLE_PPP_CONN_MAX_RETRY) { + ESP_LOGE(TAG, "PPP Connection failed %d times, stop reconnecting.", s_retry_num); + xEventGroupSetBits(s_event_group, CONNECTION_FAILED); + } else { + ESP_LOGI(TAG, "PPP Connection failed %d times, try to reconnect.", s_retry_num); + esp_netif_action_start(s_netif, 0, 0, 0); + esp_netif_action_connected(s_netif, 0, 0, 0); + } + + } +} + +#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB +static void cdc_rx_callback(int itf, cdcacm_event_t *event) +{ + size_t rx_size = 0; + if (itf != s_itf) { + // Not our channel + return; + } + esp_err_t ret = tinyusb_cdcacm_read(itf, buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size); + if (ret == ESP_OK) { + ESP_LOG_BUFFER_HEXDUMP(TAG, buf, rx_size, ESP_LOG_VERBOSE); + // pass the received data to the network interface + esp_netif_receive(s_netif, buf, rx_size, NULL); + } else { + ESP_LOGE(TAG, "Read error"); + } +} + +static void line_state_changed(int itf, cdcacm_event_t *event) +{ + s_itf = itf; // use this channel for the netif communication + ESP_LOGI(TAG, "Line state changed on channel %d", itf); +} +#else // DEVICE is UART + +static void ppp_task(void *args) +{ + uart_config_t uart_config = {}; + uart_config.baud_rate = CONFIG_EXAMPLE_CONNECT_UART_BAUDRATE; + uart_config.data_bits = UART_DATA_8_BITS; + uart_config.parity = UART_PARITY_DISABLE; + uart_config.stop_bits = UART_STOP_BITS_1; + uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; + uart_config.source_clk = UART_SCLK_DEFAULT; + + QueueHandle_t event_queue; + ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, BUF_SIZE, 0, 16, &event_queue, 0)); + ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart_config)); + ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, CONFIG_EXAMPLE_CONNECT_UART_TX_PIN, CONFIG_EXAMPLE_CONNECT_UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + ESP_ERROR_CHECK(uart_set_rx_timeout(UART_NUM_1, 1)); + + char *buffer = (char*)malloc(BUF_SIZE); + uart_event_t event; + esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_GOT_IP, esp_netif_action_connected, s_netif); + esp_netif_action_start(s_netif, 0, 0, 0); + esp_netif_action_connected(s_netif, 0, 0, 0); + while (!s_stop_task) { + xQueueReceive(event_queue, &event, pdMS_TO_TICKS(1000)); + if (event.type == UART_DATA) { + size_t len; + uart_get_buffered_data_len(UART_NUM_1, &len); + if (len) { + len = uart_read_bytes(UART_NUM_1, buffer, BUF_SIZE, 0); + ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, len, ESP_LOG_VERBOSE); + esp_netif_receive(s_netif, buffer, len, NULL); + } + } else { + ESP_LOGW(TAG, "Received UART event: %d", event.type); + } + } + free(buffer); + vTaskDelete(NULL); +} + +#endif // CONNECT_PPP_DEVICE + +esp_err_t example_ppp_connect(void) +{ + ESP_LOGI(TAG, "Start example_connect."); + +#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB + ESP_LOGI(TAG, "USB initialization"); + const tinyusb_config_t tusb_cfg = { + .device_descriptor = NULL, + .string_descriptor = NULL, + .external_phy = false, + .configuration_descriptor = NULL, + }; + + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); + + tinyusb_config_cdcacm_t acm_cfg = { + .usb_dev = TINYUSB_USBDEV_0, + .cdc_port = TINYUSB_CDC_ACM_0, + .callback_rx = &cdc_rx_callback, + .callback_rx_wanted_char = NULL, + .callback_line_state_changed = NULL, + .callback_line_coding_changed = NULL + }; + + ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg)); + /* the second way to register a callback */ + ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback( + TINYUSB_CDC_ACM_0, + CDC_EVENT_LINE_STATE_CHANGED, + &line_state_changed)); +#endif // CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB + + s_event_group = xEventGroupCreate(); + + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event, NULL)); + + esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_PPP(); + base_netif_cfg.if_desc = EXAMPLE_NETIF_DESC_PPP; + esp_netif_config_t netif_ppp_config = { .base = &base_netif_cfg, + .driver = ppp_driver_cfg, + .stack = ESP_NETIF_NETSTACK_DEFAULT_PPP + }; + + s_netif = esp_netif_new(&netif_ppp_config); + assert(s_netif); +#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB + esp_netif_action_start(s_netif, 0, 0, 0); + esp_netif_action_connected(s_netif, 0, 0, 0); +#else // DEVICE is UART + s_stop_task = false; + if (xTaskCreate(ppp_task, "ppp connect", 4096, NULL, 5, NULL) != pdTRUE) { + ESP_LOGE(TAG, "Failed to create a ppp connection task"); + return ESP_FAIL; + } +#endif // CONNECT_PPP_DEVICE + + ESP_LOGI(TAG, "Waiting for IP address"); + EventBits_t bits = xEventGroupWaitBits(s_event_group, CONNECT_BITS, pdFALSE, pdFALSE, portMAX_DELAY); + if (bits & CONNECTION_FAILED) { + ESP_LOGE(TAG, "Connection failed!"); + return ESP_FAIL; + } + ESP_LOGI(TAG, "Connected!"); + + return ESP_OK; +} + +void example_ppp_shutdown(void) +{ + ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event)); +#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_UART + s_stop_task = true; + vTaskDelay(pdMS_TO_TICKS(1000)); // wait for the ppp task to stop +#endif + + esp_netif_action_disconnected(s_netif, 0, 0, 0); + + vEventGroupDelete(s_event_group); + esp_netif_action_stop(s_netif, 0, 0, 0); + esp_netif_destroy(s_netif); + s_netif = NULL; + s_event_group = NULL; +} + diff --git a/tests/c_headers/protocol_examples_common/protocol_examples_utils.c b/tests/c_headers/protocol_examples_common/protocol_examples_utils.c new file mode 100644 index 00000000..e6e3de5f --- /dev/null +++ b/tests/c_headers/protocol_examples_common/protocol_examples_utils.c @@ -0,0 +1,388 @@ +/* + * Utility functions for protocol examples + * + * SPDX-FileCopyrightText: 2002-2021 Igor Sysoev + * 2011-2022 Nginx, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + * + * SPDX-FileContributor: 2023 Espressif Systems (Shanghai) CO LTD + */ +/* + * Copyright (C) 2002-2021 Igor Sysoev + * Copyright (C) 2011-2022 Nginx, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "protocol_examples_utils.h" + +/* Type of Escape algorithms to be used */ +#define NGX_ESCAPE_URI (0) +#define NGX_ESCAPE_ARGS (1) +#define NGX_ESCAPE_URI_COMPONENT (2) +#define NGX_ESCAPE_HTML (3) +#define NGX_ESCAPE_REFRESH (4) +#define NGX_ESCAPE_MEMCACHED (5) +#define NGX_ESCAPE_MAIL_AUTH (6) + +/* Type of Unescape algorithms to be used */ +#define NGX_UNESCAPE_URI (1) +#define NGX_UNESCAPE_REDIRECT (2) + + +uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size, unsigned int type) +{ + unsigned int n; + uint32_t *escape; + static u_char hex[] = "0123456789ABCDEF"; + + /* + * Per RFC 3986 only the following chars are allowed in URIs unescaped: + * + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + * + * And "%" can appear as a part of escaping itself. The following + * characters are not allowed and need to be escaped: %00-%1F, %7F-%FF, + * " ", """, "<", ">", "\", "^", "`", "{", "|", "}". + */ + + /* " ", "#", "%", "?", not allowed */ + + static uint32_t uri[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0xd000002d, /* 1101 0000 0000 0000 0000 0000 0010 1101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x50000000, /* 0101 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", "#", "%", "&", "+", ";", "?", not allowed */ + + static uint32_t args[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0xd800086d, /* 1101 1000 0000 0000 0000 1000 0110 1101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x50000000, /* 0101 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* not ALPHA, DIGIT, "-", ".", "_", "~" */ + + static uint32_t uri_component[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0xfc009fff, /* 1111 1100 0000 0000 1001 1111 1111 1111 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x78000001, /* 0111 1000 0000 0000 0000 0000 0000 0001 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", "#", """, "%", "'", not allowed */ + + static uint32_t html[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x500000ad, /* 0101 0000 0000 0000 0000 0000 1010 1101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x50000000, /* 0101 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", """, "'", not allowed */ + + static uint32_t refresh[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x50000085, /* 0101 0000 0000 0000 0000 0000 1000 0101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x50000000, /* 0101 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0xd8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", "%", %00-%1F */ + + static uint32_t memcached[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x00000021, /* 0000 0000 0000 0000 0000 0000 0010 0001 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + }; + + /* mail_auth is the same as memcached */ + + static uint32_t *map[] = + { uri, args, uri_component, html, refresh, memcached, memcached }; + + + escape = map[type]; + + if (dst == NULL) { + + /* find the number of the characters to be escaped */ + + n = 0; + + while (size) { + if (escape[*src >> 5] & (1U << (*src & 0x1f))) { + n++; + } + src++; + size--; + } + + return (uintptr_t) n; + } + + while (size) { + if (escape[*src >> 5] & (1U << (*src & 0x1f))) { + *dst++ = '%'; + *dst++ = hex[*src >> 4]; + *dst++ = hex[*src & 0xf]; + src++; + + } else { + *dst++ = *src++; + } + size--; + } + + return (uintptr_t) dst; +} + + +void ngx_unescape_uri(u_char **dst, u_char **src, size_t size, unsigned int type) +{ + u_char *d, *s, ch, c, decoded; + enum { + sw_usual = 0, + sw_quoted, + sw_quoted_second + } state; + + d = *dst; + s = *src; + + state = 0; + decoded = 0; + + while (size--) { + + ch = *s++; + + switch (state) { + case sw_usual: + if (ch == '?' + && (type & (NGX_UNESCAPE_URI | NGX_UNESCAPE_REDIRECT))) { + *d++ = ch; + goto done; + } + + if (ch == '%') { + state = sw_quoted; + break; + } + + *d++ = ch; + break; + + case sw_quoted: + + if (ch >= '0' && ch <= '9') { + decoded = (u_char) (ch - '0'); + state = sw_quoted_second; + break; + } + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'f') { + decoded = (u_char) (c - 'a' + 10); + state = sw_quoted_second; + break; + } + + /* the invalid quoted character */ + + state = sw_usual; + + *d++ = ch; + + break; + + case sw_quoted_second: + + state = sw_usual; + + if (ch >= '0' && ch <= '9') { + ch = (u_char) ((decoded << 4) + (ch - '0')); + + if (type & NGX_UNESCAPE_REDIRECT) { + if (ch > '%' && ch < 0x7f) { + *d++ = ch; + break; + } + + *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); + + break; + } + + *d++ = ch; + + break; + } + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'f') { + ch = (u_char) ((decoded << 4) + (c - 'a') + 10); + + if (type & NGX_UNESCAPE_URI) { + if (ch == '?') { + *d++ = ch; + goto done; + } + + *d++ = ch; + break; + } + + if (type & NGX_UNESCAPE_REDIRECT) { + if (ch == '?') { + *d++ = ch; + goto done; + } + + if (ch > '%' && ch < 0x7f) { + *d++ = ch; + break; + } + + *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); + break; + } + + *d++ = ch; + + break; + } + + /* the invalid quoted character */ + + break; + } + } + +done: + + *dst = d; + *src = s; +} + + +uint32_t example_uri_encode(char *dest, const char *src, size_t len) +{ + if (!src || !dest) { + return 0; + } + + uintptr_t ret = ngx_escape_uri((unsigned char *)dest, (unsigned char *)src, len, NGX_ESCAPE_URI_COMPONENT); + return (uint32_t)(ret - (uintptr_t)dest); +} + + +void example_uri_decode(char *dest, const char *src, size_t len) +{ + if (!src || !dest) { + return; + } + + unsigned char *src_ptr = (unsigned char *)src; + unsigned char *dst_ptr = (unsigned char *)dest; + ngx_unescape_uri(&dst_ptr, &src_ptr, len, NGX_UNESCAPE_URI); +} diff --git a/tests/c_headers/protocol_examples_common/stdin_out.c b/tests/c_headers/protocol_examples_common/stdin_out.c new file mode 100644 index 00000000..57c61fb8 --- /dev/null +++ b/tests/c_headers/protocol_examples_common/stdin_out.c @@ -0,0 +1,32 @@ +/* Common functions for protocol examples, to configure stdin and stdout. + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. + */ + +#include "protocol_examples_common.h" +#include "esp_err.h" +#include "driver/uart_vfs.h" +#include "driver/uart.h" +#include "sdkconfig.h" + +esp_err_t example_configure_stdin_stdout(void) +{ + if (uart_is_driver_installed((uart_port_t)CONFIG_ESP_CONSOLE_UART_NUM)) { + return ESP_OK; + } + // Initialize VFS & UART so we can use std::cout/cin + setvbuf(stdin, NULL, _IONBF, 0); + /* Install UART driver for interrupt-driven reads and writes */ + ESP_ERROR_CHECK( uart_driver_install( (uart_port_t)CONFIG_ESP_CONSOLE_UART_NUM, + 256, 0, 0, NULL, 0) ); + /* Tell VFS to use UART driver */ + uart_vfs_dev_use_driver(CONFIG_ESP_CONSOLE_UART_NUM); + uart_vfs_dev_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR); + /* Move the caret to the beginning of the next line on '\n' */ + uart_vfs_dev_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF); + return ESP_OK; +} diff --git a/tests/c_headers/protocol_examples_common/thread_connect.c b/tests/c_headers/protocol_examples_common/thread_connect.c new file mode 100644 index 00000000..f4dae26e --- /dev/null +++ b/tests/c_headers/protocol_examples_common/thread_connect.c @@ -0,0 +1,130 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include "esp_err.h" +#include "esp_event.h" +#include "esp_event_base.h" +#include "esp_vfs_eventfd.h" +#include "example_common_private.h" +#include "protocol_examples_common.h" +#include "protocol_examples_thread_config.h" +#include "esp_log.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +static TaskHandle_t s_ot_task_handle = NULL; +static esp_netif_t *s_openthread_netif = NULL; +static SemaphoreHandle_t s_semph_thread_attached = NULL; +static SemaphoreHandle_t s_semph_thread_set_dns_server = NULL; +static const char *TAG = "example_connect"; + +static void thread_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, + void* event_data) +{ + if (event_base == OPENTHREAD_EVENT) { + if (event_id == OPENTHREAD_EVENT_ATTACHED) { + xSemaphoreGive(s_semph_thread_attached); + } else if (event_id == OPENTHREAD_EVENT_SET_DNS_SERVER) { + xSemaphoreGive(s_semph_thread_set_dns_server); + } + } +} + +static void ot_task_worker(void *aContext) +{ + esp_openthread_platform_config_t config = { + .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), + .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), + .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), + }; + + esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_OPENTHREAD(); + esp_netif_config.if_desc = EXAMPLE_NETIF_DESC_THREAD; + esp_netif_config_t cfg = { + .base = &esp_netif_config, + .stack = &g_esp_netif_netstack_default_openthread, + }; + s_openthread_netif = esp_netif_new(&cfg); + assert(s_openthread_netif != NULL); + + // Initialize the OpenThread stack + ESP_ERROR_CHECK(esp_openthread_init(&config)); + ESP_ERROR_CHECK(esp_netif_attach(s_openthread_netif, esp_openthread_netif_glue_init(&config))); + esp_openthread_lock_acquire(portMAX_DELAY); + (void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); + esp_openthread_cli_init(); + esp_openthread_cli_create_task(); + otOperationalDatasetTlvs dataset; + otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset); + if (error != OT_ERROR_NONE) { + ESP_ERROR_CHECK(esp_openthread_auto_start(NULL)); + } else { + ESP_ERROR_CHECK(esp_openthread_auto_start(&dataset)); + } + esp_openthread_lock_release(); + + // Run the main loop + esp_openthread_launch_mainloop(); + + // Clean up + esp_openthread_netif_glue_deinit(); + esp_netif_destroy(s_openthread_netif); + esp_vfs_eventfd_unregister(); + vTaskDelete(NULL); +} + +/* tear down connection, release resources */ +void example_thread_shutdown(void) +{ + vTaskDelete(s_ot_task_handle); + esp_openthread_netif_glue_deinit(); + esp_netif_destroy(s_openthread_netif); + esp_vfs_eventfd_unregister(); + vSemaphoreDelete(s_semph_thread_set_dns_server); + vSemaphoreDelete(s_semph_thread_attached); +} + +esp_err_t example_thread_connect(void) +{ + s_semph_thread_attached = xSemaphoreCreateBinary(); + if (s_semph_thread_attached == NULL) { + return ESP_ERR_NO_MEM; + } + s_semph_thread_set_dns_server = xSemaphoreCreateBinary(); + if (s_semph_thread_set_dns_server == NULL) { + vSemaphoreDelete(s_semph_thread_attached); + return ESP_ERR_NO_MEM; + } + // 4 eventfds might be used for Thread + // * netif + // * ot task queue + // * radio driver + // * border router + esp_vfs_eventfd_config_t eventfd_config = { + .max_fds = 4, + }; + esp_vfs_eventfd_register(&eventfd_config); + ESP_ERROR_CHECK(esp_event_handler_register(OPENTHREAD_EVENT, ESP_EVENT_ANY_ID, thread_event_handler, NULL)); + if (xTaskCreate(ot_task_worker, "ot_br_main", CONFIG_EXAMPLE_THREAD_TASK_STACK_SIZE, NULL, 5, &s_ot_task_handle) != pdPASS) { + vSemaphoreDelete(s_semph_thread_attached); + vSemaphoreDelete(s_semph_thread_set_dns_server); + ESP_LOGE(TAG, "Failed to create openthread task"); + return ESP_FAIL; + } + xSemaphoreTake(s_semph_thread_attached, portMAX_DELAY); + // Wait 1s for the Thread device to set its DNS server with the NAT64 prefix. + if (xSemaphoreTake(s_semph_thread_set_dns_server, 1000 / portTICK_PERIOD_MS) != pdPASS) { + ESP_LOGW(TAG, "DNS server is not set for the Thread device, might be unable to access the Internet"); + } + return ESP_OK; +} diff --git a/tests/c_headers/protocol_examples_common/wifi_connect.c b/tests/c_headers/protocol_examples_common/wifi_connect.c new file mode 100644 index 00000000..a2b88200 --- /dev/null +++ b/tests/c_headers/protocol_examples_common/wifi_connect.c @@ -0,0 +1,247 @@ +/* + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* Common functions for protocol examples, to establish Wi-Fi or Ethernet connection. + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. + */ + +#include +#include "protocol_examples_common.h" +#include "example_common_private.h" +#include "esp_log.h" + +#if CONFIG_EXAMPLE_CONNECT_WIFI + +static const char *TAG = "example_connect"; +static esp_netif_t *s_example_sta_netif = NULL; +static SemaphoreHandle_t s_semph_get_ip_addrs = NULL; +#if CONFIG_EXAMPLE_CONNECT_IPV6 +static SemaphoreHandle_t s_semph_get_ip6_addrs = NULL; +#endif + +static int s_retry_num = 0; + +static void example_handler_on_wifi_disconnect(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + s_retry_num++; + if (s_retry_num > CONFIG_EXAMPLE_WIFI_CONN_MAX_RETRY) { + ESP_LOGI(TAG, "WiFi Connect failed %d times, stop reconnect.", s_retry_num); + /* let example_wifi_sta_do_connect() return */ + if (s_semph_get_ip_addrs) { + xSemaphoreGive(s_semph_get_ip_addrs); + } +#if CONFIG_EXAMPLE_CONNECT_IPV6 + if (s_semph_get_ip6_addrs) { + xSemaphoreGive(s_semph_get_ip6_addrs); + } +#endif + example_wifi_sta_do_disconnect(); + return; + } + wifi_event_sta_disconnected_t *disconn = event_data; + if (disconn->reason == WIFI_REASON_ROAMING) { + ESP_LOGD(TAG, "station roaming, do nothing"); + return; + } + ESP_LOGI(TAG, "Wi-Fi disconnected %d, trying to reconnect...", disconn->reason); + esp_err_t err = esp_wifi_connect(); + if (err == ESP_ERR_WIFI_NOT_STARTED) { + return; + } + ESP_ERROR_CHECK(err); +} + +static void example_handler_on_wifi_connect(void *esp_netif, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ +#if CONFIG_EXAMPLE_CONNECT_IPV6 + esp_netif_create_ip6_linklocal(esp_netif); +#endif // CONFIG_EXAMPLE_CONNECT_IPV6 +} + +static void example_handler_on_sta_got_ip(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + s_retry_num = 0; + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + if (!example_is_our_netif(EXAMPLE_NETIF_DESC_STA, event->esp_netif)) { + return; + } + ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s\" address: " IPSTR, esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip)); + if (s_semph_get_ip_addrs) { + xSemaphoreGive(s_semph_get_ip_addrs); + } else { + ESP_LOGI(TAG, "- IPv4 address: " IPSTR ",", IP2STR(&event->ip_info.ip)); + } +} + +#if CONFIG_EXAMPLE_CONNECT_IPV6 +static void example_handler_on_sta_got_ipv6(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data; + if (!example_is_our_netif(EXAMPLE_NETIF_DESC_STA, event->esp_netif)) { + return; + } + esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip); + ESP_LOGI(TAG, "Got IPv6 event: Interface \"%s\" address: " IPV6STR ", type: %s", esp_netif_get_desc(event->esp_netif), + IPV62STR(event->ip6_info.ip), example_ipv6_addr_types_to_str[ipv6_type]); + + if (ipv6_type == EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE) { + if (s_semph_get_ip6_addrs) { + xSemaphoreGive(s_semph_get_ip6_addrs); + } else { + ESP_LOGI(TAG, "- IPv6 address: " IPV6STR ", type: %s", IPV62STR(event->ip6_info.ip), example_ipv6_addr_types_to_str[ipv6_type]); + } + } +} +#endif // CONFIG_EXAMPLE_CONNECT_IPV6 + + +void example_wifi_start(void) +{ + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA(); + // Warning: the interface desc is used in tests to capture actual connection details (IP, gw, mask) + esp_netif_config.if_desc = EXAMPLE_NETIF_DESC_STA; + esp_netif_config.route_prio = 128; + s_example_sta_netif = esp_netif_create_wifi(WIFI_IF_STA, &esp_netif_config); + esp_wifi_set_default_wifi_sta_handlers(); + + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_start()); +} + + +void example_wifi_stop(void) +{ + esp_err_t err = esp_wifi_stop(); + if (err == ESP_ERR_WIFI_NOT_INIT) { + return; + } + ESP_ERROR_CHECK(err); + ESP_ERROR_CHECK(esp_wifi_deinit()); + ESP_ERROR_CHECK(esp_wifi_clear_default_wifi_driver_and_handlers(s_example_sta_netif)); + esp_netif_destroy(s_example_sta_netif); + s_example_sta_netif = NULL; +} + + +esp_err_t example_wifi_sta_do_connect(wifi_config_t wifi_config, bool wait) +{ + if (wait) { + s_semph_get_ip_addrs = xSemaphoreCreateBinary(); + if (s_semph_get_ip_addrs == NULL) { + return ESP_ERR_NO_MEM; + } +#if CONFIG_EXAMPLE_CONNECT_IPV6 + s_semph_get_ip6_addrs = xSemaphoreCreateBinary(); + if (s_semph_get_ip6_addrs == NULL) { + vSemaphoreDelete(s_semph_get_ip_addrs); + return ESP_ERR_NO_MEM; + } +#endif + } + s_retry_num = 0; + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &example_handler_on_wifi_disconnect, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &example_handler_on_sta_got_ip, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &example_handler_on_wifi_connect, s_example_sta_netif)); +#if CONFIG_EXAMPLE_CONNECT_IPV6 + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &example_handler_on_sta_got_ipv6, NULL)); +#endif + + ESP_LOGI(TAG, "Connecting to %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + esp_err_t ret = esp_wifi_connect(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "WiFi connect failed! ret:%x", ret); + return ret; + } + if (wait) { + ESP_LOGI(TAG, "Waiting for IP(s)"); +#if CONFIG_EXAMPLE_CONNECT_IPV4 + xSemaphoreTake(s_semph_get_ip_addrs, portMAX_DELAY); + vSemaphoreDelete(s_semph_get_ip_addrs); + s_semph_get_ip_addrs = NULL; +#endif +#if CONFIG_EXAMPLE_CONNECT_IPV6 + xSemaphoreTake(s_semph_get_ip6_addrs, portMAX_DELAY); + vSemaphoreDelete(s_semph_get_ip6_addrs); + s_semph_get_ip6_addrs = NULL; +#endif + if (s_retry_num > CONFIG_EXAMPLE_WIFI_CONN_MAX_RETRY) { + return ESP_FAIL; + } + } + return ESP_OK; +} + +esp_err_t example_wifi_sta_do_disconnect(void) +{ + ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &example_handler_on_wifi_disconnect)); + ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &example_handler_on_sta_got_ip)); + ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &example_handler_on_wifi_connect)); +#if CONFIG_EXAMPLE_CONNECT_IPV6 + ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, &example_handler_on_sta_got_ipv6)); +#endif + return esp_wifi_disconnect(); +} + +void example_wifi_shutdown(void) +{ + example_wifi_sta_do_disconnect(); + example_wifi_stop(); +} + +esp_err_t example_wifi_connect(void) +{ + ESP_LOGI(TAG, "Start example_connect."); + example_wifi_start(); + wifi_config_t wifi_config = { + .sta = { +#if !CONFIG_EXAMPLE_WIFI_SSID_PWD_FROM_STDIN + .ssid = CONFIG_EXAMPLE_WIFI_SSID, + .password = CONFIG_EXAMPLE_WIFI_PASSWORD, +#endif + .scan_method = EXAMPLE_WIFI_SCAN_METHOD, + .sort_method = EXAMPLE_WIFI_CONNECT_AP_SORT_METHOD, + .threshold.rssi = CONFIG_EXAMPLE_WIFI_SCAN_RSSI_THRESHOLD, + .threshold.authmode = EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD, + }, + }; +#if CONFIG_EXAMPLE_WIFI_SSID_PWD_FROM_STDIN + example_configure_stdin_stdout(); + char buf[sizeof(wifi_config.sta.ssid)+sizeof(wifi_config.sta.password)+2] = {0}; + ESP_LOGI(TAG, "Please input ssid password:"); + fgets(buf, sizeof(buf), stdin); + int len = strlen(buf); + buf[len-1] = '\0'; /* removes '\n' */ + memset(wifi_config.sta.ssid, 0, sizeof(wifi_config.sta.ssid)); + + char *rest = NULL; + char *temp = strtok_r(buf, " ", &rest); + strncpy((char*)wifi_config.sta.ssid, temp, sizeof(wifi_config.sta.ssid)); + memset(wifi_config.sta.password, 0, sizeof(wifi_config.sta.password)); + temp = strtok_r(NULL, " ", &rest); + if (temp) { + strncpy((char*)wifi_config.sta.password, temp, sizeof(wifi_config.sta.password)); + } else { + wifi_config.sta.threshold.authmode = WIFI_AUTH_OPEN; + } +#endif + return example_wifi_sta_do_connect(wifi_config, true); +} + + +#endif /* CONFIG_EXAMPLE_CONNECT_WIFI */ diff --git a/tests/tgeneral.nim b/tests/tgeneral.nim index 4e576046..69e15a70 100644 --- a/tests/tgeneral.nim +++ b/tests/tgeneral.nim @@ -20,8 +20,6 @@ discard delayMicros(100) delay(Micros(100)) esp_restart() -# vTaskDelete*( handle: any ) - echo "ESP_ERR_FLASH_BASE: " & $esp_err_to_name(ESP_ERR_FLASH_BASE) ESP_ERROR_CHECK(ESP_OK) diff --git a/utils/config.nims b/utils/config.nims new file mode 100644 index 00000000..a1192088 --- /dev/null +++ b/utils/config.nims @@ -0,0 +1 @@ +--path:"../src" diff --git a/utils/rpc_client.nim b/utils/rpc_client.nim new file mode 100644 index 00000000..505d90f2 --- /dev/null +++ b/utils/rpc_client.nim @@ -0,0 +1,12 @@ + +const RpcServerType* = "json" + +import parseopt + +include nesper/servers/rpc/rpc_cli_utils + +var p = initOptParser() + +var args = p.rpcOptions() + +args.runRpc() diff --git a/utils/rpc_server.nim b/utils/rpc_server.nim new file mode 100644 index 00000000..eb28a9dc --- /dev/null +++ b/utils/rpc_server.nim @@ -0,0 +1,15 @@ + +import nesper/servers/rpc/rpcsocket_json + +proc setupRpc(rt: var RpcRouter) = + rt.rpc("hello") do(input: string) -> string: + # example: ./rpc_cli --ip:$IP -c:1 '{"method": "hello", "params": ["world"]}' + result = "Hello " & input + + rt.rpc("add") do(a: int, b: int) -> int: + # example: ./rpc_cli --ip:$IP -c:1 '{"method": "add", "params": [1, 2]}' + result = a + b + +var rt: RpcRouter = createRpcRouter(4096) +rt.setupRpc() +startRpcSocketServer(port=Port(5555), address="::", router=rt)