├── .gitmodules ├── README.md ├── UNOR4USBBridge ├── BossaUnoR4WiFI.cpp ├── BossaUnoR4WiFi.h ├── DAP.cpp ├── DAP.h ├── OTA.cpp ├── OTA.h ├── SSE.cpp ├── SSE.h ├── UNOR4USBBridge.ino ├── at_handler.cpp ├── at_handler.h ├── chAT.hpp ├── cmds_ble_bridge.h ├── cmds_esp_generic.h ├── cmds_ota.h ├── cmds_preferences.h ├── cmds_se.h ├── cmds_wifi_SSL.h ├── cmds_wifi_netif.h ├── cmds_wifi_softAP.h ├── cmds_wifi_station.h ├── cmds_wifi_udp.h ├── commands.h ├── dap_config.h ├── freedap.c ├── freedap.h ├── incbin.h ├── parser.cpp ├── ping.cpp ├── ping.h └── server.cpp ├── arduino-cli.yaml.orig ├── boot └── boot_app0.bin ├── certificates ├── cacrt_all.pem └── gen_crt_bundle.py ├── combine.py ├── compile.sh ├── download.sh ├── export.sh ├── package.sh ├── spiffs └── spiffs.bin ├── unor4wifi-reboot ├── compile_darwin.sh ├── compile_linux.sh ├── compile_win.sh ├── go.mod ├── go.sum └── main.go └── unor4wifi-updater ├── README.md ├── package.sh ├── update_linux.sh ├── update_mac.command └── update_windows.bat /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "unor4wifi-reboot/hid"] 2 | path = unor4wifi-reboot/hid 3 | url = https://github.com/pennam/hid.git 4 | [submodule "libraries/Arduino_DebugUtils"] 5 | path = libraries/Arduino_DebugUtils 6 | url = https://github.com/arduino-libraries/Arduino_DebugUtils.git 7 | [submodule "libraries/ArduinoBLE"] 8 | path = libraries/ArduinoBLE 9 | url = https://github.com/arduino-libraries/ArduinoBLE.git 10 | [submodule "libraries/Arduino_ESP32_OTA"] 11 | path = libraries/Arduino_ESP32_OTA 12 | url = https://github.com/arduino-libraries/Arduino_ESP32_OTA.git 13 | [submodule "libraries/BOSSA"] 14 | path = libraries/BOSSA 15 | url = https://github.com/arduino/BOSSA.git 16 | branch = unor4wifi 17 | [submodule "hardware/esp32-patched/esp32"] 18 | path = hardware/esp32-patched/esp32 19 | url = https://github.com/arduino/arduino-esp32.git 20 | branch = unor4wifi-2.0.9 21 | shallow = true 22 | [submodule "libraries/ArduinoHttpClient"] 23 | path = libraries/ArduinoHttpClient 24 | url = https://github.com/arduino-libraries/ArduinoHttpClient.git 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino UNO R4 WiFi USB Bridge firmware 2 | 3 | This firmware uses [arduino-esp32](https://github.com/espressif/arduino-esp32/) 4 | 5 | ## Update submodules 6 | 7 | ``` 8 | git submodule update --init --depth 1 --no-single-branch 9 | ``` 10 | 11 | ## Get the toolchain 12 | 13 | ``` 14 | cd hardware/esp32-patched/esp32/tools 15 | ./get.py 16 | ``` 17 | 18 | ## Build the firmware 19 | 20 | ``` 21 | ./compile.sh 22 | ``` 23 | Running the compile script will: 24 | 25 | 1. Compile the firmware using `arduino-cli` 26 | 2. Export binaries in the build directory inside the `UNOR4USBBridge` folder 27 | 28 | The `compile.sh` script will produce a bunch of binary files that can be flashed using [esptool](https://github.com/espressif/esptool/releases) from the build directory: 29 | 30 | ``` 31 | esptool --chip esp32s3 --port "/dev/ttyACM0" --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x0 "UNOR4USBBridge.ino.bootloader.bin" 0x8000 "UNOR4USBBridge.ino.partitions.bin" 0xe000 "../../../boot/boot_app0.bin" 0x10000 "UNOR4USBBridge.ino.bin" 32 | ``` 33 | 34 | ## Package a single binary 35 | 36 | ``` 37 | ./export.sh 38 | ``` 39 | 40 | The `export.sh` script will take care of generating a single binary blob including the TLS certificates bundle. Running the export script will: 41 | 42 | 1. Invoke the `compile.sh` script 43 | 2. Generate the certificate bundle using this [script](certificates/gen_crt_bundle.py) and this [certificates list](certificates/certificates.pem) 44 | 3. Combine everything in a single binary blob that can be flashed from address 0x0 using [espflash](https://github.com/esp-rs/espflash/releases) 45 | 46 | ``` 47 | espflash write-bin -b 115200 0x0 S3.bin 48 | ``` 49 | 50 | ## Update your board 51 | 52 | To flash the firmware the board needs to be in `ESP download` mode. This can be done [manually](https://support.arduino.cc/hc/en-us/articles/16379769332892-Restore-the-USB-connectivity-firmware-on-UNO-R4-WiFi-with-espflash) or using the [unor4wifi-updater](unor4wifi-updater) script. 53 | 54 | Alternatively you can also use the `download.sh` script to update the firmware using the `arduino-cli`. Also in this case the `download.sh` script 55 | should be invoked after putting the board in `ESP download` mode. 56 | -------------------------------------------------------------------------------- /UNOR4USBBridge/BossaUnoR4WiFI.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | BossaUnoR4WiFi.cpp 3 | Copyright (c) 2023 Arduino SA. All right reserved. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #include "BossaUnoR4WiFi.h" 21 | #include 22 | #include 23 | 24 | void BossaUnoR4WiFiObserver::onProgress(int num, int div) { 25 | 26 | int ticks; 27 | int bars = 30; 28 | ticks = num * bars / div; 29 | 30 | if (ticks == _lastTicks) { 31 | return; 32 | } 33 | 34 | Debug.newlineOff(); 35 | DEBUG_INFO("\r["); 36 | while (ticks-- > 0) { 37 | DEBUG_INFO("="); 38 | bars--; 39 | } 40 | while (bars-- > 0) { 41 | DEBUG_INFO(" "); 42 | } 43 | Debug.newlineOn(); 44 | 45 | DEBUG_INFO("] %d%% (%d/%d pages)", num * 100 / div, num, div); 46 | 47 | _lastTicks = 0; 48 | } 49 | 50 | int BossaUnoR4WiFi::program(const char* filePath, HardwareSerial& serial, int bootPin, int resetPin) { 51 | 52 | SPIFFS.begin(); 53 | if(!SPIFFS.exists(filePath)) { 54 | DEBUG_ERROR("Update file do not exist"); 55 | return 0; 56 | } 57 | 58 | String spiffs_path = String("/spiffs") + String(filePath); 59 | setProgramMode(bootPin, resetPin); 60 | delay(500); 61 | 62 | /* Save original baudrate */ 63 | uint32_t baud = serial.baudRate(); 64 | 65 | DEBUG_INFO("Connecting to SAM-BA ... "); 66 | if (!connect(serial)) { 67 | DEBUG_ERROR("SAM-BA can't connect."); 68 | return 0; 69 | } 70 | flash(spiffs_path.c_str()); 71 | 72 | /* Restore baudrate */ 73 | serial.begin(baud); 74 | 75 | SPIFFS.remove(filePath); 76 | SPIFFS.end(); 77 | 78 | /* Reset microcontroller*/ 79 | reset(resetPin); 80 | 81 | return 1; 82 | } 83 | 84 | void BossaUnoR4WiFi::setProgramMode(int bootPin, int resetPin) { 85 | 86 | digitalWrite(bootPin, HIGH); 87 | digitalWrite(resetPin, LOW); 88 | delay(100); 89 | digitalWrite(resetPin, HIGH); 90 | delay(100); 91 | digitalWrite(resetPin, LOW); 92 | delay(100); 93 | digitalWrite(resetPin, HIGH); 94 | } 95 | 96 | void BossaUnoR4WiFi::reset(int resetPin) { 97 | 98 | digitalWrite(resetPin, LOW); 99 | delay(100); 100 | digitalWrite(resetPin, HIGH); 101 | } 102 | 103 | BossaUnoR4WiFiObserver obs; 104 | BossaUnoR4WiFi BOSSA(obs); 105 | -------------------------------------------------------------------------------- /UNOR4USBBridge/BossaUnoR4WiFi.h: -------------------------------------------------------------------------------- 1 | /* 2 | BossaUnoR4WiFiArduino.h 3 | Copyright (c) 2023 Arduino SA. All right reserved. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef _BOSSAUNOR4_H 21 | #define _BOSSAUNOR4_H 22 | 23 | #include 24 | 25 | class BossaUnoR4WiFiObserver : public FlasherObserver 26 | { 27 | public: 28 | BossaUnoR4WiFiObserver() : _lastTicks(-1) {} 29 | virtual ~BossaUnoR4WiFiObserver() {} 30 | 31 | virtual void onStatus(const char *message, ...) {}; 32 | virtual void onProgress(int num, int div); 33 | 34 | private: 35 | int _lastTicks; 36 | }; 37 | 38 | class BossaUnoR4WiFi : public BossaArduino 39 | { 40 | public: 41 | BossaUnoR4WiFi(FlasherObserver& observer) : BossaArduino(observer) {} 42 | virtual ~BossaUnoR4WiFi() {} 43 | 44 | int program(const char* filePath, HardwareSerial& serial, int bootPin, int resetPin); 45 | 46 | private: 47 | void setProgramMode(int bootPin, int resetPin); 48 | void reset(int resetPin); 49 | }; 50 | 51 | extern BossaUnoR4WiFi BOSSA; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /UNOR4USBBridge/DAP.cpp: -------------------------------------------------------------------------------- 1 | #include "DAP.h" 2 | 3 | USBHID HID; 4 | DAPHIDDevice DAP; -------------------------------------------------------------------------------- /UNOR4USBBridge/DAP.h: -------------------------------------------------------------------------------- 1 | #include "USB.h" 2 | #include "USBHID.h" 3 | 4 | extern "C" { 5 | #include "esp32-hal-tinyusb.h" 6 | #include "freedap.h" 7 | } 8 | 9 | #include "at_handler.h" 10 | 11 | extern USBHID HID; 12 | 13 | #define TUD_HID_REPORT_DESC_R4_INOUT_FEATURE(report_size, ...) \ 14 | HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2 ),\ 15 | HID_USAGE ( 0x01 ),\ 16 | HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\ 17 | /* Report ID if any */\ 18 | __VA_ARGS__ \ 19 | /* Input */ \ 20 | HID_USAGE ( 0x02 ),\ 21 | HID_LOGICAL_MIN ( 0x00 ),\ 22 | HID_LOGICAL_MAX_N ( 0xff, 2 ),\ 23 | HID_REPORT_SIZE ( 8 ),\ 24 | HID_REPORT_COUNT( report_size ),\ 25 | HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\ 26 | /* Output */ \ 27 | HID_USAGE ( 0x03 ),\ 28 | HID_LOGICAL_MIN ( 0x00 ),\ 29 | HID_LOGICAL_MAX_N ( 0xff, 2 ),\ 30 | HID_REPORT_SIZE ( 8 ),\ 31 | HID_REPORT_COUNT( report_size ),\ 32 | HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\ 33 | /* Feature */ \ 34 | HID_USAGE ( 0x04),\ 35 | HID_LOGICAL_MIN ( 0x00),\ 36 | HID_LOGICAL_MAX_N ( 0xff, 2),\ 37 | HID_REPORT_SIZE (8),\ 38 | HID_REPORT_COUNT(report_size),\ 39 | HID_FEATURE (HID_DATA | HID_VARIABLE | HID_ABSOLUTE),\ 40 | HID_COLLECTION_END \ 41 | 42 | static uint8_t const report_descriptor[] = 43 | { 44 | TUD_HID_REPORT_DESC_R4_INOUT_FEATURE(CFG_TUD_HID_EP_BUFSIZE) 45 | }; 46 | 47 | class DAPHIDDevice: public USBHIDDevice { 48 | public: 49 | DAPHIDDevice(void){ 50 | static bool initialized = false; 51 | if(!initialized){ 52 | initialized = true; 53 | HID.addDevice(this, sizeof(report_descriptor)); 54 | } 55 | } 56 | 57 | void begin(void){ 58 | HID.begin(); 59 | dap_init(); 60 | } 61 | 62 | uint16_t _onGetDescriptor(uint8_t* buffer){ 63 | memcpy(buffer, report_descriptor, sizeof(report_descriptor)); 64 | return sizeof(report_descriptor); 65 | } 66 | void _onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len) { 67 | static uint8_t TxDataBuffer[CFG_TUD_HID_EP_BUFSIZE]; 68 | dap_process_request((uint8_t*)buffer, len, TxDataBuffer, sizeof(TxDataBuffer)); 69 | HID.SendReport(report_id, TxDataBuffer, sizeof(TxDataBuffer), 5); 70 | } 71 | void _onSetFeature(uint8_t report_id, const uint8_t* buffer, uint16_t len){ 72 | if (buffer[0] == 0xAA) { 73 | usb_persist_restart(RESTART_BOOTLOADER); 74 | } 75 | } 76 | uint16_t _onGetFeature(uint8_t report_id, uint8_t* buffer, uint16_t len) { 77 | buffer[0] = FIRMWARE_MAJOR; 78 | buffer[1] = FIRMWARE_MINOR; 79 | buffer[2] = FIRMWARE_PATCH; 80 | return 3; 81 | } 82 | }; 83 | 84 | extern DAPHIDDevice DAP; -------------------------------------------------------------------------------- /UNOR4USBBridge/OTA.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of UNOR4USBBridge_OTA. 3 | 4 | Copyright 2023 ARDUINO SA (http://www.arduino.cc/) 5 | 6 | This software is released under the GNU General Public License version 3, 7 | which covers the main part of arduino-cli. 8 | The terms of this license can be found at: 9 | https://www.gnu.org/licenses/gpl-3.0.en.html 10 | 11 | You can be released from the requirements of the above licenses by purchasing 12 | a commercial license. Buying such a license is mandatory if you want to modify or 13 | otherwise use the software for commercial activities involving the Arduino 14 | software without disclosing the source code of your own applications. To purchase 15 | a commercial license, send an email to license@arduino.cc. 16 | */ 17 | 18 | /****************************************************************************** 19 | INCLUDE 20 | ******************************************************************************/ 21 | #include 22 | #include 23 | #include 24 | #include "OTA.h" 25 | #include "BossaUnoR4WiFi.h" 26 | 27 | #include "FS.h" 28 | #include "SPIFFS.h" 29 | #include "at_handler.h" 30 | 31 | /****************************************************************************** 32 | PUBLIC MEMBER FUNCTIONS 33 | ******************************************************************************/ 34 | 35 | Arduino_UNOWIFIR4_OTA::Arduino_UNOWIFIR4_OTA() 36 | : _updating_renesas(true) { 37 | 38 | } 39 | 40 | Arduino_UNOWIFIR4_OTA::~Arduino_UNOWIFIR4_OTA() { 41 | closeStorage(); 42 | } 43 | 44 | Arduino_ESP32_OTA::Error Arduino_UNOWIFIR4_OTA::begin(const char* file_path, uint32_t magic, bool formatOnFail) 45 | { 46 | /* ... configure board Magic number */ 47 | setMagic(magic); 48 | 49 | if(!SPIFFS.begin(formatOnFail)) { 50 | DEBUG_ERROR("%s: failed to initialize SPIFFS", __FUNCTION__); 51 | return Error::OtaStorageInit; 52 | } 53 | 54 | if(SPIFFS.exists(file_path)) { 55 | SPIFFS.remove(file_path); 56 | } 57 | 58 | _updating_renesas = true; 59 | 60 | SPIFFS.end(); 61 | return Error::None; 62 | } 63 | 64 | void Arduino_UNOWIFIR4_OTA::write_byte_to_flash(uint8_t data) 65 | { 66 | if(_updating_renesas) { 67 | int ret = fwrite(&data, sizeof(data), 1, _file); 68 | } else { 69 | Arduino_ESP32_OTA::write_byte_to_flash(data); 70 | } 71 | } 72 | 73 | int Arduino_UNOWIFIR4_OTA::initStorage(const char* file_path) { 74 | if(!SPIFFS.begin()) { 75 | DEBUG_ERROR("%s: failed to initialize SPIFFS", __FUNCTION__); 76 | return static_cast(Error::OtaStorageInit); 77 | } 78 | 79 | String spiffs_path = String("/spiffs") + String(file_path); 80 | _file = fopen(spiffs_path.c_str(), "wb"); 81 | 82 | if(!_file) { 83 | DEBUG_ERROR("%s: failed to write SPIFFS", __FUNCTION__); 84 | return static_cast(Error::OtaStorageInit); 85 | } 86 | return static_cast(Error::None); 87 | } 88 | 89 | int Arduino_UNOWIFIR4_OTA::closeStorage() { 90 | int res = 0; 91 | if(_file != nullptr) { 92 | res = fclose(_file); 93 | _file = nullptr; 94 | } 95 | 96 | SPIFFS.end(); 97 | return res; 98 | } 99 | 100 | int Arduino_UNOWIFIR4_OTA::download(const char * ota_url, const char* file_path) 101 | { 102 | int res = initStorage(file_path); 103 | 104 | if(res < 0) { 105 | return res; 106 | } 107 | 108 | /* Download and decode OTA file */ 109 | res = download(ota_url); 110 | 111 | closeStorage(); 112 | 113 | return res; 114 | } 115 | 116 | int Arduino_UNOWIFIR4_OTA::startDownload(const char * ota_url, const char* file_path) 117 | { 118 | int res = initStorage(file_path); 119 | 120 | if(res < 0) { 121 | return res; 122 | } 123 | 124 | /* Download and decode OTA file */ 125 | res = startDownload(ota_url); 126 | 127 | if(res < 0) { 128 | closeStorage(); 129 | } 130 | 131 | return res; 132 | } 133 | 134 | int Arduino_UNOWIFIR4_OTA::downloadPoll() 135 | { 136 | auto res = Arduino_ESP32_OTA::downloadPoll(); 137 | 138 | if(_updating_renesas && res != 0) { 139 | closeStorage(); 140 | } 141 | 142 | return res; 143 | } 144 | 145 | int Arduino_UNOWIFIR4_OTA::update(const char* file_path) 146 | { 147 | return BOSSA.program(file_path, Serial, GPIO_BOOT, GPIO_RST); 148 | } 149 | -------------------------------------------------------------------------------- /UNOR4USBBridge/OTA.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of UNOR4USBBridge_OTA. 3 | 4 | Copyright 2023 ARDUINO SA (http://www.arduino.cc/) 5 | 6 | This software is released under the GNU General Public License version 3, 7 | which covers the main part of arduino-cli. 8 | The terms of this license can be found at: 9 | https://www.gnu.org/licenses/gpl-3.0.en.html 10 | 11 | You can be released from the requirements of the above licenses by purchasing 12 | a commercial license. Buying such a license is mandatory if you want to modify or 13 | otherwise use the software for commercial activities involving the Arduino 14 | software without disclosing the source code of your own applications. To purchase 15 | a commercial license, send an email to license@arduino.cc. 16 | */ 17 | 18 | #ifndef ARDUINO_UNOWIFIR4_OTA_H_ 19 | #define ARDUINO_UNOWIFIR4_OTA_H_ 20 | 21 | /****************************************************************************** 22 | * INCLUDE 23 | ******************************************************************************/ 24 | 25 | #include 26 | 27 | /****************************************************************************** 28 | * DEFINES 29 | ******************************************************************************/ 30 | #define ARDUINO_RA4M1_OTA_MAGIC 0x23411002 31 | 32 | /****************************************************************************** 33 | * CLASS DECLARATION 34 | ******************************************************************************/ 35 | 36 | class Arduino_UNOWIFIR4_OTA : public Arduino_ESP32_OTA 37 | { 38 | public: 39 | 40 | Arduino_UNOWIFIR4_OTA(); 41 | ~Arduino_UNOWIFIR4_OTA(); 42 | 43 | enum class UNO_WiFi_R4_Error : int 44 | { 45 | StorageConfig = -20, 46 | }; 47 | 48 | using Arduino_ESP32_OTA::begin; 49 | Arduino_ESP32_OTA::Error begin(const char* file_path, uint32_t magic = ARDUINO_RA4M1_OTA_MAGIC, bool formatOnfail = false); 50 | 51 | using Arduino_ESP32_OTA::download; 52 | int download(const char * ota_url, const char* file_path); 53 | 54 | using Arduino_ESP32_OTA::startDownload; 55 | int startDownload(const char * ota_url, const char* file_path); 56 | 57 | int downloadPoll() override; 58 | using Arduino_ESP32_OTA::downloadProgress; 59 | 60 | void write_byte_to_flash(uint8_t data); 61 | 62 | using Arduino_ESP32_OTA::verify; 63 | 64 | using Arduino_ESP32_OTA::update; 65 | int update(const char* file_path); 66 | 67 | int initStorage(const char* file_path); 68 | int closeStorage(); 69 | 70 | private: 71 | FILE* _file; 72 | bool _updating_renesas; 73 | }; 74 | 75 | #endif /* ARDUINO_UNOWIFIR4_OTA_H_ */ 76 | -------------------------------------------------------------------------------- /UNOR4USBBridge/SSE.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the UNOR4USBBridge project. 3 | 4 | Copyright (c) 2024 Arduino SA 5 | 6 | This Source Code Form is subject to the terms of the Mozilla Public 7 | License, v. 2.0. If a copy of the MPL was not distributed with this 8 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | /****************************************************************************** 12 | INCLUDE 13 | ******************************************************************************/ 14 | #include 15 | 16 | #include "mbedtls/pk.h" 17 | #include "mbedtls/ctr_drbg.h" 18 | #include "mbedtls/entropy.h" 19 | 20 | #include "mbedtls/error.h" 21 | #include "mbedtls/asn1.h" 22 | #include "mbedtls/asn1write.h" 23 | #include "mbedtls/sha256.h" 24 | 25 | #include "SSE.h" 26 | 27 | /****************************************************************************** 28 | LOCAL MODULE FUNCTIONS 29 | ******************************************************************************/ 30 | 31 | /* 32 | * Keep an eye on this new implementation from mbedtls not yet merged 33 | * https://github.com/Mbed-TLS/mbedtls/pull/8703/files 34 | */ 35 | 36 | /* 37 | * https://github.com/Mbed-TLS/mbedtls/blob/aa3fa98bc4a99d21a973b891bf7bda9a27647068/library/pk_wrap.c#L543 38 | * An ASN.1 encoded signature is a sequence of two ASN.1 integers. Parse one of 39 | * those integers and convert it to the fixed-length encoding expected by PSA. 40 | */ 41 | static int extract_ecdsa_sig_int(unsigned char **from, const unsigned char *end, 42 | unsigned char *to, size_t to_len) 43 | { 44 | int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; 45 | size_t unpadded_len, padding_len; 46 | 47 | if ((ret = mbedtls_asn1_get_tag(from, end, &unpadded_len, 48 | MBEDTLS_ASN1_INTEGER)) != 0) { 49 | return ret; 50 | } 51 | 52 | while (unpadded_len > 0 && **from == 0x00) { 53 | (*from)++; 54 | unpadded_len--; 55 | } 56 | 57 | if (unpadded_len > to_len || unpadded_len == 0) { 58 | return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; 59 | } 60 | 61 | padding_len = to_len - unpadded_len; 62 | memset(to, 0x00, padding_len); 63 | memcpy(to + padding_len, *from, unpadded_len); 64 | (*from) += unpadded_len; 65 | 66 | return 0; 67 | } 68 | 69 | /* 70 | * https://github.com/Mbed-TLS/mbedtls/blob/aa3fa98bc4a99d21a973b891bf7bda9a27647068/library/pk_wrap.c#L576 71 | * Convert a signature from an ASN.1 sequence of two integers 72 | * to a raw {r,s} buffer. Note: the provided sig buffer must be at least 73 | * twice as big as int_size. 74 | */ 75 | static int extract_ecdsa_sig(unsigned char **p, const unsigned char *end, 76 | unsigned char *sig, size_t int_size) 77 | { 78 | int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; 79 | size_t tmp_size; 80 | 81 | if ((ret = mbedtls_asn1_get_tag(p, end, &tmp_size, 82 | MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { 83 | return ret; 84 | } 85 | 86 | /* Extract r */ 87 | if ((ret = extract_ecdsa_sig_int(p, end, sig, int_size)) != 0) { 88 | return ret; 89 | } 90 | /* Extract s */ 91 | if ((ret = extract_ecdsa_sig_int(p, end, sig + int_size, int_size)) != 0) { 92 | return ret; 93 | } 94 | 95 | return 0; 96 | } 97 | 98 | /* 99 | * https://github.com/Mbed-TLS/mbedtls/blob/47c74a477378ec3f0d1ba80547db836e078fa3a0/library/ecdsa.c#L609 100 | * Convert a signature (given by context) to ASN.1 101 | */ 102 | static int ecdsa_signature_to_asn1(const mbedtls_mpi *r, const mbedtls_mpi *s, 103 | unsigned char *sig, size_t sig_size, 104 | size_t *slen) 105 | { 106 | int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; 107 | unsigned char buf[MBEDTLS_ECDSA_MAX_LEN] = { 0 }; 108 | unsigned char *p = buf + sizeof(buf); 109 | size_t len = 0; 110 | 111 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_mpi(&p, buf, s)); 112 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_mpi(&p, buf, r)); 113 | 114 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, buf, len)); 115 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, buf, 116 | MBEDTLS_ASN1_CONSTRUCTED | 117 | MBEDTLS_ASN1_SEQUENCE)); 118 | 119 | if (len > sig_size) { 120 | return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; 121 | } 122 | 123 | memcpy(sig, p, len); 124 | *slen = len; 125 | 126 | return 0; 127 | } 128 | 129 | 130 | /****************************************************************************** 131 | PUBLIC MEMBER FUNCTIONS 132 | ******************************************************************************/ 133 | 134 | int Arduino_UNOWIFIR4_SSE::generateECKeyPair(unsigned char* derKey, int maxLen) 135 | { 136 | int ret = 1; 137 | mbedtls_pk_context key; 138 | mbedtls_ctr_drbg_context ctr_drbg; 139 | mbedtls_entropy_context entropy; 140 | const char *pers = "gen_key"; 141 | unsigned char mbedtls_buf[SSE_EC256_DER_PRI_KEY_LENGTH] = { 0 }; 142 | 143 | mbedtls_pk_init(&key); 144 | mbedtls_entropy_init(&entropy); 145 | mbedtls_ctr_drbg_init(&ctr_drbg); 146 | 147 | if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *) pers, strlen(pers))) != 0) { 148 | DEBUG_ERROR(" failed\n ! mbedtls_ctr_drbg_seed returned -0x%04x\n", (unsigned int) -ret); 149 | goto exit; 150 | } 151 | 152 | if ((ret = mbedtls_pk_setup(&key, mbedtls_pk_info_from_type((mbedtls_pk_type_t) MBEDTLS_PK_ECKEY))) != 0) { 153 | DEBUG_ERROR(" failed\n ! mbedtls_pk_setup returned -0x%04x", (unsigned int) -ret); 154 | goto exit; 155 | } 156 | 157 | if ((ret = mbedtls_ecp_gen_key((mbedtls_ecp_group_id) MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(key), mbedtls_ctr_drbg_random, &ctr_drbg)) != 0) { 158 | DEBUG_ERROR(" failed\n ! mbedtls_ecp_gen_key returned -0x%04x", (unsigned int) -ret); 159 | goto exit; 160 | } 161 | 162 | if ((ret = mbedtls_pk_write_key_der(&key, mbedtls_buf, sizeof(mbedtls_buf))) < 0) { 163 | DEBUG_ERROR(" failed\n ! mbedtls_pk_write_key_der returned -0x%04x", (unsigned int) -ret); 164 | goto exit; 165 | } 166 | 167 | if (ret > maxLen) { 168 | DEBUG_ERROR(" failed\n ! output buffer too small operation needs 0x%04x bytes", (unsigned int) ret); 169 | ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; 170 | goto exit; 171 | } 172 | 173 | /* Copy data from the end of mbedtls_buf buffer to der output */ 174 | memcpy(derKey, &mbedtls_buf[sizeof(mbedtls_buf) - ret], ret); 175 | 176 | exit: 177 | mbedtls_ctr_drbg_free(&ctr_drbg); 178 | mbedtls_entropy_free(&entropy); 179 | mbedtls_pk_free(&key); 180 | 181 | return ret; 182 | } 183 | 184 | int Arduino_UNOWIFIR4_SSE::exportECKeyXY(const unsigned char* derKey, int derLen, unsigned char* pubXY) 185 | { 186 | int ret = 1; 187 | mbedtls_pk_context key; 188 | mbedtls_ecp_keypair *ecp; 189 | 190 | mbedtls_pk_init(&key); 191 | 192 | /* Check if we can use parse public key */ 193 | if ((ret = mbedtls_pk_parse_key(&key, derKey, derLen, NULL, 0)) != 0) { 194 | DEBUG_ERROR(" failed\n ! mbedtls_pk_parse_key returned -0x%04x", (unsigned int) -ret); 195 | goto exit; 196 | } 197 | 198 | if (mbedtls_pk_get_type(&key) != MBEDTLS_PK_ECKEY) { 199 | DEBUG_ERROR(" failed\n ! Not an EC KEY"); 200 | goto exit; 201 | } 202 | 203 | /* Get elliptic curve point */ 204 | ecp = mbedtls_pk_ec(key); 205 | if ((ret = mbedtls_mpi_write_binary(&ecp->Q.X, &pubXY[0], SSE_EC256_X_LENGTH)) != 0) { 206 | DEBUG_ERROR(" failed\n ! mbedtls_mpi_write_binary Q.X returned -0x%04x", (unsigned int) -ret); 207 | goto exit; 208 | } 209 | 210 | if ((ret = mbedtls_mpi_write_binary(&ecp->Q.Y, &pubXY[SSE_EC256_X_LENGTH], SSE_EC256_Y_LENGTH)) != 0) { 211 | DEBUG_ERROR(" failed\n ! mbedtls_mpi_write_binary Q.XYreturned -0x%04x", (unsigned int) -ret); 212 | goto exit; 213 | } 214 | 215 | /* Success */ 216 | ret = SSE_EC256_PUB_KEY_LENGTH; 217 | 218 | exit: 219 | mbedtls_pk_free(&key); 220 | return ret; 221 | } 222 | 223 | int Arduino_UNOWIFIR4_SSE::importECKeyXY(const unsigned char* pubXY, unsigned char* derKey, int maxLen) 224 | { 225 | int ret = 1; 226 | mbedtls_pk_context key; 227 | unsigned char mbedtls_buf[SSE_EC256_DER_PUB_KEY_LENGTH] = { 0 }; 228 | mbedtls_ecp_keypair* ecp; 229 | 230 | mbedtls_pk_init(&key); 231 | 232 | if ((ret = mbedtls_pk_setup(&key, mbedtls_pk_info_from_type((mbedtls_pk_type_t) MBEDTLS_PK_ECKEY))) != 0) { 233 | DEBUG_ERROR(" failed\n ! mbedtls_pk_setup returned -0x%04x", (unsigned int) -ret); 234 | goto exit; 235 | } 236 | 237 | if (mbedtls_pk_get_type(&key) != MBEDTLS_PK_ECKEY) { 238 | DEBUG_ERROR(" failed\n ! Not an EC KEY"); 239 | goto exit; 240 | } 241 | 242 | ecp = mbedtls_pk_ec(key); 243 | 244 | if (( ret = mbedtls_ecp_group_load(&ecp->grp, MBEDTLS_ECP_DP_SECP256R1)) != 0) { 245 | DEBUG_ERROR(" failed\n ! mbedtls_ecp_group_load returned -0x%04x", (unsigned int) -ret); 246 | goto exit; 247 | } 248 | 249 | /* 250 | * Add uncompressed flag {0x04 | X | Y } 251 | * https://github.com/Mbed-TLS/mbedtls/blob/f660c7c92308b6080f8ca97fa1739370d1b2fab5/include/psa/crypto.h#L783 252 | */ 253 | mbedtls_buf[0] = 0x04; memcpy(&mbedtls_buf[1], pubXY, SSE_EC256_PUB_KEY_LENGTH); 254 | 255 | if (( ret = mbedtls_ecp_point_read_binary(&ecp->grp, &ecp->Q, mbedtls_buf, SSE_EC256_PUB_KEY_LENGTH + 1)) != 0) { 256 | DEBUG_ERROR(" failed\n ! mbedtls_ecp_point_read_binary returned -0x%04x", (unsigned int) -ret); 257 | goto exit; 258 | } 259 | 260 | if ((ret = mbedtls_pk_write_pubkey_der(&key, mbedtls_buf, sizeof(mbedtls_buf))) < 0) { 261 | DEBUG_ERROR(" failed\n ! mbedtls_pk_write_pubkey_der returned -0x%04x", (unsigned int) -ret); 262 | goto exit; 263 | } 264 | 265 | if (ret > maxLen) { 266 | DEBUG_ERROR(" failed\n ! output buffer too small operation needs 0x%04x bytes", (unsigned int) ret); 267 | ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; 268 | goto exit; 269 | } 270 | 271 | /* Copy data from the end of mbedtls_buf buffer to der output */ 272 | memcpy(derKey, &mbedtls_buf[sizeof(mbedtls_buf) - ret], ret); 273 | 274 | exit: 275 | mbedtls_pk_free(&key); 276 | return ret; 277 | } 278 | 279 | int Arduino_UNOWIFIR4_SSE::sha256(const unsigned char* message, int len, unsigned char* sha256) 280 | { 281 | int ret = 1; 282 | 283 | if((ret = mbedtls_sha256_ret(message, len, sha256, 0)) != 0) { 284 | DEBUG_ERROR(" failed\n ! mbedtls_sha256_ret returned -0x%04x\n", (unsigned int) -ret); 285 | return ret; 286 | } 287 | return SSE_SHA256_LENGTH; 288 | } 289 | 290 | int Arduino_UNOWIFIR4_SSE::sign(const unsigned char* derKey, int derLen, const unsigned char* sha256, unsigned char* sigRS) 291 | { 292 | int ret = 1; 293 | mbedtls_pk_context key; 294 | mbedtls_ctr_drbg_context ctr_drbg; 295 | mbedtls_entropy_context entropy; 296 | unsigned char mbedtls_buf[MBEDTLS_PK_SIGNATURE_MAX_SIZE] = { 0 }; 297 | unsigned char *p = (unsigned char *)&mbedtls_buf[0]; 298 | const char *pers = "gen_key"; 299 | size_t sig_size = 0; 300 | 301 | mbedtls_pk_init(&key); 302 | mbedtls_entropy_init(&entropy); 303 | mbedtls_ctr_drbg_init(&ctr_drbg); 304 | 305 | if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)pers, strlen(pers))) != 0) { 306 | DEBUG_ERROR(" failed\n ! mbedtls_ctr_drbg_seed returned -0x%04x\n", (unsigned int) -ret); 307 | goto exit; 308 | } 309 | 310 | /* verify if work using only private key*/ 311 | if ((ret = mbedtls_pk_parse_key(&key, derKey, derLen, NULL, 0)) != 0) { 312 | DEBUG_ERROR(" failed\n ! mbedtls_pk_parse_key returned -0x%04x", (unsigned int) -ret); 313 | goto exit; 314 | } 315 | 316 | if ((ret = mbedtls_pk_sign(&key, MBEDTLS_MD_SHA256, sha256, 0, mbedtls_buf, &sig_size, mbedtls_ctr_drbg_random, &ctr_drbg)) != 0) { 317 | DEBUG_ERROR(" failed\n ! mbedtls_pk_sign returned -0x%04x", (unsigned int) -ret); 318 | goto exit; 319 | } 320 | 321 | #if SSE_DEBUG_ENABLED 322 | log_v("SSE::sign: der signature"); 323 | log_buf_v(mbedtls_buf, sig_size); 324 | #endif 325 | 326 | /* Extract {r,s} values from DER signature */ 327 | extract_ecdsa_sig(&p, &mbedtls_buf[sig_size], sigRS, SSE_EC256_R_LENGTH); 328 | ret = SSE_EC256_SIGNATURE_LENGTH; 329 | 330 | exit: 331 | mbedtls_ctr_drbg_free(&ctr_drbg); 332 | mbedtls_pk_free(&key); 333 | return ret; 334 | } 335 | 336 | int Arduino_UNOWIFIR4_SSE::verify(const unsigned char* derKey, int derLen, const unsigned char* sha256, const unsigned char* sigRS) 337 | { 338 | int ret = 1; 339 | mbedtls_pk_context key; 340 | unsigned char mbedtls_buf[MBEDTLS_PK_SIGNATURE_MAX_SIZE] = { 0 }; 341 | mbedtls_mpi r,s; 342 | size_t sig_size = 0; 343 | 344 | mbedtls_mpi_init(&r); 345 | mbedtls_mpi_init(&s); 346 | mbedtls_pk_init(&key); 347 | 348 | /* Verify is only public key is needed */ 349 | if ((ret = mbedtls_pk_parse_public_key(&key, derKey, derLen)) != 0) { 350 | DEBUG_ERROR(" failed\n ! mbedtls_pk_parse_public_key returned -0x%04x", (unsigned int) -ret); 351 | goto exit; 352 | } 353 | 354 | #if SSE_DEBUG_ENABLED 355 | log_v("SSE::verify: sha256"); 356 | log_buf_v((const uint8_t *)sha256, SSE_SHA256_LENGTH); 357 | log_v("SSE::verify: compressed signature"); 358 | log_buf_v((const uint8_t *)sigRS, SSE_EC256_SIGNATURE_LENGTH); 359 | #endif 360 | 361 | /* Convert signature {r,s} values to DER */ 362 | mbedtls_mpi_read_binary( &r, sigRS, SSE_EC256_R_LENGTH ); 363 | mbedtls_mpi_read_binary( &s, sigRS + SSE_EC256_R_LENGTH, SSE_EC256_S_LENGTH ); 364 | ecdsa_signature_to_asn1(&r, &s, mbedtls_buf, MBEDTLS_PK_SIGNATURE_MAX_SIZE, &sig_size); 365 | 366 | #if SSE_DEBUG_ENABLED 367 | log_v("SSE::verify: der signature"); 368 | log_buf_v((const uint8_t *)mbedtls_buf, sig_size); 369 | #endif 370 | 371 | if ((ret = mbedtls_pk_verify(&key, MBEDTLS_MD_SHA256, sha256, SSE_SHA256_LENGTH, mbedtls_buf, sig_size)) != 0) { 372 | DEBUG_ERROR(" failed\n ! mbedtls_pk_verify returned -0x%04x", (unsigned int) -ret); 373 | goto exit; 374 | } 375 | 376 | exit: 377 | mbedtls_pk_free(&key); 378 | return ret; 379 | } 380 | 381 | Preferences sse; 382 | -------------------------------------------------------------------------------- /UNOR4USBBridge/SSE.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the UNOR4USBBridge project. 3 | 4 | Copyright (c) 2024 Arduino SA 5 | 6 | This Source Code Form is subject to the terms of the Mozilla Public 7 | License, v. 2.0. If a copy of the MPL was not distributed with this 8 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef ARDUINO_UNOWIFIR4_SSE_H_ 12 | #define ARDUINO_UNOWIFIR4_SSE_H_ 13 | 14 | /****************************************************************************** 15 | INCLUDE 16 | ******************************************************************************/ 17 | 18 | #include 19 | 20 | /****************************************************************************** 21 | * DEFINES 22 | ******************************************************************************/ 23 | 24 | #define SSE_DEBUG_ENABLED 0 25 | 26 | #define SSE_SHA256_LENGTH 32 27 | #define SSE_EC256_R_LENGTH 32 28 | #define SSE_EC256_S_LENGTH SSE_EC256_R_LENGTH 29 | #define SSE_EC256_SIGNATURE_LENGTH (SSE_EC256_R_LENGTH + SSE_EC256_S_LENGTH) 30 | #define SSE_EC256_X_LENGTH 32 31 | #define SSE_EC256_Y_LENGTH SSE_EC256_X_LENGTH 32 | #define SSE_EC256_PUB_KEY_LENGTH (SSE_EC256_X_LENGTH + SSE_EC256_Y_LENGTH) 33 | 34 | /* https://github.com/Mbed-TLS/mbedtls/blob/8bdd8cdc4fc99588f6e3ed47d3dac7b2ddbdb6af/library/pkwrite.h#L82 */ 35 | #define SSE_EC256_DER_PUB_KEY_LENGTH (30 + 2 * SSE_EC256_X_LENGTH) 36 | #define SSE_EC256_DER_PRI_KEY_LENGTH (29 + 3 * SSE_EC256_X_LENGTH) 37 | 38 | /****************************************************************************** 39 | * CLASS DECLARATION 40 | ******************************************************************************/ 41 | 42 | class Arduino_UNOWIFIR4_SSE 43 | { 44 | 45 | public: 46 | 47 | /** generateECKeyPair 48 | * 49 | * Create a new ECCurve_NIST_P256 keypair. Only public key will be available 50 | * inside KeyBuf with DER format. 51 | * 52 | * | P256 Header (26 bytes)| 0x04 (1 byte)| Public key X Y values (64 bytes) | 53 | * 54 | * @param[out] derKey output buffer containing the public key in DER format 55 | * @param[in] maxLen output buffer max size in bytes 56 | * 57 | * @return size of der buffer on Success a negative number on Failure (mbedtls error code) 58 | */ 59 | static int generateECKeyPair(unsigned char* derKey, int maxLen); 60 | 61 | /** exportECKeyXY 62 | * 63 | * Exports public key XY values from a DER formatted ECCurve_NIST_P256 key 64 | * 65 | * @param[in] derKey input buffer containing the public key in DER format 66 | * @param[in] derLen key size in bytes 67 | * @param[out] pubXY output buffer containing public key XY value. Should be at least 64 bytes 68 | * 69 | * @return size of XY buffer on Success a negative number on Failure (mbedtls error code) 70 | */ 71 | static int exportECKeyXY(const unsigned char* derKey, int derLen, unsigned char* pubXY); 72 | 73 | /** importECKeyXY 74 | * 75 | * Imports public key XY values from buffer and writes a DER formatted ECCurve_NIST_P256 public key 76 | * 77 | * @param[in] pubXY output buffer containing public key XY value. 78 | * @param[out] derKey output buffer containing the public key in DER format 79 | * @param[out] maxLen output buffer max size in bytes 80 | * 81 | * @return size of der buffer on Success a negative number on Failure (mbedtls error code) 82 | */ 83 | static int importECKeyXY(const unsigned char* pubXY, unsigned char* derKey, int maxLen); 84 | 85 | /** sha256 86 | * 87 | * One-shot sha256 88 | * 89 | * @param[in] message Input data buffer 90 | * @param[in] len Input data length 91 | * @param[out] sha256 Output buffer should be at least 32 bytes long 92 | * 93 | * @return size of sha256 buffer on Success a negative number on Failure (mbedtls error code) 94 | */ 95 | static int sha256(const unsigned char* message, int len, unsigned char* sha256); 96 | 97 | /** sign 98 | * 99 | * Computes ECDSA signature using private derKey and input sha256 100 | * 101 | * @param[in] derKey input buffer containing the private key in DER format 102 | * @param[in] derLen key size in bytes 103 | * @param[in] sha256 input sha256 104 | * @param[out] sigRS output buffer containing signature RS value. Should be at least 64 bytes 105 | * 106 | * @return size of RS buffer on Success a negative number on Failure (mbedtls error code) 107 | */ 108 | static int sign(const unsigned char* derKey, int derLen, const unsigned char* sha256, unsigned char* sigRS); 109 | 110 | /** verify 111 | * 112 | * Verify ECDSA signature using public key 113 | * 114 | * @param[in] derKey input buffer containing the public key in DER format 115 | * @param[in] derLen key size in bytes 116 | * @param[in] sha256 input sha256 117 | * @param[in] sigRS input buffer containing signature RS value 118 | * 119 | * @return 0 on Success a negative number on Failure (mbedtls error code) 120 | */ 121 | static int verify(const unsigned char* derKey, int derLen, const unsigned char* sha256, const unsigned char* sigRS); 122 | 123 | }; 124 | 125 | extern Preferences sse; 126 | 127 | #endif /* Arduino_UNOWIFIR4_SSE */ 128 | -------------------------------------------------------------------------------- /UNOR4USBBridge/UNOR4USBBridge.ino: -------------------------------------------------------------------------------- 1 | 2 | // Arduino Santiago Composta / Leven 3 | // ESP32-S3 serial-to-usb bridge and wifi host 4 | // Serial refers to tinyUSB CDC port 5 | // Serial1 refers to gpio 20/21 ( -> will become Serial object in RA4 variant P109 / P110) 6 | // Serial2 refers to gpio 2/3 ( -> will become SerialNina object in RA4 variant P501 / P502) 7 | // https://arduino.atlassian.net/wiki/spaces/HWU4/pages/3656351882/Programming+RA4M1+via+ESP32-C3 8 | 9 | // implement AT server via github.com/SudoMaker/chAT 10 | 11 | #include "at_handler.h" 12 | 13 | #include "USB.h" 14 | #include "USBCDC.h" 15 | #include "DAP.h" 16 | #include "Arduino_DebugUtils.h" 17 | 18 | //#define DEBUG_AT 19 | 20 | #define SERIAL_USER USBSerial 21 | #define SERIAL_DEBUG USBSerial 22 | #define SERIAL_USER_INTERNAL Serial 23 | 24 | #ifdef DEBUG_AT 25 | #define SERIAL_AT USBSerial 26 | #else 27 | #define SERIAL_AT Serial1 28 | #endif 29 | 30 | static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data); 31 | 32 | 33 | static uint32_t _baud = 0; 34 | static CAtHandler atHandler(&SERIAL_AT); 35 | USBCDC USBSerial(0); 36 | 37 | 38 | bool enableSTA(bool enable); 39 | bool enableAP(bool enable); 40 | 41 | ssize_t write_fn(void* cookie, const char* buf, ssize_t size) 42 | { 43 | /* redirect the bytes somewhere; writing to Serial just for an example */ 44 | USBSerial.write((uint8_t*) buf, size); 45 | return size; 46 | } 47 | 48 | void ets_putc_handler(char c) 49 | { 50 | /* this gets called from various ets_printf / esp_rom_printf calls */ 51 | static char buf[256]; 52 | static size_t buf_pos = 0; 53 | buf[buf_pos] = c; 54 | buf_pos++; 55 | if (c == '\n' || buf_pos == sizeof(buf)) { 56 | /* flush */ 57 | write_fn(NULL, buf, buf_pos); 58 | buf_pos = 0; 59 | } 60 | } 61 | 62 | /* -------------------------------------------------------------------------- */ 63 | void CAtHandler::onWiFiEvent(WiFiEvent_t event) { 64 | /* -------------------------------------------------------------------------- */ 65 | switch (event) { 66 | case ARDUINO_EVENT_WIFI_READY: 67 | wifi_status = WIFI_ST_IDLE_STATUS; 68 | break; 69 | case ARDUINO_EVENT_WIFI_SCAN_DONE: 70 | wifi_status = WIFI_ST_SCAN_COMPLETED; 71 | break; 72 | case ARDUINO_EVENT_WIFI_STA_START: 73 | wifi_status = WIFI_ST_IDLE_STATUS; 74 | break; 75 | case ARDUINO_EVENT_WIFI_STA_STOP: 76 | wifi_status = WIFI_ST_NO_MODULE; 77 | break; 78 | case ARDUINO_EVENT_WIFI_STA_CONNECTED: 79 | wifi_status = WIFI_ST_CONNECTED; 80 | break; 81 | case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: 82 | wifi_status = WIFI_ST_IDLE_STATUS; 83 | break; 84 | case ARDUINO_EVENT_WIFI_AP_START: 85 | wifi_status = WIFI_ST_AP_LISTENING; 86 | break; 87 | case ARDUINO_EVENT_WIFI_AP_STOP: 88 | wifi_status = WIFI_ST_IDLE_STATUS; 89 | break; 90 | case ARDUINO_EVENT_WIFI_AP_STACONNECTED: 91 | wifi_status = WIFI_ST_AP_CONNECTED; 92 | break; 93 | case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED: 94 | 95 | 96 | break; 97 | case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED: 98 | case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: 99 | case ARDUINO_EVENT_WIFI_STA_GOT_IP: 100 | case ARDUINO_EVENT_WIFI_STA_GOT_IP6: 101 | case ARDUINO_EVENT_WIFI_STA_LOST_IP: 102 | case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED: 103 | case ARDUINO_EVENT_WIFI_AP_GOT_IP6: 104 | case ARDUINO_EVENT_WIFI_FTM_REPORT: 105 | default: 106 | break; 107 | } 108 | 109 | } 110 | 111 | TaskHandle_t atTask; 112 | void atLoop(void* param) { 113 | while (1) { 114 | if (_baud != 1200 && _baud != 2400) { 115 | atHandler.run(); 116 | } 117 | yield(); 118 | } 119 | } 120 | 121 | /* -------------------------------------------------------------------------- */ 122 | void setup() { 123 | /* -------------------------------------------------------------------------- */ 124 | 125 | /* redirect stdout */ 126 | stdout = funopen(NULL, NULL, &write_fn, NULL, NULL); 127 | static char linebuf[256]; 128 | setvbuf(stdout, linebuf, _IOLBF, sizeof(linebuf)); 129 | 130 | /* redirect ets_printf / esp_rom_printf output */ 131 | ets_install_putc1(&ets_putc_handler); 132 | 133 | pinMode(GPIO_BOOT, OUTPUT); 134 | pinMode(GPIO_RST, OUTPUT); 135 | digitalWrite(GPIO_BOOT, HIGH); 136 | digitalWrite(GPIO_RST, HIGH); 137 | 138 | #ifdef DEBUG_AT 139 | SERIAL_AT.begin(115200); 140 | while (!SERIAL_AT); 141 | SERIAL_AT.println("READY"); 142 | #else 143 | USB.VID(0x2341); 144 | USB.PID(0x1002); 145 | USB.manufacturerName("Arduino"); 146 | USB.productName("UNO WiFi R4 CMSIS-DAP"); 147 | USB.firmwareVersion(U8TOBCD(FIRMWARE_MAJOR) << 8 | U8TOBCD(FIRMWARE_MINOR)); 148 | //USB.enableDFU(); 149 | DAP.begin(); 150 | SERIAL_USER.onEvent(usbEventCallback); 151 | SERIAL_USER.enableReboot(false); 152 | SERIAL_USER.begin(115200); 153 | SERIAL_USER.setRxBufferSize(0); 154 | SERIAL_USER.setRxBufferSize(2048); 155 | SERIAL_USER_INTERNAL.setRxBufferSize(8192); 156 | SERIAL_USER_INTERNAL.setTxBufferSize(8192); 157 | SERIAL_USER_INTERNAL.begin(115200, SERIAL_8N1, 44, 43); 158 | SERIAL_AT.setRxBufferSize(8192); 159 | SERIAL_AT.setTxBufferSize(8192); 160 | SERIAL_AT.begin(115200, SERIAL_8N1, 6, 5); 161 | USB.begin(); 162 | #endif 163 | /* Set up wifi event */ 164 | WiFi.onEvent(CAtHandler::onWiFiEvent); 165 | 166 | /* Configure ntp */ 167 | configTime(0, 0, "pool.ntp.org"); 168 | 169 | Debug.setDebugOutputStream(&USBSerial); 170 | Debug.setDebugLevel(DBG_ERROR); 171 | 172 | xTaskCreatePinnedToCore( 173 | atLoop, /* Function to implement the task */ 174 | "Task1", /* Name of the task */ 175 | 10000, /* Stack size in words */ 176 | NULL, /* Task input parameter */ 177 | 1, /* Priority of the task */ 178 | &atTask, /* Task handle. */ 179 | 0); /* Core where the task should run */ 180 | 181 | } 182 | 183 | /* 184 | arduino-builder -compile -fqbn=espressif:esp32:esp32s3:JTAGAdapter=default,PSRAM=disabled,FlashMode=qio,FlashSize=4M,LoopCore=1,EventsCore=1,USBMode=default,CDCOnBoot=cdc,MSCOnBoot=default,DFUOnBoot=default,UploadMode=default,PartitionScheme=huge_app,CPUFreq=240,UploadSpeed=921600,DebugLevel=none,EraseFlash=none -vid-pid=303A_1001 -ide-version=10820 CompostaUSBBridge.ino 185 | */ 186 | 187 | static uint8_t buf[2048]; 188 | 189 | /* -------------------------------------------------------------------------- */ 190 | void loop() { 191 | /* -------------------------------------------------------------------------- */ 192 | 193 | if (SERIAL_USER.baudRate() != _baud) { 194 | _baud = SERIAL_USER.baudRate(); 195 | } 196 | 197 | int i = 0; 198 | 199 | if (SERIAL_USER.available()) { 200 | i = min((unsigned int)SERIAL_USER.available(), sizeof(buf)); 201 | SERIAL_USER.readBytes(buf, i); 202 | } 203 | 204 | if (i > 0) { 205 | SERIAL_USER_INTERNAL.write(buf, i); 206 | } 207 | 208 | i = 0; 209 | if (SERIAL_USER_INTERNAL.available()) { 210 | i = min((unsigned int)SERIAL_USER_INTERNAL.available(), sizeof(buf)); 211 | SERIAL_USER_INTERNAL.readBytes(buf, i); 212 | } 213 | if (i > 0) { 214 | SERIAL_USER.write(buf, i); 215 | } 216 | 217 | yield(); 218 | } 219 | 220 | void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { 221 | if (event_base == ARDUINO_USB_CDC_EVENTS) { 222 | arduino_usb_cdc_event_data_t * data = (arduino_usb_cdc_event_data_t*)event_data; 223 | switch (event_id) { 224 | case ARDUINO_USB_CDC_LINE_CODING_EVENT: 225 | auto baud = data->line_coding.bit_rate; 226 | if (baud == 1200) { 227 | WiFi.disconnect(); 228 | digitalWrite(GPIO_BOOT, HIGH); 229 | digitalWrite(GPIO_RST, LOW); 230 | delay(100); 231 | digitalWrite(GPIO_RST, HIGH); 232 | delay(100); 233 | digitalWrite(GPIO_RST, LOW); 234 | delay(100); 235 | digitalWrite(GPIO_RST, HIGH); 236 | } else if (baud == 2400) { 237 | WiFi.disconnect(); 238 | digitalWrite(GPIO_BOOT, LOW); 239 | digitalWrite(GPIO_RST, HIGH); 240 | delay(100); 241 | digitalWrite(GPIO_RST, LOW); 242 | delay(100); 243 | digitalWrite(GPIO_RST, HIGH); 244 | } else { 245 | SERIAL_USER_INTERNAL.updateBaudRate(baud); 246 | } 247 | while (SERIAL_USER_INTERNAL.available()) { 248 | SERIAL_USER_INTERNAL.read(); 249 | } 250 | break; 251 | } 252 | } 253 | } 254 | 255 | extern "C" void mylogchar(char c) { 256 | SERIAL_USER.print(c); 257 | } -------------------------------------------------------------------------------- /UNOR4USBBridge/at_handler.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "at_handler.h" 4 | #include "commands.h" 5 | #include "cmds_esp_generic.h" 6 | 7 | 8 | #include "cmds_wifi_station.h" 9 | #include "cmds_wifi_softAP.h" 10 | #include "cmds_wifi_netif.h" 11 | #include "cmds_preferences.h" 12 | #include "cmds_wifi_SSL.h" 13 | #include "cmds_wifi_udp.h" 14 | #include "cmds_ble_bridge.h" 15 | #include "cmds_ota.h" 16 | #include "cmds_se.h" 17 | 18 | using namespace SudoMaker; 19 | 20 | uint8_t CAtHandler::wifi_status = WIFI_ST_IDLE_STATUS; 21 | 22 | /* -------------------------------------------------------------------------- */ 23 | CClientWrapper CAtHandler::getClient(int sock) { 24 | /* -------------------------------------------------------------------------- */ 25 | CClientWrapper rv; 26 | 27 | bool is_server = false; 28 | bool is_sslclienet = false; 29 | 30 | int internal_sock = -1; 31 | 32 | if(sock >= START_SSL_CLIENT_SOCK) { 33 | internal_sock = sock - START_SSL_CLIENT_SOCK; 34 | is_sslclienet = true; 35 | } else 36 | if(sock >= START_CLIENT_SERVER_SOCK) { 37 | internal_sock = sock - START_CLIENT_SERVER_SOCK; 38 | is_server = true; 39 | } 40 | else { 41 | internal_sock = sock; 42 | } 43 | 44 | if(internal_sock < 0 || internal_sock >= MAX_CLIENT_AVAILABLE) { 45 | rv.client = nullptr; 46 | rv.sslclient = nullptr; 47 | rv.can_delete = -1; 48 | return rv; 49 | } 50 | 51 | if (is_sslclienet) { 52 | rv.sslclient = sslclients[internal_sock]; 53 | rv.can_delete = internal_sock; 54 | } 55 | else if(is_server) { 56 | rv.client = &serverClients[internal_sock].client; 57 | rv.can_delete = -1; 58 | } 59 | else { 60 | rv.client = clients[internal_sock]; 61 | rv.can_delete = internal_sock; 62 | } 63 | return rv; 64 | } 65 | 66 | 67 | 68 | /* -------------------------------------------------------------------------- */ 69 | void CAtHandler::run() { 70 | /* -------------------------------------------------------------------------- */ 71 | at_srv.run(); 72 | 73 | // execute all the pending tasks 74 | 75 | // it is intended for the tasks to add other tasks in queue after being executed, 76 | // saving the current size of the queue 77 | auto size = tasks.size(); 78 | for(int i=0; iavailable()) { 121 | yield(); 122 | return (unsigned int)0; 123 | } 124 | return serial->read(buf, min((unsigned int)serial->available(), len)); 125 | }, 126 | .callback_io_write = [this](auto buf, auto len) { 127 | return serial->write(buf, len); 128 | }, 129 | }); 130 | 131 | at_srv.set_command_callback([this](chAT::Server & srv, const std::string & command) { 132 | auto it = command_table.find(command); 133 | 134 | if (it == command_table.end()) { 135 | return chAT::CommandStatus::ERROR; 136 | } 137 | else { 138 | return it->second(srv, srv.parser()); 139 | } 140 | }); 141 | 142 | /* SET UP COMMAND TABLE */ 143 | add_cmds_esp_generic(); 144 | add_cmds_wifi_station(); 145 | add_cmds_wifi_softAP(); 146 | add_cmds_wifi_SSL(); 147 | add_cmds_wifi_netif(); 148 | add_cmds_wifi_udp(); 149 | add_cmds_ble_bridge(); 150 | add_cmds_ota(); 151 | add_cmds_preferences(); 152 | add_cmds_se(); 153 | } 154 | -------------------------------------------------------------------------------- /UNOR4USBBridge/at_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef ARDUINO_AT_HANDLER_H 2 | #define ARDUINO_AT_HANDLER_H 3 | 4 | #include "chAT.hpp" 5 | #include "WiFi.h" 6 | #include "Server.h" 7 | #include 8 | 9 | #include "WiFiClient.h" 10 | #include 11 | #include "FS.h" 12 | #include "SPIFFS.h" 13 | 14 | #define MAX_CLIENT_AVAILABLE 8 15 | #define MAX_SERVER_AVAILABLE 4 16 | #define MAX_UDP_AVAILABLE 4 17 | 18 | #define ESP_FW_VERSION "0.6.0" 19 | #define FIRMWARE_MAJOR 0 20 | #define FIRMWARE_MINOR 6 21 | #define FIRMWARE_PATCH 0 22 | 23 | #define U8TOBCD(u) ((((u/10)%10)<<4)|(u%10)) 24 | 25 | #define GPIO_BOOT 9 26 | #define GPIO_RST 4 27 | 28 | using namespace SudoMaker; 29 | 30 | #define WIFI_ST_NO_SHIELD 255 31 | #define WIFI_ST_NO_MODULE 255 32 | #define WIFI_ST_IDLE_STATUS 0 33 | #define WIFI_ST_NO_SSID_AVAIL 1 34 | #define WIFI_ST_SCAN_COMPLETED 2 35 | #define WIFI_ST_CONNECTED 3 36 | #define WIFI_ST_CONNECT_FAILED 4 37 | #define WIFI_ST_CONNECTION_LOST 5 38 | #define WIFI_ST_DISCONNECTED 6 39 | #define WIFI_ST_AP_LISTENING 7 40 | #define WIFI_ST_AP_CONNECTED 8 41 | #define WIFI_ST_AP_FAILED 9 42 | 43 | 44 | class CClientWrapper { 45 | public: 46 | WiFiClient *client; 47 | WiFiClientSecure *sslclient; 48 | int can_delete = -1; 49 | }; 50 | 51 | class CServerClient { 52 | public: 53 | WiFiClient client; 54 | int server = -1; 55 | bool accepted = false; 56 | }; 57 | 58 | class CAtHandler { 59 | private: 60 | static uint8_t wifi_status; 61 | 62 | int last_server_client_sock; 63 | 64 | WiFiUDP * udps[MAX_UDP_AVAILABLE]; 65 | WiFiServer * serverWiFi[MAX_SERVER_AVAILABLE]; 66 | WiFiClient * clients[MAX_CLIENT_AVAILABLE]; 67 | CServerClient serverClients[MAX_CLIENT_AVAILABLE]; 68 | WiFiClientSecure * sslclients[MAX_CLIENT_AVAILABLE]; 69 | std::vector clients_ca[MAX_CLIENT_AVAILABLE]; 70 | std::vector clients_cert_pem[MAX_CLIENT_AVAILABLE]; 71 | std::vector clients_key_pem[MAX_CLIENT_AVAILABLE]; 72 | 73 | // this vector is a list of function to be called in the run function 74 | std::queue> tasks; 75 | 76 | int udps_num = 0; 77 | int servers_num = 0; 78 | int clientsToServer_num = 0; 79 | int clients_num = 0; 80 | int sslclients_num = 0; 81 | std::unordered_map> command_table; 82 | chAT::Server at_srv; 83 | HardwareSerial *serial; 84 | uint8_t* cert_in_flash_ptr = nullptr; 85 | spi_flash_mmap_handle_t cert_in_flash_handle; 86 | 87 | CClientWrapper getClient(int sock); 88 | 89 | void add_cmds_esp_generic(); 90 | void add_cmds_wifi_station(); 91 | void add_cmds_wifi_softAP(); 92 | void add_cmds_wifi_SSL(); 93 | void add_cmds_wifi_netif(); 94 | void add_cmds_wifi_udp(); 95 | void add_cmds_ble_bridge(); 96 | void add_cmds_ota(); 97 | void add_cmds_preferences(); 98 | void add_cmds_se(); 99 | public: 100 | inline void addTask(std::function task) { 101 | tasks.push(task); 102 | } 103 | 104 | /* Used by cmds_se */ 105 | std::vector se_buf; 106 | 107 | /* Used by cmds_ota */ 108 | std::vector ota_cert_buf; 109 | 110 | /* Used by cmds_preferences */ 111 | std::vector pref_buf; 112 | 113 | CAtHandler(HardwareSerial *s); 114 | CAtHandler() = delete ; 115 | static void onWiFiEvent(WiFiEvent_t event); 116 | void run(); 117 | }; 118 | 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /UNOR4USBBridge/chAT.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of chAT. 3 | Copyright (C) 2022-2023 Reimu NotMoe 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as 7 | published by the Free Software Foundation, either version 3 of the 8 | License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | namespace SudoMaker::chAT { 35 | enum class CommandMode : unsigned int { 36 | Run, Write, Read, Test 37 | }; 38 | 39 | class ATParser { 40 | public: 41 | enum class ParseState : unsigned int { 42 | Keyword, Command, Argument, Done, Malformed 43 | }; 44 | 45 | bool malformed; 46 | ParseState state; 47 | CommandMode cmd_mode; 48 | size_t keyword_count; 49 | std::string command; 50 | std::vector args; 51 | bool args_quote; 52 | size_t args_escape_count; 53 | #ifdef CHAT_STRICT_CRLF 54 | uint8_t last_data[2]; 55 | #endif 56 | size_t bytes_parsed; 57 | 58 | ATParser(); 59 | 60 | void reset(); 61 | void show(); 62 | size_t parse(const uint8_t *buf, size_t len); 63 | }; 64 | 65 | struct io_interface { 66 | typedef std::function io_callback_t; 67 | 68 | io_callback_t callback_io_read, callback_io_write; 69 | }; 70 | 71 | enum class CommandStatus { 72 | OK, ERROR, CUSTOM 73 | }; 74 | 75 | class Server; 76 | class ServerImpl; 77 | 78 | typedef std::function command_callback_t; 79 | 80 | class Server { 81 | private: 82 | std::unique_ptr pimpl; 83 | public: 84 | enum RunStatus : unsigned { 85 | OK = 0, WantRead = 0x1, WantWrite = 0x2, 86 | }; 87 | 88 | Server(); 89 | ~Server(); 90 | 91 | ATParser &parser() noexcept; 92 | 93 | void set_command_callback(command_callback_t cmd_cb); 94 | void set_io_callback(io_interface io_cbs); 95 | const io_interface& get_io_callback() noexcept; 96 | void set_nonblocking_mode(bool v) noexcept; 97 | void set_parser_debugging(bool v) noexcept; 98 | void set_write_buffer_size_limit(size_t l = 16384) noexcept; 99 | 100 | std::vector inhibit_read(size_t raw_data_len); 101 | void continue_read() noexcept; 102 | 103 | void write_data(const void *buf, size_t len); 104 | void write_cstr(const char *buf, ssize_t len = -1); 105 | void write_str(std::string str); 106 | void write_vec8(std::vector vec8); 107 | 108 | void write_response(std::string str); 109 | void write_response(const char *buf, ssize_t len = -1); 110 | void write_error_reason(std::string str); 111 | void write_error_reason(const char *buf, ssize_t len = -1); 112 | void write_error(); 113 | void write_ok(); 114 | 115 | void write_response_prompt(); 116 | void write_error_prompt(); 117 | void write_line_end(); 118 | 119 | RunStatus run(); 120 | }; 121 | 122 | inline Server::RunStatus operator|(Server::RunStatus a, Server::RunStatus b) { 123 | return static_cast(static_cast(a) | static_cast(b)); 124 | } 125 | 126 | inline Server::RunStatus operator&(Server::RunStatus a, Server::RunStatus b) { 127 | return static_cast(static_cast(a) & static_cast(b)); 128 | } 129 | 130 | inline Server::RunStatus& operator|=(Server::RunStatus& a, Server::RunStatus b) { 131 | a = a | b; 132 | return a; 133 | } 134 | 135 | inline Server::RunStatus& operator&=(Server::RunStatus& a, Server::RunStatus b) { 136 | a = a & b; 137 | return a; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /UNOR4USBBridge/cmds_ble_bridge.h: -------------------------------------------------------------------------------- 1 | #ifndef CMDS_BLE_BRIDGE_H 2 | #define CMDS_BLE_BRIDGE_H 3 | 4 | #include "at_handler.h" 5 | #include "ArduinoBLE.h" 6 | #include "utility/HCIVirtualTransport.h" 7 | 8 | extern HCIVirtualTransportClass HCIVirtualTransport; 9 | 10 | void CAtHandler::add_cmds_ble_bridge() { 11 | 12 | /* ....................................................................... */ 13 | command_table[_HCI_BEGIN] = [this](auto & srv, auto & parser) { 14 | /* ....................................................................... */ 15 | srv.write_response_prompt(); 16 | switch (parser.cmd_mode) { 17 | case chAT::CommandMode::Run: { 18 | auto res = HCIVirtualTransport.begin(); 19 | if (res) { 20 | return chAT::CommandStatus::OK; 21 | } 22 | } 23 | } 24 | return chAT::CommandStatus::ERROR; 25 | }; 26 | 27 | /* ....................................................................... */ 28 | command_table[_HCI_END] = [this](auto & srv, auto & parser) { 29 | /* ....................................................................... */ 30 | srv.write_response_prompt(); 31 | switch (parser.cmd_mode) { 32 | case chAT::CommandMode::Run: { 33 | HCIVirtualTransport.end(); 34 | return chAT::CommandStatus::OK; 35 | } 36 | } 37 | return chAT::CommandStatus::ERROR; 38 | }; 39 | 40 | /* ....................................................................... */ 41 | command_table[_HCI_WAIT] = [this](auto & srv, auto & parser) { 42 | /* ....................................................................... */ 43 | srv.write_response_prompt(); 44 | switch (parser.cmd_mode) { 45 | case chAT::CommandMode::Write: { 46 | if (parser.args.size() != 1) { 47 | return chAT::CommandStatus::ERROR; 48 | } 49 | 50 | auto &timeout = parser.args[0]; 51 | if (timeout.empty()) { 52 | return chAT::CommandStatus::ERROR; 53 | } 54 | int _timeout = atoi(timeout.c_str()); 55 | HCIVirtualTransport.wait(_timeout); 56 | return chAT::CommandStatus::OK; 57 | } 58 | } 59 | return chAT::CommandStatus::ERROR; 60 | }; 61 | 62 | /* ....................................................................... */ 63 | command_table[_HCI_AVAILABLE] = [this](auto & srv, auto & parser) { 64 | /* ....................................................................... */ 65 | switch (parser.cmd_mode) { 66 | case chAT::CommandMode::Read: { 67 | srv.write_response_prompt(); 68 | String av(HCIVirtualTransport.available()); 69 | srv.write_str((const char *)(av.c_str())); 70 | srv.write_line_end(); 71 | return chAT::CommandStatus::OK; 72 | } 73 | } 74 | return chAT::CommandStatus::ERROR; 75 | }; 76 | 77 | /* ....................................................................... */ 78 | command_table[_HCI_READ] = [this](auto & srv, auto & parser) { 79 | /* ....................................................................... */ 80 | switch (parser.cmd_mode) { 81 | case chAT::CommandMode::Run: { 82 | std::vector data; 83 | int i = 0; 84 | auto howmany = HCIVirtualTransport.available(); 85 | while (i < howmany) { 86 | data.push_back(HCIVirtualTransport.read()); 87 | i++; 88 | } 89 | data.resize(howmany); 90 | srv.write_response_prompt(); 91 | srv.write_str(String(i).c_str()); 92 | srv.write_str("|"); 93 | srv.write_vec8(data); 94 | 95 | return chAT::CommandStatus::OK; 96 | } 97 | default: 98 | return chAT::CommandStatus::ERROR; 99 | } 100 | }; 101 | 102 | /* ....................................................................... */ 103 | command_table[_HCI_WRITE] = [this](auto & srv, auto & parser) { 104 | /* ....................................................................... */ 105 | switch (parser.cmd_mode) { 106 | case chAT::CommandMode::Write: { 107 | 108 | if (parser.args.size() != 1) { 109 | return chAT::CommandStatus::ERROR; 110 | } 111 | 112 | /* data len */ 113 | auto &size_p = parser.args[0]; 114 | if (size_p.empty()) { 115 | return chAT::CommandStatus::ERROR; 116 | } 117 | 118 | int data_size = atoi(size_p.c_str()); 119 | 120 | if(data_size <= 0) { 121 | return chAT::CommandStatus::ERROR; 122 | } 123 | 124 | /* ----------------------------------- 125 | * BEGIN TRANSPARENT DATA TRANSMISSION 126 | * ----------------------------------- */ 127 | std::vector data_received; 128 | data_received = srv.inhibit_read(data_size); 129 | size_t offset = data_received.size(); 130 | 131 | if(offset < data_size) { 132 | 133 | data_received.resize(data_size); 134 | do { 135 | offset += serial->read(data_received.data() + offset, data_size - offset); 136 | } while (offset < data_size); 137 | } 138 | 139 | srv.continue_read(); 140 | 141 | unsigned long start_time = millis(); 142 | int sent = 0; 143 | while(millis() - start_time < 5000 && sent < data_received.size()){ 144 | sent += HCIVirtualTransport.write(data_received.data() + sent, data_received.size() - sent); 145 | } 146 | 147 | if (sent < data_received.size()) { 148 | return chAT::CommandStatus::ERROR; 149 | } 150 | 151 | return chAT::CommandStatus::OK; 152 | } 153 | } 154 | return chAT::CommandStatus::ERROR; 155 | }; 156 | } 157 | 158 | #endif -------------------------------------------------------------------------------- /UNOR4USBBridge/cmds_esp_generic.h: -------------------------------------------------------------------------------- 1 | #ifndef CMDS_ESP_GENERIC_H 2 | #define CMDS_ESP_GENERIC_H 3 | 4 | #include "at_handler.h" 5 | #include "ping.h" 6 | 7 | extern "C" { 8 | #include "esp32-hal-tinyusb.h" 9 | } 10 | 11 | static const uint8_t version[4] = { 12 | (FIRMWARE_MAJOR & 0xff), 13 | (FIRMWARE_MINOR & 0xff), 14 | (FIRMWARE_PATCH & 0xff), 15 | 0 16 | }; 17 | 18 | void CAtHandler::add_cmds_esp_generic() { 19 | 20 | /* ....................................................................... */ 21 | command_table[_RESET] = [this](auto & srv, auto & parser) { 22 | /* ....................................................................... */ 23 | switch (parser.cmd_mode) { 24 | case chAT::CommandMode::Run: { 25 | ESP.restart(); 26 | return chAT::CommandStatus::OK; 27 | } 28 | } 29 | return chAT::CommandStatus::ERROR; 30 | }; 31 | 32 | /* ....................................................................... */ 33 | command_table[_RESTART_BOOTLOADER] = [this](auto & srv, auto & parser) { 34 | /* ....................................................................... */ 35 | switch (parser.cmd_mode) { 36 | case chAT::CommandMode::Run: { 37 | usb_persist_restart(RESTART_BOOTLOADER); 38 | return chAT::CommandStatus::OK; 39 | } 40 | } 41 | return chAT::CommandStatus::ERROR; 42 | }; 43 | 44 | /* ....................................................................... */ 45 | command_table[_GMR] = [this](auto & srv, auto & parser) { 46 | /* ....................................................................... */ 47 | //srv.write_response_prompt(); 48 | srv.write_cstr(""); // TODO: report some useful information 49 | srv.write_line_end(); 50 | return chAT::CommandStatus::OK; 51 | }; 52 | 53 | /* ....................................................................... */ 54 | command_table[_GENERIC_CMD] = [this](auto & srv, auto & parser) { 55 | /* ....................................................................... */ 56 | switch (parser.cmd_mode) { 57 | case chAT::CommandMode::Read: { 58 | srv.write_response_prompt(); 59 | srv.write_cstr(""); // report some CMDs 60 | srv.write_line_end(); 61 | return chAT::CommandStatus::OK; 62 | } 63 | default: 64 | return chAT::CommandStatus::ERROR; 65 | } 66 | }; 67 | 68 | /* ....................................................................... */ 69 | command_table[_FWVERSION] = [this](auto & srv, auto & parser) { 70 | /* ....................................................................... */ 71 | switch (parser.cmd_mode) { 72 | case chAT::CommandMode::Read: { 73 | srv.write_response_prompt(); 74 | srv.write_cstr(ESP_FW_VERSION); 75 | srv.write_line_end(); 76 | return chAT::CommandStatus::OK; 77 | } 78 | default: 79 | return chAT::CommandStatus::ERROR; 80 | } 81 | }; 82 | 83 | /* ....................................................................... */ 84 | command_table[_FWVERSION_U32] = [this](auto & srv, auto & parser) { 85 | /* ....................................................................... */ 86 | switch (parser.cmd_mode) { 87 | case chAT::CommandMode::Read: { 88 | srv.write_response_prompt(); 89 | srv.write_data(version, sizeof(version)); 90 | srv.write_line_end(); 91 | return chAT::CommandStatus::OK; 92 | } 93 | default: 94 | return chAT::CommandStatus::ERROR; 95 | } 96 | }; 97 | 98 | /* ....................................................................... */ 99 | command_table[_FILESYSTEM] = [this](auto & srv, auto & parser) { 100 | /* ....................................................................... */ 101 | switch (parser.cmd_mode) { 102 | case chAT::CommandMode::Write: { 103 | if (parser.args.size() < 3) { 104 | return chAT::CommandStatus::ERROR; 105 | } 106 | 107 | auto &type_str = parser.args[0]; 108 | if (type_str.empty()) { 109 | return chAT::CommandStatus::ERROR; 110 | } 111 | int _type = atoi(type_str.c_str()); 112 | 113 | auto &operation_str = parser.args[1]; 114 | if (operation_str.empty()) { 115 | return chAT::CommandStatus::ERROR; 116 | } 117 | uint8_t operation = atoi(operation_str.c_str()); 118 | 119 | auto &filename = parser.args[2]; 120 | if (filename.empty()) { 121 | return chAT::CommandStatus::ERROR; 122 | } 123 | 124 | FILE* f; 125 | if (_type == 0) { 126 | switch (operation) { 127 | case WIFI_FILE_DELETE: { 128 | if(SPIFFS.remove(filename.c_str())){ 129 | return chAT::CommandStatus::OK; 130 | } 131 | return chAT::CommandStatus::ERROR; 132 | } 133 | case WIFI_FILE_WRITE: { 134 | uint8_t size = 0; 135 | if (parser.args.size() >= 3) { 136 | auto &size_str = parser.args[3]; 137 | if (size_str.empty()) { 138 | return chAT::CommandStatus::ERROR; 139 | } 140 | size = atoi(size_str.c_str()); 141 | } 142 | 143 | std::vector data_received; 144 | data_received = srv.inhibit_read(size); 145 | size_t offset = data_received.size(); 146 | 147 | if(offset < size) { 148 | 149 | data_received.resize(size); 150 | do { 151 | offset += serial->read(data_received.data() + offset, size - offset); 152 | } while (offset < size); 153 | } 154 | 155 | File file = SPIFFS.open(filename.c_str(), FILE_WRITE); 156 | if(!file){ 157 | return chAT::CommandStatus::ERROR; 158 | } 159 | int res = file.write(data_received.data(), data_received.size()); 160 | if(res == 0){ 161 | return chAT::CommandStatus::ERROR; 162 | } 163 | 164 | file.close(); 165 | srv.continue_read(); 166 | return chAT::CommandStatus::OK; 167 | } 168 | case WIFI_FILE_READ: { 169 | int offset = 0; 170 | if (parser.args.size() >= 3) { 171 | auto &offset_str = parser.args[3]; 172 | if (offset_str.empty()) { 173 | return chAT::CommandStatus::ERROR; 174 | } 175 | offset = atoi(offset_str.c_str()); 176 | } 177 | 178 | int length = 0; 179 | if (parser.args.size() >= 4) { 180 | auto &length_str = parser.args[4]; 181 | if (length_str.empty()) { 182 | return chAT::CommandStatus::ERROR; 183 | } 184 | length = atoi(length_str.c_str()); 185 | } 186 | 187 | File file = SPIFFS.open(filename.c_str()); 188 | if(!file){ 189 | return chAT::CommandStatus::ERROR; 190 | } 191 | bool end_of_file = file.seek(offset, SeekCur); 192 | if(!end_of_file){ 193 | file.close(); 194 | return chAT::CommandStatus::ERROR; 195 | } 196 | 197 | std::vector buf; 198 | length = (length < file.available()) ? length : file.available(); 199 | buf.resize(length); 200 | 201 | file.read(buf.data(), length); 202 | file.close(); 203 | 204 | String results = String(buf.size()) + "|"; 205 | 206 | srv.write_response_prompt(); 207 | srv.write_str((const char *)(results.c_str())); 208 | srv.write_vec8(buf); 209 | srv.write_line_end(); 210 | return chAT::CommandStatus::OK; 211 | } 212 | case WIFI_FILE_APPEND: { 213 | uint8_t size = 0; 214 | if (parser.args.size() >= 3) { 215 | auto &size_str = parser.args[3]; 216 | if (size_str.empty()) { 217 | return chAT::CommandStatus::ERROR; 218 | } 219 | size = atoi(size_str.c_str()); 220 | } else { 221 | return chAT::CommandStatus::ERROR; 222 | } 223 | 224 | std::vector data_received; 225 | data_received = srv.inhibit_read(size); 226 | size_t offset = data_received.size(); 227 | 228 | if(offset < size) { 229 | 230 | data_received.resize(size); 231 | do { 232 | offset += serial->read(data_received.data() + offset, size - offset); 233 | } while (offset < size); 234 | } 235 | 236 | File file = SPIFFS.open(filename.c_str(), FILE_APPEND); 237 | if(!file){ 238 | return chAT::CommandStatus::ERROR; 239 | } 240 | 241 | int res = file.write(data_received.data(), data_received.size()); 242 | if(res == 0){ 243 | return chAT::CommandStatus::ERROR; 244 | } 245 | 246 | file.close(); 247 | srv.continue_read(); 248 | 249 | return chAT::CommandStatus::OK; 250 | } 251 | default: 252 | return chAT::CommandStatus::ERROR; 253 | } 254 | } 255 | return chAT::CommandStatus::ERROR; 256 | } 257 | default: 258 | return chAT::CommandStatus::ERROR; 259 | } 260 | }; 261 | 262 | /* ....................................................................... */ 263 | command_table[_MOUNTFS] = [this](auto & srv, auto & parser) { 264 | /* ....................................................................... */ 265 | switch (parser.cmd_mode) { 266 | case chAT::CommandMode::Write: { 267 | if (parser.args.size() != 1) { 268 | return chAT::CommandStatus::ERROR; 269 | } 270 | 271 | auto &format_on_fault = parser.args[0]; 272 | if (format_on_fault.empty()) { 273 | return chAT::CommandStatus::ERROR; 274 | } 275 | int _fof = atoi(format_on_fault.c_str()); 276 | if(!SPIFFS.begin(_fof)){ 277 | return chAT::CommandStatus::ERROR; 278 | } 279 | srv.write_response_prompt(); 280 | srv.write_cstr("FS Mounted\r\n"); 281 | srv.write_line_end(); 282 | return chAT::CommandStatus::OK; 283 | } 284 | default: 285 | return chAT::CommandStatus::ERROR; 286 | } 287 | }; 288 | 289 | /* ....................................................................... */ 290 | command_table[_EXIT] = [this](auto & srv, auto & parser) { 291 | /* ....................................................................... */ 292 | return chAT::CommandStatus::OK; 293 | }; 294 | 295 | 296 | /* ....................................................................... */ 297 | command_table[_SOFTRESETWIFI] = [this](auto & srv, auto & parser) { 298 | /* ....................................................................... */ 299 | switch (parser.cmd_mode) { 300 | case chAT::CommandMode::Run: { 301 | for (int i = 0; i < MAX_CLIENT_AVAILABLE; i++) { 302 | serverClients[i].client.stop(); 303 | } 304 | 305 | for (int i = 0; i < MAX_SERVER_AVAILABLE; i++) { 306 | if (serverWiFi[i] != nullptr) { 307 | serverWiFi[i]->end(); 308 | delete serverWiFi[i]; 309 | serverWiFi[i] = nullptr; 310 | } 311 | } 312 | 313 | servers_num = 0; 314 | 315 | for (int i = 0; i < MAX_UDP_AVAILABLE; i++) { 316 | if (udps[i] != nullptr) { 317 | udps[i]->stop(); 318 | delete udps[i]; 319 | udps[i] = nullptr; 320 | } 321 | } 322 | 323 | udps_num = 0; 324 | 325 | for (int i = 0; i < MAX_CLIENT_AVAILABLE; i++) { 326 | if (clients[i] != nullptr) { 327 | clients[i]->stop(); 328 | delete clients[i]; 329 | clients[i] = nullptr; 330 | } 331 | } 332 | 333 | clients_num = 0; 334 | 335 | for (int i = 0; i < MAX_CLIENT_AVAILABLE; i++) { 336 | if (sslclients[i] != nullptr) { 337 | sslclients[i]->stop(); 338 | delete sslclients[i]; 339 | sslclients[i] = nullptr; 340 | } 341 | } 342 | sslclients_num = 0; 343 | 344 | #define IP0 IPAddress(0,0,0,0) 345 | 346 | WiFi.config(IP0,IP0,IP0); 347 | 348 | WiFi.disconnect(); 349 | WiFi.softAPdisconnect(); 350 | 351 | srv.write_response_prompt(); 352 | srv.write_line_end(); 353 | return chAT::CommandStatus::OK; 354 | } 355 | default: 356 | return chAT::CommandStatus::ERROR; 357 | } 358 | }; 359 | 360 | /* ....................................................................... */ 361 | command_table[_GETTIME] = [this](auto & srv, auto & parser) { 362 | /* ....................................................................... */ 363 | 364 | switch (parser.cmd_mode) { 365 | case chAT::CommandMode::Write: { 366 | char epoch[12]; // gettime 367 | constexpr uint32_t SECS_YR_2000 = 946684800UL; // the time at the start of y2k 368 | time_t now = time(nullptr); 369 | 370 | if (now < SECS_YR_2000) { 371 | now = 0; 372 | } 373 | 374 | srv.write_response_prompt(); 375 | sprintf(epoch,"%d", (unsigned long) now); 376 | srv.write_str((const char *) epoch); 377 | srv.write_line_end(); 378 | 379 | return chAT::CommandStatus::OK; 380 | } 381 | default: 382 | return chAT::CommandStatus::ERROR; 383 | } 384 | }; 385 | 386 | /* ....................................................................... */ 387 | command_table[_PING] = [this](auto & srv, auto & parser) { 388 | /* ....................................................................... */ 389 | switch (parser.cmd_mode) { 390 | case chAT::CommandMode::Write: { 391 | if (parser.args.size() != 4) { 392 | return chAT::CommandStatus::ERROR; 393 | } 394 | 395 | // get IP 396 | auto &target = parser.args[1]; 397 | if (target.empty()) { 398 | return chAT::CommandStatus::ERROR; 399 | } 400 | 401 | // get ttl 402 | auto &ttl = parser.args[2]; 403 | if (ttl.empty()) { 404 | return chAT::CommandStatus::ERROR; 405 | } 406 | 407 | // get count 408 | auto &cnt = parser.args[3]; 409 | if (cnt.empty()) { 410 | return chAT::CommandStatus::ERROR; 411 | } 412 | 413 | auto ping_res = execute_ping(target.c_str(), atoi(ttl.c_str()), atoi(cnt.c_str())); 414 | char rsl[8]; 415 | if (ping_res.status == ping_status::SUCCESS) { 416 | sprintf(rsl,"%.0f", ping_res.averagertt); 417 | } else { 418 | sprintf(rsl,"%d", ping_res.status); 419 | } 420 | 421 | srv.write_response_prompt(); 422 | srv.write_str((const char *) rsl); 423 | srv.write_line_end(); 424 | return chAT::CommandStatus::OK; 425 | 426 | } 427 | default: 428 | return chAT::CommandStatus::ERROR; 429 | } 430 | }; 431 | } 432 | 433 | #endif 434 | -------------------------------------------------------------------------------- /UNOR4USBBridge/cmds_ota.h: -------------------------------------------------------------------------------- 1 | #ifndef CMDS_OTA_H 2 | #define CMDS_OTA_H 3 | 4 | #include "at_handler.h" 5 | #include "OTA.h" 6 | 7 | Arduino_UNOWIFIR4_OTA OTA; 8 | void otaDownloadProgress(CAtHandler* at_handler); 9 | 10 | void CAtHandler::add_cmds_ota() { 11 | /* ....................................................................... */ 12 | command_table[_OTA_SETCAROOT] = [this](auto & srv, auto & parser) { 13 | /* ....................................................................... */ 14 | switch (parser.cmd_mode) { 15 | case chAT::CommandMode::Write: { 16 | if (parser.args.size() != 1) { 17 | return chAT::CommandStatus::ERROR; 18 | } 19 | 20 | int ca_root_size = 0; 21 | auto &ca_root_size_str = parser.args[0]; 22 | if (ca_root_size_str.empty()) { 23 | return chAT::CommandStatus::ERROR; 24 | } 25 | ca_root_size = atoi(ca_root_size_str.c_str()); 26 | if(!ca_root_size) { 27 | return chAT::CommandStatus::ERROR; 28 | } 29 | 30 | ota_cert_buf = srv.inhibit_read(ca_root_size); 31 | size_t offset = ota_cert_buf.size(); 32 | if(offset < ca_root_size) { 33 | ota_cert_buf.resize(ca_root_size); 34 | do { 35 | offset += serial->read(ota_cert_buf.data() + offset, ca_root_size - offset); 36 | } while (offset < ca_root_size); 37 | } 38 | OTA.setCACert((const char *)ota_cert_buf.data()); 39 | srv.continue_read(); 40 | return chAT::CommandStatus::OK; 41 | } 42 | default: 43 | return chAT::CommandStatus::ERROR; 44 | } 45 | }; 46 | 47 | /* ....................................................................... */ 48 | command_table[_OTA_BEGIN] = [this](auto & srv, auto & parser) { 49 | /* ....................................................................... */ 50 | switch (parser.cmd_mode) { 51 | case chAT::CommandMode::Run: { 52 | Arduino_ESP32_OTA::Error ota_error = OTA.begin(); 53 | String error = String((int)ota_error) + _ENDL; 54 | srv.write_response_prompt(); 55 | srv.write_str((const char *)(error.c_str())); 56 | srv.write_line_end(); 57 | return chAT::CommandStatus::OK; 58 | } 59 | case chAT::CommandMode::Write: { 60 | if (parser.args.size() != 1 && parser.args.size() != 2) { 61 | return chAT::CommandStatus::ERROR; 62 | } 63 | 64 | auto &path = parser.args[0]; 65 | if (path.empty()) { 66 | return chAT::CommandStatus::ERROR; 67 | } 68 | 69 | bool formatOnFail = true; 70 | if (parser.args.size() == 2) { 71 | auto &format = parser.args[1]; 72 | if (format.empty()) { 73 | return chAT::CommandStatus::ERROR; 74 | } 75 | formatOnFail = strtol(format.c_str(), NULL, 10) != 0; 76 | } 77 | 78 | Arduino_ESP32_OTA::Error ota_error = OTA.begin(path.c_str(), ARDUINO_RA4M1_OTA_MAGIC, formatOnFail); 79 | String error = String((int)ota_error) + _ENDL; 80 | srv.write_response_prompt(); 81 | srv.write_str((const char *)(error.c_str())); 82 | srv.write_line_end(); 83 | return chAT::CommandStatus::OK; 84 | 85 | } 86 | default: 87 | return chAT::CommandStatus::ERROR; 88 | } 89 | }; 90 | 91 | /* ....................................................................... */ 92 | command_table[_OTA_DOWNLOAD] = [this](auto & srv, auto & parser) { 93 | /* ....................................................................... */ 94 | switch (parser.cmd_mode) { 95 | case chAT::CommandMode::Write: { 96 | if (parser.args.size() == 1) { 97 | auto &url = parser.args[0]; 98 | if (url.empty()) { 99 | return chAT::CommandStatus::ERROR; 100 | } 101 | 102 | int ota_error = OTA.download(url.c_str()); 103 | String error = String((int)ota_error) + _ENDL; 104 | srv.write_response_prompt(); 105 | srv.write_str((const char *)(error.c_str())); 106 | srv.write_line_end(); 107 | return chAT::CommandStatus::OK; 108 | } else if(parser.args.size() == 2) { 109 | auto &url = parser.args[0]; 110 | if (url.empty()) { 111 | return chAT::CommandStatus::ERROR; 112 | } 113 | 114 | auto &path = parser.args[1]; 115 | if (path.empty()) { 116 | return chAT::CommandStatus::ERROR; 117 | } 118 | 119 | int ota_error = OTA.download(url.c_str(), path.c_str()); 120 | String error = String((int)ota_error) + _ENDL; 121 | srv.write_response_prompt(); 122 | srv.write_str((const char *)(error.c_str())); 123 | srv.write_line_end(); 124 | return chAT::CommandStatus::OK; 125 | } else { 126 | return chAT::CommandStatus::ERROR; 127 | } 128 | 129 | } 130 | default: 131 | return chAT::CommandStatus::ERROR; 132 | } 133 | }; 134 | 135 | /* ....................................................................... */ 136 | command_table[_OTA_DOWNLOAD_START] = [this](auto & srv, auto & parser) { 137 | /* ....................................................................... */ 138 | 139 | switch (parser.cmd_mode) { 140 | case chAT::CommandMode::Write: { 141 | if (parser.args.size() == 1) { 142 | auto &url = parser.args[0]; 143 | if (url.empty()) { 144 | return chAT::CommandStatus::ERROR; 145 | } 146 | 147 | int ota_error = OTA.startDownload(url.c_str()); 148 | String error = String((int)ota_error) + _ENDL; 149 | srv.write_response_prompt(); 150 | srv.write_str((const char *)(error.c_str())); 151 | srv.write_line_end(); 152 | 153 | this->addTask(std::bind(&otaDownloadProgress, this)); 154 | 155 | return chAT::CommandStatus::OK; 156 | } else if(parser.args.size() == 2) { 157 | auto &url = parser.args[0]; 158 | if (url.empty()) { 159 | return chAT::CommandStatus::ERROR; 160 | } 161 | 162 | auto &path = parser.args[1]; 163 | if (path.empty()) { 164 | return chAT::CommandStatus::ERROR; 165 | } 166 | 167 | int ota_error = OTA.startDownload(url.c_str(), path.c_str()); 168 | 169 | this->addTask(std::bind(&otaDownloadProgress, this)); 170 | 171 | String error = String((int)ota_error) + _ENDL; 172 | srv.write_response_prompt(); 173 | srv.write_str((const char *)(error.c_str())); 174 | srv.write_line_end(); 175 | return chAT::CommandStatus::OK; 176 | } else { 177 | return chAT::CommandStatus::ERROR; 178 | } 179 | 180 | } 181 | default: 182 | return chAT::CommandStatus::ERROR; 183 | } 184 | }; 185 | 186 | /* ....................................................................... */ 187 | command_table[_OTA_DOWNLOAD_PROGRESS] = [this](auto & srv, auto & parser) { 188 | /* ....................................................................... */ 189 | switch (parser.cmd_mode) { 190 | case chAT::CommandMode::Run: { 191 | int progress = OTA.downloadProgress(); 192 | String pogress_str = String((int)progress) + _ENDL; 193 | srv.write_response_prompt(); 194 | srv.write_str((const char *)(pogress_str.c_str())); 195 | srv.write_line_end(); 196 | 197 | return chAT::CommandStatus::OK; 198 | } 199 | default: 200 | return chAT::CommandStatus::ERROR; 201 | } 202 | }; 203 | 204 | /* ....................................................................... */ 205 | command_table[_OTA_VERIFY] = [this](auto & srv, auto & parser) { 206 | /* ....................................................................... */ 207 | switch (parser.cmd_mode) { 208 | case chAT::CommandMode::Run: { 209 | Arduino_ESP32_OTA::Error ota_error = OTA.verify(); 210 | String error = String((int)ota_error) + _ENDL; 211 | srv.write_response_prompt(); 212 | srv.write_str((const char *)(error.c_str())); 213 | srv.write_line_end(); 214 | return chAT::CommandStatus::OK; 215 | } 216 | default: 217 | return chAT::CommandStatus::ERROR; 218 | } 219 | }; 220 | 221 | /* ....................................................................... */ 222 | command_table[_OTA_UPDATE] = [this](auto & srv, auto & parser) { 223 | /* ....................................................................... */ 224 | switch (parser.cmd_mode) { 225 | case chAT::CommandMode::Run: { 226 | Arduino_ESP32_OTA::Error ota_error = OTA.update(); 227 | String error = String((int)ota_error) + _ENDL; 228 | srv.write_response_prompt(); 229 | srv.write_str((const char *)(error.c_str())); 230 | srv.write_line_end(); 231 | return chAT::CommandStatus::OK; 232 | } 233 | case chAT::CommandMode::Write: { 234 | if (parser.args.size() != 1) { 235 | return chAT::CommandStatus::ERROR; 236 | } 237 | 238 | auto &path = parser.args[0]; 239 | if (path.empty()) { 240 | return chAT::CommandStatus::ERROR; 241 | } 242 | 243 | int flash_error = OTA.update(path.c_str()); 244 | String error = String(flash_error) + _ENDL; 245 | srv.write_response_prompt(); 246 | srv.write_str((const char *)(error.c_str())); 247 | srv.write_line_end(); 248 | return chAT::CommandStatus::OK; 249 | } 250 | default: 251 | return chAT::CommandStatus::ERROR; 252 | } 253 | }; 254 | 255 | /* ....................................................................... */ 256 | command_table[_OTA_RESET] = [this](auto & srv, auto & parser) { 257 | /* ....................................................................... */ 258 | switch (parser.cmd_mode) { 259 | case chAT::CommandMode::Run: { 260 | OTA.reset(); 261 | srv.write_response_prompt(); 262 | srv.write_str("0"); 263 | srv.write_line_end(); 264 | return chAT::CommandStatus::OK; 265 | } 266 | default: 267 | return chAT::CommandStatus::ERROR; 268 | } 269 | }; 270 | } 271 | 272 | // in order to make the download progress downloadPoll must be called periodically 273 | // until the end, this function readds itself to the task list until completed 274 | void otaDownloadProgress(CAtHandler* at_handler) { 275 | auto res = OTA.downloadPoll(); 276 | 277 | // continue to progress the download if the state is not completed or not an error 278 | if(res == 0) { 279 | at_handler->addTask(std::bind(&otaDownloadProgress, at_handler)); 280 | } 281 | }; 282 | 283 | #endif 284 | -------------------------------------------------------------------------------- /UNOR4USBBridge/cmds_preferences.h: -------------------------------------------------------------------------------- 1 | #ifndef CMDS_PREFERENCES_H 2 | #define CMDS_PREFERENCES_H 3 | 4 | #include "at_handler.h" 5 | #include 6 | 7 | Preferences pref; 8 | 9 | void CAtHandler::add_cmds_preferences() { 10 | /* ....................................................................... */ 11 | command_table[_PREF_BEGIN] = [this](auto & srv, auto & parser) { 12 | /* ....................................................................... */ 13 | switch (parser.cmd_mode) { 14 | case chAT::CommandMode::Write: { 15 | if (parser.args.size() != 3) { 16 | return chAT::CommandStatus::ERROR; 17 | } 18 | 19 | auto &name = parser.args[0]; 20 | if (name.empty()) { 21 | return chAT::CommandStatus::ERROR; 22 | } 23 | bool readOnly = strtol(parser.args[1].c_str(), NULL, 10) != 0; 24 | auto &partition = parser.args[2]; 25 | 26 | // calling end before begin, because the renesas mcu may have been restarted 27 | pref.end(); 28 | 29 | String error = String(); 30 | if (partition.empty()) { 31 | error = String(pref.begin(name.c_str(), readOnly)) + "\r\n"; 32 | } else { 33 | error = String(pref.begin(name.c_str(), readOnly, partition.c_str())) + "\r\n"; 34 | } 35 | 36 | srv.write_response_prompt(); 37 | srv.write_str((const char *)(error.c_str())); 38 | srv.write_line_end(); 39 | return chAT::CommandStatus::OK; 40 | 41 | } 42 | default: 43 | return chAT::CommandStatus::ERROR; 44 | } 45 | }; 46 | 47 | /* ....................................................................... */ 48 | command_table[_PREF_END] = [this](auto & srv, auto & parser) { 49 | /* ....................................................................... */ 50 | switch (parser.cmd_mode) { 51 | case chAT::CommandMode::Run: { 52 | pref.end(); 53 | srv.write_response_prompt(); 54 | srv.write_str("0"); 55 | srv.write_line_end(); 56 | return chAT::CommandStatus::OK; 57 | } 58 | default: 59 | return chAT::CommandStatus::ERROR; 60 | } 61 | }; 62 | 63 | /* ....................................................................... */ 64 | command_table[_PREF_CLEAR] = [this](auto & srv, auto & parser) { 65 | /* ....................................................................... */ 66 | switch (parser.cmd_mode) { 67 | case chAT::CommandMode::Run: { 68 | String error = String(pref.clear()) + "\r\n"; 69 | srv.write_response_prompt(); 70 | srv.write_str((const char *)(error.c_str())); 71 | srv.write_line_end(); 72 | return chAT::CommandStatus::OK; 73 | } 74 | default: 75 | return chAT::CommandStatus::ERROR; 76 | } 77 | }; 78 | 79 | /* ....................................................................... */ 80 | command_table[_PREF_REMOVE] = [this](auto & srv, auto & parser) { 81 | /* ....................................................................... */ 82 | switch (parser.cmd_mode) { 83 | case chAT::CommandMode::Write: { 84 | if (parser.args.size() != 1) { 85 | return chAT::CommandStatus::ERROR; 86 | } 87 | 88 | auto &key = parser.args[0]; 89 | if (key.empty()) { 90 | return chAT::CommandStatus::ERROR; 91 | } 92 | 93 | String error = String(pref.remove(key.c_str())) + "\r\n"; 94 | srv.write_response_prompt(); 95 | srv.write_str((const char *)(error.c_str())); 96 | srv.write_line_end(); 97 | return chAT::CommandStatus::OK; 98 | 99 | } 100 | default: 101 | return chAT::CommandStatus::ERROR; 102 | } 103 | }; 104 | 105 | /* ....................................................................... */ 106 | command_table[_PREF_PUT] = [this](auto & srv, auto & parser) { 107 | /* ....................................................................... */ 108 | switch (parser.cmd_mode) { 109 | case chAT::CommandMode::Write: { 110 | if (parser.args.size() != 3) { 111 | return chAT::CommandStatus::ERROR; 112 | } 113 | 114 | auto &key = parser.args[0]; 115 | if (key.empty()) { 116 | return chAT::CommandStatus::ERROR; 117 | } 118 | int type = strtol(parser.args[1].c_str(), NULL, 10); 119 | 120 | String error = String(); 121 | switch (type) { 122 | case PreferenceType::PT_I8: { 123 | int8_t value; 124 | sscanf(parser.args[2].c_str(), "%hhd", &value); 125 | error = String(pref.putChar(key.c_str(), value)) + "\r\n"; 126 | } 127 | break; 128 | case PreferenceType::PT_U8: { 129 | uint8_t value; 130 | sscanf(parser.args[2].c_str(), "%hhu", &value); 131 | error = String(pref.putUChar(key.c_str(), value)) + "\r\n"; 132 | } 133 | break; 134 | case PreferenceType::PT_I16: { 135 | int16_t value; 136 | sscanf(parser.args[2].c_str(), "%hd", &value); 137 | error = String(pref.putShort(key.c_str(), value)) + "\r\n"; 138 | } 139 | break; 140 | case PreferenceType::PT_U16: { 141 | uint16_t value; 142 | sscanf(parser.args[2].c_str(), "%hu", &value); 143 | error = String(pref.putUShort(key.c_str(), value)) + "\r\n"; 144 | } 145 | break; 146 | case PreferenceType::PT_I32: { 147 | int32_t value; 148 | sscanf(parser.args[2].c_str(), "%d", &value); 149 | error = String(pref.putInt(key.c_str(), value)) + "\r\n"; 150 | } 151 | break; 152 | case PreferenceType::PT_U32: { 153 | uint32_t value; 154 | sscanf(parser.args[2].c_str(), "%u", &value); 155 | error = String(pref.putUInt(key.c_str(), value)) + "\r\n"; 156 | } 157 | break; 158 | case PreferenceType::PT_I64: { 159 | int64_t value; 160 | sscanf(parser.args[2].c_str(), "%lld", &value); 161 | error = String(pref.putLong64(key.c_str(), value)) + "\r\n"; 162 | } 163 | break; 164 | case PreferenceType::PT_U64: { 165 | uint64_t value; 166 | sscanf(parser.args[2].c_str(), "%llu", &value); 167 | error = String(pref.putULong64(key.c_str(), value)) + "\r\n"; 168 | } 169 | break; 170 | case PreferenceType::PT_STR: { 171 | int value = atoi(parser.args[2].c_str()); 172 | pref_buf = srv.inhibit_read(value); 173 | size_t offset = pref_buf.size(); 174 | if(offset < value) { 175 | pref_buf.resize(value); 176 | do { 177 | offset += serial->read(pref_buf.data() + offset, value - offset); 178 | } while (offset < value); 179 | } 180 | 181 | pref_buf.push_back('\0'); 182 | 183 | srv.continue_read(); 184 | error = String(pref.putString(key.c_str(), (char*)pref_buf.data())) + "\r\n"; 185 | } 186 | break; 187 | case PreferenceType::PT_BLOB: { 188 | int value = atoi(parser.args[2].c_str()); 189 | pref_buf = srv.inhibit_read(value); 190 | size_t offset = pref_buf.size(); 191 | if(offset < value) { 192 | pref_buf.resize(value); 193 | do { 194 | offset += serial->read(pref_buf.data() + offset, value - offset); 195 | } while (offset < value); 196 | } 197 | srv.continue_read(); 198 | error = String(pref.putBytes(key.c_str(), pref_buf.data(), value)) + "\r\n"; 199 | } 200 | break; 201 | default: 202 | case PreferenceType::PT_INVALID: { 203 | error = "1\r\n"; 204 | } 205 | break; 206 | } 207 | srv.write_response_prompt(); 208 | srv.write_str((const char *)(error.c_str())); 209 | srv.write_line_end(); 210 | return chAT::CommandStatus::OK; 211 | 212 | } 213 | default: 214 | return chAT::CommandStatus::ERROR; 215 | } 216 | }; 217 | 218 | /* ....................................................................... */ 219 | command_table[_PREF_TYPE] = [this](auto & srv, auto & parser) { 220 | /* ....................................................................... */ 221 | switch (parser.cmd_mode) { 222 | case chAT::CommandMode::Write: { 223 | if (parser.args.size() != 1) { 224 | return chAT::CommandStatus::ERROR; 225 | } 226 | 227 | auto &key = parser.args[0]; 228 | if (key.empty()) { 229 | return chAT::CommandStatus::ERROR; 230 | } 231 | 232 | String error = String(pref.getType(key.c_str())) + "\r\n"; 233 | 234 | srv.write_response_prompt(); 235 | srv.write_str((const char *)(error.c_str())); 236 | srv.write_line_end(); 237 | return chAT::CommandStatus::OK; 238 | 239 | } 240 | default: 241 | return chAT::CommandStatus::ERROR; 242 | } 243 | }; 244 | 245 | /* ....................................................................... */ 246 | command_table[_PREF_GET] = [this](auto & srv, auto & parser) { 247 | /* ....................................................................... */ 248 | switch (parser.cmd_mode) { 249 | case chAT::CommandMode::Write: { 250 | if (parser.args.size() < 2) { 251 | return chAT::CommandStatus::ERROR; 252 | } 253 | 254 | auto &key = parser.args[0]; 255 | if (key.empty()) { 256 | return chAT::CommandStatus::ERROR; 257 | } 258 | int type = strtol(parser.args[1].c_str(), NULL, 10); 259 | 260 | String error = String(); 261 | switch (type) { 262 | case PreferenceType::PT_I8: { 263 | int8_t value; 264 | sscanf(parser.args[2].c_str(), "%hhd", &value); 265 | error = String(pref.getChar(key.c_str(), value)) + "\r\n"; 266 | } 267 | break; 268 | case PreferenceType::PT_U8: { 269 | uint8_t value; 270 | sscanf(parser.args[2].c_str(), "%hhu", &value); 271 | error = String(pref.getUChar(key.c_str(), value)) + "\r\n"; 272 | } 273 | break; 274 | case PreferenceType::PT_I16: { 275 | int16_t value; 276 | sscanf(parser.args[2].c_str(), "%hd", &value); 277 | error = String(pref.getShort(key.c_str(), value)) + "\r\n"; 278 | } 279 | break; 280 | case PreferenceType::PT_U16: { 281 | uint16_t value; 282 | sscanf(parser.args[2].c_str(), "%hu", &value); 283 | error = String(pref.getUShort(key.c_str(), value)) + "\r\n"; 284 | } 285 | break; 286 | case PreferenceType::PT_I32: { 287 | int32_t value; 288 | sscanf(parser.args[2].c_str(), "%d", &value); 289 | error = String(pref.getInt(key.c_str(), value)) + "\r\n"; 290 | } 291 | break; 292 | case PreferenceType::PT_U32: { 293 | uint32_t value; 294 | sscanf(parser.args[2].c_str(), "%u", &value); 295 | error = String(pref.getUInt(key.c_str(), value)) + "\r\n"; 296 | } 297 | break; 298 | case PreferenceType::PT_I64: { 299 | int64_t value; 300 | sscanf(parser.args[2].c_str(), "%lld", &value); 301 | error = String(pref.getLong64(key.c_str(), value)) + "\r\n"; 302 | } 303 | break; 304 | case PreferenceType::PT_U64: { 305 | uint64_t value; 306 | sscanf(parser.args[2].c_str(), "%llu", &value); 307 | error = String(pref.getULong64(key.c_str(), value)) + "\r\n"; 308 | } 309 | break; 310 | case PreferenceType::PT_STR: { 311 | auto value = parser.args[2]; 312 | auto res = pref.getString(key.c_str(), value.c_str()); 313 | 314 | srv.write_response_prompt(); 315 | srv.write_str(String(res.length()).c_str()); 316 | srv.write_str("|"); 317 | srv.write_str(res.c_str()); 318 | srv.write_line_end(); 319 | } 320 | break; 321 | case PreferenceType::PT_BLOB: { 322 | std::vector data; 323 | int len = pref.getBytesLength(key.c_str()); 324 | data.resize(len); 325 | pref.getBytes(key.c_str(), data.data(), len); 326 | srv.write_response_prompt(); 327 | srv.write_str(String(len).c_str()); 328 | srv.write_str("|"); 329 | srv.write_vec8(data); 330 | srv.write_line_end(); 331 | } 332 | break; 333 | default: 334 | case PreferenceType::PT_INVALID: { 335 | error = "1\r\n"; 336 | } 337 | break; 338 | } 339 | 340 | 341 | if (type != PreferenceType::PT_BLOB && type != PreferenceType::PT_STR) { 342 | srv.write_response_prompt(); 343 | srv.write_str((const char *)(error.c_str())); 344 | srv.write_line_end(); 345 | } 346 | return chAT::CommandStatus::OK; 347 | 348 | } 349 | default: 350 | return chAT::CommandStatus::ERROR; 351 | } 352 | }; 353 | 354 | /* ....................................................................... */ 355 | command_table[_PREF_LEN] = [this](auto & srv, auto & parser) { 356 | /* ....................................................................... */ 357 | switch (parser.cmd_mode) { 358 | case chAT::CommandMode::Write: { 359 | if (parser.args.size() != 1) { 360 | return chAT::CommandStatus::ERROR; 361 | } 362 | 363 | auto &key = parser.args[0]; 364 | if (key.empty()) { 365 | return chAT::CommandStatus::ERROR; 366 | } 367 | String error = String(pref.getBytesLength(key.c_str())) + "\r\n"; 368 | srv.write_response_prompt(); 369 | srv.write_str((const char *)(error.c_str())); 370 | srv.write_line_end(); 371 | return chAT::CommandStatus::OK; 372 | 373 | } 374 | default: 375 | return chAT::CommandStatus::ERROR; 376 | } 377 | }; 378 | 379 | /* ....................................................................... */ 380 | command_table[_PREF_STAT] = [this](auto & srv, auto & parser) { 381 | /* ....................................................................... */ 382 | switch (parser.cmd_mode) { 383 | case chAT::CommandMode::Run: { 384 | String error = String(pref.freeEntries()) + "\r\n"; 385 | srv.write_response_prompt(); 386 | srv.write_str((const char *)(error.c_str())); 387 | srv.write_line_end(); 388 | return chAT::CommandStatus::OK; 389 | } 390 | default: 391 | return chAT::CommandStatus::ERROR; 392 | } 393 | }; 394 | } 395 | 396 | #endif 397 | -------------------------------------------------------------------------------- /UNOR4USBBridge/cmds_se.h: -------------------------------------------------------------------------------- 1 | #ifndef CMDS_SE_H 2 | #define CMDS_SE_H 3 | 4 | #include "at_handler.h" 5 | #include "SSE.h" 6 | 7 | 8 | void CAtHandler::add_cmds_se() { 9 | 10 | /* ....................................................................... */ 11 | command_table[_SOFTSE_BEGIN] = [this](auto & srv, auto & parser) { 12 | /* ....................................................................... */ 13 | switch (parser.cmd_mode) { 14 | case chAT::CommandMode::Write: { 15 | if (parser.args.size() != 3) { 16 | return chAT::CommandStatus::ERROR; 17 | } 18 | 19 | auto &name = parser.args[0]; 20 | if (name.empty()) { 21 | return chAT::CommandStatus::ERROR; 22 | } 23 | 24 | /* Allow to call begin multiple times */ 25 | sse.end(); 26 | 27 | bool readOnly = strtol(parser.args[1].c_str(), NULL, 10) != 0; 28 | auto &partition = parser.args[2]; 29 | String error = String(); 30 | if (partition.empty()) { 31 | error = String(sse.begin(name.c_str(), readOnly)) + "\r\n"; 32 | } else { 33 | error = String(sse.begin(name.c_str(), readOnly, partition.c_str())) + "\r\n"; 34 | } 35 | 36 | srv.write_response_prompt(); 37 | srv.write_str((const char *)(error.c_str())); 38 | srv.write_line_end(); 39 | return chAT::CommandStatus::OK; 40 | } 41 | default: 42 | return chAT::CommandStatus::ERROR; 43 | } 44 | }; 45 | 46 | /* ....................................................................... */ 47 | command_table[_SOFTSE_END] = [this](auto & srv, auto & parser) { 48 | /* ....................................................................... */ 49 | switch (parser.cmd_mode) { 50 | case chAT::CommandMode::Run: { 51 | sse.end(); 52 | srv.write_response_prompt(); 53 | srv.write_str("0"); 54 | srv.write_line_end(); 55 | return chAT::CommandStatus::OK; 56 | } 57 | default: 58 | return chAT::CommandStatus::ERROR; 59 | } 60 | }; 61 | 62 | /* ....................................................................... */ 63 | command_table[_SOFTSE_SERIAL] = [this](auto & srv, auto & parser) { 64 | /* ....................................................................... */ 65 | switch (parser.cmd_mode) { 66 | case chAT::CommandMode::Run: { 67 | std::vector data; 68 | data.resize(8); 69 | esp_efuse_mac_get_default(data.data()); 70 | srv.write_response_prompt(); 71 | srv.write_str(String(8).c_str()); 72 | srv.write_str("|"); 73 | srv.write_vec8(data); 74 | srv.write_line_end(); 75 | return chAT::CommandStatus::OK; 76 | } 77 | default: 78 | return chAT::CommandStatus::ERROR; 79 | } 80 | }; 81 | 82 | /* ....................................................................... */ 83 | command_table[_SOFTSE_RND] = [this](auto & srv, auto & parser) { 84 | /* ....................................................................... */ 85 | switch (parser.cmd_mode) { 86 | case chAT::CommandMode::Write: { 87 | if (parser.args.size() != 1) { 88 | return chAT::CommandStatus::ERROR; 89 | } 90 | 91 | int len = strtol(parser.args[0].c_str(), NULL, 10); 92 | 93 | std::vector data; 94 | data.resize(len); 95 | esp_fill_random(data.data(), len); 96 | srv.write_response_prompt(); 97 | srv.write_str(String(len).c_str()); 98 | srv.write_str("|"); 99 | srv.write_vec8(data); 100 | srv.write_line_end(); 101 | return chAT::CommandStatus::OK; 102 | } 103 | default: 104 | return chAT::CommandStatus::ERROR; 105 | } 106 | }; 107 | 108 | /* ....................................................................... */ 109 | command_table[_SOFTSE_PRI_KEY] = [this](auto & srv, auto & parser) { 110 | /* ....................................................................... */ 111 | switch (parser.cmd_mode) { 112 | case chAT::CommandMode::Write: { 113 | if (parser.args.size() != 1) { 114 | return chAT::CommandStatus::ERROR; 115 | } 116 | 117 | auto &key = parser.args[0]; 118 | int len = 0; 119 | int ret = 0; 120 | 121 | std::vector der; 122 | der.resize(SSE_EC256_DER_PRI_KEY_LENGTH); 123 | if ((len = Arduino_UNOWIFIR4_SSE::generateECKeyPair(der.data(), static_cast(der.size()))) < 0) { 124 | DEBUG_ERROR(" failed\n ! generateECKeyPair returned -0x%04x", (unsigned int) -ret); 125 | return chAT::CommandStatus::ERROR; 126 | } 127 | der.resize(len); 128 | if ((ret = sse.putBytes(key.c_str(), der.data(), len)) != len) { 129 | DEBUG_ERROR(" failed\n ! sse.putBytes returned -0x%04x", (unsigned int) -ret); 130 | return chAT::CommandStatus::ERROR; 131 | } 132 | #if SSE_DEBUG_ENABLED 133 | log_v("_SOFTSE_PRI_KEY: generated EC keypair"); 134 | log_buf_v((const uint8_t *)der.data(), len); 135 | #endif 136 | 137 | std::vector data; 138 | data.resize(SSE_EC256_PUB_KEY_LENGTH); 139 | if ((len = Arduino_UNOWIFIR4_SSE::exportECKeyXY(der.data(), static_cast(der.size()), data.data())) != SSE_EC256_PUB_KEY_LENGTH) { 140 | DEBUG_ERROR(" failed\n ! exportECKeyXY returned -0x%04x", (unsigned int) -ret); 141 | return chAT::CommandStatus::ERROR; 142 | } 143 | data.resize(len); 144 | srv.write_response_prompt(); 145 | srv.write_str(String(len).c_str()); 146 | srv.write_str("|"); 147 | srv.write_vec8(data); 148 | srv.write_line_end(); 149 | return chAT::CommandStatus::OK; 150 | } 151 | default: 152 | return chAT::CommandStatus::ERROR; 153 | } 154 | }; 155 | 156 | /* ....................................................................... */ 157 | command_table[_SOFTSE_PUB_KEY] = [this](auto & srv, auto & parser) { 158 | /* ....................................................................... */ 159 | switch (parser.cmd_mode) { 160 | case chAT::CommandMode::Write: { 161 | if (parser.args.size() != 1) { 162 | return chAT::CommandStatus::ERROR; 163 | } 164 | 165 | auto &key = parser.args[0]; 166 | int ret = 0; 167 | int len = 0; 168 | 169 | std::vector der; 170 | len = sse.getBytesLength(key.c_str()); 171 | der.resize(len); 172 | if ((ret = sse.getBytes(key.c_str(), der.data(), len)) < len) { 173 | DEBUG_ERROR(" failed\n ! sse.getBytes returned -0x%04x", (unsigned int) -ret); 174 | return chAT::CommandStatus::ERROR; 175 | } 176 | #if SSE_DEBUG_ENABLED 177 | log_v("_SOFTSE_PUB_KEY: stored EC keypair"); 178 | log_buf_v((const uint8_t *)der.data(), len); 179 | #endif 180 | std::vector data; 181 | data.resize(SSE_EC256_PUB_KEY_LENGTH); 182 | if ((len = Arduino_UNOWIFIR4_SSE::exportECKeyXY(der.data(), static_cast(der.size()), data.data())) != SSE_EC256_PUB_KEY_LENGTH) { 183 | DEBUG_ERROR(" failed\n ! exportECKeyXY returned -0x%04x", (unsigned int) -len); 184 | return chAT::CommandStatus::ERROR; 185 | } 186 | 187 | data.resize(len); 188 | srv.write_response_prompt(); 189 | srv.write_str(String(len).c_str()); 190 | srv.write_str("|"); 191 | srv.write_vec8(data); 192 | srv.write_line_end(); 193 | return chAT::CommandStatus::OK; 194 | } 195 | default: 196 | return chAT::CommandStatus::ERROR; 197 | } 198 | }; 199 | 200 | /* ....................................................................... */ 201 | command_table[_SOFTSE_S_V_BUF_SET] = [this](auto & srv, auto & parser) { 202 | /* ....................................................................... */ 203 | switch (parser.cmd_mode) { 204 | case chAT::CommandMode::Write: { 205 | if (parser.args.size() != 1) { 206 | return chAT::CommandStatus::ERROR; 207 | } 208 | 209 | int value = atoi(parser.args[0].c_str()); 210 | 211 | se_buf = srv.inhibit_read(value); 212 | size_t offset = se_buf.size(); 213 | if(offset < value) { 214 | se_buf.resize(value); 215 | do { 216 | offset += serial->read(se_buf.data() + offset, value - offset); 217 | } while (offset < value); 218 | } 219 | srv.continue_read(); 220 | 221 | /* Only stores message in se_buffer */ 222 | #if SSE_DEBUG_ENABLED 223 | log_v("_SOFTSE_S_V_BUF_SET: message buffer"); 224 | log_buf_v((const uint8_t *)se_buf.data(), value); 225 | #endif 226 | 227 | srv.write_response_prompt(); 228 | srv.write_str((const char *)(parser.args[0].c_str())); 229 | srv.write_line_end(); 230 | return chAT::CommandStatus::OK; 231 | } 232 | default: 233 | return chAT::CommandStatus::ERROR; 234 | } 235 | }; 236 | 237 | /* ....................................................................... */ 238 | command_table[_SOFTSE_SIGN_GET] = [this](auto & srv, auto & parser) { 239 | /* ....................................................................... */ 240 | switch (parser.cmd_mode) { 241 | case chAT::CommandMode::Write: { 242 | if (parser.args.size() != 1) { 243 | return chAT::CommandStatus::ERROR; 244 | } 245 | 246 | auto &key = parser.args[0]; 247 | int len = 0; 248 | int ret = 0; 249 | 250 | /* Read private key from non volatile storage */ 251 | std::vector der; 252 | len = sse.getBytesLength(key.c_str()); 253 | der.resize(len); 254 | if ((ret = sse.getBytes(key.c_str(), der.data(), len)) < len) { 255 | DEBUG_ERROR(" failed\n ! sse.getBytes returned -0x%04x", (unsigned int) -ret); 256 | return chAT::CommandStatus::ERROR; 257 | } 258 | 259 | #if SSE_DEBUG_ENABLED 260 | log_v("_SOFTSE_SIGN_GET: message to sign:"); 261 | log_buf_v((const uint8_t *)se_buf.data(), se_buf.size()); 262 | 263 | log_v("_SOFTSE_SIGN_GET: key to use:"); 264 | log_buf_v((const uint8_t *)der.data(), len); 265 | #endif 266 | 267 | /* sign message/digest/sha256 stored in se_buffer */ 268 | std::vector data; 269 | data.resize(SSE_EC256_SIGNATURE_LENGTH); 270 | if ((ret = Arduino_UNOWIFIR4_SSE::sign(der.data(), len, se_buf.data(), data.data())) != SSE_EC256_SIGNATURE_LENGTH) { 271 | DEBUG_ERROR(" failed\n ! sign returned -0x%04x", (unsigned int) -ret); 272 | } 273 | 274 | #if SSE_DEBUG_ENABLED 275 | log_v("_SOFTSE_SIGN_GET: {r,s} array"); 276 | log_buf_v((const uint8_t *)data.data(), ret); 277 | #endif 278 | 279 | srv.write_response_prompt(); 280 | srv.write_str(String(ret).c_str()); 281 | srv.write_str("|"); 282 | srv.write_vec8(data); 283 | srv.write_line_end(); 284 | return chAT::CommandStatus::OK; 285 | } 286 | default: 287 | return chAT::CommandStatus::ERROR; 288 | } 289 | }; 290 | 291 | /* ....................................................................... */ 292 | command_table[_SOFTSE_VERIFY_GET] = [this](auto & srv, auto & parser) { 293 | /* ....................................................................... */ 294 | switch (parser.cmd_mode) { 295 | case chAT::CommandMode::Run: { 296 | int ret = 0; 297 | 298 | /* verify data stored in se_buffer 299 | * 300 | * [ 0 - 31 ] SHA526 301 | * [ 32 - 96 ] {r,s} signature values 302 | * [ 96 - 160] EC XY values 303 | */ 304 | 305 | /* Import public key from buffer */ 306 | std::vector pub; 307 | pub.resize(SSE_EC256_DER_PUB_KEY_LENGTH); 308 | if ((ret = Arduino_UNOWIFIR4_SSE::importECKeyXY(&se_buf.data()[SSE_SHA256_LENGTH + SSE_EC256_SIGNATURE_LENGTH], pub.data(), SSE_EC256_DER_PUB_KEY_LENGTH)) < 0) { 309 | DEBUG_ERROR(" failed\n ! importECKeyXY returned -0x%04x", (unsigned int) -ret); 310 | return chAT::CommandStatus::ERROR; 311 | } 312 | pub.resize(ret); 313 | 314 | #if SSE_DEBUG_ENABLED 315 | log_v("_SOFTSE_VERIFY_GET: input buffer", se_buf.size()); 316 | log_buf_v((const uint8_t *)se_buf.data(), se_buf.size()); 317 | 318 | log_v("_SOFTSE_VERIFY_GET: EC key XY values"); 319 | log_buf_v((const uint8_t *)pub.data(), ret); 320 | #endif 321 | 322 | /* Verify data */ 323 | if ((ret = Arduino_UNOWIFIR4_SSE::verify(pub.data(), ret, &se_buf.data()[0], &se_buf.data()[SSE_SHA256_LENGTH])) != 0) { 324 | DEBUG_ERROR(" failed\n ! verify returned -0x%04x", (unsigned int) -ret); 325 | } 326 | srv.write_response_prompt(); 327 | srv.write_str((const char *)String(ret).c_str()); 328 | srv.write_line_end(); 329 | return chAT::CommandStatus::OK; 330 | } 331 | default: 332 | return chAT::CommandStatus::ERROR; 333 | } 334 | }; 335 | 336 | /* ....................................................................... */ 337 | command_table[_SOFTSE_SHA256_GET] = [this](auto & srv, auto & parser) { 338 | /* ....................................................................... */ 339 | switch (parser.cmd_mode) { 340 | case chAT::CommandMode::Run: { 341 | int ret = 0; 342 | 343 | #if SSE_DEBUG_ENABLED 344 | log_v("_SOFTSE_SHA256_GET: message to hash"); 345 | log_buf_v((const uint8_t *)se_buf.data(), se_buf.size()); 346 | #endif 347 | 348 | /* sign message/digest/sha256 stored in se_buffer */ 349 | std::vector data; 350 | data.resize(SSE_SHA256_LENGTH); 351 | if ((ret = Arduino_UNOWIFIR4_SSE::sha256(se_buf.data(), se_buf.size(), data.data())) != SSE_SHA256_LENGTH) { 352 | DEBUG_ERROR(" failed\n ! sse.getBytes returned -0x%04x", (unsigned int) -ret); 353 | } 354 | 355 | #if SSE_DEBUG_ENABLED 356 | log_v("_SOFTSE_SHA256_GET: sha256"); 357 | log_buf_v((const uint8_t *)data.data(), ret); 358 | #endif 359 | 360 | srv.write_response_prompt(); 361 | srv.write_str(String(ret).c_str()); 362 | srv.write_str("|"); 363 | srv.write_vec8(data); 364 | srv.write_line_end(); 365 | return chAT::CommandStatus::OK; 366 | } 367 | default: 368 | return chAT::CommandStatus::ERROR; 369 | } 370 | }; 371 | 372 | /* ....................................................................... */ 373 | command_table[_SOFTSE_WRITE_SLOT] = [this](auto & srv, auto & parser) { 374 | /* ....................................................................... */ 375 | switch (parser.cmd_mode) { 376 | case chAT::CommandMode::Write: { 377 | if (parser.args.size() != 2) { 378 | return chAT::CommandStatus::ERROR; 379 | } 380 | 381 | auto &key = parser.args[0]; 382 | int value = atoi(parser.args[1].c_str()); 383 | 384 | se_buf = srv.inhibit_read(value); 385 | size_t offset = se_buf.size(); 386 | if(offset < value) { 387 | se_buf.resize(value); 388 | do { 389 | offset += serial->read(se_buf.data() + offset, value - offset); 390 | } while (offset < value); 391 | } 392 | srv.continue_read(); 393 | String error = String(sse.putBytes(key.c_str(), se_buf.data(), value)) + "\r\n"; 394 | 395 | #if SSE_DEBUG_ENABLED 396 | log_v("_SOFTSE_WRITE_SLOT: input data"); 397 | log_buf_v((const uint8_t *)se_buf.data(), value); 398 | #endif 399 | 400 | srv.write_response_prompt(); 401 | srv.write_str((const char *)(error.c_str())); 402 | srv.write_line_end(); 403 | return chAT::CommandStatus::OK; 404 | } 405 | default: 406 | return chAT::CommandStatus::ERROR; 407 | } 408 | }; 409 | 410 | /* ....................................................................... */ 411 | command_table[_SOFTSE_READ_SLOT] = [this](auto & srv, auto & parser) { 412 | /* ....................................................................... */ 413 | switch (parser.cmd_mode) { 414 | case chAT::CommandMode::Write: { 415 | if (parser.args.size() != 1) { 416 | return chAT::CommandStatus::ERROR; 417 | } 418 | 419 | auto &key = parser.args[0]; 420 | std::vector data; 421 | 422 | int len = sse.getBytesLength(key.c_str()); 423 | data.resize(len); 424 | sse.getBytes(key.c_str(), data.data(), len); 425 | srv.write_response_prompt(); 426 | srv.write_str(String(len).c_str()); 427 | srv.write_str("|"); 428 | srv.write_vec8(data); 429 | srv.write_line_end(); 430 | return chAT::CommandStatus::OK; 431 | } 432 | default: 433 | return chAT::CommandStatus::ERROR; 434 | } 435 | }; 436 | } 437 | 438 | #endif 439 | -------------------------------------------------------------------------------- /UNOR4USBBridge/cmds_wifi_softAP.h: -------------------------------------------------------------------------------- 1 | #ifndef CMDS_WIFI_SOFTAP_H 2 | #define CMDS_WIFI_SOFTAP_H 3 | 4 | #include "at_handler.h" 5 | 6 | /* -------------------------------------------------------------------------- */ 7 | void CAtHandler::add_cmds_wifi_softAP() { 8 | /* -------------------------------------------------------------------------- */ 9 | /* ....................................................................... */ 10 | command_table[_BEGINSOFTAP] = [this](auto & srv, auto & parser) { 11 | /* ....................................................................... */ 12 | switch (parser.cmd_mode) { 13 | case chAT::CommandMode::Write: { 14 | if (parser.args.size() <= 0 || parser.args.size() > 5) { 15 | return chAT::CommandStatus::ERROR; 16 | } 17 | const char * ssid = NULL; 18 | const char * passphrase = NULL; 19 | int ch = 1; 20 | bool ssid_hidden = false; 21 | int max_connection = 5; 22 | switch (parser.args.size()) { 23 | case 5: { 24 | auto &_max_connection = parser.args[4]; 25 | if (_max_connection.empty()) { 26 | return chAT::CommandStatus::ERROR; 27 | } 28 | max_connection = atoi(_max_connection.c_str()); 29 | } 30 | case 4: { 31 | auto &_ssid_hidden = parser.args[3]; 32 | if (_ssid_hidden.empty()) { 33 | return chAT::CommandStatus::ERROR; 34 | } 35 | ssid_hidden = atoi(_ssid_hidden.c_str()); 36 | } 37 | case 3: { 38 | auto &_ch = parser.args[2]; 39 | if (_ch.empty()) { 40 | return chAT::CommandStatus::ERROR; 41 | } 42 | ch = atoi(_ch.c_str()); 43 | } 44 | case 2: { 45 | auto &_passphrase = parser.args[1]; 46 | if (!_passphrase.empty()) { 47 | passphrase = _passphrase.c_str(); 48 | } 49 | } 50 | case 1: { 51 | auto &_ssid = parser.args[0]; 52 | if (_ssid.empty()) { 53 | return chAT::CommandStatus::ERROR; 54 | } 55 | ssid = _ssid.c_str(); 56 | break; 57 | } 58 | default: { 59 | return chAT::CommandStatus::ERROR; 60 | } 61 | } 62 | 63 | int res = WiFi.softAP(ssid, passphrase, ch, ssid_hidden, max_connection); 64 | if(!res) { 65 | wifi_status = WIFI_ST_AP_FAILED; 66 | } 67 | String status = String(res); 68 | srv.write_response_prompt(); 69 | srv.write_str((const char *)status.c_str()); 70 | srv.write_line_end(); 71 | 72 | return chAT::CommandStatus::OK; 73 | } 74 | default: 75 | return chAT::CommandStatus::ERROR; 76 | } 77 | }; 78 | 79 | /* ....................................................................... */ 80 | command_table[_DISCONNECTSOFTAP] = [this](auto & srv, auto & parser) { 81 | /* ....................................................................... */ 82 | switch (parser.cmd_mode) { 83 | case chAT::CommandMode::Run: { 84 | WiFi.softAPdisconnect(); 85 | return chAT::CommandStatus::OK; 86 | } 87 | case chAT::CommandMode::Write: { 88 | if (parser.args.size() != 1) { 89 | return chAT::CommandStatus::ERROR; 90 | } 91 | 92 | auto &wifi_off = parser.args[0]; 93 | if (wifi_off.empty()) { 94 | return chAT::CommandStatus::ERROR; 95 | } 96 | WiFi.softAPdisconnect(atoi(wifi_off.c_str())); 97 | return chAT::CommandStatus::OK; 98 | } 99 | default: 100 | return chAT::CommandStatus::ERROR; 101 | } 102 | }; 103 | 104 | /* ....................................................................... */ 105 | command_table[_MACSOFTAP] = [this](auto & srv, auto & parser) { 106 | /* ....................................................................... */ 107 | switch (parser.cmd_mode) { 108 | case chAT::CommandMode::Read: { 109 | srv.write_response_prompt(); 110 | String ap_mac = WiFi.softAPmacAddress(); 111 | srv.write_str((const char *)(ap_mac.c_str())); 112 | srv.write_line_end(); 113 | 114 | return chAT::CommandStatus::OK; 115 | } 116 | default: 117 | return chAT::CommandStatus::ERROR; 118 | } 119 | }; 120 | 121 | /* ....................................................................... */ 122 | command_table[_IPSOFTAP] = [this](auto & srv, auto & parser) { 123 | /* ....................................................................... */ 124 | switch (parser.cmd_mode) { 125 | case chAT::CommandMode::Run: { 126 | srv.write_response_prompt(); 127 | String ap_ip = WiFi.softAPIP().toString() + "\r\n"; 128 | srv.write_str((const char *)(ap_ip.c_str())); 129 | return chAT::CommandStatus::OK; 130 | } 131 | default: 132 | return chAT::CommandStatus::ERROR; 133 | } 134 | }; 135 | 136 | /* ....................................................................... */ 137 | command_table[_GETSOFTAPSSID] = [this](auto & srv, auto & parser) { 138 | /* ....................................................................... */ 139 | switch (parser.cmd_mode) { 140 | case chAT::CommandMode::Read: { 141 | String ip_v6 = WiFi.softAPSSID() + "\r\n"; 142 | srv.write_response_prompt(); 143 | srv.write_str((const char *)(ip_v6.c_str())); 144 | return chAT::CommandStatus::OK; 145 | } 146 | default: 147 | return chAT::CommandStatus::ERROR; 148 | } 149 | }; 150 | 151 | /* ....................................................................... */ 152 | command_table[_SOFTAPCONFIG] = [this](auto & srv, auto & parser) { 153 | /* ....................................................................... */ 154 | switch (parser.cmd_mode) { 155 | case chAT::CommandMode::Write: { 156 | if (parser.args.size() != 3){ 157 | return chAT::CommandStatus::ERROR; 158 | } 159 | 160 | /* reading ips */ 161 | 162 | auto &ip = parser.args[0]; 163 | if(ip.empty()) { 164 | return chAT::CommandStatus::ERROR; 165 | } 166 | IPAddress _ip; 167 | if(!_ip.fromString(ip.c_str())) { 168 | return chAT::CommandStatus::ERROR; 169 | } 170 | 171 | 172 | auto &gw = parser.args[1]; 173 | if(gw.empty()) { 174 | return chAT::CommandStatus::ERROR; 175 | } 176 | IPAddress _gw; 177 | if(!_gw.fromString(gw.c_str())) { 178 | return chAT::CommandStatus::ERROR; 179 | } 180 | 181 | auto &nm = parser.args[2]; 182 | if(nm.empty()) { 183 | return chAT::CommandStatus::ERROR; 184 | } 185 | IPAddress _nm; 186 | if(!_nm.fromString(nm.c_str())) { 187 | return chAT::CommandStatus::ERROR; 188 | } 189 | 190 | 191 | if(!WiFi.softAPConfig(_ip,_gw,_nm)) { 192 | return chAT::CommandStatus::ERROR; 193 | } 194 | 195 | 196 | srv.write_response_prompt(); 197 | srv.write_line_end(); 198 | return chAT::CommandStatus::OK; 199 | } 200 | default: 201 | return chAT::CommandStatus::ERROR; 202 | } 203 | }; 204 | } 205 | 206 | #endif 207 | -------------------------------------------------------------------------------- /UNOR4USBBridge/commands.h: -------------------------------------------------------------------------------- 1 | #ifndef WIFI_COMMANDS_H 2 | #define WIFI_COMMANDS_H 3 | 4 | enum ip_type { 5 | IP_ADDR = 0, 6 | GATEWAY_ADDR, 7 | NETMASK_ADDR, 8 | DNS1_ADDR, 9 | DNS2_ADDR 10 | }; 11 | 12 | enum file_op { 13 | WIFI_FILE_DELETE = 0, 14 | WIFI_FILE_WRITE, 15 | WIFI_FILE_READ, 16 | WIFI_FILE_APPEND 17 | }; 18 | 19 | #define _AT "AT" 20 | #define _ENDL "\r\n" 21 | #define _WIFISCAN "+WIFISCAN" 22 | 23 | #define _GETTIME "+GETTIME" 24 | #define _RESET "+RESET" 25 | #define _RESTART_BOOTLOADER "+RESTARTBOOTLOADER" 26 | #define _GMR "+GMR" 27 | #define _GENERIC_CMD "+CMD" 28 | #define _FILESYSTEM "+FS" 29 | #define _MOUNTFS "+MOUNTFS" 30 | #define _EXIT "+EXIT" 31 | #define _MODE "+WIFIMODE" 32 | #define _BEGINSTA "+BEGINSTA" 33 | #define _GETSTATUS "+GETSTATUS" 34 | #define _RECONNECT "+RECONNECT" 35 | #define _DISCONNECT "+DISCONNECT" 36 | #define _BEGINSOFTAP "+BEGINSOFTAP" 37 | #define _MACSTA "+MACSTA" 38 | #define _MACSOFTAP "+MACSOFTAP" 39 | #define _DISCONNECTSOFTAP "+DISCONNECTSOFTAP" 40 | #define _AUTOCONNECT "+AUTOCONNECT" 41 | #define _IPSTA "+IPSTA" 42 | #define _IPSOFTAP "+IPSOFTAP" 43 | #define _IPV6 "+IPV6" 44 | #define _GETRSSI "+GETRSSI" 45 | #define _GETSSID "+GETSSID" 46 | #define _GETBSSID "+GETBSSID" 47 | #define _GETSOFTAPSSID "+GETSOFTAPSSID" 48 | #define _HOSTNAME "+HOSTNAME" 49 | #define _BEGINCLIENT "+BEGINCLIENT" 50 | #define _CLIENTSTATE "+CLIENTSTATE" 51 | #define _CLIENTCONNECTIP "+CLIENTCONNECTIP" 52 | #define _CLIENTCONNECTNAME "+CLIENTCONNECTNAME" 53 | #define _CLIENTCONNECT "+CLIENTCONNECT" 54 | #define _CLIENTSEND "+CLIENTSEND" 55 | #define _CLIENTRECEIVE "+CLIENTRECEIVE" 56 | #define _CLIENTCLOSE "+CLIENTCLOSE" 57 | #define _IPCLIENT "+IPCLIENT" 58 | #define _BEGINSERVER "+BEGINSERVER" 59 | #define _CLIENTCONNECTED "+CLIENTCONNECTED" 60 | #define _SSLBEGINCLIENT "+SSLBEGINCLIENT" 61 | #define _SETCAROOT "+SETCAROOT" 62 | #define _SETECCSLOT "+SETECCSLOT" 63 | #define _SSLCLIENTSTATE "+SSLCLIENTSTATE" 64 | #define _SSLCLIENTCONNECTNAME "+SSLCLIENTCONNECTNAME" 65 | #define _SSLCLIENTCONNECT "+SSLCLIENTCONNECT" 66 | #define _SETIP "+SETIP" 67 | #define _GETHOSTBYNAME "+HOSTBYNAME" 68 | #define _AVAILABLE "+AVAILABLE" 69 | #define _PEEK "+PEEK" 70 | #define _CLIENTFLUSH "+FLUSH" 71 | #define _REMOTEIP "+REMOTEIP" 72 | #define _REMOTEPORT "+REMOTEPORT" 73 | #define _CLIENTSTATUS "+CLIENTSTATUS" 74 | #define _SOFTRESETWIFI "+SOFTRESETWIFI" 75 | #define _SSLCLIENTCONNECTIP "+SSLCLIENTCONNECTIP" 76 | #define _SSLCLIENTSEND "+SSLCLIENTSEND" 77 | #define _SSLCLIENTCLOSE "+SSLCLIENTCLOSE" 78 | #define _SSLIPCLIENT "+SSLIPCLIENT" 79 | #define _SSLCLIENTCONNECTED "+SSLCLIENTCONNECTED" 80 | #define _SSLCLIENTRECEIVE "+SSLCLIENTRECEIVE" 81 | #define _SSLAVAILABLE "+SSLAVAILABLE" 82 | #define _SSLCLIENTSTATUS "+SSLCLIENTSTATUS" 83 | #define _SSLCLIENTFLUSH "+SSLCLIENTFLUSH" 84 | #define _SSLREMOTEIP "+SSLREMOTEIP" 85 | #define _SSLREMOTEPORT "+SSLREMOTEPORT" 86 | #define _SSLPEEK "+SSLPEEK" 87 | #define _SERVERAVAILABLE "+SERVERAVAILABLE" 88 | #define _SERVERACCEPT "+SERVERACCEPT" 89 | #define _SERVEREND "+SERVEREND" 90 | 91 | #define _UDPBEGIN "+UDPBEGIN" 92 | #define _UDPBEGINMULTI "+UDPBEGINMULTI" 93 | #define _UDPBEGINPACKET "+UDPBEGINPACKET" 94 | #define _UDPBEGINPACKETMULTI "+BEGINPACKETMULTI" 95 | #define _UDPBEGINPACKETNAME "+UDPBEGINPACKETADD" 96 | #define _UDPBEGINPACKETIP "+UDPBEGINPACKETIP" 97 | #define _UDPENDPACKET "+UDPENDPACKET" 98 | #define _UDPWRITE "+UDPWRITE" 99 | #define _UDPPARSE "+UDPPARSE" 100 | #define _UDPAVAILABLE "+UDPAVAILABLE" 101 | #define _UDPREAD "+UDPREAD" 102 | #define _UDPPEEK "+UDPPEEK" 103 | #define _UDPFLUSH "+UDPFLUSH" 104 | #define _UDPREMOTEIP "+UDPREMOTEIP" 105 | #define _UDPREMOTEPORT "+UDPREMOTEPORT" 106 | #define _UDPSTOP "+UDPSTOP" 107 | 108 | #define _PING "+PING" 109 | 110 | #define _FWVERSION "+FWVERSION" 111 | #define _FWVERSION_U32 "+FWVERSION_U32" 112 | 113 | #define _SOFTAPCONFIG "+SOFTAPCONFIG" 114 | #define _SERVERWRITE "+SERVERWRITE" 115 | 116 | #define _HCI_BEGIN "+HCIBEGIN" 117 | #define _HCI_END "+HCIEND" 118 | #define _HCI_WAIT "+HCIWAIT" 119 | #define _HCI_READ "+HCIREAD" 120 | #define _HCI_WRITE "+HCIWRITE" 121 | #define _HCI_AVAILABLE "+HCIAVAILABLE" 122 | 123 | #define _OTA_SETCAROOT "+OTASETCAROOT" 124 | #define _OTA_BEGIN "+OTABEGIN" 125 | #define _OTA_DOWNLOAD "+OTADOWNLOAD" 126 | #define _OTA_DOWNLOAD_START "+OTADOWNLOADSTART" 127 | #define _OTA_DOWNLOAD_PROGRESS "+OTADOWNLOADPROGRESS" 128 | #define _OTA_VERIFY "+OTAVERIFY" 129 | #define _OTA_UPDATE "+OTAUPDATE" 130 | #define _OTA_RESET "+OTARESET" 131 | 132 | #define _PREF_BEGIN "+PREFBEGIN" 133 | #define _PREF_END "+PREFEND" 134 | #define _PREF_CLEAR "+PREFCLEAR" 135 | #define _PREF_REMOVE "+PREFREMOVE" 136 | #define _PREF_PUT "+PREFPUT" 137 | #define _PREF_TYPE "+PREFTYPE" 138 | #define _PREF_GET "+PREFGET" 139 | #define _PREF_LEN "+PREFLEN" 140 | #define _PREF_STAT "+PREFSTAT" 141 | 142 | #define _SOFTSE_BEGIN "+SOFTSEBEGIN" 143 | #define _SOFTSE_END "+SOFTSEEND" 144 | #define _SOFTSE_SERIAL "+SOFTSE_SERIAL" 145 | #define _SOFTSE_RND "+SOFTSE_RND" 146 | #define _SOFTSE_PRI_KEY "+SOFTSE_PRI_KEY" 147 | #define _SOFTSE_PUB_KEY "+SOFTSE_PUB_KEY" 148 | #define _SOFTSE_WRITE_SLOT "+SOFTSE_WRITE_SLOT" 149 | #define _SOFTSE_READ_SLOT "+SOFTSE_READ_SLOT" 150 | #define _SOFTSE_S_V_BUF_SET "+SOFTSE_S_V_BUF_SET" 151 | #define _SOFTSE_SIGN_GET "+SOFTSE_SIGN_GET" 152 | #define _SOFTSE_VERIFY_GET "+SOFTSE_VERIFY_GET" 153 | #define _SOFTSE_SHA256_GET "+SOFTSE_SHA256_GET" 154 | 155 | 156 | 157 | #define CMD(x) _AT x _ENDL 158 | #define PROMPT(x) x ":" 159 | #define CMD_WRITE(x) _AT x "=" 160 | #define CMD_READ(x) _AT x "?" _ENDL 161 | 162 | #define START_CLIENT_SERVER_SOCK 1000 163 | #define START_SSL_CLIENT_SOCK 2000 164 | 165 | #endif 166 | -------------------------------------------------------------------------------- /UNOR4USBBridge/dap_config.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | // Copyright (c) 2017-2022, Alex Taradov . All rights reserved. 3 | 4 | #ifndef _DAP_CONFIG_H_ 5 | #define _DAP_CONFIG_H_ 6 | 7 | /*- Includes ----------------------------------------------------------------*/ 8 | #define CONFIG_BRIDGE_GPIO_BOOT 9 9 | #define CONFIG_BRIDGE_GPIO_RST 4 10 | 11 | /*- Definitions -------------------------------------------------------------*/ 12 | #define DAP_CONFIG_DEFAULT_PORT DAP_PORT_SWD 13 | #define DAP_CONFIG_DEFAULT_CLOCK 1000000 // Hz 14 | 15 | #define DAP_CONFIG_PACKET_SIZE 64 16 | #define DAP_CONFIG_PACKET_COUNT 2 17 | 18 | #define DAP_CONFIG_JTAG_DEV_COUNT 8 19 | 20 | //#define DAP_CONFIG_RESET_TARGET_FN target_specific_reset_function 21 | //#define DAP_CONFIG_VENDOR_FN vendor_command_handler_function 22 | 23 | // Attribute to use for performance-critical functions 24 | #define DAP_CONFIG_PERFORMANCE_ATTR //__attribute__((section(".ramfunc"))) 25 | 26 | // A value at which dap_clock_test() produces 1 kHz output on the SWCLK pin 27 | #define DAP_CONFIG_DELAY_CONSTANT 7700 28 | 29 | // A threshold for switching to fast clock (no added delays) 30 | // This is the frequency produced by dap_clock_test(1) on the SWCLK pin 31 | #define DAP_CONFIG_FAST_CLOCK 2400000 // Hz 32 | 33 | #define DAP_CONFIG_VENDOR_STR "Arduino" 34 | #define DAP_CONFIG_PRODUCT_STR "UNO R4 WiFi CMSIS-DAP Adapter" 35 | #define DAP_CONFIG_SER_NUM_STR "123456789101112" 36 | #define DAP_CONFIG_CMSIS_DAP_VER_STR "2.0.0" 37 | 38 | /*- Prototypes --------------------------------------------------------------*/ 39 | extern char usb_serial_number[16]; 40 | 41 | /*- Implementations ---------------------------------------------------------*/ 42 | 43 | #include "driver/gpio.h" 44 | 45 | #define CONFIG_BRIDGE_GPIO_SWDIO 8 46 | #define CONFIG_BRIDGE_GPIO_SWCLK 7 47 | 48 | //----------------------------------------------------------------------------- 49 | static inline void DAP_CONFIG_SWCLK_TCK_write(int value) 50 | { 51 | gpio_set_level(CONFIG_BRIDGE_GPIO_SWCLK, value); 52 | } 53 | 54 | //----------------------------------------------------------------------------- 55 | static inline void DAP_CONFIG_SWDIO_TMS_write(int value) 56 | { 57 | gpio_set_level(CONFIG_BRIDGE_GPIO_SWDIO, value); 58 | } 59 | 60 | //----------------------------------------------------------------------------- 61 | static inline void DAP_CONFIG_TDI_write(int value) 62 | { 63 | #ifdef DAP_CONFIG_ENABLE_JTAG 64 | HAL_GPIO_TDI_write(value); 65 | #else 66 | (void)value; 67 | #endif 68 | } 69 | 70 | //----------------------------------------------------------------------------- 71 | static inline void DAP_CONFIG_TDO_write(int value) 72 | { 73 | #ifdef DAP_CONFIG_ENABLE_JTAG 74 | HAL_GPIO_TDO_write(value); 75 | #else 76 | (void)value; 77 | #endif 78 | } 79 | 80 | //----------------------------------------------------------------------------- 81 | static inline void DAP_CONFIG_nTRST_write(int value) 82 | { 83 | (void)value; 84 | } 85 | 86 | //----------------------------------------------------------------------------- 87 | static inline void DAP_CONFIG_nRESET_write(int value) 88 | { 89 | gpio_set_level(CONFIG_BRIDGE_GPIO_RST, value); 90 | } 91 | 92 | //----------------------------------------------------------------------------- 93 | static inline int DAP_CONFIG_SWCLK_TCK_read(void) 94 | { 95 | return gpio_get_level(CONFIG_BRIDGE_GPIO_SWCLK); 96 | } 97 | 98 | //----------------------------------------------------------------------------- 99 | static inline int DAP_CONFIG_SWDIO_TMS_read(void) 100 | { 101 | return gpio_get_level(CONFIG_BRIDGE_GPIO_SWDIO); 102 | } 103 | 104 | //----------------------------------------------------------------------------- 105 | static inline int DAP_CONFIG_TDO_read(void) 106 | { 107 | #ifdef DAP_CONFIG_ENABLE_JTAG 108 | return HAL_GPIO_TDO_read(); 109 | #else 110 | return 0; 111 | #endif 112 | } 113 | 114 | //----------------------------------------------------------------------------- 115 | static inline int DAP_CONFIG_TDI_read(void) 116 | { 117 | #ifdef DAP_CONFIG_ENABLE_JTAG 118 | return HAL_GPIO_TDI_read(); 119 | #else 120 | return 0; 121 | #endif 122 | } 123 | 124 | //----------------------------------------------------------------------------- 125 | static inline int DAP_CONFIG_nTRST_read(void) 126 | { 127 | return 0; 128 | } 129 | 130 | //----------------------------------------------------------------------------- 131 | static inline int DAP_CONFIG_nRESET_read(void) 132 | { 133 | return gpio_get_level(CONFIG_BRIDGE_GPIO_RST); 134 | } 135 | 136 | //----------------------------------------------------------------------------- 137 | static inline void DAP_CONFIG_SWCLK_TCK_set(void) 138 | { 139 | gpio_set_level(CONFIG_BRIDGE_GPIO_SWCLK, true); 140 | } 141 | 142 | //----------------------------------------------------------------------------- 143 | static inline void DAP_CONFIG_SWCLK_TCK_clr(void) 144 | { 145 | gpio_set_level(CONFIG_BRIDGE_GPIO_SWCLK, false); 146 | } 147 | 148 | //----------------------------------------------------------------------------- 149 | static inline void DAP_CONFIG_SWDIO_TMS_in(void) 150 | { 151 | gpio_set_direction(CONFIG_BRIDGE_GPIO_SWDIO, GPIO_MODE_INPUT); 152 | } 153 | 154 | //----------------------------------------------------------------------------- 155 | static inline void DAP_CONFIG_SWDIO_TMS_out(void) 156 | { 157 | gpio_set_direction(CONFIG_BRIDGE_GPIO_SWDIO, GPIO_MODE_OUTPUT); 158 | } 159 | 160 | //----------------------------------------------------------------------------- 161 | static inline void DAP_CONFIG_SETUP(void) 162 | { 163 | gpio_config_t io_conf = {}; 164 | io_conf.mode = GPIO_MODE_INPUT; 165 | io_conf.pin_bit_mask = (1ULL << CONFIG_BRIDGE_GPIO_SWDIO) | (1ULL << CONFIG_BRIDGE_GPIO_SWCLK); 166 | io_conf.pull_down_en = 0; 167 | io_conf.pull_up_en = 0; 168 | gpio_config(&io_conf); 169 | #ifdef DAP_CONFIG_ENABLE_JTAG 170 | HAL_GPIO_TDO_in(); 171 | HAL_GPIO_TDI_in(); 172 | #endif 173 | } 174 | 175 | //----------------------------------------------------------------------------- 176 | static inline void DAP_CONFIG_DISCONNECT(void) 177 | { 178 | gpio_set_direction(CONFIG_BRIDGE_GPIO_SWCLK, GPIO_MODE_INPUT); 179 | gpio_set_direction(CONFIG_BRIDGE_GPIO_SWDIO, GPIO_MODE_INPUT); 180 | gpio_set_direction(CONFIG_BRIDGE_GPIO_RST, GPIO_MODE_INPUT); 181 | #ifdef DAP_CONFIG_ENABLE_JTAG 182 | HAL_GPIO_TDO_in(); 183 | HAL_GPIO_TDI_in(); 184 | #endif 185 | } 186 | 187 | //----------------------------------------------------------------------------- 188 | static inline void DAP_CONFIG_CONNECT_SWD(void) 189 | { 190 | gpio_set_direction(CONFIG_BRIDGE_GPIO_SWDIO, GPIO_MODE_OUTPUT); 191 | gpio_set_level(CONFIG_BRIDGE_GPIO_SWDIO, true); 192 | 193 | gpio_set_direction(CONFIG_BRIDGE_GPIO_SWCLK, GPIO_MODE_OUTPUT); 194 | gpio_set_level(CONFIG_BRIDGE_GPIO_SWCLK, true); 195 | 196 | gpio_set_direction(CONFIG_BRIDGE_GPIO_RST, GPIO_MODE_OUTPUT); 197 | gpio_set_level(CONFIG_BRIDGE_GPIO_RST, true); 198 | 199 | #ifdef DAP_CONFIG_ENABLE_JTAG 200 | HAL_GPIO_TDO_in(); 201 | HAL_GPIO_TDI_in(); 202 | #endif 203 | } 204 | 205 | //----------------------------------------------------------------------------- 206 | static inline void DAP_CONFIG_CONNECT_JTAG(void) 207 | { 208 | #ifdef DAP_CONFIG_ENABLE_JTAG 209 | HAL_GPIO_TDO_in(); 210 | 211 | HAL_GPIO_TDI_out(); 212 | HAL_GPIO_TDI_set(); 213 | #endif 214 | } 215 | 216 | //----------------------------------------------------------------------------- 217 | static inline void DAP_CONFIG_LED(int index, int state) 218 | { 219 | (void)index; 220 | (void)state; 221 | } 222 | 223 | //----------------------------------------------------------------------------- 224 | __attribute__((always_inline)) 225 | static inline void DAP_CONFIG_DELAY(uint32_t cycles) 226 | { 227 | volatile int i = cycles; 228 | while (i-- > 0) { 229 | __asm__ __volatile__ ("nop"); 230 | } 231 | } 232 | 233 | #endif // _DAP_CONFIG_H_ -------------------------------------------------------------------------------- /UNOR4USBBridge/freedap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Alex Taradov 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | * POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef _DAP_H_ 30 | #define _DAP_H_ 31 | 32 | /*- Includes ----------------------------------------------------------------*/ 33 | #include 34 | #include 35 | 36 | /*- Prototypes --------------------------------------------------------------*/ 37 | void dap_init(void); 38 | uint8_t dap_req_get_byte(void); 39 | uint16_t dap_req_get_half(void); 40 | uint32_t dap_req_get_word(void); 41 | void dap_resp_add_byte(uint8_t value); 42 | void dap_resp_add_word(uint32_t value); 43 | void dap_resp_set_byte(int index, uint8_t value); 44 | bool dap_is_buf_error(void); 45 | bool dap_filter_request(uint8_t *req); 46 | int dap_process_request(uint8_t *req, int req_size, uint8_t *resp, int resp_size); 47 | void dap_clock_test(int delay); 48 | 49 | #endif // _DAP_H_ 50 | -------------------------------------------------------------------------------- /UNOR4USBBridge/incbin.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file incbin.h 3 | * @author Dale Weiler 4 | * @brief Utility for including binary files 5 | * 6 | * Facilities for including binary files into the current translation unit and 7 | * making use from them externally in other translation units. 8 | */ 9 | #ifndef INCBIN_HDR 10 | #define INCBIN_HDR 11 | #include 12 | #if defined(__AVX512BW__) || \ 13 | defined(__AVX512CD__) || \ 14 | defined(__AVX512DQ__) || \ 15 | defined(__AVX512ER__) || \ 16 | defined(__AVX512PF__) || \ 17 | defined(__AVX512VL__) || \ 18 | defined(__AVX512F__) 19 | # define INCBIN_ALIGNMENT_INDEX 6 20 | #elif defined(__AVX__) || \ 21 | defined(__AVX2__) 22 | # define INCBIN_ALIGNMENT_INDEX 5 23 | #elif defined(__SSE__) || \ 24 | defined(__SSE2__) || \ 25 | defined(__SSE3__) || \ 26 | defined(__SSSE3__) || \ 27 | defined(__SSE4_1__) || \ 28 | defined(__SSE4_2__) || \ 29 | defined(__neon__) 30 | # define INCBIN_ALIGNMENT_INDEX 4 31 | #elif ULONG_MAX != 0xffffffffu 32 | # define INCBIN_ALIGNMENT_INDEX 3 33 | # else 34 | # define INCBIN_ALIGNMENT_INDEX 2 35 | #endif 36 | 37 | /* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */ 38 | #define INCBIN_ALIGN_SHIFT_0 1 39 | #define INCBIN_ALIGN_SHIFT_1 2 40 | #define INCBIN_ALIGN_SHIFT_2 4 41 | #define INCBIN_ALIGN_SHIFT_3 8 42 | #define INCBIN_ALIGN_SHIFT_4 16 43 | #define INCBIN_ALIGN_SHIFT_5 32 44 | #define INCBIN_ALIGN_SHIFT_6 64 45 | 46 | /* Actual alignment value */ 47 | #define INCBIN_ALIGNMENT \ 48 | INCBIN_CONCATENATE( \ 49 | INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), \ 50 | INCBIN_ALIGNMENT_INDEX) 51 | 52 | /* Stringize */ 53 | #define INCBIN_STR(X) \ 54 | #X 55 | #define INCBIN_STRINGIZE(X) \ 56 | INCBIN_STR(X) 57 | /* Concatenate */ 58 | #define INCBIN_CAT(X, Y) \ 59 | X ## Y 60 | #define INCBIN_CONCATENATE(X, Y) \ 61 | INCBIN_CAT(X, Y) 62 | /* Deferred macro expansion */ 63 | #define INCBIN_EVAL(X) \ 64 | X 65 | #define INCBIN_INVOKE(N, ...) \ 66 | INCBIN_EVAL(N(__VA_ARGS__)) 67 | 68 | /* Green Hills uses a different directive for including binary data */ 69 | #if defined(__ghs__) 70 | # if (__ghs_asm == 2) 71 | # define INCBIN_MACRO ".file" 72 | /* Or consider the ".myrawdata" entry in the ld file */ 73 | # else 74 | # define INCBIN_MACRO "\tINCBIN" 75 | # endif 76 | #else 77 | # define INCBIN_MACRO ".incbin" 78 | #endif 79 | 80 | #ifndef _MSC_VER 81 | # define INCBIN_ALIGN \ 82 | __attribute__((aligned(INCBIN_ALIGNMENT))) 83 | #else 84 | # define INCBIN_ALIGN __declspec(align(INCBIN_ALIGNMENT)) 85 | #endif 86 | 87 | #if defined(__arm__) || /* GNU C and RealView */ \ 88 | defined(__arm) || /* Diab */ \ 89 | defined(_ARM) /* ImageCraft */ 90 | # define INCBIN_ARM 91 | #endif 92 | 93 | #ifdef __GNUC__ 94 | /* Utilize .balign where supported */ 95 | # define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n" 96 | # define INCBIN_ALIGN_BYTE ".balign 1\n" 97 | #elif defined(INCBIN_ARM) 98 | /* 99 | * On arm assemblers, the alignment value is calculated as (1 << n) where `n' is 100 | * the shift count. This is the value passed to `.align' 101 | */ 102 | # define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n" 103 | # define INCBIN_ALIGN_BYTE ".align 0\n" 104 | #else 105 | /* We assume other inline assembler's treat `.align' as `.balign' */ 106 | # define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n" 107 | # define INCBIN_ALIGN_BYTE ".align 1\n" 108 | #endif 109 | 110 | /* INCBIN_CONST is used by incbin.c generated files */ 111 | #if defined(__cplusplus) 112 | # define INCBIN_EXTERNAL extern "C" 113 | # define INCBIN_CONST extern const 114 | #else 115 | # define INCBIN_EXTERNAL extern 116 | # define INCBIN_CONST const 117 | #endif 118 | 119 | /** 120 | * @brief Optionally override the linker section into which data is emitted. 121 | * 122 | * @warning If you use this facility, you'll have to deal with platform-specific linker output 123 | * section naming on your own 124 | * 125 | * Overriding the default linker output section, e.g for esp8266/Arduino: 126 | * @code 127 | * #define INCBIN_OUTPUT_SECTION ".irom.text" 128 | * #include "incbin.h" 129 | * INCBIN(Foo, "foo.txt"); 130 | * // Data is emitted into program memory that never gets copied to RAM 131 | * @endcode 132 | */ 133 | #if !defined(INCBIN_OUTPUT_SECTION) 134 | # if defined(__APPLE__) 135 | # define INCBIN_OUTPUT_SECTION ".const_data" 136 | # else 137 | # define INCBIN_OUTPUT_SECTION ".rodata" 138 | # endif 139 | #endif 140 | 141 | #if defined(__APPLE__) 142 | /* The directives are different for Apple branded compilers */ 143 | # define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n" 144 | # define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n" 145 | # define INCBIN_INT ".long " 146 | # define INCBIN_MANGLE "_" 147 | # define INCBIN_BYTE ".byte " 148 | # define INCBIN_TYPE(...) 149 | #else 150 | # define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n" 151 | # define INCBIN_GLOBAL(NAME) ".global " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n" 152 | # if defined(__ghs__) 153 | # define INCBIN_INT ".word " 154 | # else 155 | # define INCBIN_INT ".int " 156 | # endif 157 | # if defined(__USER_LABEL_PREFIX__) 158 | # define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__) 159 | # else 160 | # define INCBIN_MANGLE "" 161 | # endif 162 | # if defined(INCBIN_ARM) 163 | /* On arm assemblers, `@' is used as a line comment token */ 164 | # define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n" 165 | # elif defined(__MINGW32__) || defined(__MINGW64__) 166 | /* Mingw doesn't support this directive either */ 167 | # define INCBIN_TYPE(NAME) 168 | # else 169 | /* It's safe to use `@' on other architectures */ 170 | # define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n" 171 | # endif 172 | # define INCBIN_BYTE ".byte " 173 | #endif 174 | 175 | /* List of style types used for symbol names */ 176 | #define INCBIN_STYLE_CAMEL 0 177 | #define INCBIN_STYLE_SNAKE 1 178 | 179 | /** 180 | * @brief Specify the prefix to use for symbol names. 181 | * 182 | * By default this is `g', producing symbols of the form: 183 | * @code 184 | * #include "incbin.h" 185 | * INCBIN(Foo, "foo.txt"); 186 | * 187 | * // Now you have the following symbols: 188 | * // const unsigned char gFooData[]; 189 | * // const unsigned char *const gFooEnd; 190 | * // const unsigned int gFooSize; 191 | * @endcode 192 | * 193 | * If however you specify a prefix before including: e.g: 194 | * @code 195 | * #define INCBIN_PREFIX incbin 196 | * #include "incbin.h" 197 | * INCBIN(Foo, "foo.txt"); 198 | * 199 | * // Now you have the following symbols instead: 200 | * // const unsigned char incbinFooData[]; 201 | * // const unsigned char *const incbinFooEnd; 202 | * // const unsigned int incbinFooSize; 203 | * @endcode 204 | */ 205 | #if !defined(INCBIN_PREFIX) 206 | # define INCBIN_PREFIX g 207 | #endif 208 | 209 | /** 210 | * @brief Specify the style used for symbol names. 211 | * 212 | * Possible options are 213 | * - INCBIN_STYLE_CAMEL "CamelCase" 214 | * - INCBIN_STYLE_SNAKE "snake_case" 215 | * 216 | * Default option is *INCBIN_STYLE_CAMEL* producing symbols of the form: 217 | * @code 218 | * #include "incbin.h" 219 | * INCBIN(Foo, "foo.txt"); 220 | * 221 | * // Now you have the following symbols: 222 | * // const unsigned char FooData[]; 223 | * // const unsigned char *const FooEnd; 224 | * // const unsigned int FooSize; 225 | * @endcode 226 | * 227 | * If however you specify a style before including: e.g: 228 | * @code 229 | * #define INCBIN_STYLE INCBIN_STYLE_SNAKE 230 | * #include "incbin.h" 231 | * INCBIN(foo, "foo.txt"); 232 | * 233 | * // Now you have the following symbols: 234 | * // const unsigned char foo_data[]; 235 | * // const unsigned char *const foo_end; 236 | * // const unsigned int foo_size; 237 | * @endcode 238 | */ 239 | #if !defined(INCBIN_STYLE) 240 | # define INCBIN_STYLE INCBIN_STYLE_CAMEL 241 | #endif 242 | 243 | /* Style lookup tables */ 244 | #define INCBIN_STYLE_0_DATA Data 245 | #define INCBIN_STYLE_0_END End 246 | #define INCBIN_STYLE_0_SIZE Size 247 | #define INCBIN_STYLE_1_DATA _data 248 | #define INCBIN_STYLE_1_END _end 249 | #define INCBIN_STYLE_1_SIZE _size 250 | 251 | /* Style lookup: returning identifier */ 252 | #define INCBIN_STYLE_IDENT(TYPE) \ 253 | INCBIN_CONCATENATE( \ 254 | INCBIN_STYLE_, \ 255 | INCBIN_CONCATENATE( \ 256 | INCBIN_EVAL(INCBIN_STYLE), \ 257 | INCBIN_CONCATENATE(_, TYPE))) 258 | 259 | /* Style lookup: returning string literal */ 260 | #define INCBIN_STYLE_STRING(TYPE) \ 261 | INCBIN_STRINGIZE( \ 262 | INCBIN_STYLE_IDENT(TYPE)) \ 263 | 264 | /* Generate the global labels by indirectly invoking the macro with our style 265 | * type and concatenating the name against them. */ 266 | #define INCBIN_GLOBAL_LABELS(NAME, TYPE) \ 267 | INCBIN_INVOKE( \ 268 | INCBIN_GLOBAL, \ 269 | INCBIN_CONCATENATE( \ 270 | NAME, \ 271 | INCBIN_INVOKE( \ 272 | INCBIN_STYLE_IDENT, \ 273 | TYPE))) \ 274 | INCBIN_INVOKE( \ 275 | INCBIN_TYPE, \ 276 | INCBIN_CONCATENATE( \ 277 | NAME, \ 278 | INCBIN_INVOKE( \ 279 | INCBIN_STYLE_IDENT, \ 280 | TYPE))) 281 | 282 | /** 283 | * @brief Externally reference binary data included in another translation unit. 284 | * 285 | * Produces three external symbols that reference the binary data included in 286 | * another translation unit. 287 | * 288 | * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with 289 | * "Data", as well as "End" and "Size" after. An example is provided below. 290 | * 291 | * @param NAME The name given for the binary data 292 | * 293 | * @code 294 | * INCBIN_EXTERN(Foo); 295 | * 296 | * // Now you have the following symbols: 297 | * // extern const unsigned char FooData[]; 298 | * // extern const unsigned char *const FooEnd; 299 | * // extern const unsigned int FooSize; 300 | * @endcode 301 | */ 302 | #define INCBIN_EXTERN(NAME) \ 303 | INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char \ 304 | INCBIN_CONCATENATE( \ 305 | INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \ 306 | INCBIN_STYLE_IDENT(DATA))[]; \ 307 | INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char *const \ 308 | INCBIN_CONCATENATE( \ 309 | INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \ 310 | INCBIN_STYLE_IDENT(END)); \ 311 | INCBIN_EXTERNAL const unsigned int \ 312 | INCBIN_CONCATENATE( \ 313 | INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \ 314 | INCBIN_STYLE_IDENT(SIZE)) 315 | 316 | /** 317 | * @brief Include a binary file into the current translation unit. 318 | * 319 | * Includes a binary file into the current translation unit, producing three symbols 320 | * for objects that encode the data and size respectively. 321 | * 322 | * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with 323 | * "Data", as well as "End" and "Size" after. An example is provided below. 324 | * 325 | * @param NAME The name to associate with this binary data (as an identifier.) 326 | * @param FILENAME The file to include (as a string literal.) 327 | * 328 | * @code 329 | * INCBIN(Icon, "icon.png"); 330 | * 331 | * // Now you have the following symbols: 332 | * // const unsigned char IconData[]; 333 | * // const unsigned char *const IconEnd; 334 | * // const unsigned int IconSize; 335 | * @endcode 336 | * 337 | * @warning This must be used in global scope 338 | * @warning The identifiers may be different if INCBIN_STYLE is not default 339 | * 340 | * To externally reference the data included by this in another translation unit 341 | * please @see INCBIN_EXTERN. 342 | */ 343 | #ifdef _MSC_VER 344 | #define INCBIN(NAME, FILENAME) \ 345 | INCBIN_EXTERN(NAME) 346 | #else 347 | #define INCBIN(NAME, FILENAME) \ 348 | __asm__(INCBIN_SECTION \ 349 | INCBIN_GLOBAL_LABELS(NAME, DATA) \ 350 | INCBIN_ALIGN_HOST \ 351 | INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) ":\n" \ 352 | INCBIN_MACRO " \"" FILENAME "\"\n" \ 353 | INCBIN_GLOBAL_LABELS(NAME, END) \ 354 | INCBIN_ALIGN_BYTE \ 355 | INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) ":\n" \ 356 | INCBIN_BYTE "1\n" \ 357 | INCBIN_GLOBAL_LABELS(NAME, SIZE) \ 358 | INCBIN_ALIGN_HOST \ 359 | INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE) ":\n" \ 360 | INCBIN_INT INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) " - " \ 361 | INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) "\n" \ 362 | INCBIN_ALIGN_HOST \ 363 | ".text\n" \ 364 | ); \ 365 | INCBIN_EXTERN(NAME) 366 | 367 | #endif 368 | #endif 369 | -------------------------------------------------------------------------------- /UNOR4USBBridge/parser.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of chAT. 3 | Copyright (C) 2022-2023 Reimu NotMoe 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as 7 | published by the Free Software Foundation, either version 3 of the 8 | License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include "chAT.hpp" 20 | 21 | namespace SudoMaker::chAT { 22 | ATParser::ATParser() { 23 | reset(); 24 | } 25 | 26 | void ATParser::reset() { 27 | malformed = false; 28 | state = ParseState::Keyword; 29 | cmd_mode = CommandMode::Run; 30 | keyword_count = 0; 31 | command.clear(); 32 | args.clear(); 33 | args_quote = false; 34 | args_escape_count = 0; 35 | #ifdef CHAT_STRICT_CRLF 36 | last_data[0] = 0; 37 | last_data[1] = 0; 38 | #endif 39 | bytes_parsed = 0; 40 | } 41 | 42 | void ATParser::show() { 43 | const char *mode_str = nullptr; 44 | 45 | switch (cmd_mode) { 46 | case CommandMode::Run: 47 | mode_str = "run"; 48 | break; 49 | case CommandMode::Write: 50 | mode_str = "write"; 51 | break; 52 | case CommandMode::Read: 53 | mode_str = "read"; 54 | break; 55 | case CommandMode::Test: 56 | mode_str = "test"; 57 | break; 58 | } 59 | 60 | printf("[parser] malformed: %s, mode: %s, args_count: %zu\n", malformed ? "true" : "false", mode_str, args.size()); 61 | 62 | if (!command.empty()) { 63 | printf("[parser] command: %s\n", command.c_str()); 64 | } 65 | 66 | if (!args.empty()) { 67 | printf("[parser] args: "); 68 | 69 | for (size_t i=0; i 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "ping/ping_sock.h" 8 | 9 | // we are assuming that ping is a blocking call that returns the results of a ping session 10 | // async operations are not taken into account as of now 11 | // one ping session can be performed 12 | 13 | static ping_statistics _stats; 14 | static esp_ping_handle_t esp_ping_handle = nullptr; 15 | 16 | static void ping_success(esp_ping_handle_t hdl, void *args) { 17 | uint32_t elapsed_time; 18 | esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time)); 19 | // streaming average on rtt 20 | _stats.averagertt = _stats.averagertt + ((elapsed_time-_stats.averagertt)/++_stats.success_count); 21 | } 22 | 23 | static void ping_timeout(esp_ping_handle_t hdl, void *args) { 24 | _stats.timedout_count++; 25 | } 26 | 27 | static void ping_end(esp_ping_handle_t hdl, void *args) { 28 | esp_ping_stop(hdl); 29 | esp_ping_delete_session(hdl); 30 | if(_stats.success_count == 0) { 31 | // all ping request have timed out 32 | _stats.status = ping_status::TIMEOUT; 33 | } else { 34 | // at least one ping as succeded and we can return rtt value 35 | _stats.status = ping_status::SUCCESS; 36 | } 37 | } 38 | 39 | ping_statistics execute_ping(const char* address, uint8_t ttl, uint8_t count) { 40 | 41 | ip_addr_t target_addr; 42 | struct addrinfo hint; 43 | struct addrinfo *res = NULL; 44 | memset(&hint, 0, sizeof(hint)); 45 | memset(&target_addr, 0, sizeof(target_addr)); 46 | memset(&_stats, 0, sizeof(_stats)); 47 | 48 | if(getaddrinfo(address, NULL, &hint, &res) != 0) { 49 | _stats.status = ping_status::DNS_RESOLUTION_ERROR; 50 | return _stats; 51 | } 52 | 53 | struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr; 54 | inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4); 55 | freeaddrinfo(res); 56 | 57 | esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG(); 58 | ping_config.target_addr = target_addr; // target IP address 59 | ping_config.ttl = ttl; 60 | 61 | // for simplification we are not pinging indefinetly 62 | ping_config.count = count > 0 ? count : 1; 63 | 64 | /* set callback functions */ 65 | esp_ping_callbacks_t cbs; 66 | cbs.on_ping_success = ping_success; 67 | cbs.on_ping_timeout = ping_timeout; 68 | cbs.on_ping_end = ping_end; 69 | cbs.cb_args = NULL; 70 | 71 | if(esp_ping_new_session(&ping_config, &cbs, &esp_ping_handle) != ESP_OK) { 72 | _stats.status = ping_status::ERROR; 73 | return _stats; 74 | } 75 | 76 | if(esp_ping_start(esp_ping_handle) != ESP_OK) { 77 | _stats.status = ping_status::ERROR; 78 | esp_ping_delete_session(esp_ping_handle); 79 | return _stats; 80 | } 81 | 82 | _stats.status = ping_status::RUNNING; 83 | 84 | // wait for the end of ping session 85 | while(_stats.status == ping_status::RUNNING) { 86 | delay(10); 87 | } 88 | 89 | // session is deleted inside ping_end() callback 90 | 91 | return _stats; 92 | } 93 | -------------------------------------------------------------------------------- /UNOR4USBBridge/ping.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | enum class ping_status: int { 6 | ERROR = -4, 7 | DNS_RESOLUTION_ERROR = -3, 8 | TIMEOUT = -2, 9 | RUNNING = 0, 10 | SUCCESS = 1 11 | }; 12 | 13 | struct ping_statistics { 14 | uint8_t success_count; 15 | uint8_t timedout_count; 16 | float averagertt; // measured in ms 17 | volatile ping_status status; 18 | }; 19 | 20 | ping_statistics execute_ping(const char* address, uint8_t ttl, uint8_t count); 21 | -------------------------------------------------------------------------------- /UNOR4USBBridge/server.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of chAT. 3 | Copyright (C) 2022-2023 Reimu NotMoe 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as 7 | published by the Free Software Foundation, either version 3 of the 8 | License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include "chAT.hpp" 20 | 21 | namespace SudoMaker::chAT { 22 | 23 | template 24 | class input_buffer { 25 | protected: 26 | mutable uint8_t data_[S]; 27 | size_t usage_ = 0, position_ = 0; 28 | 29 | public: 30 | void reset() noexcept { 31 | usage_ = position_ = 0; 32 | } 33 | 34 | void reset_if_done() noexcept { 35 | if (usage_ == position_) { 36 | reset(); 37 | } 38 | } 39 | 40 | [[nodiscard]] uint8_t *data() const noexcept { 41 | return data_; 42 | } 43 | 44 | [[nodiscard]] size_t fresh_size() const noexcept { 45 | return usage_ - position_; 46 | } 47 | 48 | [[nodiscard]] uint8_t *fresh_begin() const noexcept { 49 | return data_ + position_; 50 | } 51 | 52 | [[nodiscard]] uint8_t *fresh_end() const noexcept { 53 | return data_ + usage_; 54 | } 55 | 56 | [[nodiscard]] size_t& usage() noexcept { 57 | return usage_; 58 | } 59 | 60 | [[nodiscard]] size_t& position() noexcept { 61 | return position_; 62 | } 63 | 64 | [[nodiscard]] size_t left() const noexcept { 65 | return S - usage_; 66 | } 67 | }; 68 | 69 | struct data_holder { 70 | uint8_t *data; 71 | size_t size, position; 72 | 73 | std::optional>> holder; 74 | 75 | auto& as_str() { 76 | return std::get(holder.value()); 77 | } 78 | 79 | auto& as_vec8() { 80 | return std::get>(holder.value()); 81 | } 82 | 83 | void resolve_holder() { 84 | if (holder.has_value()) { 85 | switch (holder.value().index()) { 86 | case 0: 87 | data = (uint8_t *) as_str().data(); 88 | size = as_str().size(); 89 | break; 90 | case 1: 91 | data = as_vec8().data(); 92 | size = as_vec8().size(); 93 | break; 94 | default: 95 | break; 96 | } 97 | } 98 | } 99 | }; 100 | 101 | class ServerImpl { 102 | public: 103 | Server *pintf; 104 | io_interface io; 105 | input_buffer<1024> buf_read; 106 | std::deque buf_write; 107 | size_t buf_write_len = 0; 108 | size_t buf_write_len_limit = 16384; 109 | command_callback_t cmd_cb; 110 | bool read_inhibited = false; 111 | bool nonblocking = false; 112 | bool parser_debug = false; 113 | 114 | ATParser parser; 115 | 116 | void do_parse() { 117 | while (!read_inhibited) { 118 | auto ibuf_fresh_size = buf_read.fresh_size(); 119 | 120 | if (ibuf_fresh_size) { 121 | size_t parsed_len = parser.parse(buf_read.fresh_begin(), ibuf_fresh_size); 122 | buf_read.position() += parsed_len; 123 | buf_read.reset_if_done(); 124 | 125 | if (parser.state == ATParser::ParseState::Done) { 126 | if (parser.malformed) { 127 | write_error(); 128 | } else { 129 | if (parser.command.empty()) { 130 | write_ok(); 131 | } else { 132 | auto rc_cmd = cmd_cb(*pintf, parser.command); 133 | 134 | if (rc_cmd == CommandStatus::OK) { 135 | write_ok(); 136 | } else if (rc_cmd == CommandStatus::ERROR) { 137 | write_error(); 138 | } 139 | } 140 | } 141 | 142 | if (parser_debug) 143 | parser.show(); 144 | 145 | parser.reset(); 146 | } 147 | } else { 148 | break; 149 | } 150 | } 151 | } 152 | 153 | void write_raw(data_holder d) { 154 | while (buf_write_len + d.size > buf_write_len_limit) { 155 | if (buf_write.empty()) { 156 | return; 157 | } else { 158 | buf_write_len -= buf_write.front().size; 159 | buf_write.pop_front(); 160 | } 161 | } 162 | 163 | auto &nd = buf_write.emplace_back(std::move(d)); 164 | nd.resolve_holder(); 165 | buf_write_len += nd.size; 166 | } 167 | 168 | std::vector inhibit_read(size_t raw_data_len) { 169 | std::vector ret; 170 | 171 | size_t consume_len = std::min(buf_read.fresh_size(), raw_data_len); 172 | 173 | if (consume_len) { 174 | ret.resize(consume_len); 175 | memcpy(ret.data(), buf_read.fresh_begin(), consume_len); 176 | buf_read.position() += consume_len; 177 | buf_read.reset_if_done(); 178 | } 179 | 180 | read_inhibited = true; 181 | 182 | return ret; 183 | } 184 | 185 | void continue_read() noexcept { 186 | read_inhibited = false; 187 | } 188 | 189 | void write_data(const void *buf, size_t len) { 190 | write_raw({(uint8_t *) buf, len, 0}); 191 | } 192 | 193 | void write_cstr(const char *buf, ssize_t len = -1) { 194 | write_data((void *) buf, len == -1 ? strlen(buf) : len); 195 | } 196 | 197 | void write_str(std::string str) { 198 | write_raw(data_holder{ 199 | .position = 0, 200 | .holder = std::move(str), 201 | }); 202 | } 203 | 204 | void write_vec8(std::vector vec8) { 205 | write_raw(data_holder{ 206 | .position = 0, 207 | .holder = std::move(vec8), 208 | }); 209 | } 210 | 211 | void write_error() { 212 | static const char str[] = "ERROR\r\n"; 213 | write_cstr(str, sizeof(str) - 1); 214 | } 215 | 216 | void write_ok() { 217 | static const char str[] = "OK\r\n"; 218 | write_cstr(str, sizeof(str) - 1); 219 | } 220 | 221 | void write_response_prompt() { 222 | write_str(parser.command); 223 | static const char str[] = ": "; 224 | write_cstr(str, sizeof(str) - 1); 225 | } 226 | 227 | void write_error_prompt() { 228 | static const char str[] = "+ERROR: "; 229 | write_cstr(str, sizeof(str) - 1); 230 | } 231 | 232 | void write_line_end() { 233 | static const char str[] = "\r\n"; 234 | write_cstr(str, sizeof(str) - 1); 235 | } 236 | 237 | using RunStatus = Server::RunStatus; 238 | 239 | RunStatus run() { 240 | Server::RunStatus ret = RunStatus::OK; 241 | 242 | // 0: nothing attempted, 1: success, 2: blocked 243 | int read_state = 0, write_state = 0; 244 | 245 | // Step 1: Read 246 | while (!read_inhibited) { 247 | auto ibuf_left = buf_read.left(); 248 | 249 | if (ibuf_left) { 250 | ssize_t rc_read = io.callback_io_read(buf_read.fresh_end(), ibuf_left); 251 | 252 | if (rc_read > 0) { 253 | read_state = 1; 254 | buf_read.usage() += rc_read; 255 | 256 | if (!nonblocking) { 257 | do_parse(); 258 | break; 259 | } 260 | } else { 261 | if (read_state == 0) { 262 | read_state = 2; 263 | } 264 | break; 265 | } 266 | } else { 267 | break; 268 | } 269 | } 270 | 271 | 272 | // Step 2: Parse 273 | do_parse(); 274 | 275 | // Step 3: Write 276 | while (true) { 277 | if (!buf_write.empty()) { 278 | auto &last_data = buf_write.front(); 279 | 280 | if (last_data.size) { 281 | ssize_t rc_write = io.callback_io_write(last_data.data + last_data.position, last_data.size - last_data.position); 282 | 283 | if (rc_write > 0) { 284 | write_state = 1; 285 | last_data.position += rc_write; 286 | if (last_data.position == last_data.size) { 287 | buf_write_len -= last_data.size; 288 | buf_write.pop_front(); 289 | } 290 | } else { 291 | if (write_state == 0) { 292 | write_state = 2; 293 | } 294 | break; 295 | } 296 | } else { 297 | buf_write.pop_front(); 298 | } 299 | } else { 300 | break; 301 | } 302 | } 303 | 304 | if (write_state == 2) { 305 | ret |= RunStatus::WantWrite; 306 | } 307 | 308 | if (read_state == 2) { 309 | ret |= RunStatus::WantRead; 310 | } 311 | 312 | return ret; 313 | } 314 | }; 315 | 316 | Server::Server() { 317 | pimpl = std::make_unique(); 318 | pimpl->pintf = this; 319 | } 320 | 321 | Server::~Server() { 322 | 323 | } 324 | 325 | ATParser &Server::parser() noexcept { 326 | return pimpl->parser; 327 | } 328 | 329 | void Server::set_command_callback(command_callback_t cmd_cb) { 330 | pimpl->cmd_cb = std::move(cmd_cb); 331 | } 332 | 333 | void Server::set_io_callback(io_interface io_cbs) { 334 | pimpl->io = std::move(io_cbs); 335 | } 336 | 337 | const io_interface &Server::get_io_callback() noexcept { 338 | return pimpl->io; 339 | } 340 | 341 | void Server::set_nonblocking_mode(bool v) noexcept { 342 | pimpl->nonblocking = v; 343 | } 344 | 345 | void Server::set_parser_debugging(bool v) noexcept { 346 | pimpl->parser_debug = v; 347 | } 348 | 349 | void Server::set_write_buffer_size_limit(size_t l) noexcept { 350 | pimpl->buf_write_len_limit = l; 351 | } 352 | 353 | Server::RunStatus Server::run() { 354 | return pimpl->run(); 355 | } 356 | 357 | std::vector Server::inhibit_read(size_t raw_data_len) { 358 | return pimpl->inhibit_read(raw_data_len); 359 | } 360 | 361 | void Server::continue_read() noexcept { 362 | pimpl->continue_read(); 363 | } 364 | 365 | void Server::write_data(const void *buf, size_t len) { 366 | return pimpl->write_data(buf, len); 367 | } 368 | 369 | void Server::write_cstr(const char *buf, ssize_t len) { 370 | return pimpl->write_cstr(buf, len); 371 | } 372 | 373 | void Server::write_str(std::string str) { 374 | return pimpl->write_str(std::move(str)); 375 | } 376 | 377 | void Server::write_vec8(std::vector vec8) { 378 | return pimpl->write_vec8(std::move(vec8)); 379 | } 380 | 381 | void Server::write_response(std::string str) { 382 | write_response_prompt(); 383 | write_str(std::move(str)); 384 | write_line_end(); 385 | } 386 | 387 | void Server::write_response(const char *buf, ssize_t len) { 388 | write_response_prompt(); 389 | write_cstr(buf, len); 390 | write_line_end(); 391 | } 392 | 393 | void Server::write_error() { 394 | return pimpl->write_error(); 395 | } 396 | 397 | void Server::write_error_reason(std::string str) { 398 | write_error_prompt(); 399 | write_str(std::move(str)); 400 | write_line_end(); 401 | } 402 | 403 | void Server::write_error_reason(const char *buf, ssize_t len) { 404 | write_error_prompt(); 405 | write_cstr(buf, len); 406 | write_line_end(); 407 | } 408 | 409 | void Server::write_ok() { 410 | return pimpl->write_ok(); 411 | } 412 | 413 | void Server::write_response_prompt() { 414 | return pimpl->write_response_prompt(); 415 | } 416 | 417 | void Server::write_error_prompt() { 418 | return pimpl->write_error_prompt(); 419 | } 420 | 421 | void Server::write_line_end() { 422 | return pimpl->write_line_end(); 423 | } 424 | 425 | } 426 | -------------------------------------------------------------------------------- /arduino-cli.yaml.orig: -------------------------------------------------------------------------------- 1 | board_manager: 2 | additional_urls: [] 3 | build_cache: 4 | compilations_before_purge: 10 5 | ttl: 720h0m0s 6 | daemon: 7 | port: "50051" 8 | directories: 9 | user: PWD 10 | library: 11 | enable_unsafe_install: false 12 | logging: 13 | file: "" 14 | format: text 15 | level: info 16 | metrics: 17 | addr: :9090 18 | enabled: true 19 | output: 20 | no_color: false 21 | sketch: 22 | always_export_binaries: false 23 | updater: 24 | enable_notification: true 25 | -------------------------------------------------------------------------------- /boot/boot_app0.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/uno-r4-wifi-usb-bridge/94d5bb2e8c2cb5492345bfb84d787fde65d1c183/boot/boot_app0.bin -------------------------------------------------------------------------------- /certificates/gen_crt_bundle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # ESP32 x509 certificate bundle generation utility 4 | # 5 | # Converts PEM and DER certificates to a custom bundle format which stores just the 6 | # subject name and public key to reduce space 7 | # 8 | # The bundle will have the format: number of certificates; crt 1 subject name length; crt 1 public key length; 9 | # crt 1 subject name; crt 1 public key; crt 2... 10 | # 11 | # Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD 12 | # 13 | # Licensed under the Apache License, Version 2.0 (the "License"); 14 | # you may not use this file except in compliance with the License. 15 | # You may obtain a copy of the License at 16 | # 17 | # http:#www.apache.org/licenses/LICENSE-2.0 18 | # 19 | # Unless required by applicable law or agreed to in writing, software 20 | # distributed under the License is distributed on an "AS IS" BASIS, 21 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22 | # See the License for the specific language governing permissions and 23 | # limitations under the License. 24 | 25 | from __future__ import with_statement 26 | 27 | import argparse 28 | import csv 29 | import os 30 | import re 31 | import struct 32 | import sys 33 | from io import open 34 | 35 | try: 36 | from cryptography import x509 37 | from cryptography.hazmat.backends import default_backend 38 | from cryptography.hazmat.primitives import serialization 39 | except ImportError: 40 | print('The cryptography package is not installed.' 41 | 'Please refer to the Get Started section of the ESP-IDF Programming Guide for ' 42 | 'setting up the required packages.') 43 | raise 44 | 45 | ca_bundle_bin_file = 'x509_crt_bundle' 46 | 47 | quiet = False 48 | 49 | 50 | def status(msg): 51 | """ Print status message to stderr """ 52 | if not quiet: 53 | critical(msg) 54 | 55 | 56 | def critical(msg): 57 | """ Print critical message to stderr """ 58 | sys.stderr.write('gen_crt_bundle.py: ') 59 | sys.stderr.write(msg) 60 | sys.stderr.write('\n') 61 | 62 | 63 | class CertificateBundle: 64 | def __init__(self): 65 | self.certificates = [] 66 | self.compressed_crts = [] 67 | 68 | if os.path.isfile(ca_bundle_bin_file): 69 | os.remove(ca_bundle_bin_file) 70 | 71 | def add_from_path(self, crts_path): 72 | 73 | found = False 74 | for file_path in os.listdir(crts_path): 75 | found |= self.add_from_file(os.path.join(crts_path, file_path)) 76 | 77 | if found is False: 78 | raise InputError('No valid x509 certificates found in %s' % crts_path) 79 | 80 | def add_from_file(self, file_path): 81 | try: 82 | if file_path.endswith('.pem'): 83 | status('Parsing certificates from %s' % file_path) 84 | with open(file_path, 'r', encoding='utf-8') as f: 85 | crt_str = f.read() 86 | self.add_from_pem(crt_str) 87 | return True 88 | 89 | elif file_path.endswith('.der'): 90 | status('Parsing certificates from %s' % file_path) 91 | with open(file_path, 'rb') as f: 92 | crt_str = f.read() 93 | self.add_from_der(crt_str) 94 | return True 95 | 96 | except ValueError: 97 | critical('Invalid certificate in %s' % file_path) 98 | raise InputError('Invalid certificate') 99 | 100 | return False 101 | 102 | def add_from_pem(self, crt_str): 103 | """ A single PEM file may have multiple certificates """ 104 | 105 | crt = '' 106 | count = 0 107 | start = False 108 | 109 | for strg in crt_str.splitlines(True): 110 | if strg == '-----BEGIN CERTIFICATE-----\n' and start is False: 111 | crt = '' 112 | start = True 113 | elif strg == '-----END CERTIFICATE-----\n' and start is True: 114 | crt += strg + '\n' 115 | start = False 116 | self.certificates.append(x509.load_pem_x509_certificate(crt.encode(), default_backend())) 117 | count += 1 118 | if start is True: 119 | crt += strg 120 | 121 | if(count == 0): 122 | raise InputError('No certificate found') 123 | 124 | status('Successfully added %d certificates' % count) 125 | 126 | def add_from_der(self, crt_str): 127 | self.certificates.append(x509.load_der_x509_certificate(crt_str, default_backend())) 128 | status('Successfully added 1 certificate') 129 | 130 | def create_bundle(self): 131 | # Sort certificates in order to do binary search when looking up certificates 132 | self.certificates = sorted(self.certificates, key=lambda cert: cert.subject.public_bytes(default_backend())) 133 | 134 | bundle = struct.pack('>H', len(self.certificates)) 135 | 136 | for crt in self.certificates: 137 | """ Read the public key as DER format """ 138 | pub_key = crt.public_key() 139 | pub_key_der = pub_key.public_bytes(serialization.Encoding.DER, serialization.PublicFormat.SubjectPublicKeyInfo) 140 | 141 | """ Read the subject name as DER format """ 142 | sub_name_der = crt.subject.public_bytes(default_backend()) 143 | 144 | name_len = len(sub_name_der) 145 | key_len = len(pub_key_der) 146 | len_data = struct.pack('>HH', name_len, key_len) 147 | 148 | bundle += len_data 149 | bundle += sub_name_der 150 | bundle += pub_key_der 151 | 152 | return bundle 153 | 154 | def add_with_filter(self, crts_path, filter_path): 155 | 156 | filter_set = set() 157 | with open(filter_path, 'r', encoding='utf-8') as f: 158 | csv_reader = csv.reader(f, delimiter=',') 159 | 160 | # Skip header 161 | next(csv_reader) 162 | for row in csv_reader: 163 | filter_set.add(row[1]) 164 | 165 | status('Parsing certificates from %s' % crts_path) 166 | crt_str = [] 167 | with open(crts_path, 'r', encoding='utf-8') as f: 168 | crt_str = f.read() 169 | 170 | # Split all certs into a list of (name, certificate string) tuples 171 | pem_crts = re.findall(r'(^.+?)\n(=+\n[\s\S]+?END CERTIFICATE-----\n)', crt_str, re.MULTILINE) 172 | 173 | filtered_crts = '' 174 | for name, crt in pem_crts: 175 | if name in filter_set: 176 | filtered_crts += crt 177 | 178 | self.add_from_pem(filtered_crts) 179 | 180 | 181 | class InputError(RuntimeError): 182 | def __init__(self, e): 183 | super(InputError, self).__init__(e) 184 | 185 | 186 | def main(): 187 | global quiet 188 | 189 | parser = argparse.ArgumentParser(description='ESP-IDF x509 certificate bundle utility') 190 | 191 | parser.add_argument('--quiet', '-q', help="Don't print non-critical status messages to stderr", action='store_true') 192 | parser.add_argument('--input', '-i', nargs='+', required=True, 193 | help='Paths to the custom certificate folders or files to parse, parses all .pem or .der files') 194 | parser.add_argument('--filter', '-f', help='Path to CSV-file where the second columns contains the name of the certificates \ 195 | that should be included from cacrt_all.pem') 196 | 197 | args = parser.parse_args() 198 | 199 | quiet = args.quiet 200 | 201 | bundle = CertificateBundle() 202 | 203 | for path in args.input: 204 | if os.path.isfile(path): 205 | if os.path.basename(path) == 'cacrt_all.pem' and args.filter: 206 | bundle.add_with_filter(path, args.filter) 207 | else: 208 | bundle.add_from_file(path) 209 | elif os.path.isdir(path): 210 | bundle.add_from_path(path) 211 | else: 212 | raise InputError('Invalid --input=%s, is neither file nor folder' % args.input) 213 | 214 | status('Successfully added %d certificates in total' % len(bundle.certificates)) 215 | 216 | crt_bundle = bundle.create_bundle() 217 | 218 | with open(ca_bundle_bin_file, 'wb') as f: 219 | f.write(crt_bundle) 220 | 221 | 222 | if __name__ == '__main__': 223 | try: 224 | main() 225 | except InputError as e: 226 | print(e) 227 | sys.exit(2) 228 | -------------------------------------------------------------------------------- /combine.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys; 4 | 5 | booloaderData = open("UNOR4USBBridge/build/esp32-patched.esp32.arduino_unor4wifi_usb_bridge/UNOR4USBBridge.ino.bootloader.bin", "rb").read() 6 | partitionData = open("UNOR4USBBridge/build/esp32-patched.esp32.arduino_unor4wifi_usb_bridge/UNOR4USBBridge.ino.partitions.bin", "rb").read() 7 | bootApp = open("boot/boot_app0.bin", "rb").read() 8 | appData = open("UNOR4USBBridge/build/esp32-patched.esp32.arduino_unor4wifi_usb_bridge/UNOR4USBBridge.ino.bin", "rb").read() 9 | spiffsData = open("spiffs/spiffs.bin", "rb").read() 10 | certsData = open("UNOR4USBBridge/build/esp32-patched.esp32.arduino_unor4wifi_usb_bridge/x509_crt_bundle", "rb").read() 11 | 12 | # Offset Size Name 13 | # 0x000000 0x008000 bootloader 14 | # 0x008000 0x001000 partitions 15 | # 0x009000 0x002000 boot_app/otadata 16 | # 0x00B000 0x045000 certs 17 | # 0x050000 0x190000 app0 18 | # 0x1E0000 0x190000 app1 19 | # 0x370000 0x080000 spiffs 20 | # 0x3F0000 0x010000 nvs 21 | 22 | # calculate the output binary size included nvs 23 | outputSize = 0x400000 24 | 25 | # allocate and init to 0xff 26 | outputData = bytearray(b'\xff') * outputSize 27 | 28 | # copy data: bootloader, partitions, app 29 | for i in range(0, len(booloaderData)): 30 | outputData[0x0000 + i] = booloaderData[i] 31 | 32 | for i in range(0, len(partitionData)): 33 | outputData[0x8000 + i] = partitionData[i] 34 | 35 | for i in range(0, len(bootApp)): 36 | outputData[0x9000 + i] = bootApp[i] 37 | 38 | for i in range(0, len(certsData)): 39 | outputData[0xB000 + i] = certsData[i] 40 | 41 | for i in range(0, len(appData)): 42 | outputData[0x50000 + i] = appData[i] 43 | 44 | for i in range(0, len(spiffsData)): 45 | outputData[0x370000 + i] = spiffsData[i] 46 | 47 | outputFilename = "UNOR4USBBridge/build/esp32-patched.esp32.arduino_unor4wifi_usb_bridge/S3-ALL.bin" 48 | 49 | # write out 50 | with open(outputFilename,"w+b") as f: 51 | f.seek(0) 52 | f.write(outputData) 53 | f.close 54 | 55 | outputFilename = "UNOR4USBBridge/build/esp32-patched.esp32.arduino_unor4wifi_usb_bridge/S3-BOOT-APP.bin" 56 | 57 | # write out 58 | with open(outputFilename,"w+b") as f: 59 | f.seek(0) 60 | f.write(outputData[:0x1E0000]) 61 | f.close 62 | 63 | outputFilename = "UNOR4USBBridge/build/esp32-patched.esp32.arduino_unor4wifi_usb_bridge/S3-APP.bin" 64 | 65 | # write out 66 | with open(outputFilename,"w+b") as f: 67 | f.seek(0) 68 | f.write(outputData[0xE000:0x1E0000]) 69 | f.close 70 | -------------------------------------------------------------------------------- /compile.sh: -------------------------------------------------------------------------------- 1 | sed s#PWD#$PWD#g arduino-cli.yaml.orig > arduino-cli.yaml 2 | arduino-cli compile -e --config-file arduino-cli.yaml --fqbn=esp32-patched:esp32:arduino_unor4wifi_usb_bridge:JTAGAdapter=default,PSRAM=disabled,FlashMode=qio,FlashSize=4M,LoopCore=1,EventsCore=1,USBMode=default,CDCOnBoot=default,MSCOnBoot=default,DFUOnBoot=default,UploadMode=default,PartitionScheme=unor4wifi,CPUFreq=240,UploadSpeed=921600,DebugLevel=none,EraseFlash=none -v UNOR4USBBridge 3 | -------------------------------------------------------------------------------- /download.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | arduino-cli compile -e --config-file arduino-cli.yaml --fqbn=esp32-patched:esp32:arduino_unor4wifi_usb_bridge:JTAGAdapter=default,PSRAM=disabled,FlashMode=qio,FlashSize=4M,LoopCore=1,EventsCore=1,USBMode=default,CDCOnBoot=default,MSCOnBoot=default,DFUOnBoot=default,UploadMode=default,PartitionScheme=unor4wifi,CPUFreq=240,UploadSpeed=921600,DebugLevel=none,EraseFlash=none UNOR4USBBridge -u -p /dev/ttyACM0 -v 4 | 5 | -------------------------------------------------------------------------------- /export.sh: -------------------------------------------------------------------------------- 1 | # modify esp32 platform.txt from g++11 to gcc+17 2 | rm -rf UNOR4USBBridge/build 3 | echo "Build Sketch" 4 | ./compile.sh 5 | echo "Build certificates bundle" 6 | python certificates/gen_crt_bundle.py -i certificates/cacrt_all.pem 7 | mv x509_crt_bundle UNOR4USBBridge/build/esp32-patched.esp32.arduino_unor4wifi_usb_bridge/x509_crt_bundle 8 | echo "Combine binaries" 9 | python combine.py 10 | echo "Done" 11 | -------------------------------------------------------------------------------- /package.sh: -------------------------------------------------------------------------------- 1 | VERSION="0.6.0" 2 | mkdir -p $VERSION 3 | echo "Build in release mode" 4 | bash export.sh > $VERSION/release.log 2>&1 5 | echo "Create release zip" 6 | zip -r $VERSION/unor4wifi-$VERSION-release.zip UNOR4USBBridge/build 7 | echo "Create unpdater packages" 8 | pushd unor4wifi-updater 9 | bash package.sh $VERSION > ../$VERSION/updater.log 2>&1 10 | popd 11 | mv unor4wifi-updater/unor4wifi-update-windows.zip $VERSION/unor4wifi-update-windows.zip 12 | mv unor4wifi-updater/unor4wifi-update-linux.zip $VERSION/unor4wifi-update-linux.zip 13 | mv unor4wifi-updater/unor4wifi-update-macos.zip $VERSION/unor4wifi-update-macos.zip 14 | echo "Build in debug mode" 15 | sed -i 's/DebugLevel=none/DebugLevel=verbose/g' compile.sh 16 | sed -i 's/DBG_ERROR/DBG_VERBOSE/g' UNOR4USBBridge/UNOR4USBBridge.ino 17 | bash export.sh > $VERSION/debug.log 2>&1 18 | echo "Create debug zip" 19 | zip -r $VERSION/unor4wifi-$VERSION-debug.zip UNOR4USBBridge/build 20 | echo "Done" 21 | echo "..." 22 | echo "Remember to restore compile script and .ino file" 23 | -------------------------------------------------------------------------------- /spiffs/spiffs.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/uno-r4-wifi-usb-bridge/94d5bb2e8c2cb5492345bfb84d787fde65d1c183/spiffs/spiffs.bin -------------------------------------------------------------------------------- /unor4wifi-reboot/compile_darwin.sh: -------------------------------------------------------------------------------- 1 | CGO_ENABLED=1 CC=x86_64-apple-darwin19-clang GOOS=darwin go build -o unor4wifi-reboot-macos 2 | -------------------------------------------------------------------------------- /unor4wifi-reboot/compile_linux.sh: -------------------------------------------------------------------------------- 1 | go build -o unor4wifi-reboot-linux64 2 | -------------------------------------------------------------------------------- /unor4wifi-reboot/compile_win.sh: -------------------------------------------------------------------------------- 1 | CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc GOOS=windows go build 2 | -------------------------------------------------------------------------------- /unor4wifi-reboot/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/arduino/unor4wifi-reboot 2 | 3 | go 1.20 4 | 5 | replace github.com/karalabe/hid => ./hid 6 | 7 | require github.com/karalabe/hid v1.0.1-0.20190806082151-9c14560f9ee8 8 | -------------------------------------------------------------------------------- /unor4wifi-reboot/go.sum: -------------------------------------------------------------------------------- 1 | github.com/karalabe/hid v1.0.1-0.20190806082151-9c14560f9ee8 h1:AP5krei6PpUCFOp20TSmxUS4YLoLvASBcArJqM/V+DY= 2 | github.com/karalabe/hid v1.0.1-0.20190806082151-9c14560f9ee8/go.mod h1:Vr51f8rUOLYrfrWDFlV12GGQgM5AT8sVh+2fY4MPeu8= 3 | -------------------------------------------------------------------------------- /unor4wifi-reboot/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Steven Stallion 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 1. Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 2. Redistributions in binary form must reproduce the above copyright 9 | // notice, this list of conditions and the following disclaimer in the 10 | // documentation and/or other materials provided with the distribution. 11 | // 12 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND 13 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | // SUCH DAMAGE. 23 | 24 | package main 25 | 26 | import ( 27 | "errors" 28 | "fmt" 29 | "os" 30 | 31 | "github.com/karalabe/hid" 32 | ) 33 | 34 | // The following example was adapted from the HIDAPI documentation to 35 | // demonstrate use of the hid package to communicate with a simple device. 36 | func reboot_unor4() error { 37 | b := make([]byte, 65) 38 | 39 | // Open the device using the VID and PID. 40 | info := hid.Enumerate(0x2341, 0x1002) 41 | 42 | var d *hid.Device 43 | var err error 44 | 45 | if len(info) == 0 { 46 | fmt.Printf("Cannot enumerate, try direct open\n") 47 | d, err = hid.Open(0x2341, 0x1002) 48 | if err != nil { 49 | fmt.Printf("No board connected\n") 50 | return errors.New("No board connected") 51 | } 52 | } else { 53 | d, _ = info[0].Open() 54 | } 55 | 56 | // Reboot 57 | b[0] = 0 58 | b[1] = 0xAA 59 | if _, err := d.SendFeatureReport(b); err != nil { 60 | return err 61 | } 62 | 63 | err = d.Close() 64 | if err != nil { 65 | return err 66 | } 67 | 68 | return nil 69 | } 70 | 71 | // The following example demonstrates use of the Enumerate function to display 72 | // device information for all HID devices attached to the system. 73 | func main() { 74 | err := reboot_unor4() 75 | if err == nil { 76 | os.Exit(0) 77 | } else { 78 | os.Exit(1) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /unor4wifi-updater/README.md: -------------------------------------------------------------------------------- 1 | # UNO R4 WiFi updater script 2 | 3 | This updater script can be used to automatically put the UNO R4 WiFi in `ESP download` mode and upload the latest firmware. 4 | 5 | The script uses [unor4wifi-reboot](unor4wifi-reboot) to put the board in `ESP download` mode through an HID message; and [espflash](https://github.com/esp-rs/espflash/releases) to flash the firmware bundle. 6 | 7 | # How to use the script 8 | 9 | ## On Windows 10 | * Extract the zip file 11 | * Disconnect all USB gadgets from your PC 12 | * Connect your UNO R4 WiFi 13 | * Double click on update.bat 14 | * Follow instructions on the terminal window 15 | * Disconnect and connect your UNO R4 WiFi from your PC 16 | 17 | On windows `espflash `do not list the board as `Espressif USB JTAG/serial debug unit` but only as generic `COMX` port. 18 | Check the `COMX` properties from the device manager to be sure what to select. 19 | 20 | ![image](https://github.com/pennam/UnoR4WiFiUpdate/assets/20436476/7c219bc1-d68e-4c61-8b43-ffffbba4955c) ![image](https://github.com/pennam/UnoR4WiFiUpdate/assets/20436476/ff2dbb8f-2d6c-4d6c-aab0-5002bd1a11d9) 21 | 22 | If Windows is overprotective click on `More info` and then `Run anyway` 23 | 24 | ![image](https://github.com/pennam/UnoR4WiFiUpdate/assets/20436476/dbd20f28-c3c5-4194-b509-f728ac6dc0df) 25 | 26 | ## On Linux 27 | * Extract the zip file 28 | * Disconnect all USB gadgets from your PC 29 | * Connect your UNO R4 WiFi 30 | * From the terminal emulator run `./update.sh` 31 | * Follow instructions on the terminal window 32 | * Disconnect and connect your UNO R4 WiFi from your PC 33 | 34 | On some distro may be required to run the script with `sudo`. 35 | 36 | ## On MacOS 37 | * Extract the zip file 38 | * Disconnect all USB gadgets from your PC 39 | * Connect your UNO R4 WiFi 40 | * Double click on update.command 41 | * Follow instructions on the terminal window 42 | * Disconnect and connect your UNO R4 WiFi from your PC 43 | 44 | If MacOS complains about the developer run this commands from the root folder of the extracted .zip file and then double click again on the script. 45 | 46 | ``` 47 | chmod a+x update.command 48 | sudo xattr -d com.apple.quarantine bin/espflash 49 | sudo xattr -d com.apple.quarantine bin/unor4wifi-reboot-macos 50 | ``` 51 | 52 | Running the script from the terminal instead of double click should avoid the commands above. 53 | 54 | ![image](https://github.com/pennam/UnoR4WiFiUpdate/assets/20436476/dd776469-ba93-4430-9b72-25a6ca75840f) 55 | 56 | # Troubleshooting 57 | * If the script report this error: `Cannot put the board in ESP mode. (via 'unor4wifi-reboot')` 58 | 59 | ## Option 1 60 | Disconnect and connect again your UNO R4 WiFi from your PC and re-run the script. 61 | 62 | ## Option 2 63 | 64 | Follow the "**Run espflash directly**" instructions provided in this Arduino Help Center article: 65 | 66 | https://support.arduino.cc/hc/en-us/articles/16379769332892-Restore-the-USB-connectivity-firmware-on-UNO-R4-WiFi-with-espflash 67 | -------------------------------------------------------------------------------- /unor4wifi-updater/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function package_windows() { 4 | mkdir -p unor4wifi-update-windows 5 | mkdir -p unor4wifi-update-windows/bin 6 | mkdir -p unor4wifi-update-windows/firmware 7 | 8 | wget https://github.com/esp-rs/espflash/releases/download/v2.0.0/espflash-x86_64-pc-windows-msvc.zip -O /tmp/espflash-x86_64-pc-windows-msvc.zip 9 | unzip /tmp/espflash-x86_64-pc-windows-msvc.zip -d unor4wifi-update-windows/bin 10 | 11 | wget https://github.com/bcmi-labs/uno-r4-wifi-usb-bridge/releases/download/0.1.0/unor4wifi-reboot-windows.zip -O /tmp/unor4wifi-reboot-windows.zip 12 | unzip /tmp/unor4wifi-reboot-windows.zip -d unor4wifi-update-windows/bin 13 | 14 | sed s#VVERSION#$VERSION#g update_windows.bat > unor4wifi-update-windows/update.bat 15 | cp ../UNOR4USBBridge/build/esp32-patched.esp32.arduino_unor4wifi_usb_bridge/S3-BOOT-APP.bin unor4wifi-update-windows/firmware/UNOR4-WIFI-S3-$VERSION.bin 16 | 17 | zip -r unor4wifi-update-windows.zip unor4wifi-update-windows 18 | 19 | } 20 | 21 | function package_linux() { 22 | mkdir -p unor4wifi-update-linux 23 | mkdir -p unor4wifi-update-linux/bin 24 | mkdir -p unor4wifi-update-linux/firmware 25 | 26 | wget https://github.com/esp-rs/espflash/releases/download/v2.0.0/espflash-x86_64-unknown-linux-gnu.zip -O /tmp/espflash-x86_64-unknown-linux-gnu.zip 27 | unzip /tmp/espflash-x86_64-unknown-linux-gnu.zip -d unor4wifi-update-linux/bin 28 | 29 | wget https://github.com/bcmi-labs/uno-r4-wifi-usb-bridge/releases/download/0.1.0/unor4wifi-reboot-linux64.zip -O /tmp/unor4wifi-reboot-linux64.zip 30 | unzip /tmp/unor4wifi-reboot-linux64.zip -d unor4wifi-update-linux/bin 31 | 32 | sed s#VVERSION#$VERSION#g update_linux.sh > unor4wifi-update-linux/update.sh 33 | cp ../UNOR4USBBridge/build/esp32-patched.esp32.arduino_unor4wifi_usb_bridge/S3-BOOT-APP.bin unor4wifi-update-linux/firmware/UNOR4-WIFI-S3-$VERSION.bin 34 | 35 | zip -r unor4wifi-update-linux.zip unor4wifi-update-linux 36 | 37 | } 38 | 39 | 40 | function package_macos() { 41 | mkdir -p unor4wifi-update-macos 42 | mkdir -p unor4wifi-update-macos/bin 43 | mkdir -p unor4wifi-update-macos/firmware 44 | 45 | wget https://github.com/esp-rs/espflash/releases/download/v2.0.0/espflash-x86_64-apple-darwin.zip -O /tmp/espflash-x86_64-apple-darwin.zip 46 | unzip /tmp/espflash-x86_64-apple-darwin.zip -d unor4wifi-update-macos/bin 47 | 48 | wget https://github.com/bcmi-labs/uno-r4-wifi-usb-bridge/releases/download/0.1.0/unor4wifi-reboot-macos.zip -O /tmp/unor4wifi-reboot-macos.zip 49 | unzip /tmp/unor4wifi-reboot-macos.zip -d unor4wifi-update-macos/bin 50 | 51 | sed s#VVERSION#$VERSION#g update_mac.command > unor4wifi-update-macos/update.command 52 | cp ../UNOR4USBBridge/build/esp32-patched.esp32.arduino_unor4wifi_usb_bridge/S3-BOOT-APP.bin unor4wifi-update-macos/firmware/UNOR4-WIFI-S3-$VERSION.bin 53 | 54 | zip -r unor4wifi-update-macos.zip unor4wifi-update-macos 55 | 56 | } 57 | 58 | if [ -z "$1" ]; then 59 | echo "No argument supplied" 60 | exit 1 61 | fi 62 | 63 | VERSION=$1 64 | 65 | # Cleanup before starting 66 | rm -rf unor4wifi-update-linux* 67 | rm -rf unor4wifi-update-windows* 68 | rm -rf unor4wifi-update-macos* 69 | 70 | package_macos 71 | package_linux 72 | package_windows 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /unor4wifi-updater/update_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #set -e 3 | 4 | SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" 5 | 6 | cd "$SCRIPTPATH" 7 | 8 | ./bin/unor4wifi-reboot-linux64 9 | if [ "$?" -ne 0 ]; then 10 | echo "Cannot put the board in ESP mode. (via 'unor4wifi-reboot')" 11 | exit 1 12 | fi 13 | 14 | echo "Start flashing firmware" 15 | sleep 1 16 | ./bin/espflash write-bin -b 115200 0x0 firmware/UNOR4-WIFI-S3-VVERSION.bin -------------------------------------------------------------------------------- /unor4wifi-updater/update_mac.command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #set -e 3 | 4 | SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" 5 | 6 | cd "$SCRIPTPATH" 7 | 8 | ./bin/unor4wifi-reboot-macos 9 | if [ "$?" -ne 0 ]; then 10 | echo "Cannot put the board in ESP mode. (via 'unor4wifi-reboot')" 11 | exit 1 12 | fi 13 | 14 | echo "Start flashing firmware" 15 | sleep 1 16 | ./bin/espflash write-bin -b 115200 0x0 firmware/UNOR4-WIFI-S3-VVERSION.bin -------------------------------------------------------------------------------- /unor4wifi-updater/update_windows.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | SETLOCAL ENABLEDELAYEDEXPANSION 3 | setlocal 4 | 5 | REM go to the folder where this bat script is located 6 | cd /d %~dp0 7 | 8 | CALL bin\unor4wifi-reboot 9 | 10 | IF %ERRORLEVEL% NEQ 0 ( 11 | GOTO ESPMODEERROR 12 | ) 13 | 14 | echo Start flashing firmware 15 | timeout /t 5 /nobreak > NUL 16 | CALL bin\espflash write-bin -b 115200 0x0 firmware\UNOR4-WIFI-S3-VVERSION.bin 17 | 18 | @pause 19 | exit /b 0 20 | 21 | :ESPMODEERROR 22 | echo Cannot put the board in ESP mode. (via 'unor4wifi-reboot') 23 | @pause 24 | exit /b 1 --------------------------------------------------------------------------------