61 Commits

Author SHA1 Message Date
Kuba Szczodrzyński
a21c07fa03 [release] v0.5.0
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2022-06-02 23:06:11 +02:00
Kuba Szczodrzyński
bdffa7ef53 [core] Add UF2-based uploader 2022-06-02 23:05:35 +02:00
Kuba Szczodrzyński
22d40825bb [tools] Extract common utilities to separate modules 2022-06-02 22:22:23 +02:00
Kuba Szczodrzyński
50f26f546c [core] Use term "family" instead of "platform" 2022-06-02 22:22:19 +02:00
Kuba Szczodrzyński
9c7ea46ec3 [core] Build UF2 OTA image after linking 2022-06-02 22:21:51 +02:00
Kuba Szczodrzyński
5b4cf53d8a [tools] uf2ota: embed build date in extension tags 2022-06-02 14:14:16 +02:00
Kuba Szczodrzyński
79a701a4d4 [tools] uf2ota: fix for Python 3.7, fix Windows path compatibility 2022-06-02 14:12:03 +02:00
Kuba Szczodrzyński
81897e634c [realtek-ambz] Export both OTA images after linking 2022-06-01 21:41:04 +02:00
Kuba Szczodrzyński
3e11da4dd4 [docs] Add resources page 2022-05-31 12:06:24 +02:00
Kuba Szczodrzyński
12aa7fef04 [docs] Move sections from README to docs, add uf2families.json 2022-05-31 11:54:05 +02:00
Kuba Szczodrzyński
1ea6420bbc [core] Add Update library 2022-05-30 22:31:04 +02:00
Kuba Szczodrzyński
f3f1f36525 [core] Add uf2ota library source 2022-05-30 22:23:40 +02:00
Kuba Szczodrzyński
dee9a98cc3 [core] Update OTA API 2022-05-30 21:59:22 +02:00
Kuba Szczodrzyński
3345ce3fb9 [docs] Add missing documents to SUMMARY.md 2022-05-28 20:13:01 +02:00
Kuba Szczodrzyński
de70583838 [core] Put full UF2 Family ID in ChipFamily 2022-05-28 20:09:10 +02:00
Kuba Szczodrzyński
a43a737004 [realtek-ambz] Implement API class methods, fix CPU clock 2022-05-28 19:41:44 +02:00
Kuba Szczodrzyński
1f6899354f [core] Add LT class API methods 2022-05-28 19:39:43 +02:00
Kuba Szczodrzyński
9110a0c47e [docs] Document the UF2 OTA format 2022-05-28 14:48:07 +02:00
Kuba Szczodrzyński
b549790798 [tools] Implement UF2 binary patching, add dumping images 2022-05-28 14:47:45 +02:00
Kuba Szczodrzyński
5df430f3be [tools] Add UF2 OTA writer tool 2022-05-27 20:53:08 +02:00
Kuba Szczodrzyński
f3e8bcd74a [realtek-ambz] Add KVS partition, update boardgen 2022-05-27 15:29:46 +02:00
Kuba Szczodrzyński
4b050e11cf [core] Add dynamic FAL_PART_TABLE generation 2022-05-27 15:29:21 +02:00
Kuba Szczodrzyński
7ddbc09564 [core] Workaround LwIPmDNS compilation 2022-05-26 14:24:51 +02:00
Kuba Szczodrzyński
d3d62f80fd [realtek-ambz] Fix WiFi encryption type conversion 2022-05-26 14:24:29 +02:00
Kuba Szczodrzyński
bc7dbe6eec [api] Fix hexdump() default parameters 2022-05-25 12:27:52 +02:00
Kuba Szczodrzyński
e625f55353 [api] Add hexdump() utility 2022-05-24 17:55:12 +02:00
Kuba Szczodrzyński
aeebff9d5d [realtek-ambz] Fix WiFiEvents linking when not needed 2022-05-24 17:47:45 +02:00
Kuba Szczodrzyński
9a3c077ef1 [core] Add FlashDB KVS library 2022-05-24 17:43:30 +02:00
Kuba Szczodrzyński
91ae692058 [core] Fix gathering external library dependencies 2022-05-24 17:30:14 +02:00
Kuba Szczodrzyński
ad590d1eb2 [release] v0.4.0
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2022-05-23 14:21:19 +02:00
Kuba Szczodrzyński
8b00358901 [core] Add lwIP-based mDNS responder library 2022-05-23 14:20:41 +02:00
Kuba Szczodrzyński
bf4d8bb9be [realtek-ambz] Update to external lwIP v2.0.0 2022-05-23 12:49:18 +02:00
Kuba Szczodrzyński
157dd2f407 [realtek-ambz] Move to external lwIP v1.4.1 2022-05-23 12:49:18 +02:00
Kuba Szczodrzyński
1dc47878d4 [core] Allow using external framework parts 2022-05-18 20:57:09 +02:00
Kuba Szczodrzyński
36e4b6fd45 [core] Write manifests when installing packages 2022-05-18 20:47:40 +02:00
Kuba Szczodrzyński
36d9a6e7e4 [realtek-ambz] Support WiFi events 2022-05-17 19:27:35 +02:00
Kuba Szczodrzyński
b7cceed4d3 [core] Create POSIX utils module 2022-05-16 15:54:52 +02:00
Kuba Szczodrzyński
694c06e7f5 [core] Allow using "arduino" as framework 2022-05-16 15:53:51 +02:00
Kuba Szczodrzyński
12338747bf [realtek-ambz] Add Wire library 2022-05-14 21:47:56 +02:00
Kuba Szczodrzyński
f4f9788f68 [github] Add board readme generated with boardgen 2022-05-14 20:20:39 +02:00
Kuba Szczodrzyński
b9cdcad262 [core] Add boardgen submodule 2022-05-14 14:21:15 +02:00
Kuba Szczodrzyński
e4cd2ddec8 [core] Split boards as base files 2022-05-10 17:48:41 +02:00
Kuba Szczodrzyński
1477649a50 [core] Move LibreTuyaAPI to core directory 2022-05-09 15:41:22 +02:00
Kuba Szczodrzyński
2e80469ab3 [core] Fix and enable SSL client support 2022-05-08 18:43:10 +02:00
Kuba Szczodrzyński
ccf63a4cdb [realtek-ambz] Add platform configs to fixups 2022-05-08 18:42:19 +02:00
Kuba Szczodrzyński
b464871f73 [realtek-ambz] Remove PolarSSL completely, fix errno 2022-05-08 18:41:56 +02:00
Kuba Szczodrzyński
e44834a95e [docs] Fix mkdocs deployment, again, again 2022-05-07 20:47:34 +02:00
Kuba Szczodrzyński
4efefc5c6c [docs] Fix mkdocs deployment, again 2022-05-07 20:38:13 +02:00
Kuba Szczodrzyński
dfd852b2db [docs] Fix mkdocs deployment 2022-05-07 20:36:41 +02:00
Kuba Szczodrzyński
afa7141ceb [docs] Add Doxygen 2022-05-07 20:35:03 +02:00
Kuba Szczodrzyński
ed76b23c69 [release] v0.3.0
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2022-05-06 20:28:10 +02:00
Kuba Szczodrzyński
a469a466ff [github] Update README.md badges 2022-05-06 20:27:43 +02:00
Kuba Szczodrzyński
589d3ef4d8 [core] Refactor realtek-ambz, cleanup, reformat, add missing copyrights 2022-05-06 20:27:06 +02:00
Kuba Szczodrzyński
5aba2eb4a1 [core] Reformat filter_rtl_hard_fault.py with updated black 2022-05-06 15:47:46 +02:00
Kuba Szczodrzyński
b7df8d7b37 [github] Add lint check workflow 2022-05-06 15:42:54 +02:00
Kuba Szczodrzyński
61554e6c7e [realtek-ambz] Describe C library functions 2022-05-06 15:42:27 +02:00
Kuba Szczodrzyński
d8cc61bc0e [realtek-ambz] Remove old WiFi library 2022-05-06 12:14:48 +02:00
Kuba Szczodrzyński
4b6e3956d6 [realtek-ambz] Implement SSL client, cleanup compilation 2022-05-05 21:25:19 +02:00
Kuba Szczodrzyński
9659ff8afa [core] Add MbedTLSClient 2022-05-05 20:56:37 +02:00
Kuba Szczodrzyński
783955cc5d [core] Refactor build system, cleanup unused files 2022-05-04 21:45:30 +02:00
Kuba Szczodrzyński
048556803b [core] Move non-interface classes to libraries 2022-04-30 21:08:09 +02:00
243 changed files with 11699 additions and 14487 deletions

29
.clang-format Normal file
View File

@@ -0,0 +1,29 @@
Language: Cpp
BasedOnStyle: LLVM
AlignAfterOpenBracket: BlockIndent
AlignArrayOfStructures: Left
AlignConsecutiveAssignments: true
AlignConsecutiveMacros: AcrossComments
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortFunctionsOnASingleLine: Empty
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BreakBeforeTernaryOperators: true
ColumnLimit: 120
ContinuationIndentWidth: 4
EmptyLineBeforeAccessModifier: Always
FixNamespaceComments: true
IndentAccessModifiers: false
IndentCaseLabels: true
IndentWidth: 4
LambdaBodyIndentation: Signature
MaxEmptyLinesToKeep: 1
# PointerAlignment: Left # TODO enable this and reformat project
QualifierAlignment: Left
ReflowComments: true
SeparateDefinitionBlocks: Always
TabWidth: 4
UseTab: Always

View File

@@ -1,11 +1,12 @@
name: Deploy docs on GitHub Pages
on:
push:
branches:
- master
jobs:
build:
docs:
name: Deploy docs
runs-on: ubuntu-latest
steps:
@@ -17,5 +18,5 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CONFIG_FILE: mkdocs.yml
EXTRA_PACKAGES: build-base
EXTRA_PACKAGES: build-base doxygen
REQUIREMENTS: docs/requirements.txt

35
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Lint check
on: [push, pull_request]
jobs:
lint-clang-format:
name: Lint with clang-format
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Check code with clang-format
uses: jidicula/clang-format-action@v4.5.0
with:
clang-format-version: "14"
lint-black:
name: Lint with black
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.9"
- name: Install test dependencies
uses: BSFishy/pip-action@v1
with:
packages: |
black
isort
- name: Check code with black
run: black --check .
- name: Check code with isort
run: isort --profile black . --check-only

6
.gitignore vendored
View File

@@ -252,3 +252,9 @@ cython_debug/
# End of https://www.toptal.com/developers/gitignore/api/c,c++,visualstudiocode,python
.vscode/settings.json
# mkdocs
xml/
ltapi/
ltambz/
hashChanges.yaml

2
.gitmodules vendored Normal file
View File

@@ -0,0 +1,2 @@
[submodule "tools/boardgen"]
url = https://github.com/kuba2k2/boardgen

102
README.md
View File

@@ -2,9 +2,14 @@
<div align="center" markdown>
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/kuba2k2/libretuya/Deploy%20docs%20on%20GitHub%20Pages?label=docs&logo=markdown)](https://kuba2k2.github.io/libretuya/)
![GitHub last commit](https://img.shields.io/github/last-commit/kuba2k2/libretuya?logo=github)
[![Code style: clang-format](https://img.shields.io/badge/code%20style-clang--format-purple.svg)](.clang-format)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Discord](https://img.shields.io/discord/967863521511608370?color=%235865F2&label=Discord&logo=discord&logoColor=white)](https://discord.gg/SyGCB9Xwtf)
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/kuba2k2/platform/libretuya.svg)](https://registry.platformio.org/platforms/kuba2k2/libretuya)
![GitHub last commit](https://img.shields.io/github/last-commit/kuba2k2/libretuya)
![RTL8710BN](https://img.shields.io/badge/-rtl8710bn-blue)
@@ -13,17 +18,17 @@
PlatformIO development platform for IoT modules manufactured by Tuya Inc.
The main goal of this project is to provide a usable build environment for IoT developers. While also providing vendor SDKs as PlatformIO cores,
the project focuses on developing working Arduino-compatible cores for supported platforms. The cores are inspired by Espressif's official core for ESP32,
which should make it easier to port/run existing ESP apps on Tuya IoT (and 3-rd party) platforms.
the project focuses on developing working Arduino-compatible cores for supported families. The cores are inspired by Espressif's official core for ESP32,
which should make it easier to port/run existing ESP apps on Tuya IoT (and 3-rd party) modules.
LibreTuya also provides a common interface for all platform implementations. The interface is based on ESP32 official libraries.
LibreTuya also provides a common interface for all family implementations. The interface is based on ESP32 official libraries.
**Note:** this project is work-in-progress.
## Usage
1. [Install PlatformIO](https://platformio.org/platformio-ide)
2. `platformio platform install libretuya`
2. `platformio platform install https://github.com/kuba2k2/libretuya`
3. Create a project, build it and upload!
4. See the [docs](https://kuba2k2.github.io/libretuya/) for any questions/problems.
@@ -31,7 +36,7 @@ LibreTuya also provides a common interface for all platform implementations. The
A (mostly) complete* list of Tuya wireless module boards.
&nbsp; | Module Name | MCU | Flash | RAM | Pins** | Wi-Fi | BLE | Platform name
&nbsp; | Module Name | MCU | Flash | RAM | Pins** | Wi-Fi | BLE | Family name
------------------------------|------------------------------------------------------------------------------------------------|-------------------------|-------|----------|-------------|-------|-----|---------------
❌ | [WB1S](https://developer.tuya.com/en/docs/iot/wb1s?id=K9duevbj3ol4x) | BK7231T @ 120 MHz | 2 MiB | 256 KiB | 18 (11 I/O) | ✔️ | ✔️ | -
❌ | [WB2L](https://developer.tuya.com/en/docs/iot/wb2l-datasheet?id=K9duegc9bualu) | BK7231T @ 120 MHz | 2 MiB | 256 KiB | 7 (5 I/O) | ✔️ | ✔️ | -
@@ -87,76 +92,6 @@ A (mostly) complete* list of Tuya wireless module boards.
** I/O count includes GPIOs, ADCs, PWM outputs and UART, but doesn't count CEN/RST and power pins.
## Project structure
```
arduino/
├─ <platform name>/ Arduino Core for specific platform
│ ├─ cores/ Core files
│ ├─ libraries/ Supported built-in libraries
├─ libretuya/
│ ├─ api/ LibreTuya API for Arduino frameworks
│ ├─ compat/ Fixes for compatibility with ESP32 framework
│ ├─ libraries/ Built-in platform-independent libraries
boards/
├─ <board name>/ Board-specific code
│ ├─ variant.cpp Arduino variant initialization
│ ├─ variant.h Arduino variant pin configs
├─ <board name>.json PlatformIO board description
builder/
├─ frameworks/ Framework builders for PlatformIO
│ ├─ <platform name>-sdk.py Vanilla SDK build system
│ ├─ <platform name>-arduino.py Arduino Core build system
├─ arduino-common.py Builder to provide ArduinoCore-API and LibreTuya APIs
├─ main.py Main PlatformIO builder
docs/ Project documentation, guides, tips, etc.
platform/
├─ <platform name>/ Platform-specific configurations
│ ├─ bin/ Binary blobs (bootloaders, etc.)
│ ├─ fixups/ Code fix-ups to replace SDK parts
│ ├─ ld/ Linker scripts
│ ├─ openocd/ OpenOCD configuration files
tools/
├─ <tool name>/ Tools used during the build
platform.json PlatformIO manifest
platform.py Custom PlatformIO script
```
## Platforms
A list of platforms currently available in this project.
Platform name | Supported MCU(s) | Arduino Core | Source SDK (PIO framework)
---------------|------------------------------------------------------------------------|--------------|--------------------------------------------------------------------------
`realtek-ambz` | Realtek [AmebaZ](https://www.amebaiot.com/en/amebaz/) SoC (`RTL87xxB`) | ✔️ | `framework-realtek-amb1` ([amb1_sdk](https://github.com/ambiot/amb1_sdk))
### Realtek Ameba
The logic behind naming of Realtek chips and their series took me some time to figure out:
- RTL8xxxA - Ameba1/Ameba Series
- RTL8xxxB - AmebaZ Series
- RTL8xxxC - AmebaZ2/ZII Series
- RTL8xxxD - AmebaD Series
As such, there are numerous CPUs with the same numbers but different series, which makes them require different code and SDKs.
- [RTL8195AM](https://www.realtek.com/en/products/communications-network-ics/item/rtl8195am)
- RTL8710AF (found in amb1_arduino)
- [RTL8711AM](https://www.realtek.com/en/products/communications-network-ics/item/rtl8711am)
- [RTL8710BN](https://www.realtek.com/en/products/communications-network-ics/item/rtl8710bn)
- RTL8710BX (found in Tuya product pages)
- RTL8710B? (found in amb1_sdk)
- RTL8711B? (found in amb1_sdk)
- [RTL8710CM](https://www.realtek.com/en/products/communications-network-ics/item/rtl8710cm)
- RTL8722CSM (found in ambd_arduino)
- RTL8720DN (found in ambd_arduino)
- [RTL8721DM](https://www.realtek.com/en/products/communications-network-ics/item/rtl8721dm)
- RTL8722DM (found in ambd_arduino)
- and probably many more
Different Ameba series are not compatible with each other. Apparently, there isn't an official public SDK for AmebaZ that can support C++ properly.
## Arduino Core support status
Note: this list will probably change with each functionality update.
@@ -171,22 +106,21 @@ Flash I/O | ❓
**CORE LIBRARIES** |
SoftwareSerial | ❌
SPI | ❌
Wire |
Wire |
**OTHER LIBRARIES** |
Wi-Fi STA/AP/Mixed | ✔️
Wi-Fi Client (SSL) | ✔️ ()
Wi-Fi Client (SSL) | ✔️ (✔️)
Wi-Fi Server | ✔️
Wi-Fi Events |
Wi-Fi Events | ✔️
IPv6 | ❌
HTTP Client (SSL) | ✔️ ()
HTTP Client (SSL) | ✔️ (✔️)
HTTP Server | ✔️
NVS / Preferences |
NVS / Preferences | ✔️
SPIFFS | ❌
BLE | -
HTTP | ❌
NTP | ❌
OTA |
MDNS |
OTA |
MDNS | ✔️
MQTT | ✅
SD | ❌

55
SUMMARY.md Normal file
View File

@@ -0,0 +1,55 @@
* [Home](README.md)
* [💻 Family list](docs/families.md)
* [✔️ Implementation status](docs/implementation-status.md)
* [🔧 Configuration](docs/config.md)
* [📁 Project structure](docs/project-structure.md)
* 🔖 Code reference
* [LibreTuya API](docs/reference/lt-api.md)
* [Class reference](ltapi/class_libre_tuya.md)
* [Static functions](ltapi/_libre_tuya_a_p_i_8cpp.md)
* [Logger](ltapi/lt__logger_8h.md)
* [Chip types & UF2 families](ltapi/_chip_type_8h.md)
* [POSIX utilities](ltapi/lt__posix__api_8h.md)
* Common API
* [Flash](ltapi/class_i_flash_class.md)
* [FS](ltapi/classfs_1_1_f_s.md)
* [Preferences](ltapi/class_i_preferences.md)
* [WiFi API](ltapi/class_i_wi_fi_generic_class.md)
* [Station](ltapi/class_i_wi_fi_s_t_a_class.md)
* [Access Point](ltapi/class_i_wi_fi_a_p_class.md)
* [Scanning](ltapi/class_i_wi_fi_scan_class.md)
* [TCP Client](ltapi/class_i_wi_fi_client.md)
* [SSL Client](ltapi/class_i_wi_fi_client_secure.md)
* [TCP Server](ltapi/class_i_wi_fi_server.md)
* [LibreTuya libraries](docs/libs-built-in.md)
* [base64](ltapi/classbase64.md)
* [HTTPClient](ltapi/class_h_t_t_p_client.md)
* [mDNS](ltapi/classm_d_n_s.md)
* NetUtils
* [ssl/MbedTLSClient](ltapi/class_mbed_t_l_s_client.md)
* [IPv6Address](ltapi/classarduino_1_1_i_pv6_address.md)
* [LwIPRxBuffer](ltapi/class_lw_i_p_rx_buffer.md)
* [Update](ltapi/class_update_class.md)
* [WebServer](ltapi/class_web_server.md)
* [WiFiMulti](ltapi/class_wi_fi_multi.md)
* [Third party libraries](docs/libs-3rd-party.md)
* Full documentation
* [Classes](ltapi/classes.md)
* [Functions](ltapi/functions.md)
* [Macros](ltapi/macros.md)
* [File list](ltapi/files.md)
* [✈️ OTA format](docs/ota/README.md)
* [uf2ota.py tool](docs/ota/uf2ota.md)
* [uf2ota.h library](docs/ota/library.md)
* [uf2ota.h reference](ltapi/uf2ota_8h.md)
* Families
* [Realtek - notes](docs/platform/realtek/README.md)
* Realtek AmebaZ Series
* Boards
* [WR3](boards/wr3/README.md)
* C library
* [Built-in functions](docs/platform/realtek-ambz/stdlib.md)
* [Memory management](docs/platform/realtek-ambz/memory-management.md)
* [Debugging](docs/platform/realtek/debugging.md)
* [Exception decoder](docs/platform/realtek/exception-decoder.md)
* [🔗 Resources](docs/resources.md)

View File

@@ -0,0 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-17. */
#include "Events.h"
uint16_t EventHandler_s::lastId = 1;

View File

@@ -0,0 +1,117 @@
/*
ESP8266WiFiGeneric.h - esp8266 Wifi support.
Based on WiFi.h from Ardiono WiFi shield library.
Copyright (c) 2011-2014 Arduino. All right reserved.
Modified by Ivan Grokhotkov, December 2014
Reworked by Markus Sattler, December 2015
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <functional>
#include "WiFiEvents.h"
typedef enum {
ARDUINO_EVENT_WIFI_READY = 0, /**< ESP32 WiFi ready */
ARDUINO_EVENT_WIFI_SCAN_DONE, /**< ESP32 finish scanning AP */
ARDUINO_EVENT_WIFI_STA_START, /**< ESP32 station start */
ARDUINO_EVENT_WIFI_STA_STOP, /**< ESP32 station stop */
ARDUINO_EVENT_WIFI_STA_CONNECTED, /**< ESP32 station connected to AP */
ARDUINO_EVENT_WIFI_STA_DISCONNECTED, /**< ESP32 station disconnected from AP */
ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE, /**< the auth mode of AP connected by ESP32 station changed */
ARDUINO_EVENT_WIFI_STA_GOT_IP,
ARDUINO_EVENT_WIFI_STA_GOT_IP6,
ARDUINO_EVENT_WIFI_STA_LOST_IP,
ARDUINO_EVENT_WIFI_AP_START, /**< ESP32 soft-AP start */
ARDUINO_EVENT_WIFI_AP_STOP, /**< ESP32 soft-AP stop */
ARDUINO_EVENT_WIFI_AP_STACONNECTED, /**< a station connected to ESP32 soft-AP */
ARDUINO_EVENT_WIFI_AP_STADISCONNECTED, /**< a station disconnected from ESP32 soft-AP */
ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED,
ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED, /**< Receive probe request packet in soft-AP interface */
ARDUINO_EVENT_WIFI_AP_GOT_IP6,
ARDUINO_EVENT_WIFI_FTM_REPORT, /**< Receive report of FTM procedure */
ARDUINO_EVENT_ETH_START,
ARDUINO_EVENT_ETH_STOP,
ARDUINO_EVENT_ETH_CONNECTED,
ARDUINO_EVENT_ETH_DISCONNECTED,
ARDUINO_EVENT_ETH_GOT_IP,
ARDUINO_EVENT_ETH_GOT_IP6,
ARDUINO_EVENT_WPS_ER_SUCCESS, /**< ESP32 station wps succeeds in enrollee mode */
ARDUINO_EVENT_WPS_ER_FAILED, /**< ESP32 station wps fails in enrollee mode */
ARDUINO_EVENT_WPS_ER_TIMEOUT, /**< ESP32 station wps timeout in enrollee mode */
ARDUINO_EVENT_WPS_ER_PIN, /**< ESP32 station wps pin code in enrollee mode */
ARDUINO_EVENT_WPS_ER_PBC_OVERLAP, /**< ESP32 station wps overlap in enrollee mode */
ARDUINO_EVENT_SC_SCAN_DONE,
ARDUINO_EVENT_SC_FOUND_CHANNEL,
ARDUINO_EVENT_SC_GOT_SSID_PSWD,
ARDUINO_EVENT_SC_SEND_ACK_DONE,
ARDUINO_EVENT_PROV_INIT,
ARDUINO_EVENT_PROV_DEINIT,
ARDUINO_EVENT_PROV_START,
ARDUINO_EVENT_PROV_END,
ARDUINO_EVENT_PROV_CRED_RECV,
ARDUINO_EVENT_PROV_CRED_FAIL,
ARDUINO_EVENT_PROV_CRED_SUCCESS,
ARDUINO_EVENT_MAX
} arduino_event_id_t;
typedef union {
wifi_event_sta_scan_done_t wifi_scan_done;
wifi_event_sta_authmode_change_t wifi_sta_authmode_change;
wifi_event_sta_connected_t wifi_sta_connected;
wifi_event_sta_disconnected_t wifi_sta_disconnected;
wifi_event_sta_wps_er_pin_t wps_er_pin;
wifi_event_sta_wps_fail_reason_t wps_fail_reason;
wifi_event_ap_probe_req_rx_t wifi_ap_probereqrecved;
wifi_event_ap_staconnected_t wifi_ap_staconnected;
wifi_event_ap_stadisconnected_t wifi_ap_stadisconnected;
wifi_event_ftm_report_t wifi_ftm_report;
ip_event_ap_staipassigned_t wifi_ap_staipassigned;
ip_event_got_ip_t got_ip;
ip_event_got_ip6_t got_ip6;
// smartconfig_event_got_ssid_pswd_t sc_got_ssid_pswd;
// esp_eth_handle_t eth_connected;
// wifi_sta_config_t prov_cred_recv;
// wifi_prov_sta_fail_reason_t prov_fail_reason;
} arduino_event_info_t;
typedef struct {
arduino_event_id_t event_id;
arduino_event_info_t event_info;
} arduino_event_t;
#define EventId arduino_event_id_t
#define EventId_t arduino_event_id_t
#define EventInfo arduino_event_info_t
#define EventInfo_t arduino_event_info_t
#define Event_t arduino_event_t
typedef void (*EventCb)(EventId event);
typedef std::function<void(EventId event, EventInfo info)> EventFuncCb;
typedef void (*EventSysCb)(Event_t *event);
typedef struct EventHandler_s {
static uint16_t lastId;
uint16_t id;
EventCb cb;
EventFuncCb fcb;
EventSysCb scb;
EventId eventId;
EventHandler_s() : id(lastId++), cb(NULL), fcb(NULL), scb(NULL) {}
} EventHandler;

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */
#pragma once
#include <stdbool.h>

View File

@@ -1,91 +0,0 @@
/*
IPv6Address.cpp - Base class that provides IPv6Address
Copyright (c) 2011 Adrian McEwen. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include <api/Print.h>
#include "IPv6Address.h"
IPv6Address::IPv6Address()
{
memset(_address.bytes, 0, sizeof(_address.bytes));
}
IPv6Address::IPv6Address(const uint8_t *address)
{
memcpy(_address.bytes, address, sizeof(_address.bytes));
}
IPv6Address::IPv6Address(const uint32_t *address)
{
memcpy(_address.bytes, (const uint8_t *)address, sizeof(_address.bytes));
}
IPv6Address& IPv6Address::operator=(const uint8_t *address)
{
memcpy(_address.bytes, address, sizeof(_address.bytes));
return *this;
}
bool IPv6Address::operator==(const uint8_t* addr) const
{
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
}
size_t IPv6Address::printTo(Print& p) const
{
/* size_t n = 0;
for(int i = 0; i < 16; i+=2) {
if(i){
n += p.print(':');
}
n += p.printf("%02x", _address.bytes[i]);
n += p.printf("%02x", _address.bytes[i+1]);
}
return n; */
}
String IPv6Address::toString() const
{
char szRet[40];
sprintf(szRet,"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
_address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3],
_address.bytes[4], _address.bytes[5], _address.bytes[6], _address.bytes[7],
_address.bytes[8], _address.bytes[9], _address.bytes[10], _address.bytes[11],
_address.bytes[12], _address.bytes[13], _address.bytes[14], _address.bytes[15]);
return String(szRet);
}
bool IPv6Address::fromString(const char *address)
{
//format 0011:2233:4455:6677:8899:aabb:ccdd:eeff
if(strlen(address) != 39){
return false;
}
char * pos = (char *)address;
size_t i = 0;
for(i = 0; i < 16; i+=2) {
if(!sscanf(pos, "%2hhx", &_address.bytes[i]) || !sscanf(pos+2, "%2hhx", &_address.bytes[i+1])){
return false;
}
pos += 5;
}
return true;
}

View File

@@ -1,98 +0,0 @@
/*
IPv6Address.h - Base class that provides IPv6Address
Copyright (c) 2011 Adrian McEwen. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <stdint.h>
#include <api/String.h>
#include <api/Print.h>
// A class to make it easier to handle and pass around IP addresses
namespace arduino {
class IPv6Address: public Printable
{
private:
union {
uint8_t bytes[16]; // IPv4 address
uint32_t dword[4];
} _address;
// Access the raw byte array containing the address. Because this returns a pointer
// to the internal structure rather than a copy of the address this function should only
// be used when you know that the usage of the returned uint8_t* will be transient and not
// stored.
uint8_t* raw_address()
{
return _address.bytes;
}
public:
// Constructors
IPv6Address();
IPv6Address(const uint8_t *address);
IPv6Address(const uint32_t *address);
virtual ~IPv6Address() {}
bool fromString(const char *address);
bool fromString(const String &address) { return fromString(address.c_str()); }
operator const uint8_t*() const
{
return _address.bytes;
}
operator const uint32_t*() const
{
return _address.dword;
}
bool operator==(const IPv6Address& addr) const
{
return (_address.dword[0] == addr._address.dword[0])
&& (_address.dword[1] == addr._address.dword[1])
&& (_address.dword[2] == addr._address.dword[2])
&& (_address.dword[3] == addr._address.dword[3]);
}
bool operator==(const uint8_t* addr) const;
// Overloaded index operator to allow getting and setting individual octets of the address
uint8_t operator[](int index) const
{
return _address.bytes[index];
}
uint8_t& operator[](int index)
{
return _address.bytes[index];
}
// Overloaded copy operators to allow initialisation of IPv6Address objects from other types
IPv6Address& operator=(const uint8_t *address);
// TODO implement printTo()
virtual size_t printTo(Print& p) const;
String toString() const;
friend class UDP;
friend class Client;
friend class Server;
};
}
using arduino::IPv6Address;

View File

@@ -1,42 +0,0 @@
#pragma once
#ifndef LT_VERSION
#define LT_VERSION 1.0.0
#endif
#ifndef LT_BOARD
#define LT_BOARD unknown
#endif
#define STRINGIFY(x) #x
#define STRINGIFY_MACRO(x) STRINGIFY(x)
#define LT_VERSION_STR STRINGIFY_MACRO(LT_VERSION)
#define LT_BOARD_STR STRINGIFY_MACRO(LT_BOARD)
#include <Arduino.h>
#include "LibreTuyaConfig.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include "lt_logger.h"
#ifdef __cplusplus
} // extern "C"
#endif
#define LT_BANNER() \
LT_LOG( \
LT_LEVEL_INFO, \
"main.cpp", \
__LINE__, \
"LibreTuya v" LT_VERSION_STR " on " LT_BOARD_STR ", compiled at " __DATE__ " " __TIME__ \
)
extern char *strdup(const char *);
// ArduinCore-API doesn't define these anymore
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
#define PGM_VOID_P const void *

View File

@@ -17,7 +17,7 @@
#include <stdint.h>
#include <stdlib.h>
#include "api/String.h"
#include <api/String.h>
typedef enum {
PT_I8,

View File

@@ -0,0 +1,84 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-17. */
#include "WiFi.h"
std::vector<EventHandler> IWiFiGenericClass::handlers;
uint16_t IWiFiGenericClass::onEvent(EventCb callback, EventId eventId) {
if (!callback)
return 0;
EventHandler handler;
handler.cb = callback;
handler.eventId = eventId;
handlers.push_back(handler);
return handler.id;
}
uint16_t IWiFiGenericClass::onEvent(EventFuncCb callback, EventId eventId) {
if (!callback)
return 0;
EventHandler handler;
handler.fcb = callback;
handler.eventId = eventId;
handlers.push_back(handler);
return handler.id;
}
uint16_t IWiFiGenericClass::onEvent(EventSysCb callback, EventId eventId) {
if (!callback)
return 0;
EventHandler handler;
handler.scb = callback;
handler.eventId = eventId;
handlers.push_back(handler);
return handler.id;
}
void IWiFiGenericClass::removeEvent(EventCb callback, EventId eventId) {
if (!callback)
return;
for (uint16_t i = 0; i < handlers.size(); i++) {
EventHandler handler = handlers[i];
if (handler.cb == callback && handler.eventId == eventId) {
handlers.erase(handlers.begin() + i);
}
}
}
void IWiFiGenericClass::removeEvent(EventSysCb callback, EventId eventId) {
if (!callback)
return;
for (uint16_t i = 0; i < handlers.size(); i++) {
EventHandler handler = handlers[i];
if (handler.scb == callback && handler.eventId == eventId) {
handlers.erase(handlers.begin() + i);
}
}
}
void IWiFiGenericClass::removeEvent(uint16_t id) {
for (uint16_t i = 0; i < handlers.size(); i++) {
EventHandler handler = handlers[i];
if (handler.id == id) {
handlers.erase(handlers.begin() + i);
}
}
}
void IWiFiGenericClass::postEvent(EventId eventId, EventInfo eventInfo) {
for (auto handler : handlers) {
if (handler.eventId != ARDUINO_EVENT_MAX && handler.eventId != eventId)
continue;
if (handler.cb) {
handler.cb(eventId);
} else if (handler.fcb) {
handler.fcb(eventId, eventInfo);
} else if (handler.scb) {
Event_t event = {
.event_id = eventId,
.event_info = eventInfo,
};
handler.scb(&event);
}
}
}

View File

@@ -26,12 +26,11 @@
#include <api/IPAddress.h>
#include <api/IPv6Address.h>
#include <api/Print.h>
#include <vector>
#include "Events.h"
#include "WiFiType.h"
// TODO wifi events
// TODO WiFiMulti library
class IWiFiClass {
public:
virtual void printDiag(Print &dest) = 0;
@@ -64,6 +63,20 @@ class IWiFiGenericClass {
static IPAddress calculateBroadcast(IPAddress ip, IPAddress subnet);
static uint8_t calculateSubnetCIDR(IPAddress subnetMask);
static String macToString(uint8_t *mac);
protected:
static std::vector<EventHandler> handlers;
public:
uint16_t onEvent(EventCb callback, EventId eventId = ARDUINO_EVENT_MAX);
uint16_t onEvent(EventFuncCb callback, EventId eventId = ARDUINO_EVENT_MAX);
uint16_t onEvent(EventSysCb callback, EventId eventId = ARDUINO_EVENT_MAX);
void removeEvent(EventCb callback, EventId eventId);
void removeEvent(EventSysCb callback, EventId eventId);
void removeEvent(uint16_t id);
protected:
static void postEvent(EventId eventId, EventInfo eventInfo);
};
class IWiFiSTAClass {

View File

@@ -22,42 +22,28 @@
#include <Arduino.h>
#include "WiFi.h"
#include "WiFiClient.h"
class IWiFiClientSecure : public IWiFiClient {
class IWiFiClientSecure {
public:
int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey);
int connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey);
virtual int
connect(IPAddress ip, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey) = 0;
virtual int
connect(const char *host, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey) = 0;
virtual int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psk) = 0;
virtual int connect(const char *host, uint16_t port, const char *pskIdent, const char *psk) = 0;
int lastError(char *buf, const size_t size);
void setInsecure(); // Don't validate the chain, just accept whatever is given. VERY INSECURE!
void setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex
void setCACert(const char *rootCA);
void setCertificate(const char *client_ca);
void setPrivateKey(const char *private_key);
bool loadCACert(Stream &stream, size_t size);
bool loadCertificate(Stream &stream, size_t size);
bool loadPrivateKey(Stream &stream, size_t size);
bool verify(const char *fingerprint, const char *domain_name);
void setHandshakeTimeout(unsigned long handshake_timeout);
WiFiClientSecure &operator=(const WiFiClientSecure &other);
bool operator==(const bool value) {
return bool() == value;
}
bool operator!=(const bool value) {
return bool() != value;
}
bool operator==(const WiFiClientSecure &);
bool operator!=(const WiFiClientSecure &rhs) {
return !this->operator==(rhs);
};
using Print::write;
virtual int lastError(char *buf, const size_t size) = 0;
virtual void setInsecure() = 0; // Don't validate the chain, just accept whatever is given. VERY INSECURE!
virtual void setPreSharedKey(const char *pskIdent, const char *psk) = 0; // psk in hex
virtual void setCACert(const char *rootCA) = 0;
virtual void setCertificate(const char *clientCA) = 0;
virtual void setPrivateKey(const char *privateKey) = 0;
virtual bool loadCACert(Stream &stream, size_t size) = 0;
virtual bool loadCertificate(Stream &stream, size_t size) = 0;
virtual bool loadPrivateKey(Stream &stream, size_t size) = 0;
virtual bool verify(const char *fingerprint, const char *domainName) = 0;
virtual void setHandshakeTimeout(unsigned long handshakeTimeout) = 0;
virtual void setAlpnProtocols(const char **alpnProtocols) = 0;
virtual bool getFingerprintSHA256(uint8_t result[32]) = 0;
};

View File

@@ -0,0 +1,173 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "WiFiType.h"
/** Argument structure for WIFI_EVENT_SCAN_DONE event */
typedef struct {
uint32_t status; /**< status of scanning APs: 0 - success, 1 - failure */
uint8_t number; /**< number of scan results */
uint8_t scan_id; /**< scan sequence number, used for block scan */
} wifi_event_sta_scan_done_t;
/** Argument structure for WIFI_EVENT_STA_CONNECTED event */
typedef struct {
uint8_t ssid[32]; /**< SSID of connected AP */
uint8_t ssid_len; /**< SSID length of connected AP */
uint8_t bssid[6]; /**< BSSID of connected AP*/
uint8_t channel; /**< channel of connected AP*/
wifi_auth_mode_t authmode; /**< authentication mode used by AP*/
} wifi_event_sta_connected_t;
/** Argument structure for WIFI_EVENT_STA_DISCONNECTED event */
typedef struct {
uint8_t ssid[32]; /**< SSID of disconnected AP */
uint8_t ssid_len; /**< SSID length of disconnected AP */
uint8_t bssid[6]; /**< BSSID of disconnected AP */
uint8_t reason; /**< reason of disconnection */
} wifi_event_sta_disconnected_t;
/** Argument structure for WIFI_EVENT_STA_AUTHMODE_CHANGE event */
typedef struct {
wifi_auth_mode_t old_mode; /**< the old auth mode of AP */
wifi_auth_mode_t new_mode; /**< the new auth mode of AP */
} wifi_event_sta_authmode_change_t;
/** Argument structure for WIFI_EVENT_STA_WPS_ER_PIN event */
typedef struct {
uint8_t pin_code[8]; /**< PIN code of station in enrollee mode */
} wifi_event_sta_wps_er_pin_t;
/** Argument structure for WIFI_EVENT_STA_WPS_ER_FAILED event */
typedef enum {
WPS_FAIL_REASON_NORMAL = 0, /**< ESP32 WPS normal fail reason */
WPS_FAIL_REASON_RECV_M2D, /**< ESP32 WPS receive M2D frame */
WPS_FAIL_REASON_MAX
} wifi_event_sta_wps_fail_reason_t;
#define MAX_SSID_LEN 32
#define MAX_PASSPHRASE_LEN 64
#define MAX_WPS_AP_CRED 3
/** Argument structure for WIFI_EVENT_STA_WPS_ER_SUCCESS event */
typedef struct {
uint8_t ap_cred_cnt; /**< Number of AP credentials received */
struct {
uint8_t ssid[MAX_SSID_LEN]; /**< SSID of AP */
uint8_t passphrase[MAX_PASSPHRASE_LEN]; /**< Passphrase for the AP */
} ap_cred[MAX_WPS_AP_CRED]; /**< All AP credentials received from WPS handshake */
} wifi_event_sta_wps_er_success_t;
/** Argument structure for WIFI_EVENT_AP_STACONNECTED event */
typedef struct {
uint8_t mac[6]; /**< MAC address of the station connected to ESP32 soft-AP */
uint8_t aid; /**< the aid that ESP32 soft-AP gives to the station connected to */
bool is_mesh_child; /**< flag to identify mesh child */
} wifi_event_ap_staconnected_t;
/** Argument structure for WIFI_EVENT_AP_STADISCONNECTED event */
typedef struct {
uint8_t mac[6]; /**< MAC address of the station disconnects to ESP32 soft-AP */
uint8_t aid; /**< the aid that ESP32 soft-AP gave to the station disconnects to */
bool is_mesh_child; /**< flag to identify mesh child */
} wifi_event_ap_stadisconnected_t;
/** Argument structure for WIFI_EVENT_AP_PROBEREQRECVED event */
typedef struct {
int rssi; /**< Received probe request signal strength */
uint8_t mac[6]; /**< MAC address of the station which send probe request */
} wifi_event_ap_probe_req_rx_t;
/**
* @brief FTM operation status types
*
*/
typedef enum {
FTM_STATUS_SUCCESS = 0, /**< FTM exchange is successful */
FTM_STATUS_UNSUPPORTED, /**< Peer does not support FTM */
FTM_STATUS_CONF_REJECTED, /**< Peer rejected FTM configuration in FTM Request */
FTM_STATUS_NO_RESPONSE, /**< Peer did not respond to FTM Requests */
FTM_STATUS_FAIL, /**< Unknown error during FTM exchange */
} wifi_ftm_status_t;
/** Argument structure for */
typedef struct {
uint8_t dlog_token; /**< Dialog Token of the FTM frame */
int8_t rssi; /**< RSSI of the FTM frame received */
uint32_t rtt; /**< Round Trip Time in pSec with a peer */
uint64_t t1; /**< Time of departure of FTM frame from FTM Responder in pSec */
uint64_t t2; /**< Time of arrival of FTM frame at FTM Initiator in pSec */
uint64_t t3; /**< Time of departure of ACK from FTM Initiator in pSec */
uint64_t t4; /**< Time of arrival of ACK at FTM Responder in pSec */
} wifi_ftm_report_entry_t;
/** Argument structure for WIFI_EVENT_FTM_REPORT event */
typedef struct {
uint8_t peer_mac[6]; /**< MAC address of the FTM Peer */
wifi_ftm_status_t status; /**< Status of the FTM operation */
uint32_t rtt_raw; /**< Raw average Round-Trip-Time with peer in Nano-Seconds */
uint32_t rtt_est; /**< Estimated Round-Trip-Time with peer in Nano-Seconds */
uint32_t dist_est; /**< Estimated one-way distance in Centi-Meters */
wifi_ftm_report_entry_t
*ftm_report_data; /**< Pointer to FTM Report with multiple entries, should be freed after use */
uint8_t ftm_report_num_entries; /**< Number of entries in the FTM Report data */
} wifi_event_ftm_report_t;
#define WIFI_STATIS_BUFFER (1 << 0)
#define WIFI_STATIS_RXTX (1 << 1)
#define WIFI_STATIS_HW (1 << 2)
#define WIFI_STATIS_DIAG (1 << 3)
#define WIFI_STATIS_PS (1 << 4)
#define WIFI_STATIS_ALL (-1)
/** Argument structure for WIFI_EVENT_ACTION_TX_STATUS event */
typedef struct {
int ifx; /**< WiFi interface to send request to */
uint32_t context; /**< Context to identify the request */
uint8_t da[6]; /**< Destination MAC address */
uint8_t status; /**< Status of the operation */
} wifi_event_action_tx_status_t;
/** Argument structure for WIFI_EVENT_ROC_DONE event */
typedef struct {
uint32_t context; /**< Context to identify the request */
} wifi_event_roc_done_t;
/** Event structure for IP_EVENT_STA_GOT_IP, IP_EVENT_ETH_GOT_IP events */
typedef struct {
esp_ip4_addr_t ip; /**< Interface IPV4 address */
esp_ip4_addr_t netmask; /**< Interface IPV4 netmask */
esp_ip4_addr_t gw; /**< Interface IPV4 gateway address */
} esp_netif_ip_info_t;
/** @brief IPV6 IP address information
*/
typedef struct {
esp_ip6_addr_t ip; /**< Interface IPV6 address */
} esp_netif_ip6_info_t;
typedef struct {
int if_index; /*!< Interface index for which the event is received (left for legacy compilation) */
void *esp_netif; /*!< Pointer to corresponding esp-netif object */
esp_netif_ip_info_t ip_info; /*!< IP address, netmask, gatway IP address */
bool ip_changed; /*!< Whether the assigned IP has changed or not */
} ip_event_got_ip_t;
/** Event structure for IP_EVENT_GOT_IP6 event */
typedef struct {
int if_index; /*!< Interface index for which the event is received (left for legacy compilation) */
void *esp_netif; /*!< Pointer to corresponding esp-netif object */
esp_netif_ip6_info_t ip6_info; /*!< IPv6 address of the interface */
int ip_index; /*!< IPv6 address index */
} ip_event_got_ip6_t;
/** Event structure for IP_EVENT_AP_STAIPASSIGNED event */
typedef struct {
esp_ip4_addr_t ip; /*!< IP address which was assigned to the station */
} ip_event_ap_staipassigned_t;

View File

@@ -34,12 +34,28 @@
#define WIFI_AP WIFI_MODE_AP
#define WIFI_AP_STA WIFI_MODE_APSTA
#define WiFiEvent_t arduino_event_id_t
#define WiFiEventInfo_t arduino_event_info_t
#define WiFiEventId_t uint16_t
struct esp_ip6_addr {
uint32_t addr[4];
uint8_t zone;
};
struct esp_ip4_addr {
uint32_t addr;
};
typedef struct esp_ip4_addr esp_ip4_addr_t;
typedef struct esp_ip6_addr esp_ip6_addr_t;
typedef enum {
WIFI_MODE_NULL = 0,
WIFI_MODE_STA,
WIFI_MODE_AP,
WIFI_MODE_APSTA,
WIFI_MODE_MAX,
WIFI_MODE_NULL = 0, /**< null mode */
WIFI_MODE_STA, /**< WiFi station mode */
WIFI_MODE_AP, /**< WiFi soft-AP mode */
WIFI_MODE_APSTA, /**< WiFi station + soft-AP mode */
WIFI_MODE_MAX
} wifi_mode_t;
typedef enum {
@@ -54,13 +70,54 @@ typedef enum {
} wl_status_t;
typedef enum {
WIFI_AUTH_INVALID = 255,
WIFI_AUTH_AUTO = 200,
WIFI_AUTH_OPEN = 0,
WIFI_AUTH_WEP = 1,
WIFI_AUTH_WPA = 5,
WIFI_AUTH_WPA2 = 6,
WIFI_AUTH_WPA_PSK = 2,
WIFI_AUTH_WPA2_PSK = 3,
WIFI_AUTH_WPA_WPA2_PSK = 4,
WIFI_AUTH_OPEN = 0, /**< authenticate mode : open */
WIFI_AUTH_WEP, /**< authenticate mode : WEP */
WIFI_AUTH_WPA_PSK, /**< authenticate mode : WPA_PSK */
WIFI_AUTH_WPA2_PSK, /**< authenticate mode : WPA2_PSK */
WIFI_AUTH_WPA_WPA2_PSK, /**< authenticate mode : WPA_WPA2_PSK */
WIFI_AUTH_WPA2_ENTERPRISE, /**< authenticate mode : WPA2_ENTERPRISE */
WIFI_AUTH_WPA3_PSK, /**< authenticate mode : WPA3_PSK */
WIFI_AUTH_WPA2_WPA3_PSK, /**< authenticate mode : WPA2_WPA3_PSK */
WIFI_AUTH_WAPI_PSK, /**< authenticate mode : WAPI_PSK */
WIFI_AUTH_WPA,
WIFI_AUTH_WPA2,
WIFI_AUTH_AUTO = 200,
WIFI_AUTH_INVALID = 255,
WIFI_AUTH_MAX
} wifi_auth_mode_t;
typedef enum {
WIFI_REASON_UNSPECIFIED = 1,
WIFI_REASON_AUTH_EXPIRE = 2,
WIFI_REASON_AUTH_LEAVE = 3,
WIFI_REASON_ASSOC_EXPIRE = 4,
WIFI_REASON_ASSOC_TOOMANY = 5,
WIFI_REASON_NOT_AUTHED = 6,
WIFI_REASON_NOT_ASSOCED = 7,
WIFI_REASON_ASSOC_LEAVE = 8,
WIFI_REASON_ASSOC_NOT_AUTHED = 9,
WIFI_REASON_DISASSOC_PWRCAP_BAD = 10,
WIFI_REASON_DISASSOC_SUPCHAN_BAD = 11,
WIFI_REASON_BSS_TRANSITION_DISASSOC = 12,
WIFI_REASON_IE_INVALID = 13,
WIFI_REASON_MIC_FAILURE = 14,
WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT = 15,
WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT = 16,
WIFI_REASON_IE_IN_4WAY_DIFFERS = 17,
WIFI_REASON_GROUP_CIPHER_INVALID = 18,
WIFI_REASON_PAIRWISE_CIPHER_INVALID = 19,
WIFI_REASON_AKMP_INVALID = 20,
WIFI_REASON_UNSUPP_RSN_IE_VERSION = 21,
WIFI_REASON_INVALID_RSN_IE_CAP = 22,
WIFI_REASON_802_1X_AUTH_FAILED = 23,
WIFI_REASON_CIPHER_SUITE_REJECTED = 24,
WIFI_REASON_INVALID_PMKID = 53,
WIFI_REASON_BEACON_TIMEOUT = 200,
WIFI_REASON_NO_AP_FOUND = 201,
WIFI_REASON_AUTH_FAIL = 202,
WIFI_REASON_ASSOC_FAIL = 203,
WIFI_REASON_HANDSHAKE_TIMEOUT = 204,
WIFI_REASON_CONNECTION_FAIL = 205,
WIFI_REASON_AP_TSF_RESET = 206,
WIFI_REASON_ROAMING = 207,
} wifi_err_reason_t;

View File

@@ -0,0 +1,66 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-09. */
#include <api/Stream.h>
class ITwoWire : public Stream {
protected:
int8_t _sda = -1;
int8_t _scl = -1;
uint32_t _freq = 0;
void (*onRequestCallback)(void);
void (*onReceiveCallback)(int);
public:
bool begin() {
return begin(_sda, _scl, _freq);
}
bool begin(uint8_t address) {
return begin(address, _sda, _scl, _freq);
}
virtual bool setPins(int8_t sda, int8_t scl) = 0;
virtual bool begin(int8_t sda, int8_t scl, uint32_t frequency = 0) = 0;
virtual bool begin(uint8_t address, int8_t sda, int8_t scl, uint32_t frequency = 0) = 0;
virtual bool end() = 0;
virtual bool setClock(uint32_t freq) = 0;
virtual void beginTransmission(uint8_t address) = 0;
virtual uint8_t endTransmission(bool stopBit) = 0;
virtual size_t requestFrom(uint8_t address, size_t len, bool stopBit) = 0;
virtual size_t write(const uint8_t *data, size_t len) = 0;
virtual int available() = 0;
virtual int read() = 0;
virtual int peek() = 0;
virtual void flush() = 0;
uint32_t getClock() {
return _freq;
}
uint8_t endTransmission() {
return endTransmission(true);
}
size_t requestFrom(uint8_t address, size_t len) {
return requestFrom(address, len, true);
}
virtual size_t write(uint8_t data) {
return write(&data, 1);
}
void onReceive(void (*cb)(int)) {
onReceiveCallback = cb;
}
void onRequest(void (*cb)(void)) {
onRequestCallback = cb;
}
};

View File

@@ -1,114 +0,0 @@
#pragma once
#include "LibreTuyaConfig.h"
#include <stdint.h>
#if LT_LOGGER_FILE
#define LT_LOG(level, file, line, ...) lt_log(level, file, line, __VA_ARGS__)
void lt_log(const uint8_t level, const char *filename, const unsigned short line, const char *format, ...);
#else
#define LT_LOG(level, file, line, ...) lt_log(level, __VA_ARGS__)
void lt_log(const uint8_t level, const char *format, ...);
#endif
#if LT_LEVEL_TRACE >= LT_LOGLEVEL
#define LT_T(...) LT_LOG(LT_LEVEL_TRACE, __FILE__, __LINE__, __VA_ARGS__)
#define LT_V(...) LT_LOG(LT_LEVEL_TRACE, __FILE__, __LINE__, __VA_ARGS__)
#else
#define LT_T(...)
#define LT_V(...)
#endif
#if LT_LEVEL_DEBUG >= LT_LOGLEVEL
#define LT_D(...) LT_LOG(LT_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#else
#define LT_D(...)
#endif
#if LT_LEVEL_INFO >= LT_LOGLEVEL
#define LT_I(...) LT_LOG(LT_LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__)
#else
#define LT_I(...)
#endif
#if LT_LEVEL_WARN >= LT_LOGLEVEL
#define LT_W(...) LT_LOG(LT_LEVEL_WARN, __FILE__, __LINE__, __VA_ARGS__)
#else
#define LT_W(...)
#endif
#if LT_LEVEL_ERROR >= LT_LOGLEVEL
#define LT_E(...) LT_LOG(LT_LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__)
#else
#define LT_E(...)
#endif
#if LT_LEVEL_FATAL >= LT_LOGLEVEL
#define LT_F(...) LT_LOG(LT_LEVEL_FATAL, __FILE__, __LINE__, __VA_ARGS__)
#else
#define LT_F(...)
#endif
// ESP32 compat
#define log_printf(...) LT_I(__VA_ARGS__)
#define log_v(...) LT_V(__VA_ARGS__)
#define log_d(...) LT_D(__VA_ARGS__)
#define log_i(...) LT_I(__VA_ARGS__)
#define log_w(...) LT_W(__VA_ARGS__)
#define log_e(...) LT_E(__VA_ARGS__)
#define log_n(...) LT_E(__VA_ARGS__)
#define isr_log_v(...) LT_V(__VA_ARGS__)
#define isr_log_d(...) LT_D(__VA_ARGS__)
#define isr_log_i(...) LT_I(__VA_ARGS__)
#define isr_log_w(...) LT_W(__VA_ARGS__)
#define isr_log_e(...) LT_E(__VA_ARGS__)
#define isr_log_n(...) LT_E(__VA_ARGS__)
#define ESP_LOGV(...) LT_V(__VA_ARGS__)
#define ESP_LOGD(...) LT_D(__VA_ARGS__)
#define ESP_LOGI(...) LT_I(__VA_ARGS__)
#define ESP_LOGW(...) LT_W(__VA_ARGS__)
#define ESP_LOGE(...) LT_E(__VA_ARGS__)
#define ESP_EARLY_LOGV(...) LT_V(__VA_ARGS__)
#define ESP_EARLY_LOGD(...) LT_D(__VA_ARGS__)
#define ESP_EARLY_LOGI(...) LT_I(__VA_ARGS__)
#define ESP_EARLY_LOGW(...) LT_W(__VA_ARGS__)
#define ESP_EARLY_LOGE(...) LT_E(__VA_ARGS__)
#define LT_T_MOD(module, ...) \
do { \
if (module) { \
LT_T(__VA_ARGS__); \
} \
} while (0)
#define LT_D_MOD(module, ...) \
do { \
if (module) { \
LT_D(__VA_ARGS__); \
} \
} while (0)
// WiFi.cpp
#define LT_T_WG(...) LT_T_MOD(LT_DEBUG_WIFI, __VA_ARGS__)
#define LT_V_WG(...) LT_T_MOD(LT_DEBUG_WIFI, __VA_ARGS__)
#define LT_D_WG(...) LT_D_MOD(LT_DEBUG_WIFI, __VA_ARGS__)
// WiFiClient.cpp
#define LT_T_WC(...) LT_T_MOD(LT_DEBUG_WIFI_CLIENT, __VA_ARGS__)
#define LT_V_WC(...) LT_T_MOD(LT_DEBUG_WIFI_CLIENT, __VA_ARGS__)
#define LT_D_WC(...) LT_D_MOD(LT_DEBUG_WIFI_CLIENT, __VA_ARGS__)
// WiFiServer.cpp
#define LT_T_WS(...) LT_T_MOD(LT_DEBUG_WIFI_SERVER, __VA_ARGS__)
#define LT_V_WS(...) LT_T_MOD(LT_DEBUG_WIFI_SERVER, __VA_ARGS__)
#define LT_D_WS(...) LT_D_MOD(LT_DEBUG_WIFI_SERVER, __VA_ARGS__)
// WiFiSTA.cpp
#define LT_T_WSTA(...) LT_T_MOD(LT_DEBUG_WIFI_STA, __VA_ARGS__)
#define LT_V_WSTA(...) LT_T_MOD(LT_DEBUG_WIFI_STA, __VA_ARGS__)
#define LT_D_WSTA(...) LT_D_MOD(LT_DEBUG_WIFI_STA, __VA_ARGS__)
// WiFiAP.cpp
#define LT_T_WAP(...) LT_T_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)
#define LT_V_WAP(...) LT_T_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)
#define LT_D_WAP(...) LT_D_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)

View File

@@ -0,0 +1,55 @@
/*
Copyright (c) 2014 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
void randomSeed(uint32_t dwSeed) {
if (dwSeed != 0) {
srand(dwSeed);
}
}
long random(long howbig) {
if (howbig == 0) {
return 0;
}
return rand() % howbig;
}
long random(long howsmall, long howbig) {
if (howsmall >= howbig) {
return howsmall;
}
long diff = howbig - howsmall;
return random(diff) + howsmall;
}
extern long map(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
extern uint16_t makeWord(uint16_t w) {
return w;
}
extern uint16_t makeWord(uint8_t h, uint8_t l) {
return (h << 8) | l;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2012 Arduino. All right reserved.
Copyright (c) 2014 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -16,19 +16,21 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RESET_H
#define RESET_H
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
extern "C" void __cxa_pure_virtual(void) __attribute__((__noreturn__));
extern "C" void __cxa_deleted_virtual(void) __attribute__((__noreturn__));
void initiateReset(int ms);
void tickReset();
void cancelReset();
#ifdef __cplusplus
void __cxa_pure_virtual(void) {
// We might want to write some diagnostics to uart in this case
// std::terminate();
while (1)
;
}
#endif
#endif
void __cxa_deleted_virtual(void) {
// We might want to write some diagnostics to uart in this case
// std::terminate();
while (1)
;
}

View File

@@ -18,16 +18,11 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef ARDUINO_AMEBA
#include <Arduino.h>
#else
#include <stdio.h>
#endif
char *dtostrf (double val, signed char width, unsigned char prec, char *sout) {
char fmt[20];
sprintf(fmt, "%%%d.%df", width, prec);
sprintf(sout, fmt, val);
return sout;
char *dtostrf(double val, signed char width, unsigned char prec, char *sout) {
char fmt[20];
sprintf(fmt, "%%%d.%df", width, prec);
sprintf(sout, fmt, val);
return sout;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2012 Arduino. All right reserved.
Copyright (c) 2015 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -29,12 +29,7 @@ static void __empty() {
// Empty
}
#include "cmsis_os.h"
void yield(void) {
vTaskDelay(1);
taskYIELD();
}
void yield(void) __attribute__((weak, alias("__empty")));
/**
* SysTick hook
@@ -46,7 +41,8 @@ static int __false() {
// Return false
return 0;
}
int sysTickHook(void) __attribute__ ((weak, alias("__false")));
int sysTickHook(void) __attribute__((weak, alias("__false")));
/**
* SVC hook
@@ -60,5 +56,6 @@ static void __halt() {
while (1)
;
}
void svcHook(void) __attribute__ ((weak, alias("__halt")));
void pendSVHook(void) __attribute__ ((weak, alias("__halt")));
void svcHook(void) __attribute__((weak, alias("__halt")));
void pendSVHook(void) __attribute__((weak, alias("__halt")));

View File

@@ -0,0 +1,111 @@
/*
Copyright (c) 2014 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
char *ltoa(long value, char *string, int radix) {
char tmp[33];
char *tp = tmp;
long i;
unsigned long v;
int sign;
char *sp;
if (string == NULL) {
return 0;
}
if (radix > 36 || radix <= 1) {
return 0;
}
sign = (radix == 10 && value < 0);
if (sign) {
v = -value;
} else {
v = (unsigned long)value;
}
while (v || tp == tmp) {
i = v % radix;
v = v / radix;
if (i < 10)
*tp++ = i + '0';
else
*tp++ = i + 'a' - 10;
}
sp = string;
if (sign)
*sp++ = '-';
while (tp > tmp)
*sp++ = *--tp;
*sp = 0;
return string;
}
char *ultoa(unsigned long value, char *string, int radix) {
char tmp[33];
char *tp = tmp;
long i;
unsigned long v = value;
char *sp;
if (string == NULL) {
return 0;
}
if (radix > 36 || radix <= 1) {
return 0;
}
while (v || tp == tmp) {
i = v % radix;
v = v / radix;
if (i < 10)
*tp++ = i + '0';
else
*tp++ = i + 'a' - 10;
}
sp = string;
while (tp > tmp)
*sp++ = *--tp;
*sp = 0;
return string;
}
char *itoa(int value, char *string, int radix) {
return ltoa(value, string, radix);
}
char *utoa(unsigned int value, char *string, int radix) {
return ultoa(value, string, radix);
}
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,54 @@
/*
Copyright (c) 2014 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include <stdint.h>
uint8_t shiftIn(pin_size_t ulDataPin, pin_size_t ulClockPin, BitOrder ulBitOrder) {
uint8_t value = 0;
uint8_t i;
for (i = 0; i < 8; ++i) {
digitalWrite(ulClockPin, HIGH);
if (ulBitOrder == LSBFIRST) {
value |= digitalRead(ulDataPin) << i;
} else {
value |= digitalRead(ulDataPin) << (7 - i);
}
digitalWrite(ulClockPin, LOW);
}
return value;
}
void shiftOut(pin_size_t ulDataPin, pin_size_t ulClockPin, BitOrder ulBitOrder, uint8_t ulVal) {
uint8_t i;
for (i = 0; i < 8; i++) {
if (ulBitOrder == LSBFIRST) {
digitalWrite(ulDataPin, !!(ulVal & (1 << i)));
} else {
digitalWrite(ulDataPin, !!(ulVal & (1 << (7 - i))));
}
digitalWrite(ulClockPin, HIGH);
digitalWrite(ulClockPin, LOW);
}
}

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
#pragma once
#include <api/FS.h>

View File

@@ -1 +1,3 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
// nop

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
#pragma once
// ESP32 WiFi examples use WiFiAP.h include

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
#pragma once
#include <api/deprecated-avr-comp/avr/pgmspace.h>

View File

@@ -1 +1,3 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
// nop

View File

@@ -0,0 +1,23 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */
enum ChipFamily {
// used in UF2 Family ID
RTL8710A = 0x9FFFD543, // Realtek Ameba1
RTL8710B = 0x22E0D6FC, // Realtek AmebaZ (realtek-ambz)
RTL8720C = 0xE08F7564, // Realtek AmebaZ2
RTL8720D = 0x3379CFE2, // Realtek AmebaD
BK7231T = 0x675A40B0, // Beken 7231T
BK7231N = 0x7B3EF230, // Beken 7231N
BL602 = 0xDE1270B7, // Boufallo 602
XR809 = 0x51E903A8, // Xradiotech 809
};
enum ChipType {
// IDs copied from rtl8710b_efuse.h
RTL8710BL = ((RTL8710B >> 24) << 8) | 0xE0, // ???
RTL8710BN = ((RTL8710B >> 24) << 8) | 0xFF, // CHIPID_8710BN / QFN32
RTL8710BU = ((RTL8710B >> 24) << 8) | 0xFE, // CHIPID_8710BU / QFN48
RTL8710BX = ((RTL8710B >> 24) << 8) | 0xFB, // CHIPID_8710BN_L0 / QFN32
RTL8711BN = ((RTL8710B >> 24) << 8) | 0xFD, // CHIPID_8711BN / QFN48
RTL8711BU = ((RTL8710B >> 24) << 8) | 0xFC, // CHIPID_8711BG / QFN68
};

View File

@@ -0,0 +1,162 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#include "LibreTuyaAPI.h"
String ipToString(const IPAddress &ip) {
char szRet[16];
sprintf(szRet, "%hhu.%hhu.%hhu.%hhu", ip[0], ip[1], ip[2], ip[3]);
return String(szRet);
}
/**
* @brief Generate random bytes using rand().
*
* @param buf destination pointer
* @param len how many bytes to generate
*/
void lt_rand_bytes(uint8_t *buf, size_t len) {
int *data = (int *)buf;
size_t i;
for (i = 0; len >= sizeof(int); len -= sizeof(int)) {
data[i++] = rand();
}
if (len) {
int rem = rand();
unsigned char *pRem = (unsigned char *)&rem;
memcpy(buf + i * sizeof(int), pRem, len);
}
}
/**
* @brief Print data pointed to by buf in hexdump-like format (hex+ASCII).
*
* @param buf source pointer
* @param len how many bytes to print
* @param offset increment printed offset by this value
* @param width how many bytes on a line
*/
void hexdump(uint8_t *buf, size_t len, uint32_t offset, uint8_t width) {
uint16_t pos = 0;
while (pos < len) {
// print hex offset
printf("%06x ", offset + pos);
// calculate current line width
uint8_t lineWidth = min(width, len - pos);
// print hexadecimal representation
for (uint8_t i = 0; i < lineWidth; i++) {
if (i % 8 == 0) {
printf(" ");
}
printf("%02x ", buf[pos + i]);
}
// print ascii representation
printf(" |");
for (uint8_t i = 0; i < lineWidth; i++) {
char c = buf[pos + i];
printf("%c", isprint(c) ? c : '.');
}
printf("|\n");
pos += lineWidth;
}
}
/**
* @brief Get LibreTuya version string.
*/
const char *LibreTuya::getVersion() {
return LT_VERSION_STR;
}
/**
* @brief Get board name.
*/
const char *LibreTuya::getBoard() {
return LT_BOARD_STR;
}
/**
* @brief Get CPU family ID.
*/
ChipFamily LibreTuya::getChipFamily() {
return FAMILY;
}
/**
* @brief Get CPU family name as string.
*/
const char *LibreTuya::getChipFamilyName() {
return STRINGIFY_MACRO(FAMILY);
}
static char *deviceName = NULL;
/**
* @brief Get device friendly name in format "LT-<board>-<chip id>".
* Can be used as hostname.
*/
const char *LibreTuya::getDeviceName() {
if (deviceName)
return deviceName;
uint32_t chipId = getChipId();
uint8_t *id = (uint8_t *)&chipId;
const char *board = getBoard();
uint8_t boardLen = strlen(board);
deviceName = (char *)malloc(3 + boardLen + 1 + 6 + 1);
sprintf(deviceName, "LT-%s-%02x%02x%02x", board, id[0], id[1], id[2]);
return deviceName;
}
static uint8_t otaRunningIndex = 0;
/**
* @brief Get the currently running firmware OTA index.
*/
uint8_t LibreTuya::otaGetRunning() {
if (otaRunningIndex)
return otaRunningIndex;
// otaRunningIndex will be correct even after switchOta()
return otaRunningIndex = otaGetStoredIndex();
}
/**
* @brief Get the OTA index for updated firmware.
*
* Note: returns 1 for chips without dual-OTA.
*/
uint8_t LibreTuya::otaGetTarget() {
if (!otaSupportsDual())
return 1;
return otaGetRunning() ^ 0b11;
}
/**
* @brief Perform OTA rollback.
*
* @return false if no second image to run, writing failed or dual-OTA not supported
*/
bool LibreTuya::otaRollback() {
if (!otaCanRollback())
return false;
if (otaGetRunning() != otaGetStoredIndex())
// force switching back to current image
return otaSwitch(true);
return true;
}
/**
* @brief Check if OTA rollback is supported and available (there is another image to run).
* @return false if no second image to run or dual-OTA not supported
*/
bool LibreTuya::otaCanRollback() {
if (!otaSupportsDual())
return false;
if (otaGetRunning() == otaGetStoredIndex())
return true;
if (otaGetRunning() == 1 && otaHasImage1())
return true;
if (otaGetRunning() == 2 && otaHasImage2())
return true;
return false;
}

View File

@@ -0,0 +1,181 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-28. */
#pragma once
// LibreTuya version macros
#ifndef LT_VERSION
#define LT_VERSION 1.0.0
#endif
#ifndef LT_BOARD
#define LT_BOARD unknown
#endif
#define STRINGIFY(x) #x
#define STRINGIFY_MACRO(x) STRINGIFY(x)
#define LT_VERSION_STR STRINGIFY_MACRO(LT_VERSION)
#define LT_BOARD_STR STRINGIFY_MACRO(LT_BOARD)
// Includes
#include "LibreTuyaConfig.h"
#include <Arduino.h>
// C includes
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include "lt_logger.h"
#include "lt_posix_api.h"
#ifdef __cplusplus
} // extern "C"
#endif
// Functional macros
#define LT_BANNER() \
LT_LOG( \
LT_LEVEL_INFO, \
__FUNCTION__, \
__LINE__, \
"LibreTuya v" LT_VERSION_STR " on " LT_BOARD_STR ", compiled at " __DATE__ " " __TIME__ \
)
// ArduinoCore-API doesn't define these anymore
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
#define PGM_VOID_P const void *
void lt_rand_bytes(uint8_t *buf, size_t len);
#ifdef __cplusplus
String ipToString(const IPAddress &ip);
void hexdump(uint8_t *buf, size_t len, uint32_t offset = 0, uint8_t width = 16);
#else
void hexdump(uint8_t *buf, size_t len, uint32_t offset, uint8_t width);
#endif
// Main class
#ifdef __cplusplus
#include <Flash.h> // for flash inline methods
#include <core/ChipType.h>
/**
* @brief Main LibreTuya API class.
*
* This class contains all functions common amongst all families.
* Implementations of these methods may vary between families.
*
* The class is accessible using the `LT` global object (defined by the family).
*/
class LibreTuya {
public: /* Common methods - note: these are documented in LibreTuyaAPI.cpp */
const char *getVersion();
const char *getBoard();
ChipFamily getChipFamily();
const char *getChipFamilyName();
const char *getDeviceName();
uint8_t otaGetRunning();
uint8_t otaGetTarget();
bool otaRollback();
bool otaCanRollback();
public: /* Inline methods */
inline uint32_t getFlashChipSize() {
return Flash.getSize();
}
// inline bool flashEraseSector(uint32_t sector) {}
// inline bool flashWrite(uint32_t offset, uint32_t *data, size_t size) {}
// inline bool flashRead(uint32_t offset, uint32_t *data, size_t size) {}
// inline bool partitionEraseRange(const esp_partition_t *partition, uint32_t offset, size_t size) {}
// inline bool partitionWrite(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size) {}
// inline bool partitionRead(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size) {}
public: /* Family-defined methods */
/**
* @brief Reboot the CPU.
*/
void restart();
public: /* CPU-related */
/**
* @brief Get CPU model ID.
*/
ChipType getChipType();
/**
* @brief Get CPU model name as string.
*/
const char *getChipModel();
/**
* @brief Get CPU unique ID. This may be based on MAC, eFuse, etc.
*/
uint32_t getChipId();
/**
* @brief Get CPU core count.
*/
uint8_t getChipCores();
/**
* @brief Get CPU core type name as string.
*/
const char *getChipCoreType();
/**
* @brief Get CPU frequency in MHz.
*/
uint32_t getCpuFreqMHz();
/**
* @brief Get CPU cycle count.
*/
inline uint32_t getCycleCount() __attribute__((always_inline));
public: /* Memory management */
/**
* @brief Get total RAM size.
*/
uint32_t getRamSize();
/**
* @brief Get total heap size.
*/
uint32_t getHeapSize();
/**
* @brief Get free heap size.
*/
uint32_t getFreeHeap();
/**
* @brief Get lowest level of free heap memory.
*/
uint32_t getMinFreeHeap();
/**
* @brief Get largest block of heap that can be allocated at once.
*/
uint32_t getMaxAllocHeap();
public: /* OTA-related */
/**
* @brief Read the currently active OTA index, i.e. the one that will boot upon restart.
*/
uint8_t otaGetStoredIndex();
/**
* @brief Check if the chip supports dual-OTA.
*/
bool otaSupportsDual();
/**
* @brief Check if OTA1 image is valid.
*/
bool otaHasImage1();
/**
* @brief Check if OTA2 image is valid.
*/
bool otaHasImage2();
/**
* @brief Try to switch OTA index to the other image.
*
* Note: should return true for chips without dual-OTA. Should return false if one of two images is not valid.
*
* @param force switch even if other image already marked as active
* @return false if writing failed; true otherwise
*/
bool otaSwitch(bool force = false);
};
extern LibreTuya LT;
extern LibreTuya ESP;
#endif

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-28. */
#pragma once
// see docs/API Configuration
@@ -21,8 +23,8 @@
#define LT_LOGGER_TIMESTAMP 1
#endif
#ifndef LT_LOGGER_FILE
#define LT_LOGGER_FILE 0
#ifndef LT_LOGGER_CALLER
#define LT_LOGGER_CALLER 1
#endif
#ifndef LT_LOGGER_TASK
@@ -42,6 +44,11 @@
#define LT_LOGLEVEL LT_LEVEL_INFO
#endif
// Free heap size debugging
#ifndef LT_LOG_HEAP
#define LT_LOG_HEAP 0
#endif
// Per-module debugging
#ifndef LT_DEBUG_WIFI
#define LT_DEBUG_WIFI 0
@@ -62,3 +69,7 @@
#ifndef LT_DEBUG_WIFI_AP
#define LT_DEBUG_WIFI_AP 0
#endif
#ifndef LT_DEBUG_SSL
#define LT_DEBUG_SSL 0
#endif

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-28. */
#include "lt_logger.h"
#include <Arduino.h>
@@ -40,8 +42,8 @@ const uint8_t colors[] = {
unsigned long millis(void);
#if LT_LOGGER_FILE
void lt_log(const uint8_t level, const char *filename, const unsigned short line, const char *format, ...) {
#if LT_LOGGER_CALLER
void lt_log(const uint8_t level, const char *caller, const unsigned short line, const char *format, ...) {
#else
void lt_log(const uint8_t level, const char *format, ...) {
#endif
@@ -85,8 +87,8 @@ void lt_log(const uint8_t level, const char *format, ...) {
#if LT_LOGGER_COLOR
"\e[0m"
#endif
#if LT_LOGGER_FILE
"%s:%hu: "
#if LT_LOGGER_CALLER
"%s():%hu: "
#endif
#if LT_LOGGER_TASK
"%s%c "
@@ -106,9 +108,9 @@ void lt_log(const uint8_t level, const char *format, ...) {
zero // append missing zeroes if printf "%11.3f" prints "0."
#endif
#endif
#if LT_LOGGER_FILE
#if LT_LOGGER_CALLER
,
filename,
caller,
line
#endif
#if LT_LOGGER_TASK

View File

@@ -0,0 +1,163 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-28. */
#pragma once
#include "LibreTuyaConfig.h"
#include <stdint.h>
#if LT_LOGGER_CALLER
#define LT_LOG(level, caller, line, ...) lt_log(level, caller, line, __VA_ARGS__)
void lt_log(const uint8_t level, const char *caller, const unsigned short line, const char *format, ...);
#else
#define LT_LOG(level, caller, line, ...) lt_log(level, __VA_ARGS__)
void lt_log(const uint8_t level, const char *format, ...);
#endif
#if LT_LEVEL_TRACE >= LT_LOGLEVEL
#define LT_T(...) LT_LOG(LT_LEVEL_TRACE, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_V(...) LT_LOG(LT_LEVEL_TRACE, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_T(...)
#define LT_V(...)
#endif
#if LT_LEVEL_DEBUG >= LT_LOGLEVEL
#define LT_D(...) LT_LOG(LT_LEVEL_DEBUG, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_D(...)
#endif
#if LT_LEVEL_INFO >= LT_LOGLEVEL
#define LT_I(...) LT_LOG(LT_LEVEL_INFO, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_I(...)
#endif
#if LT_LEVEL_WARN >= LT_LOGLEVEL
#define LT_W(...) LT_LOG(LT_LEVEL_WARN, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_W(...)
#endif
#if LT_LEVEL_ERROR >= LT_LOGLEVEL
#define LT_E(...) LT_LOG(LT_LEVEL_ERROR, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_E(...)
#endif
#if LT_LEVEL_FATAL >= LT_LOGLEVEL
#define LT_F(...) LT_LOG(LT_LEVEL_FATAL, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_F(...)
#endif
#if LT_LOG_HEAP
#define LT_HEAP_I() LT_I("Free heap: %u", LT_HEAP_FUNC());
#else
#define LT_HEAP_I()
#endif
// ESP32 compat
#define log_printf(...) LT_I(__VA_ARGS__)
#define log_v(...) LT_V(__VA_ARGS__)
#define log_d(...) LT_D(__VA_ARGS__)
#define log_i(...) LT_I(__VA_ARGS__)
#define log_w(...) LT_W(__VA_ARGS__)
#define log_e(...) LT_E(__VA_ARGS__)
#define log_n(...) LT_E(__VA_ARGS__)
#define isr_log_v(...) LT_V(__VA_ARGS__)
#define isr_log_d(...) LT_D(__VA_ARGS__)
#define isr_log_i(...) LT_I(__VA_ARGS__)
#define isr_log_w(...) LT_W(__VA_ARGS__)
#define isr_log_e(...) LT_E(__VA_ARGS__)
#define isr_log_n(...) LT_E(__VA_ARGS__)
#define ESP_LOGV(...) LT_V(__VA_ARGS__)
#define ESP_LOGD(...) LT_D(__VA_ARGS__)
#define ESP_LOGI(...) LT_I(__VA_ARGS__)
#define ESP_LOGW(...) LT_W(__VA_ARGS__)
#define ESP_LOGE(...) LT_E(__VA_ARGS__)
#define ESP_EARLY_LOGV(...) LT_V(__VA_ARGS__)
#define ESP_EARLY_LOGD(...) LT_D(__VA_ARGS__)
#define ESP_EARLY_LOGI(...) LT_I(__VA_ARGS__)
#define ESP_EARLY_LOGW(...) LT_W(__VA_ARGS__)
#define ESP_EARLY_LOGE(...) LT_E(__VA_ARGS__)
#define LT_T_MOD(module, ...) \
do { \
if (module) { \
LT_T(__VA_ARGS__); \
} \
} while (0)
#define LT_D_MOD(module, ...) \
do { \
if (module) { \
LT_D(__VA_ARGS__); \
} \
} while (0)
#define LT_RET(ret) \
LT_E("ret=%d", ret); \
return ret;
#define LT_RET_NZ(ret) \
if (ret) { \
LT_E("ret=%d", ret); \
return ret; \
}
#define LT_RET_LZ(ret) \
if (ret < 0) { \
LT_E("ret=%d", ret); \
return ret; \
}
#define LT_RET_LEZ(ret) \
if (ret <= 0) { \
LT_E("ret=%d", ret); \
return ret; \
}
#define LT_ERRNO_NZ(ret) \
if (ret) { \
LT_E("errno=%d, ret=%d", errno, ret); \
return ret; \
}
#define LT_ERRNO_LZ(ret) \
if (ret < 0) { \
LT_E("errno=%d, ret=%d", errno, ret); \
return ret; \
}
#define LT_ERRNO_LEZ(ret) \
if (ret <= 0) { \
LT_E("errno=%d, ret=%d", errno, ret); \
return ret; \
}
// WiFi.cpp
#define LT_T_WG(...) LT_T_MOD(LT_DEBUG_WIFI, __VA_ARGS__)
#define LT_V_WG(...) LT_T_MOD(LT_DEBUG_WIFI, __VA_ARGS__)
#define LT_D_WG(...) LT_D_MOD(LT_DEBUG_WIFI, __VA_ARGS__)
// WiFiClient.cpp
#define LT_T_WC(...) LT_T_MOD(LT_DEBUG_WIFI_CLIENT, __VA_ARGS__)
#define LT_V_WC(...) LT_T_MOD(LT_DEBUG_WIFI_CLIENT, __VA_ARGS__)
#define LT_D_WC(...) LT_D_MOD(LT_DEBUG_WIFI_CLIENT, __VA_ARGS__)
// WiFiServer.cpp
#define LT_T_WS(...) LT_T_MOD(LT_DEBUG_WIFI_SERVER, __VA_ARGS__)
#define LT_V_WS(...) LT_T_MOD(LT_DEBUG_WIFI_SERVER, __VA_ARGS__)
#define LT_D_WS(...) LT_D_MOD(LT_DEBUG_WIFI_SERVER, __VA_ARGS__)
// WiFiSTA.cpp
#define LT_T_WSTA(...) LT_T_MOD(LT_DEBUG_WIFI_STA, __VA_ARGS__)
#define LT_V_WSTA(...) LT_T_MOD(LT_DEBUG_WIFI_STA, __VA_ARGS__)
#define LT_D_WSTA(...) LT_D_MOD(LT_DEBUG_WIFI_STA, __VA_ARGS__)
// WiFiAP.cpp
#define LT_T_WAP(...) LT_T_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)
#define LT_V_WAP(...) LT_T_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)
#define LT_D_WAP(...) LT_D_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)
// WiFiClientSecure.cpp & implementations
#define LT_T_SSL(...) LT_T_MOD(LT_DEBUG_SSL, __VA_ARGS__)
#define LT_V_SSL(...) LT_T_MOD(LT_DEBUG_SSL, __VA_ARGS__)
#define LT_D_SSL(...) LT_D_MOD(LT_DEBUG_SSL, __VA_ARGS__)

View File

@@ -29,7 +29,7 @@
#ifdef HTTPCLIENT_1_1_COMPATIBLE
#include <WiFi.h>
// #include <WiFiClientSecure.h>
#include <WiFiClientSecure.h>
#endif
// #include <StreamString.h>
@@ -63,7 +63,7 @@ class TLSTraits : public TransportTraits {
TLSTraits(const char *CAcert, const char *clicert = nullptr, const char *clikey = nullptr)
: _cacert(CAcert), _clicert(clicert), _clikey(clikey) {}
/* std::unique_ptr<WiFiClient> create() override {
std::unique_ptr<WiFiClient> create() override {
return std::unique_ptr<WiFiClient>(new WiFiClientSecure());
}
@@ -77,7 +77,7 @@ class TLSTraits : public TransportTraits {
wcs.setPrivateKey(_clikey);
}
return true;
} */
}
protected:
const char *_cacert;

View File

@@ -33,7 +33,7 @@
#include <Arduino.h>
#include <WiFiClient.h>
// #include <WiFiClientSecure.h>
#include <WiFiClientSecure.h>
#include <memory>
/// Cookie jar support

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#pragma once
#include <time.h>

View File

@@ -0,0 +1,2 @@
DisableFormat: true
SortIncludes: Never

View File

@@ -0,0 +1,98 @@
/*
IPv6Address.cpp - Base class that provides IPv6Address
Copyright (c) 2011 Adrian McEwen. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "IPv6Address.h"
#include <Arduino.h>
#include <api/Print.h>
IPv6Address::IPv6Address() {
memset(_address.bytes, 0, sizeof(_address.bytes));
}
IPv6Address::IPv6Address(const uint8_t *address) {
memcpy(_address.bytes, address, sizeof(_address.bytes));
}
IPv6Address::IPv6Address(const uint32_t *address) {
memcpy(_address.bytes, (const uint8_t *)address, sizeof(_address.bytes));
}
IPv6Address &IPv6Address::operator=(const uint8_t *address) {
memcpy(_address.bytes, address, sizeof(_address.bytes));
return *this;
}
bool IPv6Address::operator==(const uint8_t *addr) const {
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
}
size_t IPv6Address::printTo(Print &p) const {
/* size_t n = 0;
for(int i = 0; i < 16; i+=2) {
if(i){
n += p.print(':');
}
n += p.printf("%02x", _address.bytes[i]);
n += p.printf("%02x", _address.bytes[i+1]);
}
return n; */
}
String IPv6Address::toString() const {
char szRet[40];
sprintf(
szRet,
"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
_address.bytes[0],
_address.bytes[1],
_address.bytes[2],
_address.bytes[3],
_address.bytes[4],
_address.bytes[5],
_address.bytes[6],
_address.bytes[7],
_address.bytes[8],
_address.bytes[9],
_address.bytes[10],
_address.bytes[11],
_address.bytes[12],
_address.bytes[13],
_address.bytes[14],
_address.bytes[15]
);
return String(szRet);
}
bool IPv6Address::fromString(const char *address) {
// format 0011:2233:4455:6677:8899:aabb:ccdd:eeff
if (strlen(address) != 39) {
return false;
}
char *pos = (char *)address;
size_t i = 0;
for (i = 0; i < 16; i += 2) {
if (!sscanf(pos, "%2hhx", &_address.bytes[i]) || !sscanf(pos + 2, "%2hhx", &_address.bytes[i + 1])) {
return false;
}
pos += 5;
}
return true;
}

View File

@@ -0,0 +1,97 @@
/*
IPv6Address.h - Base class that provides IPv6Address
Copyright (c) 2011 Adrian McEwen. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <api/Print.h>
#include <api/String.h>
#include <stdint.h>
// A class to make it easier to handle and pass around IP addresses
namespace arduino {
class IPv6Address : public Printable {
private:
union {
uint8_t bytes[16]; // IPv4 address
uint32_t dword[4];
} _address;
// Access the raw byte array containing the address. Because this returns a pointer
// to the internal structure rather than a copy of the address this function should only
// be used when you know that the usage of the returned uint8_t* will be transient and not
// stored.
uint8_t *raw_address() {
return _address.bytes;
}
public:
// Constructors
IPv6Address();
IPv6Address(const uint8_t *address);
IPv6Address(const uint32_t *address);
virtual ~IPv6Address() {}
bool fromString(const char *address);
bool fromString(const String &address) {
return fromString(address.c_str());
}
operator const uint8_t *() const {
return _address.bytes;
}
operator const uint32_t *() const {
return _address.dword;
}
bool operator==(const IPv6Address &addr) const {
return (_address.dword[0] == addr._address.dword[0]) && (_address.dword[1] == addr._address.dword[1]) &&
(_address.dword[2] == addr._address.dword[2]) && (_address.dword[3] == addr._address.dword[3]);
}
bool operator==(const uint8_t *addr) const;
// Overloaded index operator to allow getting and setting individual octets of the address
uint8_t operator[](int index) const {
return _address.bytes[index];
}
uint8_t &operator[](int index) {
return _address.bytes[index];
}
// Overloaded copy operators to allow initialisation of IPv6Address objects from other types
IPv6Address &operator=(const uint8_t *address);
// TODO implement printTo()
virtual size_t printTo(Print &p) const;
String toString() const;
friend class UDP;
friend class Client;
friend class Server;
};
} // namespace arduino
using arduino::IPv6Address;

View File

@@ -19,7 +19,7 @@ size_t LwIPRxBuffer::r_available() {
return 0;
}
uint16_t count = 0;
int res = lwip_ioctl(_sock, FIONREAD, &count);
int res = lwip_ioctl(_sock, FIONREAD, &count);
if (res < 0) {
_failed = true;
return 0;

View File

@@ -0,0 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
#pragma once
#include "../IPv6Address.h"

View File

@@ -0,0 +1,442 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
#include "MbedTLSClient.h"
#include <IPAddress.h>
#include <WiFi.h>
#include <WiFiClient.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include <mbedtls/debug.h>
#include <mbedtls/platform.h>
#include <mbedtls/sha256.h>
#include <mbedtls/ssl.h>
#ifdef __cplusplus
} // extern "C"
#endif
MbedTLSClient::MbedTLSClient() : WiFiClient() {
init(); // ensure the context is zero filled
}
MbedTLSClient::MbedTLSClient(int sock) : WiFiClient(sock) {
init(); // ensure the context is zero filled
}
void MbedTLSClient::stop() {
WiFiClient::stop();
LT_V_SSL("Closing SSL connection");
if (_sslCfg.ca_chain) {
mbedtls_x509_crt_free(&_caCert);
}
if (_sslCfg.key_cert) {
mbedtls_x509_crt_free(&_clientCert);
mbedtls_pk_free(&_clientKey);
}
mbedtls_ssl_free(&_sslCtx);
mbedtls_ssl_config_free(&_sslCfg);
}
void MbedTLSClient::init() {
// Realtek AmbZ: init platform here to ensure HW crypto is initialized in ssl_init
mbedtls_platform_set_calloc_free(calloc, free);
mbedtls_ssl_init(&_sslCtx);
mbedtls_ssl_config_init(&_sslCfg);
}
int MbedTLSClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
return connect(ipToString(ip).c_str(), port, timeout);
}
int MbedTLSClient::connect(const char *host, uint16_t port, int32_t timeout) {
if (_pskIdentStr && _pskStr)
return connect(host, port, timeout, NULL, NULL, NULL, _pskIdentStr, _pskStr) == 0;
return connect(host, port, timeout, _caCertStr, _clientCertStr, _clientKeyStr, NULL, NULL) == 0;
}
int MbedTLSClient::connect(
IPAddress ip, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey
) {
return connect(ipToString(ip).c_str(), port, 0, rootCABuf, clientCert, clientKey, NULL, NULL) == 0;
}
int MbedTLSClient::connect(
const char *host, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey
) {
return connect(host, port, 0, rootCABuf, clientCert, clientKey, NULL, NULL) == 0;
}
int MbedTLSClient::connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psk) {
return connect(ipToString(ip).c_str(), port, 0, NULL, NULL, NULL, pskIdent, psk) == 0;
}
int MbedTLSClient::connect(const char *host, uint16_t port, const char *pskIdent, const char *psk) {
return connect(host, port, 0, NULL, NULL, NULL, pskIdent, psk) == 0;
}
static int ssl_random(void *data, unsigned char *output, size_t len) {
lt_rand_bytes((uint8_t *)output, len);
return 0;
}
void debug_cb(void *ctx, int level, const char *file, int line, const char *str) {
LT_I("%04d: |%d| %s", line, level, str);
}
int MbedTLSClient::connect(
const char *host,
uint16_t port,
int32_t timeout,
const char *rootCABuf,
const char *clientCert,
const char *clientKey,
const char *pskIdent,
const char *psk
) {
LT_D_SSL("Free heap before TLS: TODO");
if (!rootCABuf && !pskIdent && !psk && !_insecure && !_useRootCA)
return -1;
if (timeout <= 0)
timeout = _timeout; // use default when -1 passed as timeout
IPAddress addr = WiFi.hostByName(host);
if (!(uint32_t)addr)
return -1;
int ret = WiFiClient::connect(addr, port, timeout);
if (ret < 0) {
LT_E("SSL socket failed");
return ret;
}
char *uid = "lt-ssl"; // TODO
LT_V_SSL("Init SSL");
init();
// mbedtls_debug_set_threshold(4);
// mbedtls_ssl_conf_dbg(&_sslCfg, debug_cb, NULL);
ret = mbedtls_ssl_config_defaults(
&_sslCfg,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT
);
LT_RET_NZ(ret);
#ifdef MBEDTLS_SSL_ALPN
if (_alpnProtocols) {
ret = mbedtls_ssl_conf_alpn_protocols(&_sslCfg, _alpnProtocols);
LT_RET_NZ(ret);
}
#endif
if (_insecure) {
mbedtls_ssl_conf_authmode(&_sslCfg, MBEDTLS_SSL_VERIFY_NONE);
} else if (rootCABuf) {
mbedtls_x509_crt_init(&_caCert);
mbedtls_ssl_conf_authmode(&_sslCfg, MBEDTLS_SSL_VERIFY_REQUIRED);
ret = mbedtls_x509_crt_parse(&_caCert, (const unsigned char *)rootCABuf, strlen(rootCABuf) + 1);
mbedtls_ssl_conf_ca_chain(&_sslCfg, &_caCert, NULL);
if (ret < 0) {
mbedtls_x509_crt_free(&_caCert);
LT_RET(ret);
}
} else if (_useRootCA) {
return -1; // not implemented
} else if (pskIdent && psk) {
#ifdef MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED
uint16_t len = strlen(psk);
if ((len & 1) != 0 || len > 2 * MBEDTLS_PSK_MAX_LEN) {
LT_E("PSK length invalid");
return -1;
}
unsigned char pskBin[MBEDTLS_PSK_MAX_LEN] = {};
for (uint8_t i = 0; i < len; i++) {
uint8_t c = psk[i];
c |= 0b00100000; // make lowercase
c -= '0' * (c >= '0' && c <= '9');
c -= ('a' - 10) * (c >= 'a' && c <= 'z');
if (c > 0xf)
return -1;
pskBin[i / 2] |= c << (4 * ((i & 1) ^ 1));
}
ret = mbedtls_ssl_conf_psk(&_sslCfg, pskBin, len / 2, (const unsigned char *)pskIdent, strlen(pskIdent));
LT_RET_NZ(ret);
#else
return -1;
#endif
} else {
return -1;
}
if (!_insecure && clientCert && clientKey) {
mbedtls_x509_crt_init(&_clientCert);
mbedtls_pk_init(&_clientKey);
LT_V_SSL("Loading client cert");
ret = mbedtls_x509_crt_parse(&_clientCert, (const unsigned char *)clientCert, strlen(clientCert) + 1);
if (ret < 0) {
mbedtls_x509_crt_free(&_clientCert);
LT_RET(ret);
}
LT_V_SSL("Loading private key");
ret = mbedtls_pk_parse_key(&_clientKey, (const unsigned char *)clientKey, strlen(clientKey) + 1, NULL, 0);
if (ret < 0) {
mbedtls_x509_crt_free(&_clientCert);
LT_RET(ret);
}
mbedtls_ssl_conf_own_cert(&_sslCfg, &_clientCert, &_clientKey);
}
LT_V_SSL("Setting TLS hostname");
ret = mbedtls_ssl_set_hostname(&_sslCtx, host);
LT_RET_NZ(ret);
mbedtls_ssl_conf_rng(&_sslCfg, ssl_random, NULL);
ret = mbedtls_ssl_setup(&_sslCtx, &_sslCfg);
LT_RET_NZ(ret);
_sockTls = fd();
mbedtls_ssl_set_bio(&_sslCtx, &_sockTls, mbedtls_net_send, mbedtls_net_recv, NULL);
mbedtls_net_set_nonblock((mbedtls_net_context *)&_sockTls);
LT_V_SSL("SSL handshake");
if (_handshakeTimeout == 0)
_handshakeTimeout = timeout;
unsigned long start = millis();
while (ret = mbedtls_ssl_handshake(&_sslCtx)) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
LT_RET(ret);
}
if ((millis() - start) > _handshakeTimeout) {
LT_E("SSL handshake timeout");
return -1;
}
delay(2);
}
if (clientCert && clientKey) {
LT_D_SSL(
"Protocol %s, ciphersuite %s",
mbedtls_ssl_get_version(&_sslCtx),
mbedtls_ssl_get_ciphersuite(&_sslCtx)
);
ret = mbedtls_ssl_get_record_expansion(&_sslCtx);
if (ret >= 0)
LT_D_SSL("Record expansion: %d", ret);
else {
LT_W("Record expansion unknown");
}
}
LT_V_SSL("Verifying certificate");
ret = mbedtls_ssl_get_verify_result(&_sslCtx);
if (ret) {
char buf[512];
memset(buf, 0, sizeof(buf));
mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", ret);
LT_E("Failed to verify peer certificate! Verification info: %s", buf);
return ret;
}
if (rootCABuf)
mbedtls_x509_crt_free(&_caCert);
if (clientCert)
mbedtls_x509_crt_free(&_clientCert);
if (clientKey != NULL)
mbedtls_pk_free(&_clientKey);
return 0; // OK
}
size_t MbedTLSClient::write(const uint8_t *buf, size_t size) {
int ret = -1;
while ((ret = mbedtls_ssl_write(&_sslCtx, buf, size)) <= 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) {
LT_RET(ret);
}
delay(2);
}
return ret;
}
int MbedTLSClient::available() {
bool peeked = _peeked >= 0;
if (!connected())
return peeked;
int ret = mbedtls_ssl_read(&_sslCtx, NULL, 0);
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) {
stop();
return peeked ? peeked : ret;
}
return mbedtls_ssl_get_bytes_avail(&_sslCtx) + peeked;
}
int MbedTLSClient::read(uint8_t *buf, size_t size) {
bool peeked = false;
int toRead = available();
if ((!buf && size) || toRead <= 0)
return -1;
if (!size)
return 0;
if (_peeked >= 0) {
buf[0] = _peeked;
_peeked = -1;
size--;
toRead--;
if (!size || !toRead)
return 1;
buf++;
peeked = true;
}
int ret = mbedtls_ssl_read(&_sslCtx, buf, size);
if (ret < 0) {
stop();
return peeked ? peeked : ret;
}
return ret + peeked;
}
int MbedTLSClient::peek() {
if (_peeked >= 0)
return _peeked;
_peeked = timedRead();
return _peeked;
}
void MbedTLSClient::flush() {}
int MbedTLSClient::lastError(char *buf, const size_t size) {
return 0; // TODO (?)
}
void MbedTLSClient::setInsecure() {
_caCertStr = NULL;
_clientCertStr = NULL;
_clientKeyStr = NULL;
_pskIdentStr = NULL;
_pskStr = NULL;
_insecure = true;
}
void MbedTLSClient::setPreSharedKey(const char *pskIdent, const char *psk) {
_pskIdentStr = pskIdent;
_pskStr = psk;
}
void MbedTLSClient::setCACert(const char *rootCA) {
_caCertStr = rootCA;
}
void MbedTLSClient::setCertificate(const char *clientCA) {
_clientCertStr = clientCA;
}
void MbedTLSClient::setPrivateKey(const char *privateKey) {
_clientKeyStr = privateKey;
}
char *streamToStr(Stream &stream, size_t size) {
char *buf = (char *)malloc(size + 1);
if (!buf)
return NULL;
if (size != stream.readBytes(buf, size)) {
free(buf);
return NULL;
}
buf[size] = '\0';
return buf;
}
bool MbedTLSClient::loadCACert(Stream &stream, size_t size) {
char *str = streamToStr(stream, size);
if (str) {
_caCertStr = str;
return true;
}
return false;
}
bool MbedTLSClient::loadCertificate(Stream &stream, size_t size) {
char *str = streamToStr(stream, size);
if (str) {
_clientCertStr = str;
return true;
}
return false;
}
bool MbedTLSClient::loadPrivateKey(Stream &stream, size_t size) {
char *str = streamToStr(stream, size);
if (str) {
_clientKeyStr = str;
return true;
}
return false;
}
bool MbedTLSClient::verify(const char *fingerprint, const char *domainName) {
uint8_t fpLocal[32] = {};
uint16_t len = strlen(fingerprint);
uint8_t byte = 0;
for (uint8_t i = 0; i < len; i++) {
uint8_t c = fingerprint[i];
while ((c == ' ' || c == ':') && i < len) {
c = fingerprint[++i];
}
c |= 0b00100000; // make lowercase
c -= '0' * (c >= '0' && c <= '9');
c -= ('a' - 10) * (c >= 'a' && c <= 'z');
if (c > 0xf)
return -1;
fpLocal[byte / 2] |= c << (4 * ((byte & 1) ^ 1));
byte++;
if (byte >= 64)
break;
}
uint8_t fpRemote[32];
if (!getFingerprintSHA256(fpRemote))
return false;
if (memcmp(fpLocal, fpRemote, 32)) {
LT_D_SSL("Fingerprints don't match");
return false;
}
if (!domainName)
return true;
// TODO domain name verification
return true;
}
void MbedTLSClient::setHandshakeTimeout(unsigned long handshakeTimeout) {
_handshakeTimeout = handshakeTimeout * 1000;
}
void MbedTLSClient::setAlpnProtocols(const char **alpnProtocols) {
_alpnProtocols = alpnProtocols;
}
bool MbedTLSClient::getFingerprintSHA256(uint8_t result[32]) {
const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(&_sslCtx);
if (!cert) {
LT_E("Failed to get peer certificate");
return false;
}
mbedtls_sha256_context shaCtx;
mbedtls_sha256_init(&shaCtx);
mbedtls_sha256_starts(&shaCtx, false);
mbedtls_sha256_update(&shaCtx, cert->raw.p, cert->raw.len);
mbedtls_sha256_finish(&shaCtx, result);
return true;
}

View File

@@ -0,0 +1,90 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
#pragma once
#include <api/WiFiClient.h>
#include <api/WiFiClientSecure.h>
#include <WiFiClient.h> // extend family's WiFiClient impl
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include <mbedtls/net.h>
#ifdef __cplusplus
} // extern "C"
#endif
class MbedTLSClient : public WiFiClient, public IWiFiClientSecure {
private:
mbedtls_ssl_context _sslCtx;
mbedtls_ssl_config _sslCfg;
mbedtls_x509_crt _caCert;
mbedtls_x509_crt _clientCert;
mbedtls_pk_context _clientKey;
uint32_t _handshakeTimeout = 0;
void init();
int _sockTls = -1;
bool _insecure = false;
bool _useRootCA = false;
int _peeked = -1;
const char *_caCertStr;
const char *_clientCertStr;
const char *_clientKeyStr;
const char *_pskIdentStr;
const char *_pskStr;
const char **_alpnProtocols;
int connect(
const char *host,
uint16_t port,
int32_t timeout,
const char *rootCABuf,
const char *clientCert,
const char *clientKey,
const char *pskIdent,
const char *psk
);
public:
MbedTLSClient();
MbedTLSClient(int sock);
int connect(IPAddress ip, uint16_t port, int32_t timeout);
int connect(const char *host, uint16_t port, int32_t timeout);
int connect(IPAddress ip, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey);
int connect(const char *host, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey);
int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psk);
int connect(const char *host, uint16_t port, const char *pskIdent, const char *psk);
size_t write(const uint8_t *buf, size_t size);
int available();
int read(uint8_t *buf, size_t size);
int peek();
void flush();
void stop();
int lastError(char *buf, const size_t size);
void setInsecure(); // Don't validate the chain, just accept whatever is given. VERY INSECURE!
void setPreSharedKey(const char *pskIdent, const char *psk); // psk in hex
void setCACert(const char *rootCA);
void setCertificate(const char *clientCA);
void setPrivateKey(const char *privateKey);
bool loadCACert(Stream &stream, size_t size);
bool loadCertificate(Stream &stream, size_t size);
bool loadPrivateKey(Stream &stream, size_t size);
bool verify(const char *fingerprint, const char *domainName);
void setHandshakeTimeout(unsigned long handshakeTimeout);
void setAlpnProtocols(const char **alpnProtocols);
bool getFingerprintSHA256(uint8_t result[32]);
using WiFiClient::connect;
using WiFiClient::read;
};

View File

@@ -0,0 +1,199 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-29. */
#include "Update.h"
UpdateClass::UpdateClass() : ctx(NULL), info(NULL), buf(NULL) {
cleanup();
}
/**
* @brief Initialize the update process.
*
* @param size total UF2 file size
* @param command must be U_FLASH
* @return false if parameters are invalid or update is running, true otherwise
*/
bool UpdateClass::begin(size_t size, int command, int unused2, uint8_t unused3, const char *unused4) {
if (ctx)
return false;
cleanup();
ctx = uf2_ctx_init(LT.otaGetTarget(), FAMILY);
info = uf2_info_init();
if (!size)
return errorArd(UPDATE_ERROR_SIZE);
if (command != U_FLASH)
return errorArd(UPDATE_ERROR_BAD_ARGUMENT);
bytesTotal = size;
return true;
}
/**
* @brief Finalize the update process. Check for errors and update completion, then activate the new firmware image.
*
* @param evenIfRemaining no idea
* @return false in case of errors or no update running, true otherwise
*/
bool UpdateClass::end(bool evenIfRemaining) {
if (hasError() || !ctx)
// false if not running
return false;
if (!isFinished() && !evenIfRemaining) {
// abort if not finished
return errorArd(UPDATE_ERROR_ABORT);
}
// TODO what is evenIfRemaining for?
if (!LT.otaSwitch())
// try to activate the second OTA
return errorArd(UPDATE_ERROR_ACTIVATE);
cleanup();
return true;
}
/**
* @brief Write a chunk of data to the buffer or flash memory.
*
* It's advised to write in 512-byte chunks (or its multiples).
*
* @param data
* @param len
* @return size_t
*/
size_t UpdateClass::write(uint8_t *data, size_t len) {
size_t written = 0;
if (hasError() || !ctx)
// 0 if not running
return 0;
/* while (buf == bufPos && len >= UF2_BLOCK_SIZE) {
// buffer empty and entire block is in data
if (!tryWriteData(data, UF2_BLOCK_SIZE)) {
// returns 0 if data contains an invalid block
return written;
}
data += UF2_BLOCK_SIZE;
len -= UF2_BLOCK_SIZE;
written += UF2_BLOCK_SIZE;
} */
// write until buffer space is available
uint16_t toWrite;
while (len && (toWrite = min(len, bufLeft()))) {
tryWriteData(data, toWrite);
if (hasError())
// return on errors
return written;
data += toWrite;
len -= toWrite;
written += toWrite;
}
return written;
}
size_t UpdateClass::writeStream(Stream &data) {
size_t written = 0;
if (hasError() || !ctx)
// 0 if not running
return 0;
uint32_t lastData = millis();
// loop until the update is complete
while (remaining()) {
// check stream availability
int available = data.available();
if (available <= 0) {
if (millis() - lastData > UPDATE_TIMEOUT_MS) {
// waited for data too long; abort with error
errorArd(UPDATE_ERROR_STREAM);
return written;
}
continue;
}
// available > 0
lastData = millis();
// read data to fit in the remaining buffer space
bufAlloc();
uint16_t read = data.readBytes(bufPos, bufLeft());
bufPos += read;
written += read;
tryWriteData();
if (hasError())
// return on errors
return written;
}
}
/**
* @brief Try to use the buffer as a block to write. In case of UF2 errors,
* error codes are set, the update is aborted and 0 is returned
*
* @param data received data to copy to buffer or NULL if already in buffer
* @param len received data length - must be at most bufLeft()
* @return size_t "used" data size - 0 or 512
*/
size_t UpdateClass::tryWriteData(uint8_t *data, size_t len) {
uf2_block_t *block = NULL;
if (len == UF2_BLOCK_SIZE) {
// data has a complete block
block = (uf2_block_t *)data;
} else if (data && len) {
// data has a part of a block, copy it to buffer
bufAlloc();
memcpy(bufPos, data, len);
bufPos += len;
}
if (!block && bufSize() == UF2_BLOCK_SIZE) {
// use buffer as block (only if not found above)
block = (uf2_block_t *)buf;
}
// a complete block has been found
if (block) {
if (errorUf2(uf2_check_block(ctx, block)))
// block is invalid
return 0;
if (errUf2 == UF2_ERR_IGNORE)
// treat ignored blocks as valid
return UF2_BLOCK_SIZE;
if (!bytesWritten) {
// parse header block to allow retrieving firmware info
if (errorUf2(uf2_parse_header(ctx, block, info)))
// header is invalid
return 0;
if (bytesTotal == UPDATE_SIZE_UNKNOWN) {
// set total update size from block count info
bytesTotal = block->block_count * UF2_BLOCK_SIZE;
} else if (bytesTotal != block->block_count * UF2_BLOCK_SIZE) {
// given update size does not match the block count
return errorArd(UPDATE_ERROR_SIZE);
}
} else {
// write data blocks normally
if (errorUf2(uf2_write(ctx, block)))
// block writing failed
return 0;
}
// increment total writing progress
bytesWritten += UF2_BLOCK_SIZE;
// call progress callback
if (callback)
callback(bytesWritten, bytesTotal);
return UF2_BLOCK_SIZE;
}
return 0;
}
UpdateClass Update;

View File

@@ -0,0 +1,150 @@
#pragma once
#include <Arduino.h>
#include <functional>
#include "uf2ota/uf2ota.h"
// No Error
#define UPDATE_ERROR_OK (0)
// Flash Write Failed
#define UPDATE_ERROR_WRITE (1)
// Flash Erase Failed
#define UPDATE_ERROR_ERASE (2)
// Flash Read Failed
#define UPDATE_ERROR_READ (3)
// Not Enough Space
#define UPDATE_ERROR_SPACE (4)
// Bad Size Given
#define UPDATE_ERROR_SIZE (5)
// Stream Read Timeout
#define UPDATE_ERROR_STREAM (6)
// MD5 Check Failed
#define UPDATE_ERROR_MD5 (7)
// Wrong Magic Byte
#define UPDATE_ERROR_MAGIC_BYTE (8)
// Could Not Activate The Firmware
#define UPDATE_ERROR_ACTIVATE (9)
// Partition Could Not be Found
#define UPDATE_ERROR_NO_PARTITION (10)
// Bad Argument
#define UPDATE_ERROR_BAD_ARGUMENT (11)
// Aborted
#define UPDATE_ERROR_ABORT (12)
#define UPDATE_SIZE_UNKNOWN 0xFFFFFFFF
#define U_FLASH 0
#define U_SPIFFS 100
#define U_AUTH 200
#define ENCRYPTED_BLOCK_SIZE 16
#define UPDATE_TIMEOUT_MS 30 * 1000
class UpdateClass {
public:
typedef std::function<void(size_t, size_t)> THandlerFunction_Progress;
public: /* Update.cpp */
UpdateClass();
bool begin(
size_t size = UPDATE_SIZE_UNKNOWN,
int command = U_FLASH,
int unused2 = -1,
uint8_t unused3 = LOW,
const char *unused4 = NULL // this is for SPIFFS
);
bool end(bool evenIfRemaining = false);
size_t write(uint8_t *data, size_t len);
size_t writeStream(Stream &data);
bool canRollBack();
bool rollBack();
// bool setMD5(const char *expected_md5);
private: /* Update.cpp */
size_t tryWriteData(uint8_t *data = NULL, size_t len = 0);
public: /* UpdateUtil.cpp */
UpdateClass &onProgress(THandlerFunction_Progress callback);
void abort();
void printError(Print &out);
const char *errorString();
const char *getFirmwareName();
const char *getFirmwareVersion();
const char *getLibreTuyaVersion();
const char *getBoardName();
private: /* UpdateUtil.cpp */
void cleanup();
bool errorUf2(uf2_err_t err);
bool errorArd(uint8_t err);
void bufAlloc();
uint16_t bufLeft();
uint16_t bufSize();
private:
// uf2ota context
uf2_ota_t *ctx;
uf2_info_t *info;
// block buffer
uint8_t *buf;
uint8_t *bufPos;
// update progress - multiplies of 512 bytes
uint32_t bytesWritten;
uint32_t bytesTotal;
// errors
uf2_err_t errUf2;
uint8_t errArd;
// progress callback
THandlerFunction_Progress callback;
// String _target_md5;
// MD5Builder _md5;
public:
String md5String(void) {
// return _md5.toString();
}
void md5(uint8_t *result) {
// return _md5.getBytes(result);
}
uint8_t getError() {
return errArd;
}
uf2_err_t getUF2Error() {
return errUf2;
}
void clearError() {
errorUf2(UF2_ERR_OK);
}
bool hasError() {
return errArd != UPDATE_ERROR_OK;
}
bool isRunning() {
return ctx != NULL;
}
bool isFinished() {
return bytesWritten == bytesTotal;
}
size_t size() {
return bytesTotal;
}
size_t progress() {
return bytesWritten;
}
size_t remaining() {
return bytesTotal - bytesWritten;
}
};
extern UpdateClass Update;

View File

@@ -0,0 +1,162 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-30. */
#include "Update.h"
static const uint8_t errorMap[] = {
UPDATE_ERROR_OK, /* UF2_ERR_OK - no error */
UPDATE_ERROR_OK, /* UF2_ERR_IGNORE - block should be ignored */
UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_MAGIC - wrong magic numbers */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_FAMILY - family ID mismatched */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_NOT_HEADER - block is not a header */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_OTA_VER - unknown/invalid OTA format version */
UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_OTA_WRONG - no data for current OTA index */
UPDATE_ERROR_NO_PARTITION, /* UF2_ERR_PART_404 - no partition with that name */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_ONE - only one partition tag in a block */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_UNSET - attempted to write without target partition */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_DATA_TOO_LONG - data too long - tags won't fit */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_SEQ_MISMATCH - sequence number mismatched */
UPDATE_ERROR_ERASE, /* UF2_ERR_ERASE_FAILED - erasing flash failed */
UPDATE_ERROR_WRITE, /* UF2_ERR_WRITE_FAILED - writing to flash failed */
UPDATE_ERROR_WRITE /* UF2_ERR_WRITE_LENGTH - wrote fewer data than requested */
};
static char errorStr[14];
/**
* @brief Set the callback invoked after writing data to flash.
*/
UpdateClass &UpdateClass::onProgress(THandlerFunction_Progress callback) {
this->callback = callback;
return *this;
}
void UpdateClass::cleanup() {
free(ctx); // NULL in constructor
ctx = NULL;
uf2_info_free(info); // NULL in constructor
info = NULL;
free(buf); // NULL in constructor
buf = bufPos = NULL;
bytesWritten = 0;
bytesTotal = 0;
errUf2 = UF2_ERR_OK;
errArd = UPDATE_ERROR_OK;
}
/**
* @brief Check for UF2 errors. Set errArd and errUf2 in case of errors.
* Ignored blocks are not reported as errors.
* Abort the update.
* Use like: "if (errorUf2(...)) return false;"
* @return true if err is not OK, false otherwise
*/
bool UpdateClass::errorUf2(uf2_err_t err) {
if (err <= UF2_ERR_IGNORE)
return false;
cleanup();
errUf2 = err;
errArd = errorMap[err];
return true;
}
/**
* @brief Set errUf2 and errArd according to given Arduino error code.
* Abort the update.
* Use like: "return errorArd(...);"
* @return false - always
*/
bool UpdateClass::errorArd(uint8_t err) {
cleanup();
errUf2 = UF2_ERR_OK;
errArd = err;
return false;
}
/**
* @brief Abort the update with UPDATE_ERROR_ABORT reason.
*/
void UpdateClass::abort() {
errorArd(UPDATE_ERROR_ABORT);
}
void UpdateClass::bufAlloc() {
if (!buf)
buf = bufPos = (uint8_t *)malloc(UF2_BLOCK_SIZE);
}
uint16_t UpdateClass::bufLeft() {
return buf + UF2_BLOCK_SIZE - bufPos;
}
uint16_t UpdateClass::bufSize() {
return bufPos - buf;
}
/**
* @brief Print string error info to the stream.
*/
void UpdateClass::printError(Print &out) {
out.println(errorString());
}
/**
* @brief Get string representation of the error in format
* "ard=..,uf2=..". Returns "" if no error.
*/
const char *UpdateClass::errorString() {
if (!errArd)
return "";
sprintf(errorStr, "ard=%u,uf2=%u", errArd, errUf2);
return errorStr;
}
/**
* @brief Get firmware name from UF2 info.
*/
const char *UpdateClass::getFirmwareName() {
if (info)
return info->fw_name;
return NULL;
}
/**
* @brief Get firmware version from UF2 info.
*/
const char *UpdateClass::getFirmwareVersion() {
if (info)
return info->fw_version;
return NULL;
}
/**
* @brief Get LibreTuya version from UF2 info.
*/
const char *UpdateClass::getLibreTuyaVersion() {
if (info)
return info->lt_version;
return NULL;
}
/**
* @brief Get target board name from UF2 info.
*/
const char *UpdateClass::getBoardName() {
if (info)
return info->board;
return NULL;
}
/**
* @brief See LT.otaCanRollback() for more info.
*/
bool UpdateClass::canRollBack() {
return LT.otaCanRollback();
}
/**
* @brief See LT.otaRollback() for more info.
*/
bool UpdateClass::rollBack() {
return LT.otaRollback();
}

View File

@@ -0,0 +1,32 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-29. */
#include "uf2priv.h"
uf2_err_t uf2_binpatch(uint8_t *data, const uint8_t *binpatch, uint8_t binpatch_len) {
const uint8_t *binpatch_end = binpatch + binpatch_len;
// +2 to make sure opcode and length is present
while ((binpatch + 2) < binpatch_end) {
uf2_opcode_t opcode = binpatch[0];
uint8_t len = binpatch[1];
switch (opcode) {
case UF2_OPC_DIFF32:
uf2_binpatch_diff32(data, binpatch + 1);
break;
}
// advance by opcode + length + data
binpatch += len + 2;
}
return UF2_ERR_OK;
}
void uf2_binpatch_diff32(uint8_t *data, const uint8_t *patch) {
uint8_t num_offs = patch[0] - 4; // read offset count
uint32_t diff = *((uint32_t *)(patch + 1)); // read diff value
patch += 5; // skip num_offs and diff value
for (uint8_t i = 0; i < num_offs; i++) {
// patch the data
uint8_t offs = patch[i];
uint32_t *value = (uint32_t *)(data + offs);
*(value) += diff;
}
}

View File

@@ -0,0 +1,26 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-29. */
#pragma once
#include "uf2types.h"
/**
* @brief Apply binary patch to data.
*
* @param data input data
* @param data_len input data length
* @param binpatch binary patch data
* @param binpatch_len binary patch data length
* @return uf2_err_t error code
*/
uf2_err_t uf2_binpatch(uint8_t *data, const uint8_t *binpatch, uint8_t binpatch_len);
/**
* Apply DIFF32 binary patch.
*
* @param data input data
* @param len input data length
* @param patch patch data, incl. length byte
* @return uf2_err_t error code
*/
void uf2_binpatch_diff32(uint8_t *data, const uint8_t *patch);

View File

@@ -0,0 +1,100 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-29. */
#include "uf2priv.h"
uf2_ota_t *uf2_ctx_init(uint8_t ota_idx, uint32_t family_id) {
uf2_ota_t *ctx = (uf2_ota_t *)zalloc(sizeof(uf2_ota_t));
ctx->ota_idx = ota_idx;
ctx->family_id = family_id;
return ctx;
}
uf2_info_t *uf2_info_init() {
uf2_info_t *info = (uf2_info_t *)zalloc(sizeof(uf2_info_t));
return info;
}
void uf2_info_free(uf2_info_t *info) {
if (!info)
return;
free(info->fw_name);
free(info->fw_version);
free(info->lt_version);
free(info->board);
free(info);
}
uf2_err_t uf2_check_block(uf2_ota_t *ctx, uf2_block_t *block) {
if (block->magic1 != UF2_MAGIC_1)
return UF2_ERR_MAGIC;
if (block->magic2 != UF2_MAGIC_2)
return UF2_ERR_MAGIC;
if (block->magic3 != UF2_MAGIC_3)
return UF2_ERR_MAGIC;
if (block->file_container)
// ignore file containers, for now
return UF2_ERR_IGNORE;
if (!block->has_family_id || block->file_size != ctx->family_id)
// require family_id
return UF2_ERR_FAMILY;
return UF2_ERR_OK;
}
uf2_err_t uf2_parse_header(uf2_ota_t *ctx, uf2_block_t *block, uf2_info_t *info) {
if (!block->has_tags || block->file_container || block->len)
// header must have tags and no data
return UF2_ERR_NOT_HEADER;
uf2_err_t err = uf2_parse_block(ctx, block, info);
if (err)
return err;
if ((ctx->ota_idx == 1 && !ctx->has_ota1) || !ctx->has_ota2)
return UF2_ERR_OTA_WRONG;
return UF2_ERR_OK;
}
uf2_err_t uf2_write(uf2_ota_t *ctx, uf2_block_t *block) {
if (ctx->seq == 0)
return uf2_parse_header(ctx, block, NULL);
if (block->not_main_flash || !block->len)
// ignore blocks not meant for flashing
return UF2_ERR_IGNORE;
uf2_err_t err = uf2_parse_block(ctx, block, NULL);
if (err)
return err;
if (!ctx->part1 && !ctx->part2)
// no partitions set at all
return UF2_ERR_PART_UNSET;
fal_partition_t part = uf2_get_target_part(ctx);
if (!part)
// image is not for current OTA scheme
return UF2_ERR_IGNORE;
if (ctx->ota_idx == 2 && ctx->binpatch_len) {
// apply binpatch
err = uf2_binpatch(block->data, ctx->binpatch, ctx->binpatch_len);
if (err)
return err;
}
int ret;
// erase sectors if needed
if (!uf2_is_erased(ctx, block->addr, block->len)) {
ret = fal_partition_erase(part, block->addr, block->len);
if (ret < 0)
return UF2_ERR_ERASE_FAILED;
ctx->erased_offset = block->addr;
ctx->erased_length = ret;
}
// write data to flash
ret = fal_partition_write(part, block->addr, block->data, block->len);
if (ret < 0)
return UF2_ERR_WRITE_FAILED;
if (ret != block->len)
return UF2_ERR_WRITE_LENGTH;
return UF2_ERR_OK;
}

View File

@@ -0,0 +1,68 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */
#pragma once
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include "uf2types.h"
/**
* @brief Create an UF2 OTA context.
*
* @param ota_idx target OTA index
* @param family_id expected family ID
* @return uf2_ota_t* heap-allocated structure
*/
uf2_ota_t *uf2_ctx_init(uint8_t ota_idx, uint32_t family_id);
/**
* @brief Create an UF2 Info structure.
*
* @return uf2_info_t* heap-allocated structure
*/
uf2_info_t *uf2_info_init();
/**
* @brief Free values in the info structure AND the structure itself.
*
* @param info structure to free; may be NULL
*/
void uf2_info_free(uf2_info_t *info);
/**
* @brief Check if block is valid.
*
* @param ctx context
* @param block block to check
* @return uf2_err_t error code; UF2_ERR_OK and UF2_ERR_IGNORE denote valid blocks
*/
uf2_err_t uf2_check_block(uf2_ota_t *ctx, uf2_block_t *block);
/**
* @brief Parse header block (LibreTuya UF2 first block).
*
* Note: caller should call uf2_check_block() first.
*
* @param ctx context
* @param block block to parse
* @param info structure to write firmware info, NULL if not used
* @return uf2_err_t error code
*/
uf2_err_t uf2_parse_header(uf2_ota_t *ctx, uf2_block_t *block, uf2_info_t *info);
/**
* @brief Write the block to flash memory.
*
* Note: caller should call uf2_check_block() first.
*
* @param ctx context
* @param block block to write
* @return uf2_err_t error code
*/
uf2_err_t uf2_write(uf2_ota_t *ctx, uf2_block_t *block);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,146 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-29. */
#include "uf2priv.h"
uf2_err_t uf2_parse_block(uf2_ota_t *ctx, uf2_block_t *block, uf2_info_t *info) {
if (block->block_seq != ctx->seq)
// sequence number must match
return UF2_ERR_SEQ_MISMATCH;
ctx->seq++; // increment sequence number after checking it
if (!block->has_tags)
// no tags in this block, no further processing needed
return UF2_ERR_OK;
if (block->len > (476 - 4 - 4))
// at least one tag + last tag must fit
return UF2_ERR_DATA_TOO_LONG;
uint8_t *tags_start = block->data + block->len;
uint8_t tags_len = 476 - block->len;
uint8_t tags_pos = 0;
if (block->has_md5)
tags_len -= 24;
ctx->binpatch_len = 0; // binpatch applies to one block only
char *part1 = NULL;
char *part2 = NULL;
uf2_tag_type_t type;
while (tags_pos < tags_len) {
uint8_t len = uf2_read_tag(tags_start + tags_pos, &type);
if (!len)
break;
tags_pos += 4; // skip tag header
uint8_t *tag = tags_start + tags_pos;
char **str_dest = NULL; // char* to copy the tag into
switch (type) {
case UF2_TAG_OTA_VERSION:
if (tag[0] != 1)
return UF2_ERR_OTA_VER;
break;
case UF2_TAG_FIRMWARE:
if (info)
str_dest = &(info->fw_name);
break;
case UF2_TAG_VERSION:
if (info)
str_dest = &(info->fw_version);
break;
case UF2_TAG_LT_VERSION:
if (info)
str_dest = &(info->lt_version);
break;
case UF2_TAG_BOARD:
if (info)
str_dest = &(info->board);
break;
case UF2_TAG_HAS_OTA1:
ctx->has_ota1 = tag[0];
break;
case UF2_TAG_HAS_OTA2:
ctx->has_ota2 = tag[0];
break;
case UF2_TAG_PART_1:
str_dest = &(part1);
break;
case UF2_TAG_PART_2:
str_dest = &(part2);
break;
case UF2_TAG_BINPATCH:
ctx->binpatch = tag;
ctx->binpatch_len = len;
break;
default:
break;
}
if (str_dest) {
*str_dest = (char *)zalloc(len + 1);
memcpy(*str_dest, tag, len);
}
// align position to 4 bytes
tags_pos += (((len - 1) / 4) + 1) * 4;
}
if (part1 && part2) {
// update current target partition
uf2_err_t err = uf2_update_parts(ctx, part1, part2);
if (err)
return err;
} else if (part1 || part2) {
// only none or both partitions can be specified
return UF2_ERR_PART_ONE;
}
return UF2_ERR_OK;
}
uint8_t uf2_read_tag(const uint8_t *data, uf2_tag_type_t *type) {
uint8_t len = data[0];
if (!len)
return 0;
uint32_t tag_type = *((uint32_t *)data);
if (!tag_type)
return 0;
*type = tag_type >> 8; // remove tag length byte
return len - 4;
}
uf2_err_t uf2_update_parts(uf2_ota_t *ctx, char *part1, char *part2) {
// reset both target partitions
ctx->part1 = NULL;
ctx->part2 = NULL;
// reset offsets as they probably don't apply to this partition
ctx->erased_offset = 0;
ctx->erased_length = 0;
if (part1[0]) {
ctx->part1 = fal_partition_find(part1);
if (!ctx->part1)
return UF2_ERR_PART_404;
}
if (part2[0]) {
ctx->part2 = fal_partition_find(part2);
if (!ctx->part2)
return UF2_ERR_PART_404;
}
return UF2_ERR_OK;
}
fal_partition_t uf2_get_target_part(uf2_ota_t *ctx) {
if (ctx->ota_idx == 1)
return ctx->part1;
if (ctx->ota_idx == 2)
return ctx->part2;
return NULL;
}
bool uf2_is_erased(uf2_ota_t *ctx, uint32_t offset, uint32_t length) {
uint32_t erased_end = ctx->erased_offset + ctx->erased_length;
uint32_t end = offset + length;
return (offset >= ctx->erased_offset) && (end <= erased_end);
}

View File

@@ -0,0 +1,61 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */
#pragma once
// include family stdlib APIs
#include <WVariant.h>
#include "uf2binpatch.h"
#include "uf2types.h"
/**
* @brief Parse a block and extract information from tags.
*
* @param ctx context
* @param block block to parse
* @param info structure to write firmware info, NULL if not used
* @return uf2_err_t error code
*/
uf2_err_t uf2_parse_block(uf2_ota_t *ctx, uf2_block_t *block, uf2_info_t *info);
/**
* @brief Parse a tag.
*
* @param data pointer to tag header beginning
* @param type [out] parsed tag type
* @return uint8_t parsed tag data length (excl. header); 0 if invalid/last tag
*/
uint8_t uf2_read_tag(const uint8_t *data, uf2_tag_type_t *type);
/**
* @brief Update destination partitions in context.
*
* Partition names cannot be NULL.
*
* Returns UF2_ERR_IGNORE if specified partitions don't match the
* current OTA index.
*
* @param ctx context
* @param part1 partition 1 name or empty string
* @param part2 partition 2 name or empty string
* @return uf2_err_t error code
*/
uf2_err_t uf2_update_parts(uf2_ota_t *ctx, char *part1, char *part2);
/**
* @brief Get target flashing partition, depending on OTA index.
*
* @param ctx context
* @return fal_partition_t target partition or NULL if not set
*/
fal_partition_t uf2_get_target_part(uf2_ota_t *ctx);
/**
* Check if specified flash memory region was already erased during update.
*
* @param ctx context
* @param offset offset to check
* @param length length to check
* @return bool true/false
*/
bool uf2_is_erased(uf2_ota_t *ctx, uint32_t offset, uint32_t length);

View File

@@ -0,0 +1,104 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <fal.h>
#define UF2_MAGIC_1 0x0A324655
#define UF2_MAGIC_2 0x9E5D5157
#define UF2_MAGIC_3 0x0AB16F30
#define UF2_BLOCK_SIZE sizeof(uf2_block_t)
typedef struct __attribute__((packed)) {
// 32 byte header
uint32_t magic1;
uint32_t magic2;
// flags split as bitfields
bool not_main_flash : 1;
uint16_t dummy1 : 11;
bool file_container : 1;
bool has_family_id : 1;
bool has_md5 : 1;
bool has_tags : 1;
uint16_t dummy2 : 16;
uint32_t addr;
uint32_t len;
uint32_t block_seq;
uint32_t block_count;
uint32_t file_size; // or familyID;
uint8_t data[476];
uint32_t magic3;
} uf2_block_t;
typedef struct {
uint32_t seq; // current block sequence number
uint8_t *binpatch; // current block's binpatch (if any) -> pointer inside block->data
uint8_t binpatch_len; // binpatch length
bool has_ota1; // image has any data for OTA1
bool has_ota2; // image has any data for OTA2
uint8_t ota_idx; // target OTA index
uint32_t family_id; // expected family ID
uint32_t erased_offset; // offset of region erased during update
uint32_t erased_length; // length of erased region
fal_partition_t part1; // OTA1 target partition
fal_partition_t part2; // OTA2 target partition
} uf2_ota_t;
typedef struct {
char *fw_name;
char *fw_version;
char *lt_version;
char *board;
} uf2_info_t;
typedef enum {
UF2_TAG_VERSION = 0x9FC7BC, // version of firmware file - UTF8 semver string
UF2_TAG_PAGE_SIZE = 0x0BE9F7, // page size of target device (32 bit unsigned number)
UF2_TAG_SHA2 = 0xB46DB0, // SHA-2 checksum of firmware (can be of various size)
UF2_TAG_DEVICE = 0x650D9D, // description of device (UTF8)
UF2_TAG_DEVICE_ID = 0xC8A729, // device type identifier
// LibreTuya custom, tags
UF2_TAG_OTA_VERSION = 0x5D57D0, // format version
UF2_TAG_BOARD = 0xCA25C8, // board name (lowercase code)
UF2_TAG_FIRMWARE = 0x00DE43, // firmware description / name
UF2_TAG_BUILD_DATE = 0x822F30, // build date/time as Unix timestamp
UF2_TAG_LT_VERSION = 0x59563D, // LT version (semver)
UF2_TAG_PART_1 = 0x805946, // OTA1 partition name
UF2_TAG_PART_2 = 0xA1E4D7, // OTA2 partition name
UF2_TAG_HAS_OTA1 = 0xBBD965, // image has any data for OTA1
UF2_TAG_HAS_OTA2 = 0x92280E, // image has any data for OTA2
UF2_TAG_BINPATCH = 0xB948DE, // binary patch to convert OTA1->OTA2
} uf2_tag_type_t;
typedef enum {
UF2_OPC_DIFF32 = 0xFE,
} uf2_opcode_t;
typedef enum {
UF2_ERR_OK = 0,
UF2_ERR_IGNORE, // block should be ignored
UF2_ERR_MAGIC, // wrong magic numbers
UF2_ERR_FAMILY, // family ID mismatched
UF2_ERR_NOT_HEADER, // block is not a header
UF2_ERR_OTA_VER, // unknown/invalid OTA format version
UF2_ERR_OTA_WRONG, // no data for current OTA index
UF2_ERR_PART_404, // no partition with that name
UF2_ERR_PART_ONE, // only one partition tag in a block
UF2_ERR_PART_UNSET, // image broken - attempted to write without target partition
UF2_ERR_DATA_TOO_LONG, // data too long - tags won't fit
UF2_ERR_SEQ_MISMATCH, // sequence number mismatched
UF2_ERR_ERASE_FAILED, // erasing flash failed
UF2_ERR_WRITE_FAILED, // writing to flash failed
UF2_ERR_WRITE_LENGTH, // wrote fewer data than requested
} uf2_err_t;

View File

@@ -0,0 +1,2 @@
DisableFormat: true
SortIncludes: Never

View File

@@ -0,0 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-23. */
#pragma once
#include "mDNS.h"

View File

@@ -0,0 +1,153 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-23. */
#ifdef LT_HAS_LWIP2
#include "mDNS.h"
extern "C" {
#include <lwip/apps/mdns.h>
#include <lwip/netif.h>
}
static u8_t mdns_netif_client_id = 0; // TODO fix this
struct mdns_domain {
/* Encoded domain name */
u8_t name[256];
/* Total length of domain name, including zero */
u16_t length;
/* Set if compression of this domain is not allowed */
u8_t skip_compression;
};
/** Description of a service */
struct mdns_service {
/** TXT record to answer with */
struct mdns_domain txtdata;
/** Name of service, like 'myweb' */
char name[MDNS_LABEL_MAXLEN + 1];
/** Type of service, like '_http' */
char service[MDNS_LABEL_MAXLEN + 1];
/** Callback function and userdata
* to update txtdata buffer */
service_get_txt_fn_t txt_fn;
void *txt_userdata;
/** TTL in seconds of SRV/TXT replies */
u32_t dns_ttl;
/** Protocol, TCP or UDP */
u16_t proto;
/** Port of the service */
u16_t port;
};
/** Description of a host/netif */
struct mdns_host {
/** Hostname */
char name[MDNS_LABEL_MAXLEN + 1];
/** Pointer to services */
struct mdns_service *services[MDNS_MAX_SERVICES];
/** TTL in seconds of A/AAAA/PTR replies */
u32_t dns_ttl;
};
static String mdnsInstanceName = "default_instance";
mDNS::mDNS() {}
mDNS::~mDNS() {}
bool mDNS::begin(const char *hostname) {
mdns_resp_init();
struct netif *netif = netif_list;
while (netif != NULL) {
// TODO: detect mdns_netif_client_id by checking netif_get_client_data()
// and finding the requested hostname in struct mdns_host
if (netif_is_up(netif) && mdns_resp_add_netif(netif, hostname, 255) != ERR_OK) {
return false;
}
netif = netif->next;
}
return true;
}
void mDNS::end() {
struct netif *netif = netif_list;
while (netif != NULL) {
if (netif_is_up(netif))
mdns_resp_remove_netif(netif);
netif = netif->next;
}
}
void mDNS::setInstanceName(String name) {
mdnsInstanceName = name;
}
bool mDNS::addService(char *service, char *proto, uint16_t port) {
char _service[strlen(service) + 2];
char _proto[strlen(proto) + 2];
_service[0] = '_';
_proto[0] = '_';
// prepend names with _
strcpy(_service + 1, service + (service[0] == '_'));
strcpy(_proto + 1, proto + (proto[0] == '_'));
mdns_sd_proto protocol = DNSSD_PROTO_UDP;
if (strncmp(_proto + 1, "tcp", 3) == 0)
protocol = DNSSD_PROTO_TCP;
struct netif *netif = netif_list;
while (netif != NULL) {
if (netif_is_up(netif)) {
mdns_resp_add_service(netif, mdnsInstanceName.c_str(), service, protocol, port, 255, NULL, NULL);
}
netif = netif->next;
}
}
bool mDNS::addServiceTxt(char *name, char *proto, char *key, char *value) {
char _name[strlen(name) + 2];
char _proto[strlen(proto) + 2];
_name[0] = '_';
_proto[0] = '_';
// prepend names with _
strcpy(_name + 1, name + (name[0] == '_'));
strcpy(_proto + 1, proto + (proto[0] == '_'));
mdns_sd_proto protocol = DNSSD_PROTO_UDP;
if (strncmp(_proto + 1, "tcp", 3) == 0)
protocol = DNSSD_PROTO_TCP;
struct netif *netif = netif_list;
struct mdns_host *mdns;
struct mdns_service *service;
uint8_t txt_len = strlen(key) + strlen(value) + 1;
char *txt = (char *)malloc(txt_len + 1);
sprintf(txt, "%s=%s", key, value);
while (netif != NULL) {
if (netif_is_up(netif)) {
mdns = (struct mdns_host *)netif_get_client_data(netif, mdns_netif_client_id);
for (uint8_t i = 0; i < MDNS_MAX_SERVICES; i++) {
service = mdns->services[i];
if (service == NULL)
continue;
if (strcmp(service->service, _name) || service->proto != protocol)
continue;
if (mdns_resp_add_service_txtitem(service, txt, txt_len) != ERR_OK) {
free(txt);
return false;
}
}
}
netif = netif->next;
}
free(txt);
return true;
}
MDNSResponder MDNS;
#endif

View File

@@ -0,0 +1,119 @@
/*
ESP8266 Multicast DNS (port of CC3000 Multicast DNS library)
Version 1.1
Copyright (c) 2013 Tony DiCola (tony@tonydicola.com)
ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com)
MDNS-SD Suport 2015 Hristo Gochkov (hristo@espressif.com)
Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com)
Rewritten for ESP32 by Hristo Gochkov (hristo@espressif.com)
This is a simple implementation of multicast DNS query support for an Arduino
running on ESP32 chip.
Usage:
- Include the ESP32 Multicast DNS library in the sketch.
- Call the begin method in the sketch's setup and provide a domain name (without
the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the
Adafruit CC3000 class instance. Optionally provide a time to live (in seconds)
for the DNS record--the default is 1 hour.
- Call the update method in each iteration of the sketch's loop function.
License (MIT license):
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#pragma once
#include <Arduino.h>
#include <api/IPv6Address.h>
class mDNS {
public:
mDNS();
~mDNS();
bool begin(const char *hostname);
void end();
void setInstanceName(String name);
bool addService(char *service, char *proto, uint16_t port);
bool addServiceTxt(char *name, char *proto, char *key, char *value);
// void enableArduino(uint16_t port = 3232, bool auth = false);
// void disableArduino();
// void enableWorkstation(esp_interface_t interface = ESP_IF_WIFI_STA);
// void disableWorkstation();
IPAddress queryHost(char *host, uint32_t timeout = 2000);
int queryService(char *service, char *proto);
String hostname(int idx);
IPAddress IP(int idx);
IPv6Address IPv6(int idx);
uint16_t port(int idx);
int numTxt(int idx);
bool hasTxt(int idx, const char *key);
String txt(int idx, const char *key);
String txt(int idx, int txtIdx);
String txtKey(int idx, int txtIdx);
void setInstanceName(const char *name) {
setInstanceName(String(name));
}
void setInstanceName(char *name) {
setInstanceName(String(name));
}
bool addService(const char *service, const char *proto, uint16_t port) {
return addService((char *)service, (char *)proto, port);
}
bool addService(String service, String proto, uint16_t port) {
return addService(service.c_str(), proto.c_str(), port);
}
void addServiceTxt(const char *name, const char *proto, const char *key, const char *value) {
addServiceTxt((char *)name, (char *)proto, (char *)key, (char *)value);
}
void addServiceTxt(String name, String proto, String key, String value) {
addServiceTxt(name.c_str(), proto.c_str(), key.c_str(), value.c_str());
}
IPAddress queryHost(const char *host, uint32_t timeout = 2000) {
return queryHost((char *)host, timeout);
}
IPAddress queryHost(String host, uint32_t timeout = 2000) {
return queryHost(host.c_str(), timeout);
}
int queryService(const char *service, const char *proto) {
return queryService((char *)service, (char *)proto);
}
int queryService(String service, String proto) {
return queryService(service.c_str(), proto.c_str());
}
};
typedef mDNS MDNSResponder;
extern MDNSResponder MDNS;

View File

@@ -0,0 +1,25 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-24. */
#pragma once
// Flash device configuration
extern const struct fal_flash_dev flash0;
#define FAL_FLASH_DEV_NAME "flash0"
#define FAL_FLASH_DEV_TABLE \
{ &flash0, }
#define FAL_DEV_NAME_MAX 16 // no need for 24 chars (default)
// Partition table
#define FAL_PART_HAS_TABLE_CFG
#define FAL_PART_TABLE_ITEM(part_lower, part_upper) \
{ \
.magic_word = FAL_PART_MAGIC_WORD, /* magic word */ \
.name = #part_lower, /* lowercase name as string */ \
.flash_name = FAL_FLASH_DEV_NAME, /* flash device name */ \
.offset = FLASH_##part_upper##_OFFSET, /* partition offset macro as uppercase string */ \
.len = FLASH_##part_upper##_LENGTH, /* partition length macro as uppercase string */ \
},

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _FDB_CFG_H_
#define _FDB_CFG_H_
/* using KVDB feature */
#define FDB_USING_KVDB
#ifdef FDB_USING_KVDB
/* Auto update KV to latest default when current KVDB version number is changed. @see fdb_kvdb.ver_num */
// #define FDB_KV_AUTO_UPDATE
#endif
/* using TSDB (Time series database) feature */
// #define FDB_USING_TSDB
/* Using FAL storage mode */
#define FDB_USING_FAL_MODE
#ifdef FDB_USING_FAL_MODE
/* the flash write granularity, unit: bit
* only support 1(nor flash)/ 8(stm32f2/f4)/ 32(stm32f1) */
#define FDB_WRITE_GRAN 8
#endif
/* Using file storage mode by LIBC file API, like fopen/fread/fwrte/fclose */
// #define FDB_USING_FILE_LIBC_MODE
/* Using file storage mode by POSIX file API, like open/read/write/close */
// #define FDB_USING_FILE_POSIX_MODE
/* MCU Endian Configuration, default is Little Endian Order. */
// #define FDB_BIG_ENDIAN
/* log print macro. default EF_PRINT macro is printf() */
#define FDB_PRINT(...)
/* print debug information */
// #define FDB_DEBUG_ENABLE
#endif /* _FDB_CFG_H_ */

View File

@@ -0,0 +1,2 @@
DisableFormat: true
SortIncludes: Never

View File

@@ -0,0 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-16. */
extern char *strdup(const char *);
extern int strcasecmp(const char *s1, const char *s2);
extern int strncasecmp(const char *s1, const char *s2, size_t n);

View File

@@ -0,0 +1,97 @@
/* $OpenBSD: strcasecmp.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */
/*
* Copyright (c) 1987, 1993
* The Regents of the University of California. 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.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 <string.h>
typedef unsigned char u_char;
/*
* This array is designed for mapping upper and lower case letter
* together for a case independent comparison. The mappings are
* based upon ascii character sequences.
*/
static const u_char charmap[] = {
'\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
'\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
'\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
'\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
'\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
'\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
'\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
'\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
'\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
'\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
'\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
'\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
'\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
'\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
'\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
'\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
'\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
'\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
'\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
'\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
'\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
'\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317',
'\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327',
'\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337',
'\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
'\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
};
int
strcasecmp(const char *s1, const char *s2)
{
const u_char *cm = charmap;
const u_char *us1 = (const u_char *)s1;
const u_char *us2 = (const u_char *)s2;
while (cm[*us1] == cm[*us2++])
if (*us1++ == '\0')
return (0);
return (cm[*us1] - cm[*--us2]);
}
int
strncasecmp(const char *s1, const char *s2, size_t n)
{
if (n != 0) {
const u_char *cm = charmap;
const u_char *us1 = (const u_char *)s1;
const u_char *us2 = (const u_char *)s2;
do {
if (cm[*us1] != cm[*us2++])
return (cm[*us1] - cm[*--us2]);
if (*us1++ == '\0')
break;
} while (--n != 0);
}
return (0);
}

View File

@@ -1,6 +1,8 @@
#include "LibreTuyaAPI.h"
/* Copyright (c) Kuba Szczodrzyński 2022-05-16. */
__weak char *strdup(const char *s) {
#include <stddef.h>
__attribute__((weak)) char *strdup(const char *s) {
size_t len = strlen(s) + 1;
void *newp = malloc(len);
if (newp == NULL)

View File

@@ -1,58 +1,29 @@
/*
Arduino.h - Main include file for the Arduino SDK
Copyright (c) 2005-2013 Arduino Team. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Copyright (c) Kuba Szczodrzyński 2022-04-23. */
#pragma once
#ifdef __cplusplus
#include "WCharacterFixup.h"
#endif
#define PinMode PinModeArduino // this conflicts with SDK enum
#include <api/ArduinoAPI.h>
#include <api/LibreTuyaAPI.h>
#include <core/LibreTuyaAPI.h>
#undef PinMode
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
extern "C" uint32_t SystemCoreClock;
#else
extern uint32_t SystemCoreClock;
#define clockCyclesPerMicrosecond() ( SystemCoreClock / 1000000L )
#define clockCyclesToMicroseconds(a) ( ((a) * 1000L) / (SystemCoreClock / 1000L) )
#define microsecondsToClockCycles(a) ( (a) * (SystemCoreClock / 1000000L) )
#include "WVariant.h"
#ifdef __cplusplus
} // extern "C"
#endif
#define clockCyclesPerMicrosecond() (SystemCoreClock / 1000000L)
#define clockCyclesToMicroseconds(a) (((a)*1000L) / (SystemCoreClock / 1000L))
#define microsecondsToClockCycles(a) ((a) * (SystemCoreClock / 1000000L))
// Include board variant
#include "variant.h"
#define interrupts() vPortClearInterruptMask(0)
#define interrupts() vPortClearInterruptMask(0)
#define noInterrupts() ulPortSetInterruptMask()
/*
* \brief SAM3 products have only one reference for ADC
*/
typedef enum _eAnalogReference
{
AR_DEFAULT,
} eAnalogReference ;
// Include family-specific code
#include "WVariant.h"
// Include board variant
#include "variant.h"

View File

@@ -1,7 +0,0 @@
#include <api/Print.h>
class CountingStream : public Print
{
virtual size_t write(uint8_t) { return 1; };
virtual size_t write(const uint8_t *buffer, size_t size) { return size; };
};

View File

@@ -1,198 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2015 ARM Limited
*
* 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.
*/
#ifndef MBED_FUNCTIONPOINTER_H
#define MBED_FUNCTIONPOINTER_H
#include <string.h>
#include <stdint.h>
/* If we had variaditic templates, this wouldn't be a problem, but until C++11 is enabled, we are stuck with multiple classes... */
/** A class for storing and calling a pointer to a static or member function
*/
template <typename R, typename A1>
class FunctionPointerArg1{
public:
/** Create a FunctionPointer, attaching a static function
*
* @param function The static function to attach (default is none)
*/
FunctionPointerArg1(R (*function)(A1) = 0) {
attach(function);
}
/** Create a FunctionPointer, attaching a member function
*
* @param object The object pointer to invoke the member function on (i.e. the this pointer)
* @param function The address of the member function to attach
*/
template<typename T>
FunctionPointerArg1(T *object, R (T::*member)(A1)) {
attach(object, member);
}
/** Attach a static function
*
* @param function The static function to attach (default is none)
*/
void attach(R (*function)(A1)) {
_p.function = function;
_membercaller = 0;
}
/** Attach a member function
*
* @param object The object pointer to invoke the member function on (i.e. the this pointer)
* @param function The address of the member function to attach
*/
template<typename T>
void attach(T *object, R (T::*member)(A1)) {
_p.object = static_cast<void*>(object);
*reinterpret_cast<R (T::**)(A1)>(_member) = member;
_membercaller = &FunctionPointerArg1::membercaller<T>;
}
/** Call the attached static or member function
*/
R call(A1 a) {
if (_membercaller == 0 && _p.function) {
return _p.function(a);
} else if (_membercaller && _p.object) {
return _membercaller(_p.object, _member, a);
}
return (R)0;
}
/** Get registered static function
*/
R(*get_function(A1))() {
return _membercaller ? (R(*)(A1))0 : (R(*)(A1))_p.function;
}
R operator ()(A1 a) {
return call(a);
}
operator bool(void) const {
return (_membercaller != NULL ? _p.object : (void*)_p.function) != NULL;
}
private:
template<typename T>
static R membercaller(void *object, uintptr_t *member, A1 a) {
T* o = static_cast<T*>(object);
R (T::**m)(A1) = reinterpret_cast<R (T::**)(A1)>(member);
return (o->**m)(a);
}
union {
R (*function)(A1); // static function pointer
void *object; // object this pointer
} _p;
uintptr_t _member[4]; // aligned raw member function pointer storage - converted back by registered _membercaller
R (*_membercaller)(void*, uintptr_t*, A1); // registered membercaller function to convert back and call _m.member on _object
};
/** A class for storing and calling a pointer to a static or member function (R ()(void))
*/
template <typename R>
class FunctionPointerArg1<R, void>{
public:
/** Create a FunctionPointer, attaching a static function
*
* @param function The static function to attach (default is none)
*/
FunctionPointerArg1(R (*function)(void) = 0) {
attach(function);
}
/** Create a FunctionPointer, attaching a member function
*
* @param object The object pointer to invoke the member function on (i.e. the this pointer)
* @param function The address of the void member function to attach
*/
template<typename T>
FunctionPointerArg1(T *object, R (T::*member)(void)) {
attach(object, member);
}
/** Attach a static function
*
* @param function The void static function to attach (default is none)
*/
void attach(R (*function)(void)) {
_p.function = function;
_membercaller = 0;
}
/** Attach a member function
*
* @param object The object pointer to invoke the member function on (i.e. the this pointer)
* @param function The address of the void member function to attach
*/
template<typename T>
void attach(T *object, R (T::*member)(void)) {
_p.object = static_cast<void*>(object);
*reinterpret_cast<R (T::**)(void)>(_member) = member;
_membercaller = &FunctionPointerArg1::membercaller<T>;
}
/** Call the attached static or member function
*/
R call(){
if (_membercaller == 0 && _p.function) {
return _p.function();
} else if (_membercaller && _p.object) {
return _membercaller(_p.object, _member);
}
return (R)0;
}
/** Get registered static function
*/
R(*get_function())() {
return _membercaller ? (R(*)())0 : (R(*)())_p.function;
}
R operator ()(void) {
return call();
}
operator bool(void) const {
return (_membercaller != NULL ? _p.object : (void*)_p.function) != NULL;
}
private:
template<typename T>
static R membercaller(void *object, uintptr_t *member) {
T* o = static_cast<T*>(object);
R (T::**m)(void) = reinterpret_cast<R (T::**)(void)>(member);
return (o->**m)();
}
union {
R (*function)(void); // static function pointer
void *object; // object this pointer
} _p;
uintptr_t _member[4]; // aligned raw member function pointer storage - converted back by registered _membercaller
R (*_membercaller)(void*, uintptr_t*); // registered membercaller function to convert back and call _m.member on _object
};
typedef FunctionPointerArg1<void, void> FunctionPointer;
typedef FunctionPointerArg1<void, int> event_callback_t;
#endif

View File

@@ -16,117 +16,83 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "Arduino.h"
#include "LOGUARTClass.h"
#include <Arduino.h>
#define LOG_UART_MODIFIABLE_BAUD_RATE 1
#ifdef __cplusplus
extern "C" {
#endif
#include "osdep_service.h"
#include "rtl8710b.h"
extern int LOGUART_SetBaud(uint32_t BaudRate); // from fixups/log_uart.c
#ifdef __cplusplus
}
#endif
RingBuffer rx_buffer0;
LOGUARTClass::LOGUARTClass(int dwIrq, RingBuffer* pRx_buffer )
{
_rx_buffer = pRx_buffer;
_dwIrq = dwIrq;
LOGUARTClass::LOGUARTClass(int dwIrq, RingBuffer *pRx_buffer) {
_rx_buffer = pRx_buffer;
_dwIrq = dwIrq;
}
// Protected Methods //////////////////////////////////////////////////////////////
void IrqHandler(void) {
uint8_t data = 0;
BOOL PullMode = _FALSE;
uint32_t IrqEn = DiagGetIsrEnReg();
DiagSetIsrEnReg(0);
// Public Methods //////////////////////////////////////////////////////////////
void IrqHandler( void )
{
uint8_t data = 0;
BOOL PullMode = _FALSE;
uint32_t IrqEn = DiagGetIsrEnReg();
DiagSetIsrEnReg(0);
data = DiagGetChar(PullMode);
if ( data > 0 )
data = DiagGetChar(PullMode);
if (data > 0)
rx_buffer0.store_char(data);
DiagSetIsrEnReg(IrqEn);
DiagSetIsrEnReg(IrqEn);
}
void LOGUARTClass::begin( const uint32_t dwBaudRate )
{
DIAG_UartReInit((IRQ_FUN) IrqHandler);
NVIC_SetPriority(UART_LOG_IRQ, 10);
LOGUART_SetBaud(dwBaudRate);
void LOGUARTClass::begin(const uint32_t dwBaudRate) {
DIAG_UartReInit((IRQ_FUN)IrqHandler);
NVIC_SetPriority(UART_LOG_IRQ, 10);
LOGUART_SetBaud(dwBaudRate);
}
void LOGUARTClass::end( void )
{
// clear any received data
_rx_buffer->_iHead = _rx_buffer->_iTail ;
void LOGUARTClass::end(void) {
// clear any received data
_rx_buffer->_iHead = _rx_buffer->_iTail;
}
int LOGUARTClass::available( void )
{
return (uint32_t)(SERIAL_BUFFER_SIZE + _rx_buffer->_iHead - _rx_buffer->_iTail) % SERIAL_BUFFER_SIZE ;
int LOGUARTClass::available(void) {
return (uint32_t)(SERIAL_BUFFER_SIZE + _rx_buffer->_iHead - _rx_buffer->_iTail) % SERIAL_BUFFER_SIZE;
}
int LOGUARTClass::peek( void )
{
int LOGUARTClass::peek(void) {
if ( _rx_buffer->_iHead == _rx_buffer->_iTail )
return -1 ;
return _rx_buffer->_aucBuffer[_rx_buffer->_iTail] ;
if (_rx_buffer->_iHead == _rx_buffer->_iTail)
return -1;
return _rx_buffer->_aucBuffer[_rx_buffer->_iTail];
}
int LOGUARTClass::read( void )
{
// if the head isn't ahead of the tail, we don't have any characters
if ( _rx_buffer->_iHead == _rx_buffer->_iTail )
return -1 ;
uint8_t uc = _rx_buffer->_aucBuffer[_rx_buffer->_iTail] ;
_rx_buffer->_iTail = (unsigned int)(_rx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE ;
return uc ;
int LOGUARTClass::read(void) {
// if the head isn't ahead of the tail, we don't have any characters
if (_rx_buffer->_iHead == _rx_buffer->_iTail)
return -1;
uint8_t uc = _rx_buffer->_aucBuffer[_rx_buffer->_iTail];
_rx_buffer->_iTail = (unsigned int)(_rx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE;
return uc;
}
void LOGUARTClass::flush( void )
{
// TODO:
// while ( serial_writable(&(this->sobj)) != 1 );
/*
// Wait for transmission to complete
while ((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY)
;
*/
void LOGUARTClass::flush(void) {
// TODO:
// while ( serial_writable(&(this->sobj)) != 1 );
/*
// Wait for transmission to complete
while ((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY)
;
*/
}
size_t LOGUARTClass::write( const uint8_t uc_data )
{
DiagPutChar(uc_data);
return 1;
size_t LOGUARTClass::write(const uint8_t uc_data) {
DiagPutChar(uc_data);
return 1;
}
LOGUARTClass Serial(UART_LOG_IRQ, &rx_buffer0);
bool Serial_available() {
return Serial.available() > 0;
return Serial.available() > 0;
}

View File

@@ -8,7 +8,7 @@
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
@@ -18,36 +18,41 @@
#pragma once
#include "api/HardwareSerial.h"
#include "api/RingBuffer.h"
#include <api/HardwareSerial.h>
#include <api/RingBuffer.h>
using namespace arduino;
class LOGUARTClass : public HardwareSerial
{
// TODO this class begs to be rewritten :(
class LOGUARTClass : public HardwareSerial {
public:
LOGUARTClass(int dwIrq, RingBuffer* pRx_buffer );
LOGUARTClass(int dwIrq, RingBuffer *pRx_buffer);
void begin(const uint32_t dwBaudRate);
inline void begin(const uint32_t dwBaudRate, uint16_t config) {
begin(dwBaudRate); // TODO implement this properly
}
void end(void);
int available(void);
int peek(void);
int read(void);
void flush(void);
size_t write(const uint8_t c);
void begin(const uint32_t dwBaudRate);
using Print::write; // pull in write(str) and write(buf, size) from Print
inline void begin(const uint32_t dwBaudRate, uint16_t config) {
begin(dwBaudRate); // TODO implement this properly
}
operator bool() { return true; }; // UART always active
void end(void);
int available(void);
int peek(void);
int read(void);
void flush(void);
size_t write(const uint8_t c);
using Print::write; // pull in write(str) and write(buf, size) from Print
operator bool() {
return true; // UART always active
}
protected:
RingBuffer *_rx_buffer;
RingBuffer *_rx_buffer;
int _dwIrq;
int _dwIrq;
private:
friend bool Serial_available();
friend bool Serial_available();
};

View File

@@ -0,0 +1,145 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */
#include <LibreTuyaAPI.h>
extern "C" {
#include <flash_api.h>
#include <rtl8710b.h>
#include <sys_api.h>
}
void LibreTuya::restart() {
sys_reset();
}
/* CPU-related */
ChipType LibreTuya::getChipType() {
uint8_t chipId;
EFUSE_OneByteReadROM(9902, 0xF8, &chipId, L25EOUTVOLTAGE);
return (ChipType)(((RTL8710B >> 24) << 8) | chipId);
}
const char *LibreTuya::getChipModel() {
return STRINGIFY_MACRO(MCU);
}
uint32_t LibreTuya::getChipId() {
uint32_t chipId = 0;
uint8_t *id = (uint8_t *)&chipId;
// 9902 was extracted from ROM disassembly, probably not needed
EFUSE_OneByteReadROM(9902, 0x3B, id + 0, L25EOUTVOLTAGE);
EFUSE_OneByteReadROM(9902, 0x3C, id + 1, L25EOUTVOLTAGE);
EFUSE_OneByteReadROM(9902, 0x3D, id + 2, L25EOUTVOLTAGE);
return chipId;
}
uint8_t LibreTuya::getChipCores() {
return 1;
}
const char *LibreTuya::getChipCoreType() {
return "ARM Cortex-M4F";
}
uint32_t LibreTuya::getCpuFreqMHz() {
return CPU_ClkGet(false) / 1000000;
}
inline uint32_t LibreTuya::getCycleCount() {
return microsecondsToClockCycles(micros());
}
/* Memory management */
uint32_t LibreTuya::getRamSize() {
return 256 * 1024;
}
uint32_t LibreTuya::getHeapSize() {
return configTOTAL_HEAP_SIZE;
}
uint32_t LibreTuya::getFreeHeap() {
return xPortGetFreeHeapSize();
}
uint32_t LibreTuya::getMinFreeHeap() {
return xPortGetMinimumEverFreeHeapSize();
}
uint32_t LibreTuya::getMaxAllocHeap() {
return 0;
}
/* OTA-related */
uint8_t LibreTuya::otaGetStoredIndex() {
uint32_t *otaAddress = (uint32_t *)0x8009000;
if (*otaAddress == 0xFFFFFFFF)
return 1;
uint32_t otaCounter = *((uint32_t *)0x8009004);
// even count of zero-bits means OTA1, odd count means OTA2
// this allows to switch OTA images by simply clearing next bits,
// without needing to erase the flash
uint8_t count = 0;
for (uint8_t i = 0; i < 32; i++) {
if ((otaCounter & (1 << i)) == 0)
count++;
}
return 1 + (count % 2);
}
bool LibreTuya::otaSupportsDual() {
return true;
}
bool LibreTuya::otaHasImage1() {
uint8_t *ota1Addr = (uint8_t *)(SPI_FLASH_BASE + FLASH_OTA1_OFFSET);
return memcmp(ota1Addr, "81958711", 8) == 0;
}
bool LibreTuya::otaHasImage2() {
uint8_t *ota2Addr = (uint8_t *)(SPI_FLASH_BASE + FLASH_OTA2_OFFSET);
return memcmp(ota2Addr, "81958711", 8) == 0;
}
bool LibreTuya::otaSwitch(bool force) {
if (!force && otaGetRunning() != otaGetStoredIndex())
// OTA has already been switched
return true;
// this function does:
// - read OTA1 firmware magic from 0xB000
// - read OTA2 address from 0x9000
// - read OTA2 firmware magic from that address
// - read current OTA switch value from 0x9004
// - reset OTA switch to 0xFFFFFFFF if it's 0x0
// - check first non-zero bit of OTA switch
// - write OTA switch with first non-zero bit cleared
// sys_clear_ota_signature();
// ok, this function is broken (crashes with HardFault)
if (!otaHasImage1() || !otaHasImage2())
return false;
uint32_t value = HAL_READ32(SPI_FLASH_BASE, FLASH_SYSTEM_OFFSET + 4);
if (value == 0) {
// TODO does this work at all?
FLASH_EreaseDwordsXIP(FLASH_SYSTEM_OFFSET + 4, 1);
}
uint8_t i;
// find first non-zero bit
for (i = 0; i < 32; i++) {
if (value & (1 << i))
break;
}
// clear the bit
value &= ~(1 << i);
// write OTA switch to flash
flash_write_word(NULL, FLASH_SYSTEM_OFFSET + 4, value);
return true;
}
/* Global instance */
LibreTuya LT;

View File

@@ -1,76 +0,0 @@
#ifndef _POWER_MANAGEMENT_H_
#define _POWER_MANAGEMENT_H_
#include <inttypes.h>
/**
* @class PowerManagementClass PowerManagement.h
* @brief Power management in Ameba
*/
class PowerManagementClass {
public:
/**
* @brief Allow OS automatically save power while idle
*
* As OS consider it would idle for more than 2s, it will invoke system suspend.
* If wlan is associated with AP, than it will under asslociated idle state.
*/
static void sleep(void);
static void sleep(uint32_t bitflg);
/**
* @brief Disallow OS automatically save power while idle
*/
static void active(void);
static void active(uint32_t bitflg);
/**
* @brief Reserved PLL while sleep
*
* Reserve PLL would keep FIFO of peripherals (Ex. UART) but cost more power (around 5mA).
* If we don't reserve PLL, it saves more power but we might missing data because FIFO is turned of this way.
*
* @param[in] reserve true for reserved, false for non-reserved
*/
static void setPllReserved(bool reserve);
/**
* @brief Enter deepsleep immediately
*
* Invoke deepsleep would make system enter deepsleep state immediately.
* It's the state that saves most power.
* As it wakeup from deepsleep, the system would behave just like reboot.
*
* @param[in] duration_ms wakeup after specific time in unit of millisecond
*/
static void deepsleep(uint32_t duration_ms);
/**
* @brief Check if system is allowed enter any power save state
*
* The pin 18 (GPIOE_5) is designed as safe lock.
* If pin 18 is HIGH, then we prevent Ameba enter any power save state.\n\n
* Under any power save state, we are not able to flash image to Ameba.
* Thus if user misuse deepsleep and make Ameba enter deepsleep immediately after boot up,
* then he would find it's hard to flash image.
* In this case, he can pull up pin 18.
*
* @return true if system not allowed enter any power save state, and false vise versa
*/
static bool safeLock();
/**
* @brief Reboot system
*
* Reboot system in soft way. Some registers is not powered off in this case, but mostly we could regard this as reboot.
*/
static void softReset();
private:
static bool reservePLL;
};
extern PowerManagementClass PowerManagement;
#endif

View File

@@ -1,17 +1,12 @@
#if 1 // !defined(BOARD_RTL8710)
#include <Arduino.h>
#include "Arduino.h"
#ifdef __cplusplus
extern "C" {
extern void pinRemoveMode(pin_size_t pinNumber);
extern void _tone(uint32_t ulPin, unsigned int frequency, unsigned long duration);
}
#endif
// a wrapper that support default value of duration
void tone(uint32_t ulPin, unsigned int frequency, unsigned long duration)
{
_tone(ulPin, frequency, duration);
void tone(uint32_t ulPin, unsigned int frequency, unsigned long duration) {
_tone(ulPin, frequency, duration);
}
#endif
void noTone(uint32_t ulPin) {
pinRemoveMode(ulPin);
}

View File

@@ -1,16 +1,14 @@
#include "Arduino.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "gpio_api.h"
#include "gpio_irq_api.h"
#include "gpio_irq_ex_api.h"
#include <Arduino.h>
#include <gpio_api.h>
#include <gpio_irq_api.h>
#include <gpio_irq_ex_api.h>
extern void *gpio_pin_struct[PINS_COUNT];
extern void *gpio_irq_handler_list[PINS_COUNT];
extern bool pinInvalid(pin_size_t pinNumber);
extern void pinRemoveMode(pin_size_t pinNumber);
void gpioIrqHandler(uint32_t id, gpio_irq_event event) {
if (gpio_irq_handler_list[id] != NULL) {
((void (*)(uint32_t, uint32_t))gpio_irq_handler_list[id])(id, (uint32_t)event);
@@ -36,7 +34,7 @@ void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callback, PinStatus
if (g_APinDescription[interruptNumber].ulPinType == NOT_INITIAL) {
// allocate memory if pin not used before
gpio = malloc(sizeof(gpio_irq_t));
gpio = malloc(sizeof(gpio_irq_t));
gpio_pin_struct[interruptNumber] = gpio;
gpio_irq_init(gpio, g_APinDescription[interruptNumber].pinname, gpioIrqHandler, interruptNumber);
g_APinDescription[interruptNumber].ulPinType = PIO_GPIO_IRQ;
@@ -78,7 +76,3 @@ void detachInterrupt(pin_size_t interruptNumber) {
}
gpio_irq_handler_list[interruptNumber] = NULL;
}
#ifdef __cplusplus
}
#endif

View File

@@ -1,77 +0,0 @@
/*
Copyright (c) 2011 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <inttypes.h>
#include "api/Common.h"
extern "C" {
extern void rtl_srandom( uint32_t seed );
extern uint32_t rtl_random( void );
}
#ifndef srand
#define srand rtl_srandom
#endif
#ifndef rand
#define rand rtl_random
#endif
extern void randomSeed( uint32_t dwSeed )
{
if ( dwSeed != 0 )
{
srand( dwSeed ) ;
}
}
extern long random( long howbig )
{
if ( howbig == 0 )
{
return 0 ;
}
return rand() % howbig;
}
extern long random( long howsmall, long howbig )
{
if (howsmall >= howbig)
{
return howsmall;
}
long diff = howbig - howsmall;
return random(diff) + howsmall;
}
extern long map(long x, long in_min, long in_max, long out_min, long out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
extern uint16_t makeWord( uint16_t w )
{
return w ;
}
extern uint16_t makeWord( uint8_t h, uint8_t l )
{
return (h << 8) | l ;
}

View File

@@ -1,104 +1,60 @@
#pragma once
#include "wiring_os.h"
#include "wiring_watchdog.h"
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>
#define boolean boolean_rtl
#include "rtl_lib.h"
#undef boolean
#include "sdk_extern.h"
#include "sdk_mem.h"
#include "sdk_os.h"
/* moved from Arduino.h */
/*
* \brief Set CPU CLK 166MHz
* clk : 0 - 166666666 Hz, 1 - 83333333 Hz, 2 - 41666666 Hz, 3 - 20833333 Hz, 4 - 10416666 Hz, 5 - 4000000 Hz
* baud: 38400,...
*/
extern void Init_CPU_CLK_UART(int clkn, int baud);
extern void sys_info(void);
/* HalGetChipId:
* 0xff - RTL8711AM, 0xfe - RTL8195AM, 0xfd - RTL8711AF,
* 0xfc - RTL8710AF, 0xfb - RTL8711AN, 0xfa - RTL8710AM */
extern unsigned char HalGetChipId(void);
extern unsigned int HalGetCpuClk(void);
extern void wait_us(int us);
#define delay_us wait_us
extern void yield(void);
#ifndef printf
#define printf rtl_printf
#endif
#ifndef sprintf
#define sprintf rtl_sprintf
#endif
#define NOT_INITIAL (1UL<<0)
#define PIO_GPIO (1UL<<1)
#define PIO_PWM (1UL<<2)
#define PIO_I2C (1UL<<3)
#define PIO_ADC (1UL<<4)
#define PIO_DAC (1UL<<5)
#define PIO_GPIO_IRQ (1UL<<6)
#define NOT_INITIAL (1UL << 0)
#define PIO_GPIO (1UL << 1)
#define PIO_PWM (1UL << 2)
#define PIO_I2C (1UL << 3)
#define PIO_ADC (1UL << 4)
#define PIO_DAC (1UL << 5)
#define PIO_GPIO_IRQ (1UL << 6)
#define PWM_MODE_ENABLED 1
#define PWM_MODE_DISABLED 0
/* Types used for the tables below */
typedef struct _PinDescription
{
// HW PinNames
uint32_t pinname;
// Current Pin Type
uint32_t ulPinType;
// Supported Pin Function
uint32_t ulPinAttribute;
// Current Pin Mode
uint32_t ulPinMode;
} PinDescription ;
/* Pins table to be instanciated into variant.cpp */
extern PinDescription g_APinDescription[];
extern bool pinInvalid(pin_size_t pinNumber);
extern void pinRemoveMode(pin_size_t pinNumber);
/* moved from wiring_digital.h */
/**************************** Extend API by RTK ***********************************/
extern uint32_t digitalPinToPort( uint32_t pinNumber );
extern uint32_t digitalPinToBitMask( uint32_t pinNumber );
/* moved from wiring_analog.h */
/*
* \brief Set the resolution of analogRead return values. Default is 10 bits (range from 0 to 1023).
*
* \param res
*/
extern void analogReadResolution(int res);
/*
* \brief Set the resolution of analogWrite parameters. Default is 8 bits (range from 0 to 255).
*
* \param res
*/
extern void analogWriteResolution(int res);
extern void analogOutputInit( void ) ;
extern void analogWritePeriod(int us);
/* moved from wiring_constants.h */
#define DEFAULT 1
#define DEFAULT 1
#define EXTERNAL 0
#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
#define round(x) ((x) >= 0 ? (long)((x) + 0.5) : (long)((x)-0.5))
extern uint32_t ulPortSetInterruptMask( void );
extern void vPortClearInterruptMask( uint32_t ulNewMask );
typedef enum _eAnalogReference {
AR_DEFAULT,
} eAnalogReference;
// Types used for the table below
typedef struct _PinDescription {
// HW PinNames
uint32_t pinname;
// Current Pin Type
uint32_t ulPinType;
// Supported Pin Function
uint32_t ulPinAttribute;
// Current Pin Mode
uint32_t ulPinMode;
} PinDescription;
// Pins table to be instantiated into variant.cpp
extern PinDescription g_APinDescription[];
// Additional Wiring functions
extern uint32_t digitalPinToPort(uint32_t pinNumber);
extern uint32_t digitalPinToBitMask(uint32_t pinNumber);
extern void analogReadResolution(int res);
extern void analogWriteResolution(int res);
extern void analogOutputInit(void);
extern void analogWritePeriod(int us);
extern void wait_for_debug();
#ifdef __cplusplus
} // extern "C"

View File

@@ -1,100 +0,0 @@
/*
* WebSocketClient + SSL/TSL
* RTL8710AF pvvx 12/12/2016
*
*/
#include "Arduino.h"
#include "WebSocketClient.h"
WebSocketClient::WebSocketClient() {
}
WebSocketClient::WebSocketClient(char *url, int port, char *path, char* origin) {
client = create_wsclient(url, port, path, origin);
}
WebSocketClient::~WebSocketClient() {
close();
}
int WebSocketClient::begin(char *url, int port, char *path, char* origin) {
client = create_wsclient(url, port, path, origin);
if(client != NULL) return 1;
else return 0;
}
int WebSocketClient::connect() {
if(client != NULL) return ws_connect_url(client);
else return -1;
}
void WebSocketClient::send(char* message, int message_len, int use_mask) {
if(client != NULL && client->readyState > CLOSED) {
ws_send(message, message_len, use_mask, client);
ws_poll(0, client);
}
}
void WebSocketClient::sendBinary(uint8_t* message, int message_len, int use_mask) {
if(client != NULL && client->readyState > CLOSED) {
ws_sendBinary(message, message_len, use_mask, client);
ws_poll(0, client);
}
}
void WebSocketClient::sendPing() {
if(client != NULL && client->readyState > CLOSED) {
ws_sendPing(client);
ws_poll(0, client);
}
}
void WebSocketClient::poll(int timeout) {
if(client != NULL) ws_poll(timeout, client);
}
readyStateValues WebSocketClient::getReadyState() {
if(client != NULL) return ws_getReadyState(client);
else return CLOSED;
}
void WebSocketClient::dispatch(void (*callback)(wsclient_context *, int))
{
if(client != NULL) ws_dispatch(callback);
}
void WebSocketClient::close() {
if(client != NULL) {
ws_close(client);
if(client->ssl) {
free(client->ssl);
client->ssl = NULL;
}
client = NULL;
}
}
extern "C" void set_ssl_func(wsclient_context *wsclient); // in example_wsclient.c
void WebSocketClient::ssl_func_on(void)
{
set_ssl_func(client);
/*
client->fun_ops.ssl_fun_ops.memory_set_own = memory_set_own;
client->fun_ops.ssl_fun_ops.net_connect = net_connect;
client->fun_ops.ssl_fun_ops.ssl_init = ssl_init;
client->fun_ops.ssl_fun_ops.ssl_set_endpoint = ssl_set_endpoint;
client->fun_ops.ssl_fun_ops.ssl_set_authmode = ssl_set_authmode;
client->fun_ops.ssl_fun_ops.ssl_set_rng = ssl_set_rng;
client->fun_ops.ssl_fun_ops.ssl_set_bio = ssl_set_bio;
client->fun_ops.ssl_fun_ops.ssl_handshake = ssl_handshake;
client->fun_ops.ssl_fun_ops.net_close = net_close;
client->fun_ops.ssl_fun_ops.ssl_free = ssl_free;
client->fun_ops.ssl_fun_ops.ssl_read = ssl_read;
client->fun_ops.ssl_fun_ops.ssl_write = ssl_write;
client->fun_ops.ssl_fun_ops.net_recv = net_recv;
client->fun_ops.ssl_fun_ops.net_send = net_send;
client->ssl = (void *)zalloc(sizeof(struct _ssl_context)); // 380 bytes
*/
}

View File

@@ -1,116 +0,0 @@
/*
* WebSocketClient + SSL/TSL
* RTL8710AF pvvx 12/12/2016
*
*/
#ifndef WEBSOCKETCLIENT_H_
#define WEBSOCKETCLIENT_H_
#include <Arduino.h>
extern "C" {
#include "libwsclient.h"
#include "wsclient_api.h"
}
class WebSocketClient {
public:
WebSocketClient();
~WebSocketClient();
/*************************************************************************************************
** Function Name : begin
** Description : Creating the websocket client context structure
** Input : url:websocket server's url
** port:websocket server's port, if not given, default 80 for "ws", 443 for "wss"
** origin: the address or url of your self
** Return : Ok: 1
** Failed: 0
**************************************************************************************************/
WebSocketClient(char *url, int port, char *path = NULL, char* origin = NULL);
int begin(char *url, int port, char *path = NULL, char* origin = NULL);
/*************************************************************************************************
** Function Name : connect
** Description : Connecting to the websocket server
** Input : wsclient: the websocket client context created by create_wsclientfunction
** Return : Connected: the socket value
** Failed: -1
**************************************************************************************************/
int connect();
/*************************************************************************************************
** Function Name : ws_send
** Description : Create the sending string data and copy to tx_buf
** Input : message: the string that send to server(cannot exceeding the MAX_DATA_LEN
** message_len: the length of the string
** use_mask: 0/1; 1 means using mask for bynary
** wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void send(char* message, int message_len, int use_mask);
/*************************************************************************************************
** Function Name : sendBinary
** Description : Create the sending binary data and copy to tx_buf
** Input : message: the binary that send to server(cannot exceeding the MAX_DATA_LEN
** message_len: the length of the binary
** use_mask: 0/1; 1 means using mask for bynary
** wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void sendBinary(uint8_t* message, int message_len, int use_mask);
/*************************************************************************************************
** Function Name : sendPing
** Description : Sending Ping to websocket server
** Input : wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void sendPing();
/*************************************************************************************************
** Function Name : poll
** Description : Receicing data from server and send the data in tx_buf
** Input : timeout(in milliseconds)
wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void poll(int timeout);
/*************************************************************************************************
** Function Name : dispatch
** Description : callback function when getting message from server
** Input : function that resolve the message received and the message length
** Return : None
**************************************************************************************************/
void dispatch(void (*callback)(wsclient_context *, int));
/*************************************************************************************************
** Function Name : getReadyState
** Description : Getting the connection status
** Input : wsclient: the websocket client context
** Return : readyStateValues(4 types: CLOSING, CLOSED, CONNECTING, OPEN )
**************************************************************************************************/
readyStateValues getReadyState();
/*************************************************************************************************
** Function Name : close
** Description : Closing the connection with websocket server
** Input : wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void close();
/*************************************************************************************************
** Function Name : ssl_func_on
** Description : Set SSL/TSL function
** Input : None
** Return : None
**************************************************************************************************/
void ssl_func_on(void);
private:
wsclient_context *client;
};
#endif // WEBSOCKETCLIENT_H_

View File

@@ -1,214 +0,0 @@
#include <platform_opts.h>
#include <lwip/sockets.h>
#include <lwip/netif.h>
#include <platform/platform_stdlib.h>
#include "ard_socket.h"
int start_server(uint16_t port, uint8_t protMode)
{
int _sock;
int timeout;
if(protMode == 0) {
timeout = 3000;
_sock = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
lwip_setsockopt(_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
} else {
timeout = 1000;
_sock = lwip_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
lwip_setsockopt(_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
}
if (_sock < 0) {
printf("\r\nERROR opening socket\r\n");
return -1;
}
struct sockaddr_in localHost;
memset(&localHost, 0, sizeof(localHost));
localHost.sin_family = AF_INET;
localHost.sin_port = htons(port);
localHost.sin_addr.s_addr = INADDR_ANY;
if (lwip_bind(_sock, (struct sockaddr *)&localHost, sizeof(localHost)) < 0) {
printf("\r\nERROR on binding\r\n");
return -1;
}
return _sock;
}
int sock_listen(int sock, int max)
{
if(lwip_listen(sock , max) < 0){
printf("\r\nERROR on listening\r\n");
return -1;
}
return 0;
}
int get_available(int sock)
{
int enable = 1;
int timeout;
int client_fd;
int err;
struct sockaddr_in cli_addr;
socklen_t client = sizeof(cli_addr);
do {
client_fd = lwip_accept(sock, (struct sockaddr *) &cli_addr, &client);
if (client_fd < 0) {
err = get_sock_errno(sock);
if (err != EAGAIN) {
break;
}
}
} while (client_fd < 0);
if(client_fd < 0){
printf("\r\nERROR on accept\r\n");
return -1;
}
else {
timeout = 3000;
lwip_setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
timeout = 30000;
lwip_setsockopt(client_fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
lwip_setsockopt(client_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
lwip_setsockopt(client_fd, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
printf("\r\nA client connected to this server :\r\n[PORT]: %d\r\n[IP]:%s\r\n\r\n", ntohs(cli_addr.sin_port), inet_ntoa(cli_addr.sin_addr.s_addr));
return client_fd;
}
}
int get_receive(int sock, uint8_t* data, int length, int flag, uint32_t *peer_addr, uint16_t *peer_port)
{
int ret = 0;
struct sockaddr from;
socklen_t fromlen;
uint8_t backup_recvtimeout = 0;
int backup_recv_timeout, recv_timeout, len;
if (flag & 0x01) {
// for MSG_PEEK, we try to peek packets by changing receiving timeout to 10ms
ret = lwip_getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &backup_recv_timeout, &len);
if (ret >= 0) {
recv_timeout = 10;
ret = lwip_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &recv_timeout, sizeof(recv_timeout));
if (ret >= 0) {
backup_recvtimeout = 1;
}
}
}
ret = lwip_recvfrom(sock, data, length, flag, &from, &fromlen);
if ( ret >= 0 ) {
if (peer_addr != NULL) {
*peer_addr = ((struct sockaddr_in *)&from)->sin_addr.s_addr;
}
if (peer_port != NULL) {
*peer_port = ntohs(((struct sockaddr_in *)&from)->sin_port);
}
}
if ((flag & 0x01) && (backup_recvtimeout == 1)) {
// restore receiving timeout
lwip_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &backup_recv_timeout, sizeof(recv_timeout));
}
return ret;
}
int get_sock_errno(int sock)
{
int so_error;
socklen_t len = sizeof(so_error);
getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_error, &len);
return so_error;
}
int set_sock_recv_timeout(int sock, int timeout)
{
return lwip_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
}
void stop_socket(int sock)
{
lwip_close(sock);
}
int send_data(int sock, const uint8_t *data, uint16_t len)
{
int ret;
ret = lwip_write(sock, data, len);
return ret;
}
int sendto_data(int sock, const uint8_t *data, uint16_t len, uint32_t peer_ip, uint16_t peer_port)
{
int ret;
struct sockaddr_in peer_addr;
memset(&peer_addr, 0, sizeof(peer_addr));
peer_addr.sin_family = AF_INET;
peer_addr.sin_addr.s_addr = peer_ip;
peer_addr.sin_port = htons(peer_port);
ret = lwip_sendto(sock, data, len, 0, (struct sockaddr*)&peer_addr, sizeof(struct sockaddr_in));
return ret;
}
int start_client(uint32_t ipAddress, uint16_t port, uint8_t protMode)
{
int enable = 1;
int timeout;
int _sock;
if(protMode == 0)//tcp
_sock = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
else
_sock = lwip_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (_sock < 0) {
printf("\r\nERROR opening socket\r\n");
return -1;
}
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = ipAddress;
serv_addr.sin_port = htons(port);
if (protMode == 0){//TCP MODE
if(connect(_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == 0){
printf("\r\nConnect to Server successful!\r\n");
timeout = 3000;
lwip_setsockopt(_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
timeout = 30000;
lwip_setsockopt(_sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
lwip_setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable));
lwip_setsockopt(_sock, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
return _sock;
}
else{
printf("\r\nConnect to Server failed!\r\n");
stop_socket(_sock);
return -1;
}
}
else {
//printf("\r\nUdp client setup Server's information successful!\r\n");
}
return _sock;
}

View File

@@ -1,25 +0,0 @@
#ifndef ARD_SOCKET_H
#define ARD_SOCKET_H
#include "main.h"
int start_server(uint16_t port, uint8_t protMode);
int sock_listen(int sock, int max);
int get_available(int sock);
int get_receive(int sock, uint8_t* data, int length, int flag, uint32_t *peer_addr, uint16_t *peer_port);
int get_sock_errno(int sock);
int set_sock_recv_timeout(int sock, int timeout);
void stop_socket(int sock);
int send_data(int sock, const uint8_t *data, uint16_t len);
int sendto_data(int sock, const uint8_t *data, uint16_t len, uint32_t peer_ip, uint16_t peer_port);
int start_client(uint32_t ipAddress, uint16_t port, uint8_t protMode);
#endif

View File

@@ -1,402 +0,0 @@
#include "Arduino.h"
#include <sockets.h>
#include <lwip/netif.h>
#include <mbedtls/ssl.h>
#include <mbedtls/net_sockets.h>
#include <mbedtls/error.h>
#include <mbedtls/debug.h>
#include "ard_ssl.h"
#define ARDUINO_MBEDTLS_DEBUG_LEVEL 0 // Set to 0 to disable debug messsages, 5 to enable all debug messages
static unsigned int ard_ssl_arc4random(void)
{
unsigned int res = xTaskGetTickCount();
static unsigned int seed = 0xDEADB00B;
seed = ((seed & 0x007F00FF) << 7) ^
((seed & 0x0F80FF00) >> 8) ^ // be sure to stir those low bits
(res << 13) ^ (res >> 9); // using the clock too!
return seed;
}
static void get_random_bytes(void *buf, size_t len)
{
unsigned int ranbuf;
unsigned int *lp;
int i, count;
count = len / sizeof(unsigned int);
lp = (unsigned int *) buf;
for (i = 0; i < count; i ++) {
lp[i] = ard_ssl_arc4random();
len -= sizeof(unsigned int);
}
if (len > 0) {
ranbuf = ard_ssl_arc4random();
memcpy(&lp[i], &ranbuf, len);
}
}
static int my_random(void *p_rng, unsigned char *output, size_t output_len)
{
p_rng = p_rng;
get_random_bytes(output, output_len);
return 0;
}
static int my_verify(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
{
char buf[1024];
((void)data);
mbedtls_x509_crt_info(buf, (sizeof(buf) - 1), "", crt);
if(ARDUINO_MBEDTLS_DEBUG_LEVEL < 3)
return(0);
printf( "\nVerify requested for (Depth %d):\n", depth );
printf( "%s", buf );
if ((*flags) == 0)
printf(" This certificate has no flags\n");
else
{
mbedtls_x509_crt_verify_info(buf, sizeof( buf ), " ! ", *flags);
printf("%s\n", buf);
}
return(0);
}
static void* my_calloc(size_t nelements, size_t elementSize)
{
size_t size;
void *ptr = NULL;
size = nelements * elementSize;
ptr = pvPortMalloc(size);
if(ptr)
memset(ptr, 0, size);
return ptr;
}
static void my_debug(void *ctx, int level, const char *file, int line, const char *str )
{
const char *p, *basename;
ctx = ctx; // Remove unused parameter warning
// Extract basename from file
for( p = basename = file; *p != '\0'; p++ )
if( *p == '/' || *p == '\\' )
basename = p + 1;
printf("%s:%04d: |%d| %s", basename, line, level, str );
}
int start_ssl_client(sslclient_context *ssl_client, uint32_t ipAddress, uint32_t port, unsigned char* rootCABuff, unsigned char* cli_cert, unsigned char* cli_key, unsigned char* pskIdent, unsigned char* psKey, char* SNI_hostname)
{
int ret = 0;
//int timeout;
int enable = 1;
int keep_idle = 30;
mbedtls_x509_crt* cacert = NULL;
mbedtls_x509_crt* _cli_crt = NULL;
mbedtls_pk_context* _clikey_rsa = NULL;
do {
ssl_client->socket = -1;
ssl_client->socket = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ssl_client->socket < 0) {
printf("ERROR: opening socket failed! \r\n");
ret = -1;
break;
}
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = ipAddress;
serv_addr.sin_port = htons(port);
lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_KEEPIDLE, &keep_idle, sizeof(keep_idle));
if (lwip_connect(ssl_client->socket, ((struct sockaddr *)&serv_addr), sizeof(serv_addr)) < 0) {
lwip_close(ssl_client->socket);
printf("ERROR: Connect to Server failed! \r\n");
ret = -1;
break;
} else {
/*/
if (lwip_connect(ssl_client->socket, ((struct sockaddr *)&serv_addr), sizeof(serv_addr)) == 0) {
timeout = ssl_client->recvTimeout;
lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
timeout = 30000;
lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable));
lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
} else {
printf("ERROR: Connect to Server failed!\r\n");
ret = -1;
break;
}//*/
mbedtls_platform_set_calloc_free(my_calloc,vPortFree);
ssl_client->ssl = (mbedtls_ssl_context *)malloc(sizeof(mbedtls_ssl_context));
ssl_client->conf = (mbedtls_ssl_config *)malloc(sizeof(mbedtls_ssl_config));
if (( ssl_client->ssl == NULL )||( ssl_client->conf == NULL )) {
printf("ERROR: malloc ssl failed! \r\n");
ret = -1;
break;
}
mbedtls_ssl_init(ssl_client->ssl);
mbedtls_ssl_config_init(ssl_client->conf);
if (ARDUINO_MBEDTLS_DEBUG_LEVEL > 0) {
mbedtls_ssl_conf_verify(ssl_client->conf, my_verify, NULL);
mbedtls_ssl_conf_dbg(ssl_client->conf, my_debug, NULL);
mbedtls_debug_set_threshold(ARDUINO_MBEDTLS_DEBUG_LEVEL);
}
if((mbedtls_ssl_config_defaults(ssl_client->conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
printf("ERROR: mbedtls ssl config defaults failed! \r\n");
ret = -1;
break;
}
mbedtls_ssl_conf_rng(ssl_client->conf, my_random, NULL);
if (rootCABuff != NULL) {
// Configure mbedTLS to use certificate authentication method
cacert = (mbedtls_x509_crt *) mbedtls_calloc( sizeof(mbedtls_x509_crt), 1);
mbedtls_x509_crt_init(cacert);
if (mbedtls_x509_crt_parse(cacert, rootCABuff, (strlen((char*)rootCABuff)) + 1) != 0) {
printf("ERROR: mbedtls x509 crt parse failed! \r\n");
ret = -1;
break;
}
mbedtls_ssl_conf_ca_chain(ssl_client->conf, cacert, NULL);
mbedtls_ssl_conf_authmode(ssl_client->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
} else if (pskIdent != NULL && psKey != NULL) {
// Configure mbedTLS to use PSK authentication method
// Check for max length and even number of chars
uint16_t pskey_char_len = strlen((char*)psKey);
if ( ((pskey_char_len % 2) != 0) || (pskey_char_len > 2*MBEDTLS_PSK_MAX_LEN) ) {
printf("ERROR: TLS PSK not in valid hex format or too long \n");
return -1;
}
uint16_t psk_len = pskey_char_len/2;
unsigned char psk[MBEDTLS_PSK_MAX_LEN];
// Convert PSK from hexadecimal chars to binary
int i;
for (i = 0; i < pskey_char_len; i = i + 2) {
char c = psKey[i];
// Convert first 4 bits
if (c >= '0' && c <= '9') {
c = c - '0';
} else if (c >= 'A' && c <= 'F') {
c = c - 'A' + 10;
} else if (c >= 'a' && c <= 'f') {
c = c - 'a' + 10;
} else {
printf("ERROR: TLS PSK not in valid hex format \n");
return -1;
}
psk[i/2] = c << 4;
c = psKey[i+1];
// Convert next 4 bits
if (c >= '0' && c <= '9') {
c = c - '0';
} else if (c >= 'A' && c <= 'F') {
c = c - 'A' + 10;
} else if (c >= 'a' && c <= 'f') {
c = c - 'a' + 10;
} else {
printf("ERROR: TLS PSK not in valid hex format \r\n");
return -1;
}
psk[i/2] |= c;
}
if (mbedtls_ssl_conf_psk(ssl_client->conf, psk, psk_len, pskIdent, strlen((char*)pskIdent)) != 0) {
printf("ERROR: mbedtls conf psk failed! \r\n");
}
} else {
mbedtls_ssl_conf_authmode(ssl_client->conf, MBEDTLS_SSL_VERIFY_NONE);
}
if ((cli_cert != NULL) && (cli_key != NULL)) {
_cli_crt = (mbedtls_x509_crt *) mbedtls_calloc( sizeof(mbedtls_x509_crt), 1);
if (_cli_crt == NULL) {
printf("ERROR: malloc client_crt failed! \r\n");
ret = -1;
break;
}
mbedtls_x509_crt_init(_cli_crt);
_clikey_rsa = (mbedtls_pk_context *) mbedtls_calloc( sizeof(mbedtls_pk_context), 1);
if (_clikey_rsa == NULL) {
printf("ERROR: malloc client_rsa failed! \r\n");
ret = -1;
break;
}
mbedtls_pk_init(_clikey_rsa);
if (mbedtls_x509_crt_parse(_cli_crt, cli_cert, strlen((char*)cli_cert)+1) != 0) {
printf("ERROR: mbedtls x509 parse client_crt failed! \r\n");
ret = -1;
break;
}
if (mbedtls_pk_parse_key(_clikey_rsa, cli_key, strlen((char*)cli_key)+1, NULL, 0) != 0) {
printf("ERROR: mbedtls x509 parse client_rsa failed! \r\n");
ret = -1;
break;
}
mbedtls_ssl_conf_own_cert(ssl_client->conf, _cli_crt, _clikey_rsa);
}
if((mbedtls_ssl_setup(ssl_client->ssl, ssl_client->conf)) != 0) {
printf("ERROR: mbedtls ssl setup failed!\r\n");
ret = -1;
break;
}
mbedtls_ssl_set_bio(ssl_client->ssl, &ssl_client->socket, mbedtls_net_send, mbedtls_net_recv, NULL);
mbedtls_ssl_set_hostname(ssl_client->ssl, SNI_hostname);
ret = mbedtls_ssl_handshake(ssl_client->ssl);
if (ret < 0) {
printf("ERROR: mbedtls ssl handshake failed: -0x%04X \r\n", -ret);
ret = -1;
} else {
if (ARDUINO_MBEDTLS_DEBUG_LEVEL > 0) {
printf("mbedTLS SSL handshake success \r\n");
}
}
//mbedtls_debug_set_threshold(ARDUINO_MBEDTLS_DEBUG_LEVEL);
}
} while (0);
if (_clikey_rsa) {
mbedtls_pk_free(_clikey_rsa);
mbedtls_free(_clikey_rsa);
_clikey_rsa = NULL;
}
if (_cli_crt) {
mbedtls_x509_crt_free(_cli_crt);
mbedtls_free(_cli_crt);
_cli_crt = NULL;
}
if (cacert) {
mbedtls_x509_crt_free(cacert);
mbedtls_free(cacert);
cacert = NULL;
}
if (ret < 0) {
if (ssl_client->socket >= 0) {
mbedtls_net_free((mbedtls_net_context *)&ssl_client->socket);
ssl_client->socket = -1;
}
if (ssl_client->ssl != NULL) {
mbedtls_ssl_free(ssl_client->ssl);
free(ssl_client->ssl);
ssl_client->ssl = NULL;
}
if (ssl_client->conf != NULL) {
mbedtls_ssl_config_free(ssl_client->conf);
free(ssl_client->conf);
ssl_client->conf = NULL;
}
}
return ssl_client->socket;
}
void stop_ssl_socket(sslclient_context *ssl_client)
{
lwip_shutdown(ssl_client->socket, SHUT_RDWR);
lwip_close(ssl_client->socket);
//mbedtls_net_free((mbedtls_net_context *)&ssl_client->socket);
ssl_client->socket = -1;
if (ssl_client->ssl != NULL) {
mbedtls_ssl_free(ssl_client->ssl);
free(ssl_client->ssl);
ssl_client->ssl = NULL;
}
if (ssl_client->conf != NULL) {
mbedtls_ssl_config_free(ssl_client->conf);
free(ssl_client->conf);
ssl_client->conf = NULL;
}
}
int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, uint16_t len)
{
int ret = -1;
if (ssl_client->ssl != NULL) {
ret = mbedtls_ssl_write(ssl_client->ssl, data, len);
}
return ret;
}
int get_ssl_receive(sslclient_context *ssl_client, uint8_t* data, int length, int flag)
{
int ret = 0;
uint8_t has_backup_recvtimeout = 0;
int backup_recv_timeout, recv_timeout;
socklen_t len;
if (ssl_client->ssl == NULL) {
return 0;
}
if (flag & 0x01) {
// peek for 10ms
ret = lwip_getsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &backup_recv_timeout, &len);
if (ret >= 0) {
recv_timeout = 100;
ret = lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &recv_timeout, sizeof(recv_timeout));
if (ret >= 0) {
has_backup_recvtimeout = 1;
}
}
}
memset(data, 0, length);
ret = mbedtls_ssl_read(ssl_client->ssl, data, length);
if ((flag & 0x01) && (has_backup_recvtimeout == 1)) {
// restore receiving timeout
lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &backup_recv_timeout, sizeof(recv_timeout));
}
return ret;
}
int get_ssl_sock_errno(sslclient_context *ssl_client) {
int so_error;
socklen_t len = sizeof(so_error);
lwip_getsockopt(ssl_client->socket, SOL_SOCKET, SO_ERROR, &so_error, &len);
return so_error;
}
int get_ssl_bytes_avail(sslclient_context *ssl_client) {
if (ssl_client->ssl != NULL) {
return mbedtls_ssl_get_bytes_avail(ssl_client->ssl);
} else {
return 0;
}
}

View File

@@ -1,26 +0,0 @@
#ifndef ARD_SSL_H
#define ARD_SSL_H
struct mbedtls_ssl_context;
struct mbedtls_ssl_config;
typedef struct {
int socket;
int recvTimeout;
mbedtls_ssl_context *ssl;
mbedtls_ssl_config *conf;
} sslclient_context;
int start_ssl_client(sslclient_context *ssl_client, uint32_t ipAddress, uint32_t port, unsigned char* rootCABuff, unsigned char* cli_cert, unsigned char* cli_key, unsigned char* pskIdent, unsigned char* psKey, char* SNI_hostname);
void stop_ssl_socket(sslclient_context *ssl_client);
int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, uint16_t len);
int get_ssl_receive(sslclient_context *ssl_client, uint8_t* data, int length, int flag);
int get_ssl_sock_errno(sslclient_context *ssl_client);
int get_ssl_bytes_avail(sslclient_context *ssl_client);
#endif

View File

@@ -1,71 +0,0 @@
// Simple Base64 code
// (c) Copyright 2010 MCQN Ltd.
// Released under Apache License, version 2.0
#include "b64.h"
/* Simple test program
#include <stdio.h>
void main()
{
char* in = "amcewen";
char out[22];
b64_encode(in, 15, out, 22);
out[21] = '\0';
printf(out);
}
*/
int b64_encode(const unsigned char* aInput, int aInputLen, unsigned char* aOutput, int aOutputLen)
{
// Work out if we've got enough space to encode the input
// Every 6 bits of input becomes a byte of output
if (aOutputLen < (aInputLen*8)/6)
{
// FIXME Should we return an error here, or just the length
return (aInputLen*8)/6;
}
// If we get here we've got enough space to do the encoding
const char* b64_dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
if (aInputLen == 3)
{
aOutput[0] = b64_dictionary[aInput[0] >> 2];
aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4|(aInput[1]>>4)];
aOutput[2] = b64_dictionary[(aInput[1]&0x0F)<<2|(aInput[2]>>6)];
aOutput[3] = b64_dictionary[aInput[2]&0x3F];
}
else if (aInputLen == 2)
{
aOutput[0] = b64_dictionary[aInput[0] >> 2];
aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4|(aInput[1]>>4)];
aOutput[2] = b64_dictionary[(aInput[1]&0x0F)<<2];
aOutput[3] = '=';
}
else if (aInputLen == 1)
{
aOutput[0] = b64_dictionary[aInput[0] >> 2];
aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4];
aOutput[2] = '=';
aOutput[3] = '=';
}
else
{
// Break the input into 3-byte chunks and process each of them
int i;
for (i = 0; i < aInputLen/3; i++)
{
b64_encode(&aInput[i*3], 3, &aOutput[i*4], 4);
}
if (aInputLen % 3 > 0)
{
// It doesn't fit neatly into a 3-byte chunk, so process what's left
b64_encode(&aInput[i*3], aInputLen % 3, &aOutput[i*4], aOutputLen - (i*4));
}
}
}

View File

@@ -1,7 +0,0 @@
#ifndef b64_h
#define b64_h
int b64_encode(const unsigned char* aInput, int aInputLen, unsigned char* aOutput, int aOutputLen);
#endif

View File

@@ -1,26 +0,0 @@
/*
Copyright (c) 2011 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
extern "C" void __cxa_pure_virtual(void) ;
/* We compile with nodefaultlibs, so we need to provide an error
* handler for an empty pure virtual function */
extern "C" void __cxa_pure_virtual(void) {
while(1)
;
}

View File

@@ -1,36 +0,0 @@
#ifndef ARD_DEBUG_H
#define ARD_DEBUG_H
#include <stddef.h>
#include <stdint.h>
#ifdef DEBUG_RTL_CORE
#define DEBUGV(...) rtl_printf(__VA_ARGS__)
#endif
#ifndef DEBUGV
#define DEBUGV(...)
#endif
#ifdef __cplusplus
void hexdump(void * addr, int len = 16);
#else
void hexdump(void * addr, int len);
#endif
#ifdef __cplusplus
extern "C" {
#endif
void print_udp_pcb(void);
void print_tcp_pcb(void);
void DumpForOneBytes(void *addr, int cnt); // cnt max 0x70 !
void __panic_func(const char* file, int line, const char* func) __attribute__((noreturn));
#define panic() __panic_func(__FILE__, __LINE__, __func__)
#ifdef __cplusplus
}
#endif
#endif//ARD_DEBUG_H

View File

@@ -1,116 +0,0 @@
/*
Copyright (c) 2011 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "api/itoa.h"
#include <string.h>
#ifdef __cplusplus
extern "C"{
#endif // __cplusplus
extern char* ltoa(long value, char *string, int radix)
{
char tmp[33];
char *tp = tmp;
long i;
unsigned long v;
int sign;
char *sp;
if ( string == NULL )
{
return 0 ;
}
if (radix > 36 || radix <= 1)
{
return 0 ;
}
sign = (radix == 10 && value < 0);
if (sign)
{
v = -value;
}
else
{
v = (unsigned long)value;
}
while (v || tp == tmp)
{
i = v % radix;
v = v / radix;
if (i < 10)
*tp++ = i+'0';
else
*tp++ = i + 'a' - 10;
}
sp = string;
if (sign)
*sp++ = '-';
while (tp > tmp)
*sp++ = *--tp;
*sp = 0;
return string;
}
extern char* ultoa(unsigned long value, char *string, int radix)
{
char tmp[33];
char *tp = tmp;
long i;
unsigned long v = value;
char *sp;
if ( string == NULL )
{
return 0;
}
if (radix > 36 || radix <= 1)
{
return 0;
}
while (v || tp == tmp)
{
i = v % radix;
v = v / radix;
if (i < 10)
*tp++ = i+'0';
else
*tp++ = i + 'a' - 10;
}
sp = string;
while (tp > tmp)
*sp++ = *--tp;
*sp = 0;
return string;
}
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -1,120 +0,0 @@
#ifndef EASYWSCLIENT_H
#define EASYWSCLIENT_H
#include <platform/platform_stdlib.h>
/****************Define the debug message level*********************/
#define DEBUG_WSCLIENT 1
#define WSCLIENT_LOG(level, fmt, ...) printf("\n\r[WSCLIENT %s] %s: " fmt "\n", level, __FUNCTION__, ##__VA_ARGS__)
#if DEBUG_WSCLIENT == 2
#define WSCLIENT_DEBUG(fmt, ...) WSCLIENT_LOG("DEBUG", fmt, ##__VA_ARGS__)
#else
#define WSCLIENT_DEBUG(fmt, ...)
#endif
#if DEBUG_WSCLIENT
#define WSCLIENT_ERROR(fmt, ...) WSCLIENT_LOG("ERROR", fmt, ##__VA_ARGS__)
#else
#define WSCLIENT_ERROR(fmt, ...)
#endif
/*******************************************************************/
/****************Define the structures used*************************/
typedef enum{
CLOSING,
CLOSED,
CONNECTING,
OPEN
} readyStateValues;
struct wsheader_type{
unsigned header_size;
int fin;
int mask;
enum opcode_type {
CONTINUATION = 0x0,
TEXT_FRAME = 0x1,
BINARY_FRAME = 0x2,
CLOSE = 8,
PING = 9,
PONG = 0xa,
} opcode;
int N0;
uint64_t N;
uint8_t masking_key[4];
};
struct _wsclient_context;
struct _ssl_context;
struct ssl_fun_ops{
int (*memory_set_own)( void * (*malloc_func)( size_t ),void (*free_func)( void * ) );
int (*net_connect)( int *fd, const char *host, int port );
int (*ssl_init)( struct _ssl_context *ssl );
void (*ssl_set_endpoint)( struct _ssl_context *ssl, int endpoint );
void (*ssl_set_authmode)( struct _ssl_context *ssl, int authmode );
void (*ssl_set_rng)( struct _ssl_context *ssl,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng );
void (*ssl_set_bio)( struct _ssl_context *ssl,
int (*f_recv)(void *, unsigned char *, size_t), void *p_recv,
int (*f_send)(void *, const unsigned char *, size_t), void *p_send );
int (*ssl_handshake)( struct _ssl_context *ssl );
void (*net_close)( int fd );
void (*ssl_free)( struct _ssl_context *ssl );
int (*ssl_read)( struct _ssl_context *ssl, unsigned char *buf, size_t len );
int (*ssl_write)( struct _ssl_context *ssl, const unsigned char *buf, size_t len );
const char *(*ssl_get_ciphersuite)( const struct _ssl_context *ssl );
int (*net_recv)( void *ctx, unsigned char *buf, size_t len );
int (*net_send)( void *ctx, const unsigned char *buf, size_t len );
};
struct ws_fun_ops{
int (*hostname_connect)(struct _wsclient_context *wsclient);
void (*client_close)(struct _wsclient_context *wsclient);
int (*client_send)(struct _wsclient_context *wsclient, unsigned char *data, size_t data_len);
int (*client_read)(struct _wsclient_context *wsclient, unsigned char *data, size_t data_len);
struct ssl_fun_ops ssl_fun_ops;
};
typedef struct _wsclient_context{
char host[128];
char path[128];
char origin[200];
int port;
uint8_t use_ssl;
int sockfd;
readyStateValues readyState;
int tx_len;
void *ssl;
uint8_t *txbuf;
uint8_t *rxbuf;
uint8_t *receivedData;
struct ws_fun_ops fun_ops;
}wsclient_context;
/*******************************************************************/
/****************General functions used by wsclient*****************/
void ws_get_random_bytes(void *buf, size_t len);
void* ws_malloc(unsigned int size);
void ws_free(void *buf);
int ws_client_handshake(wsclient_context *wsclient);
int ws_check_handshake(wsclient_context *wsclient);
void ws_sendData(uint8_t type, size_t message_size, uint8_t* message, int useMask, wsclient_context *wsclient);
/*******************************************************************/
/*************Functions used by wsclient without SSL****************/
int ws_hostname_connect(wsclient_context *wsclient);
int ws_client_read(wsclient_context *wsclient, unsigned char *data, size_t data_len);
int ws_client_send(wsclient_context *wsclient, unsigned char *data, size_t data_len);
void ws_client_close(wsclient_context *wsclient);
/*******************************************************************/
/***************Functions used by wsclient with SSL*****************/
int wss_hostname_connect(wsclient_context *wsclient);
int wss_client_read(wsclient_context *wsclient, unsigned char *data, size_t data_len);
int wss_client_send(wsclient_context *wsclient, unsigned char *data, size_t data_len);
void wss_client_close(wsclient_context *wsclient);
/*******************************************************************/
#endif

View File

@@ -1,122 +0,0 @@
#include "Arduino.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "lwip_netconf.h"
#include "lwip/udp.h"
#include "lwip/tcpip.h"
#include "lwip/err.h"
//#include "lwip/mem.h"
#include "lwip/tcp.h"
#include "lwip/tcp_impl.h"
#include "lwip/udp.h"
/* Get one byte from the 4-byte address */
#define ip4_addr1(ipaddr) (((u8_t*)(ipaddr))[0])
#define ip4_addr2(ipaddr) (((u8_t*)(ipaddr))[1])
#define ip4_addr3(ipaddr) (((u8_t*)(ipaddr))[2])
#define ip4_addr4(ipaddr) (((u8_t*)(ipaddr))[3])
/* These are cast to u16_t, with the intent that they are often arguments
* to printf using the U16_F format from cc.h. */
#define ip4_addr1_16(ipaddr) ((u16_t)ip4_addr1(ipaddr))
#define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr))
#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr))
#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr))
#define IP2STR(ipaddr) ip4_addr1_16(ipaddr), \
ip4_addr2_16(ipaddr), \
ip4_addr3_16(ipaddr), \
ip4_addr4_16(ipaddr)
#define IPSTR "%d.%d.%d.%d"
extern const char * const tcp_state_str[];
/*
static const char * const tcp_state_str[] = {
"CLOSED",
"LISTEN",
"SYN_SENT",
"SYN_RCVD",
"ESTABLISHED",
"FIN_WAIT_1",
"FIN_WAIT_2",
"CLOSE_WAIT",
"CLOSING",
"LAST_ACK",
"TIME_WAIT"
};
*/
/******************************************************************************
* FunctionName : debug
* Parameters :
* Returns :
*******************************************************************************/
void print_udp_pcb(void)
{
struct udp_pcb *pcb;
bool prt_none = true;
rtl_printf("UDP pcbs:\n");
for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
rtl_printf("flg:%02x\t" IPSTR ":%d\t" IPSTR ":%d\trecv:%p\n", pcb->flags, IP2STR(&pcb->local_ip), pcb->local_port, IP2STR(&pcb->remote_ip), pcb->remote_port, pcb->recv );
prt_none = false;
}
if(prt_none) rtl_printf("none\n");
}
/******************************************************************************
* FunctionName : debug
* Parameters :
* Returns :
*******************************************************************************/
void print_tcp_pcb(void)
{
struct tcp_pcb *pcb;
rtl_printf("Active PCB states:\n");
bool prt_none = true;
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
rtl_printf("Port %d|%d\tflg:%02x\ttmr:%p\t%s\n", pcb->local_port, pcb->remote_port, pcb->flags, pcb->tmr, tcp_state_str[pcb->state]);
prt_none = false;
}
if(prt_none) rtl_printf("none\n");
rtl_printf("Listen PCB states:\n");
prt_none = true;
for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) {
rtl_printf("Port %d|%d\tflg:%02x\ttmr:%p\t%s\n", pcb->local_port, pcb->remote_port, pcb->flags, pcb->tmr, tcp_state_str[pcb->state]);
prt_none = false;
}
if(prt_none) rtl_printf("none\n");
rtl_printf("TIME-WAIT PCB states:\n");
prt_none = true;
for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
rtl_printf("Port %d|%d\tflg:%02x\ttmr:%p\t%s\n", pcb->local_port, pcb->remote_port, pcb->flags, pcb->tmr, tcp_state_str[pcb->state]);
prt_none = false;
}
if(prt_none) rtl_printf("none\n");
}
/******************************************************************************
* FunctionName : debug
* Parameters :
* Returns :
*******************************************************************************/
#if 0
//------------------------------------------------------------------------------
void chow_tcp_connection_info(void)
{
rtl_printf("TCP Server connections:\n");
TCP_SERV_CFG * p;
TCP_SERV_CONN * ts_conn;
bool prt_none = true;
for(p = phcfg; p != NULL; p = p->next) {
for(ts_conn = p->conn_links; ts_conn != NULL; ts_conn = ts_conn->next) {
rtl_printf("%d "IPSTR ":%d %s\n", p->port, ts_conn->remote_ip.b[0], ts_conn->remote_ip.b[1], ts_conn->remote_ip.b[2], ts_conn->remote_ip.b[3], ts_conn->remote_port, tspsrv_srvconn_state_msg(ts_conn->state) );
prt_none = false;
}
}
if(prt_none) rtl_printf("none\n");
}
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -18,87 +18,62 @@
*/
#define ARDUINO_MAIN
#include "Arduino.h"
#include "cmsis_os.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include "rtl8710b.h"
#include "FreeRTOS.h"
#include "task.h"
#include "diag.h"
extern void HalCpuClkConfig(u8 CpuType);
extern void SystemCoreClockUpdate(void);
extern void En32KCalibration(void);
extern int tcm_heap_freeSpace(void);
extern void console_init(void);
#ifdef __cplusplus
} // extern "C"
#endif
osThreadId main_tid = 0;
#include <Arduino.h>
#include <cmsis_os.h>
// Weak empty variant initialization function.
// May be redefined by variant files.
void initVariant() __attribute__((weak));
void initVariant() { }
void initVariant() {}
// Initialize C library
extern "C" void __libc_init_array(void);
/*
* \brief Init Random()
* \note Use in void __low_level_init(void) { Init_Rand(); } !
*/
void Init_Rand(void)
{
extern u32 _rand_z1, _rand_z2, _rand_z3, _rand_z4, _rand_first;
u32 *p = (u32 *)0x1FFFFF00;
while(p < (u32 *)0x20000000) _rand_z1 ^= *p++;
_rand_z1 ^= (*((u32 *)0x40002018) << 24) ^ (*((u32 *)0x40002118) << 16) ^ (*((u32 *)0x40002218) << 8) ^ *((u32 *)0x40002318);
_rand_z2 = ((_rand_z1 & 0x007F00FF) << 7) ^ ((_rand_z1 & 0x0F80FF00) >> 8);
_rand_z3 = ((_rand_z2 & 0x007F00FF) << 7) ^ ((_rand_z2 & 0x0F80FF00) >> 8);
_rand_z4 = ((_rand_z3 & 0x007F00FF) << 7) ^ ((_rand_z3 & 0x0F80FF00) >> 8);
_rand_first = 1;
osThreadId main_tid = 0;
void main_task(const void *arg) {
setup();
for (;;) {
loop();
if (serialEventRun)
serialEventRun();
yield();
}
}
/*
* \brief handle sketch
*/
void main_task( void const *arg )
{
setup();
int main(void) {
LT_BANNER();
init();
for (;;)
{
loop();
if (serialEventRun) serialEventRun();
yield();
}
__libc_init_array();
initVariant();
osThreadDef(main_task, osPriorityRealtime, 1, 4096 * 4);
main_tid = osThreadCreate(osThread(main_task), NULL);
osKernelStart();
while (1)
;
return 0;
}
void serialEvent() __attribute__((weak));
bool Serial_available() __attribute__((weak));
/*
* \brief Main entry point of Arduino application
*/
int main( void )
{
LT_BANNER();
init();
__libc_init_array();
initVariant();
osThreadDef(main_task, osPriorityRealtime, 1, 4096*4);
main_tid = osThreadCreate(osThread (main_task), NULL);
osKernelStart();
while(1);
return 0;
void serialEventRun(void) {
if (Serial_available && serialEvent && Serial_available())
serialEvent();
}
void wait_for_debug() {
while (((CoreDebug->DHCSR) & CoreDebug_DHCSR_C_DEBUGEN_Msk) == 0) {
asm("nop");
}
delay(1000);
}

View File

@@ -1,29 +0,0 @@
#ifndef __NETBIOS_H__
#define __NETBIOS_H__
/** default port number for "NetBIOS Name service */
#define NETBIOS_PORT 137
/** size of a NetBIOS name */
#define NETBIOS_NAME_LEN 16
#ifndef NET_IF_NUM
#define NET_IF_NUM 2
#endif
#ifdef __cplusplus
extern "C" {
#endif
extern char netbios_name[NET_IF_NUM][NETBIOS_NAME_LEN + 1]; // default netifs/interfacenum: 0 - SoftAP, 1 - Station, 2 - Ethernet
// struct udp_pcb * netbios_pcb(void);
void netbios_init(void);
bool netbios_set_name(unsigned char interfacenum, char * name); // default netifs/interfacenum: 0 - SoftAP, 1 - Station, 2 - Ethernet
bool netbios_off(void);
#ifdef __cplusplus
}
#endif
#endif /* __NETBIOS_H__ */

View File

@@ -1,215 +0,0 @@
/*
Copyright (c) 2014 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
#include <assert.h>
#include <debug.h>
#include <Arduino.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include <diag.h>
extern void *pvPortMalloc( size_t xWantedSize );
extern void *os_zalloc( size_t xWantedSize );
extern void vPortFree( void *pv );
extern void *pvPortReAlloc( void *pv, size_t xWantedSize );
extern size_t xPortGetFreeHeapSize( void );
extern size_t xPortGetMinimumEverFreeHeapSize( void );
extern void *tcm_heap_malloc(int size);
extern void *tcm_heap_calloc(int size);
extern void tcm_heap_free(void * mem);
extern void tcm_heap_dump(void);
extern int tcm_heap_freeSpace(void);
__attribute__((noreturn)) void __panic_func(const char* file, int line, const char* func)
{
DiagPrintf("\r\nPanic: %s, line: %d, %s\r\n");
while(1);
}
#ifdef __cplusplus
} // extern "C"
#endif
void hexdump(void * ptr, int cnt)
{
char * p = (char *) ptr;
int c = cnt;
while(c > 64) {
DumpForOneBytes((void *)p, 64);
p += 64;
c -= 64;
}
if(c != 0) DumpForOneBytes((void *)p, c);
}
void debug_on(void)
{
ConfigDebugErr = -1;
ConfigDebugInfo = -1;
ConfigDebugWarn = -1;
CfgSysDebugErr = -1;
CfgSysDebugInfo = -1;
CfgSysDebugWarn = -1;
}
void sys_info(void) {
rtl_printf("\r\nCLK CPU\t\t%d Hz\r\nRAM heap\t%d bytes\r\nTCM heap\t%d bytes\r\n",
HalGetCpuClk(), xPortGetFreeHeapSize(), tcm_heap_freeSpace());
}
/* void * malloc(size_t size)
{
void * ret;
if((ret = pvPortMalloc(size)) == NULL)
ret = tcm_heap_malloc(size);
return ret;
}
void * zalloc(size_t size)
{
void * ret;
if((ret = pvPortMalloc(size)) == NULL)
ret = tcm_heap_calloc(size);
else memset(ret, 0, size);
return ret;
}
void *calloc(size_t count, size_t size)
{
return zalloc(count * size);
} */
void free(void *pv)
{
vPortFree(pv);
}
void * realloc(void *pv, size_t size)
{
return pvPortReAlloc(pv, size);
}
/* void *operator new(size_t size)
{
void * ret;
if((ret = zalloc(size)) == NULL) {
DiagPrintf("\r\nMEM error!\r\n");
while(1);
}
return ret;
}
void *operator new[](size_t size)
{
void * ret;
if((ret = zalloc(size)) == NULL) {
DiagPrintf("\r\nMEM error!\r\n");
while(1);
}
return ret;
}
void operator delete(void * ptr)
{
free(ptr);
}
void operator delete[](void * ptr)
{
free(ptr);
} */
/*
extern "C" void __cxa_pure_virtual(void) __attribute__ ((__noreturn__));
extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__));
void __cxa_pure_virtual(void)
{
panic();
}
void __cxa_deleted_virtual(void)
{
panic();
}
typedef struct {
uint8_t guard;
uint8_t ps;
} guard_t;
extern "C" int __cxa_guard_acquire(__guard* pg)
{
uint8_t ps = xt_rsil(15);
if (reinterpret_cast<guard_t*>(pg)->guard) {
xt_wsr_ps(ps);
return 0;
}
reinterpret_cast<guard_t*>(pg)->ps = ps;
return 1;
}
extern "C" void __cxa_guard_release(__guard* pg)
{
reinterpret_cast<guard_t*>(pg)->guard = 1;
xt_wsr_ps(reinterpret_cast<guard_t*>(pg)->ps);
}
extern "C" void __cxa_guard_abort(__guard* pg)
{
xt_wsr_ps(reinterpret_cast<guard_t*>(pg)->ps);
}
namespace std
{
void __throw_bad_function_call()
{
panic();
}
void __throw_length_error(char const*)
{
panic();
}
void __throw_bad_alloc()
{
panic();
}
void __throw_logic_error(const char* str)
{
panic();
}
void __throw_out_of_range(const char* str)
{
panic();
}
}
*/

View File

@@ -0,0 +1,53 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-06. */
#pragma once
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
// disable typedef in basic_types.h
#define boolean boolean_rtl
#include <strproc.h> // define string macros first
#undef isdigit // then remove them, as they conflict
#undef islower // with ctype.h macros
#undef isprint
#undef isspace
#undef isxdigit
#undef strtol
#undef strtoul
#include <ameba_soc.h>
#include <gpio_api.h>
#include <main.h>
#include <rand.h>
#include <rt_lib_rom.h>
#include <rtl_lib.h>
#include <wait_api.h>
// remove previously defined workaround
#undef boolean
// stdio.h
#define printf rtl_printf
#define sprintf rtl_sprintf
// moved from syscalls.h
#define _close __rtl_close
#define _fstat __rtl_fstat
#define _isatty __rtl_isatty
#define _lseek __rtl_lseek
#define _open __rtl_open
#define _read __rtl_read
#define _write __rtl_write
#define _sbrk __rtl_sbrk
#define delay_us wait_us
extern void wait_us(int us);
extern int LOGUART_SetBaud(uint32_t BaudRate); // from fixups/log_uart.c
extern void DumpForOneBytes(void *addr, int cnt); // cnt max 0x70!
extern void SystemCoreClockUpdate(void);
extern int _sscanf_patch(const char *buf, const char *fmt, ...);

View File

@@ -0,0 +1,16 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-06. */
#include "sdk_mem.h"
#include <Arduino.h> // for memset
void *pvPortZalloc(size_t size) {
void *pvReturn = pvPortMalloc(size);
if (pvReturn)
memset(pvReturn, 0, size);
return pvReturn;
}
void *pvPortCalloc(size_t nmemb, size_t size) {
return pvPortZalloc(nmemb * size);
}

View File

@@ -0,0 +1,21 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-06. */
#pragma once
#include <stddef.h>
// provide extern functions directly, as callers
// generally don't expect needing to include malloc()
extern void *pvPortMalloc(size_t xWantedSize);
extern void *pvPortZalloc(size_t size);
extern void *pvPortCalloc(size_t nmemb, size_t size);
extern void *pvPortReAlloc(void *pv, size_t xWantedSize);
extern void vPortFree(void *pv);
#define malloc pvPortMalloc
#define zalloc pvPortZalloc
#define calloc pvPortCalloc
#define realloc pvPortReAlloc
#define free vPortFree
#define LT_HEAP_FUNC xPortGetFreeHeapSize

Some files were not shown because too many files have changed in this diff Show More