Compare commits

...

39 Commits

Author SHA1 Message Date
c45cff5f83 Implement USB HID Device Support for ESP32-S2 (#5538)
* Add support and example for USB HID Devices
* Add support and example for USB Vendor
2021-08-23 17:27:34 +03:00
b1d072df9f Implements UART SerialHardware Refactoring on top of IDF (#5549)
## Summary
This PR is a complete reffactoring of UART Serial Hardware and respective HAL in order to use IDF instead of current Register manipulation approach. 

It  implements Arduino SerialEvent functionality. 

Fix #5287  
Fix #5273 
Fix #5519 
Fix #5247 
Fix #5403
Fix #5429
Fix #5047
Fix #5463
Fix #5362 
Fix #5112  
Fix #5443 

## Impact
It solves many reported issues related to UART.
It was tested and works fine for ESP32, ESP-S2 and ESP32-C3.
2021-08-23 17:25:33 +03:00
929cf2c2d5 Add ATMegaZero ESP32-S2 microcontroller (#5569)
## Summary
Adds support for the new ATMegaZero ESP32-S2 board. Adds the relevant section to `boards.txt` and adds the folder for the `atmegazero-esp32s2` with the proper `pins_arduino.h` to the `variants` folder.

## Impact
Allows users to compile code properly for the ATMegaZero ESP32-S2 without having to use a similar board and manually entering pin numbers.

## Links
[ATMegaZero ESP32-S2](https://atmegazero.com/#/atmegazero_esp32s2_overview)

Thanks!
2021-08-23 17:21:48 +03:00
87853353db Add SparkFun ESP32 MicroMod microcontroller (#5556)
* Add SparkFun ESP32 MicroMod to boards.txt

Copied from the tarball found in the SparkFun board manager
[JSON](https://raw.githubusercontent.com/sparkfun/Arduino_Boards/main/IDE_Board_Manager/package_sparkfun_index.json),
v1.0.1 -- [tarball can be found here](https://github.com/sparkfun/Arduino_Boards/raw/main/IDE_Board_Manager/sparkfun-esp32-1.0.1.tar.bz2)

* Add `pins_arduino.h` for ESP32 MicroMod

Co-authored-by: Me No Dev <me-no-dev@users.noreply.github.com>
2021-08-23 17:20:35 +03:00
e265bd0d7c Add new board (Deneyap Mini) (#5531)
* The board.txt has been updated. Added variant folder.

Necessary additions for deneyapMiniKart were made in board.txt. Added pins_arduino to variant folder. deneyapMiniKart information has been added.

* Added variant files and added text in board.txt

Co-authored-by: Me No Dev <me-no-dev@users.noreply.github.com>
2021-08-23 17:20:21 +03:00
94809ce38b fix: timerRead() is returning the last read value, not the actual #3434 (#5498) 2021-08-23 17:19:37 +03:00
Tim
29455a0447 Fix issue #5507 "Constructor WebServer::WebServer(IPAddress addr, int… (#5509)
* Fix issue #5507 "Constructor WebServer::WebServer(IPAddress addr, int port) produces an unexpected result"

"The class Webserver is declared with two explicit constructors, one with signature:
WebServer::WebServer(IPAddress addr, int port)
Using this results in a server listening on the port number obtained by converting the value of the IPAddress addr argument (in host byte order) to a uint32_t and then to a uint16_t, which is manifestly not the result that would be expected.
...
As for a fix, we can assume from these results that this constructor is not being used and therefore could simply be deleted."

* Issue 5507
Reverse changes in commit bee1e7088cef913391155f096b42cd4bb89c5c6f after discussion.
Alternative fix to be done.

* Fix issue #5507 "Constructor WebServer::WebServer(IPAddress addr, int port) produces an unexpected result"

This change adds support for multi-homed servers to libraries/WiFi.  It was assumed to be there already by libraries/WebServer, but was not.
This led to unexpected results when the IP address-specific constructor of class WebServer was used (see issue 5507).

This change was tested using three concurrent instances of WebServer, one bound to the WiFi station address, one bound to the WiFi soft AP address,
and one bound to INADDR_ANY.  See libraries/WebServer/examples/MultiHomedServers for the test method.

* Fix issue #5507 "Constructor WebServer::WebServer(IPAddress addr, int port) produces an unexpected result" (cont.)
This fixes what I think might be the cause of CI failures on GitHub for the previous commit, namely the absence of an include file
in examples/MultiHomedServers.

* Fix issue #5507 "Constructor WebServer::WebServer(IPAddress addr, int port) produces an unexpected result" (cont.)

Change port numbers in examples/MultiHomedServers per pull-request comment from me-no-dev ...
"for this test to be valid, both servers should be on the same port. That is how you can make sure that the functionality works."
2021-08-23 15:51:06 +03:00
78499c459b Added Franzininho WiFi board support (#5570)
This PR adds the support for the Franzininho WiFi board in CDC and MSC modes.

References: https://franzininho.github.io/docs-franzininho-site/docs/franzininho-wifi/franzininho-wifi/
https://github.com/Franzininho/imagens-franzininho/blob/main/franzininho_wifi/pinagem-franzininho-wifi.png
2021-08-23 15:43:17 +03:00
ce680708ec Fixed the external link to a new tab (LOLIN boards) (#5571)
Moved links to the botton
2021-08-23 15:41:07 +03:00
90c01dab77 Fix build error in ESP-IDF
Fixes: https://github.com/espressif/arduino-esp32/issues/5562
2021-08-19 13:54:10 +03:00
000d967db3 Merge pull request #5547 from pedrominatel/docs/wemos_boards
[DOCS] Added LOLIN dev kits links to the boards list in the documentation
2021-08-18 12:19:22 +01:00
44dd99f5a5 Merge branch 'master' into docs/wemos_boards 2021-08-18 12:00:52 +01:00
b580bb23fd Add log_buf to pretty print buffers
```
/* 0x0000 */ 0x7b, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,    // {{..............
/* 0x0010 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,    // ................
```
2021-08-17 17:01:16 +03:00
a7ea737f30 Added LOLIN boards o the boards list. 2021-08-16 09:35:36 +01:00
2af8cc3485 Fixes Touchpad Interrupt (#5527)
Fixes #5493 

## Summary
PR #4996 has broken Touch Interrupt functionality by removing a single line of code used to set a Register.


## Impact
This PR fixes issue #5493 by reverting the removal of necessary code as described above.
2021-08-12 10:05:47 +03:00
e5bd18d6aa Fix I2C Scan for S2 and C3 (#5528)
Thanks @chegewara

I2C Scan was failing because i2c_master_write does not accept size of 0. This change checks and skips that call if no length is provided (usually when scanning)
2021-08-12 10:03:33 +03:00
a4118ea889 Add ET-Board BUILTIN_LED pin (#5490)
ET-Board
No internal LED pin
Missing internal LED pin have been added to this commit.
2021-08-11 14:32:37 +03:00
Tim
c4fcab28e4 Fix issue #5506 "WebServer serveStatic () can cause LoadProhibited exception in _svfprintf_r" (#5508)
"Using a Core Debug Level of Verbose and the WebServer serveStatic() function with the default value of nullptr for its cache_header argument, results in a LoadProhibited exception in _svfprintf_r().
This is because serveStatic() calls log_v() with cache_header corresponding to a "%s" in its format but without checking that cache_header is not nullptr, and then logv() (indirectly) calls _svfprintf_r().
On the other hand, with a Core Debug Level other than Verbose, this does not occur."

Changed serveStatic() to the check value of cache_header and if it is nullptr, instead pass an empty string to log_v().
2021-08-11 14:31:07 +03:00
0acbe781f5 Increase default timeout for WiFiClient from 3ms to 3s (#5496)
## Summary
https://github.com/espressif/arduino-esp32/pull/5487 introduced a default timeout for WiFiClient, however the default was specified in milliseconds instead of seconds, see be84c8219c (commitcomment-54358731)
This 3ms timeout breaks OTA when the processor is busy.

## Impact
Sets the default to a saner value, fixes OTA.
2021-08-11 14:17:38 +03:00
0b0dfab3cf Fix race in log_printf (#5523)
Fixes: https://github.com/espressif/arduino-esp32/issues/5513

Can still race if Serial.begin() is not called in setup()
2021-08-11 13:46:08 +03:00
5fd737925f Fix wrong attenuation being set to pins (#5522)
Fixes: https://github.com/espressif/arduino-esp32/issues/5503
2021-08-11 13:05:26 +03:00
5bb8177aa1 Add initial support for USB MSC (#5466)
* Add initial support for USB MSC

* Add Firmware Upload/Download With MSC

Current running firmware is available as file inside the MSC Disk. To update the firmware on the ESP, just copy a regular firmware bin into the drive

* Support overwriting of the firmware file

Overwriting a file is done totally differently on MacOS, Windows and Linux. This change supports it on all of them.

* Allow CDC, FirmwareMSC and DFU to be enabled on boot

* Add example ESP32-S2 USB-ONLY board

* Various device code optimizations

Added `end()` methods to MSC classes
Made begin() methods safe to be called multiple times
Optimized CDC class

* Fix CDC Connect/Disconnect detection in Arduino IDE on Windows

* Rework cdc_write

* Update ESP32-S2 board configs
2021-08-02 15:35:13 +03:00
be84c8219c [WiFiClient] Default connection timeout, when no timeout provided (#5487)
## The problem
WiFiClient's connect method variant where no timeout is passed can block esp32 MCU and may then cause watchdog to kick in and reset the device. This behavior is different from that, what is in arduino-esp8266 core.

## Summary
Some cross-esp libraries (working both on esp32 and 8266), like PubSubClient simply call connect method on WiFiClient, to get connected to remote server. However, connect behavior varies betwen esp arduino 8266 and esp arduino 32 cores. This pull request tries introduce same behavior - to make connect method non-blocking on esp32, like it is with 8266 arduino core.

## Proposed solution
Introduce default fixed timeout that can be changed by #define - by default set to 3 seconds.

### Affected components: 
WiFiClient

### Affected methods:
```c++ 
int connect(IPAddress ip, uint16_t port);
int connect(const char *host, uint16_t port);
```

### Impact
May impact projects or libraries using connect method variant without specified timeout, where:
- remote is located far away or
- connection is heavily limited, or
- remote is slow, when it comes to accept the connection
2021-08-02 15:05:44 +03:00
31127f4260 Support for Transfer-Encoding headers that specify "identify" (#5486)
In [HTTPClient](https://github.com/espressif/arduino-esp32/tree/master/libraries/HTTPClient), if the `Transfer-Encoding` header is set to `identity`, an error (Transfer-Encoding not supported) will occur.

HTTPClient will consider the request as `identity` if the `Transfer-Encoding` header is not set. But it is also defined a response with `identity` explicitly set in the `Transfer-Encoding` header (ref:[MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding)).

This pull request will allow the request to be processed normally even when `identity` is explicitly set.
2021-08-02 15:04:48 +03:00
4365a45401 Fix WiFi Deinit bug
Fixes: https://github.com/espressif/arduino-esp32/issues/4842
2021-08-02 14:57:55 +03:00
023ae75b97 Rework pulseIn to work on ESP32-C3
Fixes: https://github.com/espressif/arduino-esp32/issues/5488
2021-08-02 14:53:11 +03:00
c5a1f3efd7 Corrected CLASSIC_BT_ENABLED to CONFIG_BT_CLASSIC_ENABLED. (#5471)
CLASSIC_BT_ENABLED never worked as was incorrectly named, e.g. see
https://github.com/nkolban/esp32-snippets/issues/890#issuecomment-521520934
Now corrected to the update-to-date name CONFIG_BT_CLASSIC_ENABLED.
2021-07-29 15:06:43 +03:00
9406f8e464 Weaken tinyusb callbacks (#5475) 2021-07-29 14:52:30 +03:00
65eafd16b5 Fixes ESP32-S2 LEDC PWM #5375 #5050 (#5452)
Fixes: #5375
Fixes: #5050
2021-07-26 20:37:44 +03:00
d5a98f9a39 Doc: Updated Troubleshooting file (#5430) 2021-07-26 20:37:12 +03:00
3780b5c924 Unbreak integer to string conversion functions. (#5423) (#5438) 2021-07-26 16:06:13 +03:00
1775dd1faa Add ReadTheDocs CI (#5456) 2021-07-26 16:04:20 +03:00
6972695d95 IDF master c69f0ec32 (#5449)
esp-dsp: master f4d7d6e
esp-face: master 420fc7e
esp-rainmaker: f1b82c7
esp32-camera: master 6a9497b
esp_littlefs: master b58f00c
2021-07-26 15:56:05 +03:00
e0e5c88658 Create PULL_REQUEST_TEMPLATE.md (#5439)
Adding first version of PULL_REQUEST_TEMPLATE.md.

The purpose of this template is to improve and simplify writing more accurate Release Notes.
2021-07-26 15:37:14 +03:00
6e47e18a1b Add files via upload (#5433)
Newly compiled esp32-camera driver from https://github.com/espressif/esp32-camera
2021-07-22 20:10:49 +03:00
34125cee1d Examples update, add a note for configTime() that only one ntp server is supported by lwip (#5343)
lwip lib bundled with esp32 Arduino supports only one ntp server. Any additional servers set are just silently ignored.
This default is different from esp8266 Arduino core and very confusing. Most of the examples provided uses 3 different ntp servers for redundancy while only the first one is used actually.
Addressing issue #4964
2021-07-22 20:09:28 +03:00
ee24736042 Update README.md 2021-07-22 17:05:54 +03:00
5458df0a54 Fixes Serial.end() hanging on ESP32-S2 (#5434)
Fixes: #5429
2021-07-21 17:02:06 +03:00
e12d8c8ff1 fix: WiFiClientSecure connection timeout (#5398) (#5418)
Closes #5398

Using the same non-blocking socket connect pattern for respecting connection timeout, copied from WiFiClient::connect.

WiFiClient::connect uses lwip_connect_r, whereas start_ssl_client uses lwip_connect. I haven't found what is the difference between them. I tested both, both work ok, so I kept lwip_connect.
2021-07-21 13:20:23 +03:00
711 changed files with 22344 additions and 2414 deletions

16
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,16 @@
----------------------------------------------------------------------------------------------------------------------------------------------------
This entire section can be deleted if all items are checked.
*By completing this PR sufficiently, you help us to improve the quality of Release Notes*
### Checklist
1. [ ] Please provide specific title of the PR describing the change, including the component name (eg."Update of Documentation link on Readme.md")
2. [ ] Please provide related links (eg. Issue, other Project, submodule PR..)
----------------------------------------------------------------------------------------------------------------------------------------------------
## Summary
Please describe your proposed PR and what it contains.
## Impact
Please describe impact of your PR and it's function.

38
.github/workflows/docs.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: ReadTheDocs CI
on:
push:
branches:
- master
- release/*
paths:
- 'docs/**'
- '.github/workflows/docs.yml'
pull_request:
paths:
- 'docs/**'
- '.github/workflows/docs.yml'
jobs:
build-docs:
name: Build ReadTheDocs
runs-on: ubuntu-latest
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v2
with:
submodules: true
- uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Build
run: |
sudo apt update
sudo apt install python3-pip python3-setuptools
# GitHub CI installs pip3 and setuptools outside the path.
# Update the path to include them and run.
PATH=/home/runner/.local/bin:$PATH pip3 install --user -r ./docs/requirements.txt
cd ./docs && PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" make html

View File

@ -34,6 +34,9 @@ set(CORE_SRCS
cores/esp32/StreamString.cpp
cores/esp32/USB.cpp
cores/esp32/USBCDC.cpp
cores/esp32/USBMSC.cpp
cores/esp32/FirmwareMSC.cpp
cores/esp32/firmware_msc_fat.c
cores/esp32/wiring_pulse.c
cores/esp32/wiring_shift.c
cores/esp32/WMath.cpp
@ -73,6 +76,14 @@ set(LIBRARY_SRCS
libraries/Ticker/src/Ticker.cpp
libraries/Update/src/Updater.cpp
libraries/Update/src/HttpsOTAUpdate.cpp
libraries/USB/src/USBHID.cpp
libraries/USB/src/USBHIDMouse.cpp
libraries/USB/src/USBHIDKeyboard.cpp
libraries/USB/src/USBHIDGamepad.cpp
libraries/USB/src/USBHIDConsumerControl.cpp
libraries/USB/src/USBHIDSystemControl.cpp
libraries/USB/src/USBHIDVendor.cpp
libraries/USB/src/USBVendor.cpp
libraries/WebServer/src/WebServer.cpp
libraries/WebServer/src/Parsing.cpp
libraries/WebServer/src/detail/mimetable.cpp
@ -151,6 +162,7 @@ set(includedirs
libraries/SPI/src
libraries/Ticker/src
libraries/Update/src
libraries/USB/src
libraries/WebServer/src
libraries/WiFiClientSecure/src
libraries/WiFi/src

View File

@ -25,7 +25,7 @@ Now you can install the latest 2.0.0 version from the boards manager.
Latest Stable Release [![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) [![Release Date](https://img.shields.io/github/release-date/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) [![Downloads](https://img.shields.io/github/downloads/espressif/arduino-esp32/latest/total.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/)
Latest Development Release [![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32/all.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) [![Release Date](https://img.shields.io/github/release-date-pre/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) [![Downloads](https://img.shields.io/github/downloads-pre/espressif/arduino-esp32/latest/total.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/)
Latest Development Release [![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32/all.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/) [![Release Date](https://img.shields.io/github/release-date-pre/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/) [![Downloads](https://img.shields.io/github/downloads-pre/espressif/arduino-esp32/latest/total.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/)
### Documentation

File diff suppressed because it is too large Load Diff

423
cores/esp32/FirmwareMSC.cpp Normal file
View File

@ -0,0 +1,423 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "FirmwareMSC.h"
#if CONFIG_TINYUSB_MSC_ENABLED
#include <cstring>
#include "esp_partition.h"
#include "esp_ota_ops.h"
#include "esp32-hal.h"
#include "pins_arduino.h"
#include "firmware_msc_fat.h"
#ifndef USB_FW_MSC_VENDOR_ID
#define USB_FW_MSC_VENDOR_ID "ESP32" //max 8 chars
#endif
#ifndef USB_FW_MSC_PRODUCT_ID
#define USB_FW_MSC_PRODUCT_ID "Firmware MSC"//max 16 chars
#endif
#ifndef USB_FW_MSC_PRODUCT_REVISION
#define USB_FW_MSC_PRODUCT_REVISION "1.0" //max 4 chars
#endif
#ifndef USB_FW_MSC_VOLUME_NAME
#define USB_FW_MSC_VOLUME_NAME "ESP32-FWMSC" //max 11 chars
#endif
#ifndef USB_FW_MSC_SERIAL_NUMBER
#define USB_FW_MSC_SERIAL_NUMBER 0x00000000
#endif
ESP_EVENT_DEFINE_BASE(ARDUINO_FIRMWARE_MSC_EVENTS);
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
//General Variables
static uint8_t * msc_ram_disk = NULL;
static fat_boot_sector_t * msc_boot = NULL;
static uint8_t * msc_table = NULL;
static uint16_t msc_table_sectors = 0;
static uint16_t msc_total_sectors = 0;
static bool mcs_is_fat16 = false;
//Firmware Read
static const esp_partition_t* msc_run_partition = NULL;
static uint16_t fw_start_sector = 0;
static uint16_t fw_end_sector = 0;
static size_t fw_size = 0;
static fat_dir_entry_t * fw_entry = NULL;
//Firmware Write
typedef enum {
MSC_UPDATE_IDLE,
MSC_UPDATE_STARTING,
MSC_UPDATE_RUNNING,
MSC_UPDATE_END
} msc_update_state_t;
static const esp_partition_t* msc_ota_partition = NULL;
static msc_update_state_t msc_update_state = MSC_UPDATE_IDLE;
static uint16_t msc_update_start_sector = 0;
static uint32_t msc_update_bytes_written = 0;
static fat_dir_entry_t * msc_update_entry = NULL;
static uint32_t get_firmware_size(const esp_partition_t* partition){
esp_image_metadata_t data;
const esp_partition_pos_t running_pos = {
.offset = partition->address,
.size = partition->size,
};
data.start_addr = running_pos.offset;
esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data);
return data.image_len;
}
//Get number of sectors required based on the size of the firmware and OTA partition
static size_t msc_update_get_required_disk_sectors(){
size_t data_sectors = 16;
size_t total_sectors = 0;
msc_run_partition = esp_ota_get_running_partition();
msc_ota_partition = esp_ota_get_next_update_partition(NULL);
if(msc_run_partition){
fw_size = get_firmware_size(msc_run_partition);
data_sectors += FAT_SIZE_TO_SECTORS(fw_size);
log_d("APP size: %u (%u sectors)", fw_size, FAT_SIZE_TO_SECTORS(fw_size));
} else {
log_w("APP partition not found. Reading disabled");
}
if(msc_ota_partition){
data_sectors += FAT_SIZE_TO_SECTORS(msc_ota_partition->size);
log_d("OTA size: %u (%u sectors)", msc_ota_partition->size, FAT_SIZE_TO_SECTORS(msc_ota_partition->size));
} else {
log_w("OTA partition not found. Writing disabled");
}
msc_table_sectors = fat_sectors_per_alloc_table(data_sectors, false);
total_sectors = data_sectors + msc_table_sectors + 2;
if(total_sectors > 0xFF4){
log_d("USING FAT16");
mcs_is_fat16 = true;
total_sectors -= msc_table_sectors;
msc_table_sectors = fat_sectors_per_alloc_table(data_sectors, true);
total_sectors += msc_table_sectors;
} else {
log_d("USING FAT12");
mcs_is_fat16 = false;
}
log_d("FAT data sectors: %u", data_sectors);
log_d("FAT table sectors: %u", msc_table_sectors);
log_d("FAT total sectors: %u (%uKB)", total_sectors, (total_sectors * DISK_SECTOR_SIZE) / 1024);
return total_sectors;
}
//setup the ramdisk and add the firmware download file
static bool msc_update_setup_disk(const char * volume_label, uint32_t serial_number){
msc_total_sectors = msc_update_get_required_disk_sectors();
uint8_t ram_sectors = msc_table_sectors + 2;
msc_ram_disk = (uint8_t*)calloc(ram_sectors, DISK_SECTOR_SIZE);
if(!msc_ram_disk){
log_e("Failed to allocate RAM Disk: %u bytes", ram_sectors * DISK_SECTOR_SIZE);
return false;
}
fw_start_sector = ram_sectors;
fw_end_sector = fw_start_sector;
msc_boot = fat_add_boot_sector(msc_ram_disk, msc_total_sectors, msc_table_sectors, fat_file_system_type(mcs_is_fat16), volume_label, serial_number);
msc_table = fat_add_table(msc_ram_disk, msc_boot, mcs_is_fat16);
//fat_dir_entry_t * label = fat_add_label(msc_ram_disk, volume_label);
if(msc_run_partition){
fw_entry = fat_add_root_file(msc_ram_disk, 0, "FIRMWARE", "BIN", fw_size, 2, mcs_is_fat16);
fw_end_sector = FAT_SIZE_TO_SECTORS(fw_size) + fw_start_sector;
}
return true;
}
static void msc_update_delete_disk(){
fw_entry = NULL;
fw_size = 0;
fw_end_sector = 0;
fw_start_sector = 0;
msc_table = NULL;
msc_boot = NULL;
msc_table_sectors = 0;
msc_total_sectors = 0;
msc_run_partition = NULL;
msc_ota_partition = NULL;
msc_update_state = MSC_UPDATE_IDLE;
msc_update_start_sector = 0;
msc_update_bytes_written = 0;
msc_update_entry = NULL;
free(msc_ram_disk);
msc_ram_disk = NULL;
}
//filter out entries to only include BINs in the root folder
static fat_dir_entry_t * msc_update_get_root_bin_entry(uint8_t index){
fat_dir_entry_t * entry = (fat_dir_entry_t *)(msc_ram_disk + ((msc_boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE) + (index * sizeof(fat_dir_entry_t)));
fat_lfn_entry_t * lfn = (fat_lfn_entry_t*)entry;
//empty entry
if(entry->file_magic == 0){
return NULL;
}
//long file name
if(lfn->attr == 0x0F && lfn->type == 0x00 && lfn->first_cluster == 0x0000){
return NULL;
}
//only files marked as archives
if(entry->file_attr != FAT_FILE_ATTR_ARCHIVE){
return NULL;
}
//deleted
if(entry->file_magic == 0xE5 || entry->file_magic == 0x05){
return NULL;
}
//not bins
if(memcmp("BIN", entry->file_extension, 3)){
return NULL;
}
return entry;
}
//get an empty bin (the host will add an entry for file about to be written with size of zero)
static fat_dir_entry_t * msc_update_find_new_bin(){
for(uint8_t i=16; i;){
i--;
fat_dir_entry_t * entry = msc_update_get_root_bin_entry(i);
if(entry && entry->file_size == 0){
return entry;
}
}
return NULL;
}
//get a bin starting from particular sector
static fat_dir_entry_t * msc_update_find_bin(uint16_t sector){
for(uint8_t i=16; i; ){
i--;
fat_dir_entry_t * entry = msc_update_get_root_bin_entry(i);
if(entry && entry->data_start_sector == (sector - msc_boot->sectors_per_alloc_table)){
return entry;
}
}
return NULL;
}
//write the new data and erase the flash blocks when necessary
static esp_err_t msc_update_write(const esp_partition_t *partition, uint32_t offset, void *data, size_t size){
esp_err_t err = ESP_OK;
if((offset & (SPI_FLASH_SEC_SIZE-1)) == 0){
err = esp_partition_erase_range(partition, offset, SPI_FLASH_SEC_SIZE);
log_v("ERASE[0x%08X]: %s", offset, (err != ESP_OK)?"FAIL":"OK");
if(err != ESP_OK){
return err;
}
}
return esp_partition_write(partition, offset, data, size);
}
//called when error was encountered while updating
static void msc_update_error(){
log_e("UPDATE_ERROR: %u", msc_update_bytes_written);
arduino_firmware_msc_event_data_t p = {0};
p.error.size = msc_update_bytes_written;
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_ERROR_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
msc_update_state = MSC_UPDATE_IDLE;
msc_update_entry = NULL;
msc_update_bytes_written = 0;
msc_update_start_sector = 0;
}
//called when all firmware bytes have been received
static void msc_update_end(){
log_d("UPDATE_END: %u", msc_update_entry->file_size);
msc_update_state = MSC_UPDATE_END;
size_t ota_size = get_firmware_size(msc_ota_partition);
if(ota_size != msc_update_entry->file_size){
log_e("OTA SIZE MISMATCH %u != %u", ota_size, msc_update_entry->file_size);
msc_update_error();
return;
}
if(!ota_size || esp_ota_set_boot_partition(msc_ota_partition) != ESP_OK){
log_e("ENABLING OTA PARTITION FAILED");
msc_update_error();
return;
}
arduino_firmware_msc_event_data_t p = {0};
p.end.size = msc_update_entry->file_size;
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_END_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
}
static int32_t msc_write(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize){
//log_d("lba: %u, offset: %u, bufsize: %u", lba, offset, bufsize);
if(lba < fw_start_sector){
//write to sectors that are in RAM
memcpy(msc_ram_disk + (lba * DISK_SECTOR_SIZE) + offset, buffer, bufsize);
if(msc_ota_partition && lba == (fw_start_sector - 1)){
//monitor the root folder table
if(msc_update_state <= MSC_UPDATE_RUNNING){
fat_dir_entry_t * update_entry = msc_update_find_new_bin();
if(update_entry) {
if(msc_update_entry) {
log_v("REPLACING ENTRY");
} else {
log_v("ASSIGNING ENTRY");
}
if(msc_update_state <= MSC_UPDATE_STARTING){
msc_update_state = MSC_UPDATE_STARTING;
msc_update_bytes_written = 0;
msc_update_start_sector = 0;
}
msc_update_entry = update_entry;
} else if(msc_update_state == MSC_UPDATE_RUNNING){
if(!msc_update_entry && msc_update_start_sector){
msc_update_entry = msc_update_find_bin(msc_update_start_sector);
}
if(msc_update_entry && msc_update_bytes_written >= msc_update_entry->file_size){
msc_update_end();
}
}
}
}
} else if(msc_ota_partition && lba >= msc_update_start_sector){
//handle writes to the region where the new firmware will be uploaded
arduino_firmware_msc_event_data_t p = {0};
if(msc_update_state <= MSC_UPDATE_STARTING && buffer[0] == 0xE9){
msc_update_state = MSC_UPDATE_RUNNING;
msc_update_start_sector = lba;
msc_update_bytes_written = 0;
log_d("UPDATE_START: %u (0x%02X)", lba, lba - msc_boot->sectors_per_alloc_table);
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_START_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
if(msc_update_write(msc_ota_partition, ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) == ESP_OK){
log_v("UPDATE_WRITE: %u %u", ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, bufsize);
msc_update_bytes_written = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset + bufsize;
p.write.offset = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset;
p.write.size = bufsize;
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_WRITE_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
} else {
msc_update_error();
return 0;
}
} else if(msc_update_state == MSC_UPDATE_RUNNING){
if(msc_update_entry && msc_update_entry->file_size && msc_update_bytes_written < msc_update_entry->file_size && (msc_update_bytes_written + bufsize) >= msc_update_entry->file_size){
bufsize = msc_update_entry->file_size - msc_update_bytes_written;
}
if(msc_update_write(msc_ota_partition, ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) == ESP_OK){
log_v("UPDATE_WRITE: %u %u", ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, bufsize);
msc_update_bytes_written = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset + bufsize;
p.write.offset = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset;
p.write.size = bufsize;
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_WRITE_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
if(msc_update_entry && msc_update_entry->file_size && msc_update_bytes_written >= msc_update_entry->file_size){
msc_update_end();
}
} else {
msc_update_error();
return 0;
}
}
}
return bufsize;
}
static int32_t msc_read(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize){
//log_d("lba: %u, offset: %u, bufsize: %u", lba, offset, bufsize);
if(lba < fw_start_sector){
memcpy(buffer, msc_ram_disk + (lba * DISK_SECTOR_SIZE) + offset, bufsize);
} else if(msc_run_partition && lba < fw_end_sector){
//read the currently running firmware
if(esp_partition_read(msc_run_partition, ((lba - fw_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) != ESP_OK){
return 0;
}
} else {
memset(buffer, 0, bufsize);
}
return bufsize;
}
static bool msc_start_stop(uint8_t power_condition, bool start, bool load_eject){
//log_d("power: %u, start: %u, eject: %u", power_condition, start, load_eject);
arduino_firmware_msc_event_data_t p = {0};
p.power.power_condition = power_condition;
p.power.start = start;
p.power.load_eject = load_eject;
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_POWER_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
return true;
}
static volatile TaskHandle_t msc_task_handle = NULL;
static void msc_task(void *pvParameters){
for (;;) {
if(msc_update_state == MSC_UPDATE_END){
delay(100);
esp_restart();
}
delay(100);
}
msc_task_handle = NULL;
vTaskDelete(NULL);
}
FirmwareMSC::FirmwareMSC():msc(){}
FirmwareMSC::~FirmwareMSC(){
end();
}
bool FirmwareMSC::begin(){
if(msc_ram_disk){
return true;
}
if(!msc_update_setup_disk(USB_FW_MSC_VOLUME_NAME, USB_FW_MSC_SERIAL_NUMBER)){
return false;
}
if(!msc_task_handle){
xTaskCreateUniversal(msc_task, "msc_disk", 1024, NULL, 2, (TaskHandle_t*)&msc_task_handle, 0);
if(!msc_task_handle){
msc_update_delete_disk();
return false;
}
}
msc.vendorID(USB_FW_MSC_VENDOR_ID);
msc.productID(USB_FW_MSC_PRODUCT_ID);
msc.productRevision(USB_FW_MSC_PRODUCT_REVISION);
msc.onStartStop(msc_start_stop);
msc.onRead(msc_read);
msc.onWrite(msc_write);
msc.mediaPresent(true);
msc.begin(msc_boot->fat12_sector_num, DISK_SECTOR_SIZE);
return true;
}
void FirmwareMSC::end(){
msc.end();
if(msc_task_handle){
vTaskDelete(msc_task_handle);
msc_task_handle = NULL;
}
msc_update_delete_disk();
}
void FirmwareMSC::onEvent(esp_event_handler_t callback){
onEvent(ARDUINO_FIRMWARE_MSC_ANY_EVENT, callback);
}
void FirmwareMSC::onEvent(arduino_firmware_msc_event_t event, esp_event_handler_t callback){
arduino_usb_event_handler_register_with(ARDUINO_FIRMWARE_MSC_EVENTS, event, callback, this);
}
#if ARDUINO_USB_MSC_ON_BOOT
FirmwareMSC MSC_Update;
#endif
#endif /* CONFIG_USB_MSC_ENABLED */

70
cores/esp32/FirmwareMSC.h Normal file
View File

@ -0,0 +1,70 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdbool.h>
#include "USBMSC.h"
#if CONFIG_TINYUSB_MSC_ENABLED
#include "esp_event.h"
ESP_EVENT_DECLARE_BASE(ARDUINO_FIRMWARE_MSC_EVENTS);
typedef enum {
ARDUINO_FIRMWARE_MSC_ANY_EVENT = ESP_EVENT_ANY_ID,
ARDUINO_FIRMWARE_MSC_START_EVENT = 0,
ARDUINO_FIRMWARE_MSC_WRITE_EVENT,
ARDUINO_FIRMWARE_MSC_END_EVENT,
ARDUINO_FIRMWARE_MSC_ERROR_EVENT,
ARDUINO_FIRMWARE_MSC_POWER_EVENT,
ARDUINO_FIRMWARE_MSC_MAX_EVENT,
} arduino_firmware_msc_event_t;
typedef union {
struct {
size_t offset;
size_t size;
} write;
struct {
uint8_t power_condition;
bool start;
bool load_eject;
} power;
struct {
size_t size;
} end;
struct {
size_t size;
} error;
} arduino_firmware_msc_event_data_t;
class FirmwareMSC {
private:
USBMSC msc;
public:
FirmwareMSC();
~FirmwareMSC();
bool begin();
void end();
void onEvent(esp_event_handler_t callback);
void onEvent(arduino_firmware_msc_event_t event, esp_event_handler_t callback);
};
#if ARDUINO_USB_MSC_ON_BOOT
extern FirmwareMSC MSC_Update;
#endif
#endif /* CONFIG_TINYUSB_MSC_ENABLED */

View File

@ -5,87 +5,137 @@
#include "pins_arduino.h"
#include "HardwareSerial.h"
#include "soc/soc_caps.h"
#ifndef SOC_RX0
#if CONFIG_IDF_TARGET_ESP32
#define SOC_RX0 3
#elif CONFIG_IDF_TARGET_ESP32S2
#define SOC_RX0 44
#elif CONFIG_IDF_TARGET_ESP32C3
#define SOC_RX0 20
#endif
#endif
#ifndef SOC_TX0
#if CONFIG_IDF_TARGET_ESP32
#define SOC_TX0 1
#elif CONFIG_IDF_TARGET_ESP32S2
#define SOC_TX0 43
#elif CONFIG_IDF_TARGET_ESP32C3
#define SOC_TX0 21
#endif
#endif
void serialEvent(void) __attribute__((weak));
void serialEvent(void) {}
#if SOC_UART_NUM > 1
#ifndef RX1
#if CONFIG_IDF_TARGET_ESP32
#define RX1 9
#elif CONFIG_IDF_TARGET_ESP32S2
#define RX1 18
#elif CONFIG_IDF_TARGET_ESP32C3
#define RX1 18
#endif
#endif
#ifndef TX1
#if CONFIG_IDF_TARGET_ESP32
#define TX1 10
#elif CONFIG_IDF_TARGET_ESP32S2
#define TX1 17
#elif CONFIG_IDF_TARGET_ESP32C3
#define TX1 19
#endif
#endif
void serialEvent1(void) __attribute__((weak));
void serialEvent1(void) {}
#endif /* SOC_UART_NUM > 1 */
#if SOC_UART_NUM > 2
#ifndef RX2
#if CONFIG_IDF_TARGET_ESP32
#define RX2 16
#endif
#endif
#ifndef TX2
#if CONFIG_IDF_TARGET_ESP32
#define TX2 17
#endif
#else
#ifndef RX1
#define RX1 18
#endif
#ifndef TX1
#define TX1 17
#endif
#endif
void serialEvent2(void) __attribute__((weak));
void serialEvent2(void) {}
#endif /* SOC_UART_NUM > 2 */
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
HardwareSerial Serial0(0);
#else
HardwareSerial Serial(0);
#endif
#if SOC_UART_NUM > 1
HardwareSerial Serial1(1);
#if CONFIG_IDF_TARGET_ESP32
#endif
#if SOC_UART_NUM > 2
HardwareSerial Serial2(2);
#endif
#endif
void serialEventRun(void)
{
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
if(Serial0.available()) serialEvent();
#else
if(Serial.available()) serialEvent();
#endif
#if SOC_UART_NUM > 1
if(Serial1.available()) serialEvent1();
#endif
#if SOC_UART_NUM > 2
if(Serial2.available()) serialEvent2();
#endif
}
HardwareSerial::HardwareSerial(int uart_nr) : _uart_nr(uart_nr), _uart(NULL) {}
void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms, uint8_t rxfifo_full_thrhd)
{
if(0 > _uart_nr || _uart_nr > 2) {
log_e("Serial number is invalid, please use 0, 1 or 2");
if(0 > _uart_nr || _uart_nr >= SOC_UART_NUM) {
log_e("Serial number is invalid, please use numers from 0 to %u", SOC_UART_NUM - 1);
return;
}
if(_uart) {
end();
// in this case it is a begin() over a previous begin() - maybe to change baud rate
// thus do not disable debug output
end(false);
}
if(_uart_nr == 0 && rxPin < 0 && txPin < 0) {
#if CONFIG_IDF_TARGET_ESP32
rxPin = 3;
txPin = 1;
#elif CONFIG_IDF_TARGET_ESP32S2
rxPin = 44;
txPin = 43;
#elif CONFIG_IDF_TARGET_ESP32C3
rxPin = 20;
txPin = 21;
#endif
rxPin = SOC_RX0;
txPin = SOC_TX0;
}
#if SOC_UART_NUM > 1
if(_uart_nr == 1 && rxPin < 0 && txPin < 0) {
rxPin = RX1;
txPin = TX1;
}
#if CONFIG_IDF_TARGET_ESP32
#endif
#if SOC_UART_NUM > 2
if(_uart_nr == 2 && rxPin < 0 && txPin < 0) {
rxPin = RX2;
txPin = TX2;
}
#endif
_uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, 256, invert, rxfifo_full_thrhd);
_tx_pin = txPin;
_rx_pin = rxPin;
_uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, 256, invert, rxfifo_full_thrhd);
if (!baud) {
// using baud rate as zero, forces it to try to detect the current baud rate in place
uartStartDetectBaudrate(_uart);
time_t startMillis = millis();
unsigned long detectedBaudRate = 0;
@ -93,7 +143,7 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
yield();
}
end();
end(false);
if(detectedBaudRate) {
delay(100); // Give some time...
@ -101,8 +151,6 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
} else {
log_e("Could not detect baudrate. Serial data at the port must be present within the timeout for detection to be possible");
_uart = NULL;
_tx_pin = 255;
_rx_pin = 255;
}
}
}
@ -112,21 +160,16 @@ void HardwareSerial::updateBaudRate(unsigned long baud)
uartSetBaudRate(_uart, baud);
}
void HardwareSerial::end()
void HardwareSerial::end(bool turnOffDebug)
{
if(uartGetDebug() == _uart_nr) {
if(turnOffDebug && uartGetDebug() == _uart_nr) {
uartSetDebug(0);
}
delay(10);
log_v("pins %d %d",_tx_pin, _rx_pin);
uartEnd(_uart, _tx_pin, _rx_pin);
uartEnd(_uart);
_uart = 0;
}
size_t HardwareSerial::setRxBufferSize(size_t new_size) {
return uartResizeRxBuffer(_uart, new_size);
}
void HardwareSerial::setDebugOutput(bool en)
{
if(_uart == 0) {
@ -212,10 +255,16 @@ uint32_t HardwareSerial::baudRate()
}
HardwareSerial::operator bool() const
{
return true;
return uartIsDriverInstalled(_uart);
}
void HardwareSerial::setRxInvert(bool invert)
{
uartSetRxInvert(_uart, invert);
}
void HardwareSerial::setPins(uint8_t rxPin, uint8_t txPin)
{
uartSetPins(_uart, rxPin, txPin);
}

View File

@ -49,6 +49,7 @@
#include "Stream.h"
#include "esp32-hal.h"
#include "soc/soc_caps.h"
class HardwareSerial: public Stream
{
@ -56,7 +57,7 @@ public:
HardwareSerial(int uart_nr);
void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 112);
void end();
void end(bool turnOffDebug = true);
void updateBaudRate(unsigned long baud);
int available(void);
int availableForWrite(void);
@ -98,33 +99,33 @@ public:
uint32_t baudRate();
operator bool() const;
size_t setRxBufferSize(size_t);
void setDebugOutput(bool);
void setRxInvert(bool);
void setPins(uint8_t rxPin, uint8_t txPin);
protected:
int _uart_nr;
uart_t* _uart;
uint8_t _tx_pin;
uint8_t _rx_pin;
};
extern void serialEventRun(void) __attribute__((weak));
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
#ifndef ARDUINO_SERIAL_PORT
#define ARDUINO_SERIAL_PORT 0
#ifndef ARDUINO_USB_CDC_ON_BOOT
#define ARDUINO_USB_CDC_ON_BOOT 0
#endif
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
#include "USB.h"
#include "USBCDC.h"
extern HardwareSerial Serial0;
#else
extern HardwareSerial Serial;
#endif
#if SOC_UART_NUM > 1
extern HardwareSerial Serial1;
#if CONFIG_IDF_TARGET_ESP32
#endif
#if SOC_UART_NUM > 2
extern HardwareSerial Serial2;
#endif
#endif

View File

@ -11,10 +11,13 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "USB.h"
#if CONFIG_TINYUSB_ENABLED
#include "esp32-hal.h"
#include "esp32-hal-tinyusb.h"
#include "USB.h"
#if CONFIG_TINYUSB_ENABLED
#include "common/tusb_common.h"
#ifndef USB_VID
#define USB_VID USB_ESPRESSIF_VID
@ -31,14 +34,16 @@
#ifndef USB_SERIAL
#define USB_SERIAL "0"
#endif
#ifndef USB_WEBUSB_ENABLED
#define USB_WEBUSB_ENABLED false
#endif
#ifndef USB_WEBUSB_URL
#define USB_WEBUSB_URL "https://espressif.github.io/arduino-esp32/webusb.html"
#endif
#if CFG_TUD_DFU_RUNTIME
static uint16_t load_dfu_descriptor(uint8_t * dst, uint8_t * itf)
{
#define DFU_ATTR_CAN_DOWNLOAD 1
#define DFU_ATTR_CAN_UPLOAD 2
#define DFU_ATTR_MANIFESTATION_TOLERANT 4
#define DFU_ATTR_WILL_DETACH 8
#define DFU_ATTRS (DFU_ATTR_CAN_DOWNLOAD | DFU_ATTR_CAN_UPLOAD | DFU_ATTR_MANIFESTATION_TOLERANT)
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB DFU_RT");
@ -120,8 +125,8 @@ ESPUSB::ESPUSB(size_t task_stack_size, uint8_t event_task_priority)
,usb_protocol(MISC_PROTOCOL_IAD)
,usb_attributes(TUSB_DESC_CONFIG_ATT_SELF_POWERED)
,usb_power_ma(500)
,webusb_enabled(false)
,webusb_url("https://espressif.github.io/arduino-esp32/webusb.html")
,webusb_enabled(USB_WEBUSB_ENABLED)
,webusb_url(USB_WEBUSB_URL)
,_started(false)
,_task_stack_size(task_stack_size)
,_event_task_priority(event_task_priority)

View File

@ -14,12 +14,13 @@
#pragma once
#include "sdkconfig.h"
#if CONFIG_TINYUSB_ENABLED
#include "Arduino.h"
#include "esp_event.h"
#include "USBCDC.h"
#include "esp_event.h"
#define ARDUINO_USB_ON_BOOT (ARDUINO_USB_CDC_ON_BOOT|ARDUINO_USB_MSC_ON_BOOT|ARDUINO_USB_DFU_ON_BOOT)
ESP_EVENT_DECLARE_BASE(ARDUINO_USB_EVENTS);

View File

@ -11,24 +11,22 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp32-hal.h"
#include "esp32-hal-tinyusb.h"
#include "USB.h"
#if CONFIG_TINYUSB_CDC_ENABLED
#include "USBCDC.h"
#if CONFIG_TINYUSB_ENABLED
#include "esp32-hal-tinyusb.h"
ESP_EVENT_DEFINE_BASE(ARDUINO_USB_CDC_EVENTS);
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
#if CFG_TUD_CDC
#define MAX_USB_CDC_DEVICES 2
USBCDC * devices[MAX_USB_CDC_DEVICES] = {NULL, NULL};
static uint16_t load_cdc_descriptor(uint8_t * dst, uint8_t * itf)
{
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB CDC");
// Interface number, string index, attributes, detach timeout, transfer size */
uint8_t descriptor[TUD_CDC_DESC_LEN] = {
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(*itf, str_index, 0x85, 64, 0x03, 0x84, 64)
@ -41,7 +39,6 @@ static uint16_t load_cdc_descriptor(uint8_t * dst, uint8_t * itf)
// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
{
//isr_log_v("itf: %u, dtr: %u, rts: %u", itf, dtr, rts);
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
devices[itf]->_onLineState(dtr, rts);
}
@ -50,7 +47,6 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
// Invoked when line coding is change via SET_LINE_CODING
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding)
{
//isr_log_v("itf: %u, bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u", itf, p_line_coding->bit_rate, p_line_coding->data_bits, p_line_coding->stop_bits, p_line_coding->parity);
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
devices[itf]->_onLineCoding(p_line_coding->bit_rate, p_line_coding->stop_bits, p_line_coding->parity, p_line_coding->data_bits);
}
@ -59,7 +55,6 @@ void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding)
// Invoked when received new data
void tud_cdc_rx_cb(uint8_t itf)
{
//isr_log_v("itf: %u", itf);
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
devices[itf]->_onRX();
}
@ -72,15 +67,14 @@ void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms){
// Invoked when space becomes available in TX buffer
void tud_cdc_tx_complete_cb(uint8_t itf){
//isr_log_v("itf: %u", itf);
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL && devices[itf]->tx_sem != NULL){
xSemaphoreGive(devices[itf]->tx_sem);
devices[itf]->_onTX();
}
}
static size_t tinyusb_cdc_write(uint8_t itf, const uint8_t *buffer, size_t size){
if(itf >= MAX_USB_CDC_DEVICES){
if(itf >= MAX_USB_CDC_DEVICES || devices[itf] == NULL || devices[itf]->tx_sem == NULL){
return 0;
}
if(!tud_cdc_n_connected(itf)){
@ -90,8 +84,15 @@ static size_t tinyusb_cdc_write(uint8_t itf, const uint8_t *buffer, size_t size)
while(tosend){
uint32_t space = tud_cdc_n_write_available(itf);
if(!space){
delay(1);
continue;
//make sure that we do not get previous semaphore
xSemaphoreTake(devices[itf]->tx_sem, 0);
//wait for tx_complete
if(xSemaphoreTake(devices[itf]->tx_sem, 200 / portTICK_PERIOD_MS) == pdTRUE){
space = tud_cdc_n_write_available(itf);
}
if(!space){
return sofar;
}
}
if(tosend < space){
space = tosend;
@ -103,7 +104,7 @@ static size_t tinyusb_cdc_write(uint8_t itf, const uint8_t *buffer, size_t size)
sofar += sent;
tosend -= sent;
tud_cdc_n_write_flush(itf);
xSemaphoreTake(devices[itf]->tx_sem, portMAX_DELAY);
//xSemaphoreTake(devices[itf]->tx_sem, portMAX_DELAY);
}
return sofar;
}
@ -113,21 +114,21 @@ static void ARDUINO_ISR_ATTR cdc0_write_char(char c)
tinyusb_cdc_write(0, (const uint8_t *)&c, 1);
}
//void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
static void usb_unplugged_cb(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
((USBCDC*)arg)->_onUnplugged();
}
USBCDC::USBCDC(uint8_t itfn) : itf(itfn), bit_rate(0), stop_bits(0), parity(0), data_bits(0), dtr(false), rts(false), connected(false), reboot_enable(true), rx_queue(NULL) {
USBCDC::USBCDC(uint8_t itfn) : itf(itfn), bit_rate(0), stop_bits(0), parity(0), data_bits(0), dtr(false), rts(false), connected(false), reboot_enable(true), rx_queue(NULL), tx_sem(NULL) {
tinyusb_enable_interface(USB_INTERFACE_CDC, TUD_CDC_DESC_LEN, load_cdc_descriptor);
if(itf < MAX_USB_CDC_DEVICES){
devices[itf] = this;
tx_sem = NULL;
arduino_usb_event_handler_register_with(ARDUINO_USB_EVENTS, ARDUINO_USB_STOPPED_EVENT, usb_unplugged_cb, this);
}
}
USBCDC::~USBCDC(){
end();
}
void USBCDC::onEvent(esp_event_handler_t callback){
onEvent(ARDUINO_USB_CDC_ANY_EVENT, callback);
}
@ -137,6 +138,10 @@ void USBCDC::onEvent(arduino_usb_cdc_event_t event, esp_event_handler_t callback
size_t USBCDC::setRxBufferSize(size_t rx_queue_len){
if(rx_queue){
if(!rx_queue_len){
vQueueDelete(rx_queue);
rx_queue = NULL;
}
return 0;
}
rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t));
@ -148,15 +153,19 @@ size_t USBCDC::setRxBufferSize(size_t rx_queue_len){
void USBCDC::begin(unsigned long baud)
{
setRxBufferSize(256);//default if not preset
if(tx_sem == NULL){
tx_sem = xSemaphoreCreateBinary();
xSemaphoreTake(tx_sem, 0);
}
setRxBufferSize(256);//default if not preset
devices[itf] = this;
}
void USBCDC::end()
{
connected = false;
devices[itf] = NULL;
setRxBufferSize(0);
if (tx_sem != NULL) {
vSemaphoreDelete(tx_sem);
tx_sem = NULL;
@ -176,6 +185,11 @@ void USBCDC::_onUnplugged(void){
enum { CDC_LINE_IDLE, CDC_LINE_1, CDC_LINE_2, CDC_LINE_3 };
void USBCDC::_onLineState(bool _dtr, bool _rts){
static uint8_t lineState = CDC_LINE_IDLE;
if(dtr == _dtr && rts == _rts){
return; // Skip duplicate events
}
dtr = _dtr;
rts = _rts;
@ -183,6 +197,11 @@ void USBCDC::_onLineState(bool _dtr, bool _rts){
if(!dtr && rts){
if(lineState == CDC_LINE_IDLE){
lineState++;
if(connected){
connected = false;
arduino_usb_cdc_event_data_t p = {0};
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_DISCONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
}
} else {
lineState = CDC_LINE_IDLE;
}
@ -212,7 +231,7 @@ void USBCDC::_onLineState(bool _dtr, bool _rts){
connected = true;
arduino_usb_cdc_event_data_t p = {0};
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_CONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
} else if(!dtr && !rts && connected){
} else if(!dtr && connected){
connected = false;
arduino_usb_cdc_event_data_t p = {0};
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_DISCONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
@ -228,7 +247,7 @@ void USBCDC::_onLineState(bool _dtr, bool _rts){
void USBCDC::_onLineCoding(uint32_t _bit_rate, uint8_t _stop_bits, uint8_t _parity, uint8_t _data_bits){
if(bit_rate != _bit_rate || data_bits != _data_bits || stop_bits != _stop_bits || parity != _parity){
// ArduinoIDE sends LineCoding with 1200bps baud to reset the device
if(_bit_rate == 1200){
if(reboot_enable && _bit_rate == 1200){
usb_persist_restart(RESTART_BOOTLOADER);
} else {
bit_rate = _bit_rate;
@ -317,7 +336,7 @@ size_t USBCDC::read(uint8_t *buffer, size_t size)
void USBCDC::flush(void)
{
if(itf >= MAX_USB_CDC_DEVICES){
if(itf >= MAX_USB_CDC_DEVICES || tx_sem == NULL){
return;
}
tud_cdc_n_write_flush(itf);
@ -325,7 +344,7 @@ void USBCDC::flush(void)
int USBCDC::availableForWrite(void)
{
if(itf >= MAX_USB_CDC_DEVICES){
if(itf >= MAX_USB_CDC_DEVICES || tx_sem == NULL){
return -1;
}
return tud_cdc_n_write_available(itf);
@ -364,10 +383,8 @@ USBCDC::operator bool() const
return connected;
}
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
USBCDC Serial(0);
#endif
#endif /* CONFIG_TINYUSB_CDC_ENABLED */
#endif /* CONFIG_TINYUSB_ENABLED */

View File

@ -13,13 +13,12 @@
// limitations under the License.
#pragma once
#include <inttypes.h>
#include "Stream.h"
#include "esp32-hal.h"
#include "sdkconfig.h"
#if CONFIG_TINYUSB_CDC_ENABLED
#include <inttypes.h>
#include "esp_event.h"
#include "Stream.h"
ESP_EVENT_DECLARE_BASE(ARDUINO_USB_CDC_EVENTS);
@ -54,6 +53,7 @@ class USBCDC: public Stream
{
public:
USBCDC(uint8_t itf=0);
~USBCDC();
void onEvent(esp_event_handler_t callback);
void onEvent(arduino_usb_cdc_event_t event, esp_event_handler_t callback);
@ -129,7 +129,7 @@ protected:
};
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
extern USBCDC Serial;
#endif

260
cores/esp32/USBMSC.cpp Normal file
View File

@ -0,0 +1,260 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "USBMSC.h"
#if CONFIG_TINYUSB_MSC_ENABLED
#include "esp32-hal-tinyusb.h"
extern "C" uint16_t tusb_msc_load_descriptor(uint8_t * dst, uint8_t * itf)
{
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB MSC");
uint8_t ep_num = tinyusb_get_free_duplex_endpoint();
TU_VERIFY (ep_num != 0);
uint8_t descriptor[TUD_MSC_DESC_LEN] = {
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(*itf, str_index, ep_num, (uint8_t)(0x80 | ep_num), 64)
};
*itf+=1;
memcpy(dst, descriptor, TUD_MSC_DESC_LEN);
return TUD_MSC_DESC_LEN;
}
typedef struct {
bool media_present;
uint8_t vendor_id[8];
uint8_t product_id[16];
uint8_t product_rev[4];
uint16_t block_size;
uint32_t block_count;
bool (*start_stop)(uint8_t power_condition, bool start, bool load_eject);
int32_t (*read)(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
int32_t (*write)(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
} msc_lun_t;
static const uint8_t MSC_MAX_LUN = 3;
static uint8_t MSC_ACTIVE_LUN = 0;
static msc_lun_t msc_luns[MSC_MAX_LUN];
static void cplstr(void *dst, const void * src, size_t max_len){
if(!src || !dst || !max_len){
return;
}
size_t l = strlen((const char *)src);
if(l > max_len){
l = max_len;
}
memcpy(dst, src, l);
}
// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation
uint8_t tud_msc_get_maxlun_cb(void)
{
log_v("%u", MSC_ACTIVE_LUN);
return MSC_ACTIVE_LUN;
}
// Invoked when received SCSI_CMD_INQUIRY
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
{
log_v("[%u]", lun);
cplstr(vendor_id , msc_luns[lun].vendor_id, 8);
cplstr(product_id , msc_luns[lun].product_id, 16);
cplstr(product_rev, msc_luns[lun].product_rev, 4);
}
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun)
{
log_v("[%u]: %u", lun, msc_luns[lun].media_present);
return msc_luns[lun].media_present; // RAM disk is always ready
}
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
// Application update block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
{
log_v("[%u]", lun);
if(!msc_luns[lun].media_present){
*block_count = 0;
*block_size = 0;
return;
}
*block_count = msc_luns[lun].block_count;
*block_size = msc_luns[lun].block_size;
}
// Invoked when received Start Stop Unit command
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
{
log_v("[%u] power: %u, start: %u, eject: %u", lun, power_condition, start, load_eject);
if(msc_luns[lun].start_stop){
return msc_luns[lun].start_stop(power_condition, start, load_eject);
}
return true;
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
{
log_v("[%u], lba: %u, offset: %u, bufsize: %u", lun, lba, offset, bufsize);
if(!msc_luns[lun].media_present){
return 0;
}
if(msc_luns[lun].read){
return msc_luns[lun].read(lba, offset, buffer, bufsize);
}
return 0;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and return number of written bytes
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
{
log_v("[%u], lba: %u, offset: %u, bufsize: %u", lun, lba, offset, bufsize);
if(!msc_luns[lun].media_present){
return 0;
}
if(msc_luns[lun].write){
return msc_luns[lun].write(lba, offset, buffer, bufsize);
}
return 0;
}
// Callback invoked when received an SCSI command not in built-in list below
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
// - READ10 and WRITE10 has their own callbacks
int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize)
{
// read10 & write10 has their own callback and MUST not be handled here
log_v("[%u] cmd: %u, bufsize: %u", lun, scsi_cmd[0], bufsize);
void const* response = NULL;
uint16_t resplen = 0;
// most scsi handled is input
bool in_xfer = true;
if(!msc_luns[lun].media_present){
return -1;
}
switch (scsi_cmd[0]) {
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
// Host is about to read/write etc ... better not to disconnect disk
resplen = 0;
break;
default:
// Set Sense = Invalid Command Operation
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
// negative means error -> tinyusb could stall and/or response with failed status
resplen = -1;
break;
}
// return resplen must not larger than bufsize
if (resplen > bufsize) resplen = bufsize;
if (response && (resplen > 0)) {
if (in_xfer) {
memcpy(buffer, response, resplen);
} else {
// SCSI output
}
}
return resplen;
}
USBMSC::USBMSC(){
if(MSC_ACTIVE_LUN < MSC_MAX_LUN){
_lun = MSC_ACTIVE_LUN;
MSC_ACTIVE_LUN++;
msc_luns[_lun].media_present = false;
msc_luns[_lun].vendor_id[0] = 0;
msc_luns[_lun].product_id[0] = 0;
msc_luns[_lun].product_rev[0] = 0;
msc_luns[_lun].block_size = 0;
msc_luns[_lun].block_count = 0;
msc_luns[_lun].start_stop = NULL;
msc_luns[_lun].read = NULL;
msc_luns[_lun].write = NULL;
}
if(_lun == 0){
tinyusb_enable_interface(USB_INTERFACE_MSC, TUD_MSC_DESC_LEN, tusb_msc_load_descriptor);
}
}
USBMSC::~USBMSC(){
end();
}
bool USBMSC::begin(uint32_t block_count, uint16_t block_size){
msc_luns[_lun].block_size = block_size;
msc_luns[_lun].block_count = block_count;
if(!msc_luns[_lun].block_size || !msc_luns[_lun].block_count || !msc_luns[_lun].read || !msc_luns[_lun].write){
return false;
}
return true;
}
void USBMSC::end(){
msc_luns[_lun].media_present = false;
msc_luns[_lun].vendor_id[0] = 0;
msc_luns[_lun].product_id[0] = 0;
msc_luns[_lun].product_rev[0] = 0;
msc_luns[_lun].block_size = 0;
msc_luns[_lun].block_count = 0;
msc_luns[_lun].start_stop = NULL;
msc_luns[_lun].read = NULL;
msc_luns[_lun].write = NULL;
}
void USBMSC::vendorID(const char * vid){
cplstr(msc_luns[_lun].vendor_id, vid, 8);
}
void USBMSC::productID(const char * pid){
cplstr(msc_luns[_lun].product_id, pid, 16);
}
void USBMSC::productRevision(const char * rev){
cplstr(msc_luns[_lun].product_rev, rev, 4);
}
void USBMSC::onStartStop(msc_start_stop_cb cb){
msc_luns[_lun].start_stop = cb;
}
void USBMSC::onRead(msc_read_cb cb){
msc_luns[_lun].read = cb;
}
void USBMSC::onWrite(msc_write_cb cb){
msc_luns[_lun].write = cb;
}
void USBMSC::mediaPresent(bool media_present){
msc_luns[_lun].media_present = media_present;
}
#endif /* CONFIG_TINYUSB_MSC_ENABLED */

51
cores/esp32/USBMSC.h Normal file
View File

@ -0,0 +1,51 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "sdkconfig.h"
#if CONFIG_TINYUSB_MSC_ENABLED
// Invoked when received Start Stop Unit command
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
typedef bool (*msc_start_stop_cb)(uint8_t power_condition, bool start, bool load_eject);
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
typedef int32_t (*msc_read_cb)(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
// Process data in buffer to disk's storage and return number of written bytes
typedef int32_t (*msc_write_cb)(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
class USBMSC
{
public:
USBMSC();
~USBMSC();
bool begin(uint32_t block_count, uint16_t block_size);
void end();
void vendorID(const char * vid);//max 8 chars
void productID(const char * pid);//max 16 chars
void productRevision(const char * ver);//max 4 chars
void mediaPresent(bool media_present);
void onStartStop(msc_start_stop_cb cb);
void onRead(msc_read_cb cb);
void onWrite(msc_write_cb cb);
private:
uint8_t _lun;
};
#endif /* CONFIG_TINYUSB_MSC_ENABLED */

View File

@ -104,8 +104,10 @@ void __analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation)
adc1_config_channel_atten(channel, attenuation);
}
__analogInit();
if((__pin_attenuation[pin] != ADC_ATTENDB_MAX) || (attenuation != __analogAttenuation)){
__pin_attenuation[pin] = attenuation;
}
}
bool __adcAttachPin(uint8_t pin){
int8_t channel = digitalPinToAnalogChannel(pin);
@ -113,6 +115,7 @@ bool __adcAttachPin(uint8_t pin){
log_e("Pin %u is not ADC pin!", pin);
return false;
}
__analogInit();
int8_t pad = digitalPinToTouchChannel(pin);
if(pad >= 0){
#if CONFIG_IDF_TARGET_ESP32

View File

@ -1848,7 +1848,9 @@ i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size,
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
if(size){
i2c_master_write(cmd, buff, size, ACK_CHECK_EN);
}
//if send stop?
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(i2c->num, cmd, timeOutMillis / portTICK_RATE_MS);

View File

@ -115,6 +115,10 @@ static void _ledcSetupTimer(uint8_t chan, uint32_t div_num, uint8_t bit_num, boo
uint8_t group=(chan/8), timer=((chan/2)%4);
static bool tHasStarted = false;
static uint16_t _activeChannels = 0;
#if CONFIG_IDF_TARGET_ESP32S2
// ESP32-S2 TRM v1.0 on Page 789 -> BIT LEDC_TICK_SEL_TIMERx is 0 for LEDC_PWM_CLK and 1 for REF_TICK
apb_clk = 0;
#endif
if(!tHasStarted) {
tHasStarted = true;
periph_module_enable(PERIPH_LEDC_MODULE);

View File

@ -66,6 +66,8 @@ extern "C"
#define ARDUHAL_LOG_COLOR_I ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_GREEN)
#define ARDUHAL_LOG_COLOR_D ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_CYAN)
#define ARDUHAL_LOG_COLOR_V ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_GRAY)
#define ARDUHAL_LOG_COLOR_PRINT(letter) log_printf(ARDUHAL_LOG_COLOR_ ## letter)
#define ARDUHAL_LOG_COLOR_PRINT_END log_printf(ARDUHAL_LOG_RESET_COLOR)
#else
#define ARDUHAL_LOG_COLOR_E
#define ARDUHAL_LOG_COLOR_W
@ -73,12 +75,15 @@ extern "C"
#define ARDUHAL_LOG_COLOR_D
#define ARDUHAL_LOG_COLOR_V
#define ARDUHAL_LOG_RESET_COLOR
#define ARDUHAL_LOG_COLOR_PRINT(letter)
#define ARDUHAL_LOG_COLOR_PRINT_END
#endif
const char * pathToFileName(const char * path);
int log_printf(const char *fmt, ...);
void log_print_buf(const uint8_t *b, size_t len);
#define ARDUHAL_SHORT_LOG_FORMAT(letter, format) ARDUHAL_LOG_COLOR_ ## letter format ARDUHAL_LOG_RESET_COLOR "\r\n"
#define ARDUHAL_LOG_FORMAT(letter, format) ARDUHAL_LOG_COLOR_ ## letter "[%6u][" #letter "][%s:%u] %s(): " format ARDUHAL_LOG_RESET_COLOR "\r\n", (unsigned long) (esp_timer_get_time() / 1000ULL), pathToFileName(__FILE__), __LINE__, __FUNCTION__
@ -87,78 +92,96 @@ int log_printf(const char *fmt, ...);
#ifndef USE_ESP_IDF_LOG
#define log_v(format, ...) log_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__)
#define isr_log_v(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__)
#define log_buf_v(b,l) do{ARDUHAL_LOG_COLOR_PRINT(V);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0)
#else
#define log_v(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_VERBOSE, TAG, format, ##__VA_ARGS__);}while(0)
#define isr_log_v(format, ...) do {ets_printf(LOG_FORMAT(V, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0)
#define log_buf_v(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_VERBOSE);}while(0)
#endif
#else
#define log_v(format, ...)
#define isr_log_v(format, ...)
#define log_buf_v(b,l)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
#ifndef USE_ESP_IDF_LOG
#define log_d(format, ...) log_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__)
#define isr_log_d(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__)
#define log_buf_d(b,l) do{ARDUHAL_LOG_COLOR_PRINT(D);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0)
#else
#define log_d(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_DEBUG, TAG, format, ##__VA_ARGS__);}while(0)
#define isr_log_d(format, ...) do {ets_printf(LOG_FORMAT(D, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0)
#define log_buf_d(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_DEBUG);}while(0)
#endif
#else
#define log_d(format, ...)
#define isr_log_d(format, ...)
#define log_buf_d(b,l)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
#ifndef USE_ESP_IDF_LOG
#define log_i(format, ...) log_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__)
#define isr_log_i(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__)
#define log_buf_i(b,l) do{ARDUHAL_LOG_COLOR_PRINT(I);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0)
#else
#define log_i(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, TAG, format, ##__VA_ARGS__);}while(0)
#define isr_log_i(format, ...) do {ets_printf(LOG_FORMAT(I, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0)
#define log_buf_i(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_INFO);}while(0)
#endif
#else
#define log_i(format, ...)
#define isr_log_i(format, ...)
#define log_buf_i(b,l)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_WARN
#ifndef USE_ESP_IDF_LOG
#define log_w(format, ...) log_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__)
#define isr_log_w(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__)
#define log_buf_w(b,l) do{ARDUHAL_LOG_COLOR_PRINT(W);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0)
#else
#define log_w(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_WARN, TAG, format, ##__VA_ARGS__);}while(0)
#define isr_log_w(format, ...) do {ets_printf(LOG_FORMAT(W, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0)
#define log_buf_w(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_WARN);}while(0)
#endif
#else
#define log_w(format, ...)
#define isr_log_w(format, ...)
#define log_buf_w(b,l)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
#ifndef USE_ESP_IDF_LOG
#define log_e(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
#define isr_log_e(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
#define log_buf_e(b,l) do{ARDUHAL_LOG_COLOR_PRINT(E);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0)
#else
#define log_e(format, ...) do {log_to_esp(TAG, ESP_LOG_ERROR, format, ##__VA_ARGS__);}while(0)
#define isr_log_e(format, ...) do {ets_printf(LOG_FORMAT(E, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0)
#define log_buf_e(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_ERROR);}while(0)
#endif
#else
#define log_e(format, ...)
#define isr_log_e(format, ...)
#define log_buf_e(b,l)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_NONE
#ifndef USE_ESP_IDF_LOG
#define log_n(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
#define isr_log_n(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
#define log_buf_n(b,l) do{ARDUHAL_LOG_COLOR_PRINT(E);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0)
#else
#define log_n(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, TAG, format, ##__VA_ARGS__);}while(0)
#define isr_log_n(format, ...) do {ets_printf(LOG_FORMAT(E, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0)
#define log_buf_n(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_ERROR);}while(0)
#endif
#else
#define log_n(format, ...)
#define isr_log_n(format, ...)
#define log_buf_n(b,l)
#endif
#include "esp_log.h"

View File

@ -44,6 +44,8 @@ static void setTimeZone(long offset, int daylight)
/*
* configTime
* Source: https://github.com/esp8266/Arduino/blob/master/cores/esp8266/time.c
* Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
* see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
* */
void configTime(long gmtOffset_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3)
{
@ -63,6 +65,8 @@ void configTime(long gmtOffset_sec, int daylightOffset_sec, const char* server1,
/*
* configTzTime
* sntp setup using TZ environment variable
* Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
* see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
* */
void configTzTime(const char* tz, const char* server1, const char* server2, const char* server3)
{

View File

@ -119,8 +119,9 @@ void ARDUINO_ISR_ATTR __timerISR(void * arg){
}
}
uint64_t timerRead(hw_timer_t *timer){
uint64_t inline timerRead(hw_timer_t *timer){
timer->dev->update = 1;
while (timer->dev->update) {};
uint64_t h = timer->dev->cnt_high;
uint64_t l = timer->dev->cnt_low;
return (h << 32) | l;

View File

@ -72,20 +72,15 @@ static void configure_pins(usb_hal_context_t *usb)
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config)
{
log_i("Driver installation...");
// Hal init
usb_hal_context_t hal = {
.use_external_phy = config->external_phy
};
usb_hal_init(&hal);
configure_pins(&hal);
if (!tusb_init()) {
log_e("Can't initialize the TinyUSB stack.");
return ESP_FAIL;
}
log_i("Driver installed");
return ESP_OK;
}
@ -106,6 +101,7 @@ static tusb_str_t WEBUSB_URL = "";
static tusb_str_t USB_DEVICE_PRODUCT = "";
static tusb_str_t USB_DEVICE_MANUFACTURER = "";
static tusb_str_t USB_DEVICE_SERIAL = "";
static tusb_str_t USB_DEVICE_LANGUAGE = "\x09\x04";//English (0x0409)
static uint8_t USB_DEVICE_ATTRIBUTES = 0;
static uint16_t USB_DEVICE_POWER = 0;
@ -140,7 +136,7 @@ static tusb_desc_device_t tinyusb_device_descriptor = {
static uint32_t tinyusb_string_descriptor_len = 4;
static char * tinyusb_string_descriptor[MAX_STRING_DESCRIPTORS] = {
// array of pointer to string descriptors
"\x09\x04", // 0: is supported language is English (0x0409)
USB_DEVICE_LANGUAGE, // 0: is supported language
USB_DEVICE_MANUFACTURER,// 1: Manufacturer
USB_DEVICE_PRODUCT, // 2: Product
USB_DEVICE_SERIAL, // 3: Serials, should use chip ID
@ -263,7 +259,7 @@ static tinyusb_endpoints_usage_t tinyusb_endpoints;
/**
* @brief Invoked when received GET CONFIGURATION DESCRIPTOR.
*/
uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
__attribute__ ((weak)) uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
{
//log_d("%u", index);
return tinyusb_config_descriptor;
@ -272,7 +268,7 @@ uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
/**
* @brief Invoked when received GET DEVICE DESCRIPTOR.
*/
uint8_t const *tud_descriptor_device_cb(void)
__attribute__ ((weak)) uint8_t const *tud_descriptor_device_cb(void)
{
//log_d("");
return (uint8_t const *)&tinyusb_device_descriptor;
@ -281,7 +277,7 @@ uint8_t const *tud_descriptor_device_cb(void)
/**
* @brief Invoked when received GET STRING DESCRIPTOR request.
*/
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
__attribute__ ((weak)) uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
//log_d("%u (0x%x)", index, langid);
static uint16_t _desc_str[127];
@ -428,12 +424,6 @@ static bool tinyusb_load_enabled_interfaces(){
log_e("Descriptor Load Failed");
return false;
} else {
if(i == USB_INTERFACE_CDC){
if(!tinyusb_reserve_out_endpoint(3) ||!tinyusb_reserve_in_endpoint(4) || !tinyusb_reserve_in_endpoint(5)){
log_e("CDC Reserve Endpoints Failed");
return false;
}
}
dst += len;
}
}
@ -509,6 +499,7 @@ static void tinyusb_apply_device_config(tinyusb_device_config_t *config){
&& (config->usb_class != TUSB_CLASS_CDC)
){
config->usb_class = TUSB_CLASS_CDC;
config->usb_protocol = 0x00;
}
WEBUSB_ENABLED = config->webusb_enabled;
@ -563,31 +554,43 @@ static void usb_device_task(void *param) {
/*
* PUBLIC API
* */
static const char *tinyusb_interface_names[USB_INTERFACE_MAX] = {"MSC", "DFU", "HID", "VENDOR", "CDC", "MIDI", "CUSTOM"};
static bool tinyusb_is_initialized = false;
esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb)
{
if((interface >= USB_INTERFACE_MAX) || (tinyusb_loaded_interfaces_mask & (1U << interface))){
log_e("Interface %u not enabled", interface);
if(tinyusb_is_initialized){
log_e("TinyUSB has already started! Interface %s not enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]);
return ESP_FAIL;
}
if((interface >= USB_INTERFACE_MAX) || (tinyusb_loaded_interfaces_mask & (1U << interface))){
log_e("Interface %s invalid or already enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]);
return ESP_FAIL;
}
if(interface == USB_INTERFACE_CDC){
if(!tinyusb_reserve_out_endpoint(3) ||!tinyusb_reserve_in_endpoint(4) || !tinyusb_reserve_in_endpoint(5)){
log_e("CDC Reserve Endpoints Failed");
return ESP_FAIL;
}
}
tinyusb_loaded_interfaces_mask |= (1U << interface);
tinyusb_config_descriptor_len += descriptor_len;
tinyusb_loaded_interfaces_callbacks[interface] = cb;
log_d("Interface %u enabled", interface);
log_d("Interface %s enabled", tinyusb_interface_names[interface]);
return ESP_OK;
}
esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
static bool initialized = false;
if(initialized){
if(tinyusb_is_initialized){
return ESP_OK;
}
initialized = true;
tinyusb_is_initialized = true;
tinyusb_endpoints.val = 0;
//tinyusb_endpoints.val = 0;
tinyusb_apply_device_config(config);
if (!tinyusb_load_enabled_interfaces()) {
initialized = false;
tinyusb_is_initialized = false;
return ESP_FAIL;
}
@ -605,7 +608,7 @@ esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
}
if (esp_register_shutdown_handler(usb_persist_shutdown_handler) != ESP_OK) {
initialized = false;
tinyusb_is_initialized = false;
return ESP_FAIL;
}
@ -614,7 +617,7 @@ esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
};
esp_err_t err = tinyusb_driver_install(&tusb_cfg);
if (err != ESP_OK) {
initialized = false;
tinyusb_is_initialized = false;
return err;
}
xTaskCreate(usb_device_task, "usbd", 4096, NULL, configMAX_PRIORITIES - 1, NULL);
@ -690,84 +693,4 @@ uint8_t tinyusb_get_free_out_endpoint(void){
return 0;
}
/*
void usb_dw_reg_dump(void)
{
#define USB_PRINT_REG(r) printf("USB0." #r " = 0x%x;\n", USB0.r)
#define USB_PRINT_IREG(i, r) printf("USB0.in_ep_reg[%u]." #r " = 0x%x;\n", i, USB0.in_ep_reg[i].r)
#define USB_PRINT_OREG(i, r) printf("USB0.out_ep_reg[%u]." #r " = 0x%x;\n", i, USB0.out_ep_reg[i].r)
uint8_t i;
USB_PRINT_REG(gotgctl);
USB_PRINT_REG(gotgint);
USB_PRINT_REG(gahbcfg);
USB_PRINT_REG(gusbcfg);
USB_PRINT_REG(grstctl);
USB_PRINT_REG(gintsts);
USB_PRINT_REG(gintmsk);
USB_PRINT_REG(grxstsr);
USB_PRINT_REG(grxstsp);
USB_PRINT_REG(grxfsiz);
USB_PRINT_REG(gnptxsts);
USB_PRINT_REG(gpvndctl);
USB_PRINT_REG(ggpio);
USB_PRINT_REG(guid);
USB_PRINT_REG(gsnpsid);
USB_PRINT_REG(ghwcfg1);
USB_PRINT_REG(ghwcfg2);
USB_PRINT_REG(ghwcfg3);
USB_PRINT_REG(ghwcfg4);
USB_PRINT_REG(glpmcfg);
USB_PRINT_REG(gpwrdn);
USB_PRINT_REG(gdfifocfg);
USB_PRINT_REG(gadpctl);
USB_PRINT_REG(hptxfsiz);
USB_PRINT_REG(hcfg);
USB_PRINT_REG(hfir);
USB_PRINT_REG(hfnum);
USB_PRINT_REG(hptxsts);
USB_PRINT_REG(haint);
USB_PRINT_REG(haintmsk);
USB_PRINT_REG(hflbaddr);
USB_PRINT_REG(hprt);
USB_PRINT_REG(dcfg);
USB_PRINT_REG(dctl);
USB_PRINT_REG(dsts);
USB_PRINT_REG(diepmsk);
USB_PRINT_REG(doepmsk);
USB_PRINT_REG(daint);
USB_PRINT_REG(daintmsk);
USB_PRINT_REG(dtknqr1);
USB_PRINT_REG(dtknqr2);
USB_PRINT_REG(dvbusdis);
USB_PRINT_REG(dvbuspulse);
USB_PRINT_REG(dtknqr3_dthrctl);
USB_PRINT_REG(dtknqr4_fifoemptymsk);
USB_PRINT_REG(deachint);
USB_PRINT_REG(deachintmsk);
USB_PRINT_REG(pcgctrl);
USB_PRINT_REG(pcgctrl1);
USB_PRINT_REG(gnptxfsiz);
for (i = 0; i < 4; i++) {
printf("USB0.dieptxf[%u] = 0x%x;\n", i, USB0.dieptxf[i]);
}
// for (i = 0; i < 16; i++) {
// printf("USB0.diepeachintmsk[%u] = 0x%x;\n", i, USB0.diepeachintmsk[i]);
// }
// for (i = 0; i < 16; i++) {
// printf("USB0.doepeachintmsk[%u] = 0x%x;\n", i, USB0.doepeachintmsk[i]);
// }
for (i = 0; i < 7; i++) {
printf("// EP %u:\n", i);
USB_PRINT_IREG(i, diepctl);
USB_PRINT_IREG(i, diepint);
USB_PRINT_IREG(i, dieptsiz);
USB_PRINT_IREG(i, diepdma);
USB_PRINT_IREG(i, dtxfsts);
USB_PRINT_OREG(i, doepctl);
USB_PRINT_OREG(i, doepint);
USB_PRINT_OREG(i, doeptsiz);
USB_PRINT_OREG(i, doepdma);
}
}
*/
#endif /* CONFIG_TINYUSB_ENABLED */

View File

@ -82,11 +82,11 @@ void usb_persist_restart(restart_type_t mode);
// The following definitions and functions are to be used only by the drivers
typedef enum {
USB_INTERFACE_CDC,
USB_INTERFACE_MSC,
USB_INTERFACE_DFU,
USB_INTERFACE_HID,
USB_INTERFACE_VENDOR,
USB_INTERFACE_MSC,
USB_INTERFACE_CDC,
USB_INTERFACE_MIDI,
USB_INTERFACE_CUSTOM,
USB_INTERFACE_MAX

View File

@ -95,6 +95,7 @@ void __touchInit()
SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_MEAS_EN_CLR);
//clear touch enable
WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, 0x0);
SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_TOUCH_SLP_TIMER_EN);
__touchSetCycles(__touchMeasureCycles, __touchSleepCycles);
esp_intr_alloc(ETS_RTC_CORE_INTR_SOURCE, (int)ARDUINO_ISR_FLAG, __touchISR, NULL, &touch_intr_handle);
#else

View File

@ -14,205 +14,88 @@
#include "esp32-hal-uart.h"
#include "esp32-hal.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_attr.h"
#include "soc/uart_reg.h"
#include "soc/uart_struct.h"
#include "soc/io_mux_reg.h"
#include "soc/gpio_sig_map.h"
#include "soc/rtc.h"
#include "driver/uart.h"
#include "hal/uart_ll.h"
#include "esp_intr_alloc.h"
#include "esp_system.h"
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "soc/dport_reg.h"
#include "esp32/rom/ets_sys.h"
#include "esp32/rom/uart.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "soc/dport_reg.h"
#include "esp32s2/rom/ets_sys.h"
#include "esp32s2/rom/uart.h"
#include "soc/periph_defs.h"
#elif CONFIG_IDF_TARGET_ESP32C3
#include "esp32c3/rom/ets_sys.h"
#include "esp32c3/rom/uart.h"
#include "soc/periph_defs.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#else // ESP32 Before IDF 4.0
#include "rom/ets_sys.h"
#include "rom/uart.h"
#include "esp_intr.h"
#endif
#if CONFIG_IDF_TARGET_ESP32
#define UART_PORTS_NUM 3
#define UART_REG_BASE(u) ((u==0)?DR_REG_UART_BASE:( (u==1)?DR_REG_UART1_BASE:( (u==2)?DR_REG_UART2_BASE:0)))
#define UART_RXD_IDX(u) ((u==0)?U0RXD_IN_IDX:( (u==1)?U1RXD_IN_IDX:( (u==2)?U2RXD_IN_IDX:0)))
#define UART_TXD_IDX(u) ((u==0)?U0TXD_OUT_IDX:( (u==1)?U1TXD_OUT_IDX:( (u==2)?U2TXD_OUT_IDX:0)))
#define UART_INTR_SOURCE(u) ((u==0)?ETS_UART0_INTR_SOURCE:( (u==1)?ETS_UART1_INTR_SOURCE:((u==2)?ETS_UART2_INTR_SOURCE:0)))
#elif CONFIG_IDF_TARGET_ESP32S2
#define UART_PORTS_NUM 2
#define UART_REG_BASE(u) ((u==0)?DR_REG_UART_BASE:( (u==1)?DR_REG_UART1_BASE:0))
#define UART_RXD_IDX(u) ((u==0)?U0RXD_IN_IDX:( (u==1)?U1RXD_IN_IDX:0))
#define UART_TXD_IDX(u) ((u==0)?U0TXD_OUT_IDX:( (u==1)?U1TXD_OUT_IDX:0))
#define UART_INTR_SOURCE(u) ((u==0)?ETS_UART0_INTR_SOURCE:( (u==1)?ETS_UART1_INTR_SOURCE:0))
#else
#define UART_PORTS_NUM 2
#define UART_REG_BASE(u) ((u==0)?DR_REG_UART_BASE:( (u==1)?DR_REG_UART1_BASE:0))
#define UART_RXD_IDX(u) ((u==0)?U0RXD_IN_IDX:( (u==1)?U1RXD_IN_IDX:0))
#define UART_TXD_IDX(u) ((u==0)?U0TXD_OUT_IDX:( (u==1)?U1TXD_OUT_IDX:0))
#define UART_INTR_SOURCE(u) ((u==0)?ETS_UART0_INTR_SOURCE:( (u==1)?ETS_UART1_INTR_SOURCE:0))
#endif
#include "soc/soc_caps.h"
#include "soc/uart_struct.h"
static int s_uart_debug_nr = 0;
struct uart_struct_t {
uart_dev_t * dev;
#if !CONFIG_DISABLE_HAL_LOCKS
xSemaphoreHandle lock;
#endif
uint8_t num;
xQueueHandle queue;
intr_handle_t intr_handle;
bool has_peek;
uint8_t peek_byte;
};
#if CONFIG_DISABLE_HAL_LOCKS
#define UART_MUTEX_LOCK()
#define UART_MUTEX_UNLOCK()
static uart_t _uart_bus_array[] = {
{&UART0, 0, NULL, NULL},
{&UART1, 1, NULL, NULL},
#if CONFIG_IDF_TARGET_ESP32
{&UART2, 2, NULL, NULL}
{0, false, 0},
#if SOC_UART_NUM > 1
{1, false, 0},
#endif
#if SOC_UART_NUM > 2
{2, false, 0},
#endif
};
#else
#define UART_MUTEX_LOCK() do {} while (xSemaphoreTake(uart->lock, portMAX_DELAY) != pdPASS)
#define UART_MUTEX_UNLOCK() xSemaphoreGive(uart->lock)
static uart_t _uart_bus_array[] = {
{&UART0, NULL, 0, NULL, NULL},
{&UART1, NULL, 1, NULL, NULL},
#if CONFIG_IDF_TARGET_ESP32
{&UART2, NULL, 2, NULL, NULL}
{NULL, 0, false, 0},
#if SOC_UART_NUM > 1
{NULL, 1, false, 0},
#endif
#if SOC_UART_NUM > 2
{NULL, 2, false, 0},
#endif
};
#endif
static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb);
static void ARDUINO_ISR_ATTR _uart_isr(void *arg)
{
uint8_t i, c;
BaseType_t xHigherPriorityTaskWoken;
uart_t* uart;
for(i=0;i<UART_PORTS_NUM;i++){
uart = &_uart_bus_array[i];
if(uart->intr_handle == NULL){
continue;
}
uart->dev->int_clr.rxfifo_full = 1;
uart->dev->int_clr.frm_err = 1;
uart->dev->int_clr.rxfifo_tout = 1;
#if CONFIG_IDF_TARGET_ESP32
while(uart->dev->status.rxfifo_cnt || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
c = uart->dev->fifo.rw_byte;
#else
uint32_t fifo_reg = UART_FIFO_AHB_REG(i);
while(uart->dev->status.rxfifo_cnt) {
c = ESP_REG(fifo_reg);
#endif
if(uart->queue != NULL) {
xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
}
}
}
if (xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR();
}
}
static void uartEnableInterrupt(uart_t* uart, uint8_t rxfifo_full_thrhd)
{
UART_MUTEX_LOCK();
uart->dev->conf1.rxfifo_full_thrhd = rxfifo_full_thrhd;
#if CONFIG_IDF_TARGET_ESP32
uart->dev->conf1.rx_tout_thrhd = 2;
#else
uart->dev->mem_conf.rx_tout_thrhd = 2;
#endif
uart->dev->conf1.rx_tout_en = 1;
uart->dev->int_ena.rxfifo_full = 1;
uart->dev->int_ena.frm_err = 1;
uart->dev->int_ena.rxfifo_tout = 1;
uart->dev->int_clr.val = 0xffffffff;
esp_intr_alloc(UART_INTR_SOURCE(uart->num), (int)ARDUINO_ISR_FLAG, _uart_isr, NULL, &uart->intr_handle);
UART_MUTEX_UNLOCK();
}
static void uartDisableInterrupt(uart_t* uart)
{
UART_MUTEX_LOCK();
uart->dev->conf1.val = 0;
uart->dev->int_ena.val = 0;
uart->dev->int_clr.val = 0xffffffff;
esp_intr_free(uart->intr_handle);
uart->intr_handle = NULL;
UART_MUTEX_UNLOCK();
}
static void uartDetachRx(uart_t* uart, uint8_t rxPin)
bool uartIsDriverInstalled(uart_t* uart)
{
if(uart == NULL) {
return;
}
pinMatrixInDetach(UART_RXD_IDX(uart->num), false, false);
uartDisableInterrupt(uart);
return 0;
}
static void uartDetachTx(uart_t* uart, uint8_t txPin)
{
if(uart == NULL) {
return;
if (uart_is_driver_installed(uart->num)) {
return true;
}
pinMatrixOutDetach(txPin, false, false);
return false;
}
static void uartAttachRx(uart_t* uart, uint8_t rxPin, bool inverted, uint8_t rxfifo_full_thrhd)
void uartSetPins(uart_t* uart, uint8_t rxPin, uint8_t txPin)
{
if(uart == NULL || rxPin >= GPIO_PIN_COUNT) {
if(uart == NULL || rxPin >= SOC_GPIO_PIN_COUNT || txPin >= SOC_GPIO_PIN_COUNT) {
return;
}
pinMode(rxPin, INPUT);
uartEnableInterrupt(uart, rxfifo_full_thrhd);
pinMatrixInAttach(rxPin, UART_RXD_IDX(uart->num), inverted);
UART_MUTEX_LOCK();
ESP_ERROR_CHECK(uart_set_pin(uart->num, txPin, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
UART_MUTEX_UNLOCK();
}
static void uartAttachTx(uart_t* uart, uint8_t txPin, bool inverted)
{
if(uart == NULL || txPin >= GPIO_PIN_COUNT) {
return;
}
pinMode(txPin, OUTPUT);
pinMatrixOutAttach(txPin, UART_TXD_IDX(uart->num), inverted, false);
}
uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t queueLen, bool inverted, uint8_t rxfifo_full_thrhd)
{
if(uart_nr >= UART_PORTS_NUM) {
if(uart_nr >= SOC_UART_NUM) {
return NULL;
}
@ -222,6 +105,10 @@ uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rx
uart_t* uart = &_uart_bus_array[uart_nr];
if (uart_is_driver_installed(uart_nr)) {
uartEnd(uart);
}
#if !CONFIG_DISABLE_HAL_LOCKS
if(uart->lock == NULL) {
uart->lock = xSemaphoreCreateMutex();
@ -231,190 +118,144 @@ uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rx
}
#endif
if(queueLen && uart->queue == NULL) {
uart->queue = xQueueCreate(queueLen, sizeof(uint8_t)); //initialize the queue
if(uart->queue == NULL) {
return NULL;
}
}
#if CONFIG_IDF_TARGET_ESP32C3
#else
if(uart_nr == 1){
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART1_CLK_EN);
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART1_RST);
#if CONFIG_IDF_TARGET_ESP32
} else if(uart_nr == 2){
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART2_CLK_EN);
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART2_RST);
#endif
} else {
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART_CLK_EN);
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART_RST);
}
#endif
uartFlush(uart);
uartSetBaudRate(uart, baudrate);
UART_MUTEX_LOCK();
uart->dev->conf0.val = config;
#define TWO_STOP_BITS_CONF 0x3
#define ONE_STOP_BITS_CONF 0x1
if ( uart->dev->conf0.stop_bit_num == TWO_STOP_BITS_CONF) {
uart->dev->conf0.stop_bit_num = ONE_STOP_BITS_CONF;
uart->dev->rs485_conf.dl1_en = 1;
uart_config_t uart_config;
uart_config.baud_rate = baudrate;
uart_config.data_bits = (config & 0xc) >> 2;
uart_config.parity = (config & 0x3);
uart_config.stop_bits = (config & 0x30) >> 4;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
uart_config.rx_flow_ctrl_thresh = rxfifo_full_thrhd;
uart_config.source_clk = UART_SCLK_APB;
ESP_ERROR_CHECK(uart_driver_install(uart_nr, 2*queueLen, 0, 0, NULL, 0));
ESP_ERROR_CHECK(uart_param_config(uart_nr, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(uart_nr, txPin, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// Is it right or the idea is to swap rx and tx pins?
if (inverted) {
// invert signal for both Rx and Tx
ESP_ERROR_CHECK(uart_set_line_inverse(uart_nr, UART_SIGNAL_TXD_INV | UART_SIGNAL_RXD_INV));
}
// tx_idle_num : idle interval after tx FIFO is empty(unit: the time it takes to send one bit under current baudrate)
// Setting it to 0 prevents line idle time/delays when sending messages with small intervals
uart->dev->idle_conf.tx_idle_num = 0; //
UART_MUTEX_UNLOCK();
if(rxPin != -1) {
uartAttachRx(uart, rxPin, inverted, rxfifo_full_thrhd);
}
if(txPin != -1) {
uartAttachTx(uart, txPin, inverted);
}
addApbChangeCallback(uart, uart_on_apb_change);
uartFlush(uart);
return uart;
}
void uartEnd(uart_t* uart, uint8_t txPin, uint8_t rxPin)
void uartEnd(uart_t* uart)
{
if(uart == NULL) {
return;
}
removeApbChangeCallback(uart, uart_on_apb_change);
UART_MUTEX_LOCK();
if(uart->queue != NULL) {
vQueueDelete(uart->queue);
uart->queue = NULL;
}
uart->dev->conf0.val = 0;
uart_driver_delete(uart->num);
UART_MUTEX_UNLOCK();
uartDetachRx(uart, rxPin);
uartDetachTx(uart, txPin);
}
size_t uartResizeRxBuffer(uart_t * uart, size_t new_size) {
if(uart == NULL) {
return 0;
}
UART_MUTEX_LOCK();
if(uart->queue != NULL) {
vQueueDelete(uart->queue);
uart->queue = xQueueCreate(new_size, sizeof(uint8_t));
if(uart->queue == NULL) {
UART_MUTEX_UNLOCK();
return 0;
}
}
UART_MUTEX_UNLOCK();
return new_size;
}
void uartSetRxInvert(uart_t* uart, bool invert)
{
if (uart == NULL)
return;
#if 0
// POTENTIAL ISSUE :: original code only set/reset rxd_inv bit
// IDF or LL set/reset the whole inv_mask!
if (invert)
uart->dev->conf0.rxd_inv = 1;
ESP_ERROR_CHECK(uart_set_line_inverse(uart->num, UART_SIGNAL_RXD_INV));
else
uart->dev->conf0.rxd_inv = 0;
ESP_ERROR_CHECK(uart_set_line_inverse(uart->num, UART_SIGNAL_INV_DISABLE));
#else
// this implementation is better over IDF API because it only affects RXD
// this is supported in ESP32, ESP32-S2 and ESP32-C3
uart_dev_t *hw = UART_LL_GET_HW(uart->num);
if (invert)
hw->conf0.rxd_inv = 1;
else
hw->conf0.rxd_inv = 0;
#endif
}
uint32_t uartAvailable(uart_t* uart)
{
if(uart == NULL || uart->queue == NULL) {
if(uart == NULL) {
return 0;
}
#ifdef UART_READ_RX_FIFO
return (uxQueueMessagesWaiting(uart->queue) + uart->dev->status.rxfifo_cnt) ;
#else
return uxQueueMessagesWaiting(uart->queue);
#endif
UART_MUTEX_LOCK();
size_t available;
uart_get_buffered_data_len(uart->num, &available);
if (uart->has_peek) available++;
UART_MUTEX_UNLOCK();
return available;
}
uint32_t uartAvailableForWrite(uart_t* uart)
{
if(uart == NULL) {
return 0;
}
return 0x7f - uart->dev->status.txfifo_cnt;
UART_MUTEX_LOCK();
uint32_t available = uart_ll_get_txfifo_len(UART_LL_GET_HW(uart->num));
UART_MUTEX_UNLOCK();
return available;
}
#ifdef UART_READ_RX_FIFO
void uartRxFifoToQueue(uart_t* uart)
{
uint8_t c;
UART_MUTEX_LOCK();
//disable interrupts
uart->dev->int_ena.val = 0;
uart->dev->int_clr.val = 0xffffffff;
#if CONFIG_IDF_TARGET_ESP32
while (uart->dev->status.rxfifo_cnt || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
c = uart->dev->fifo.rw_byte;
#else
uint32_t fifo_reg = UART_FIFO_AHB_REG(uart->num);
while (uart->dev->status.rxfifo_cnt) {
c = ESP_REG(fifo_reg);
#endif
xQueueSend(uart->queue, &c, 0);
}
//enable interrupts
uart->dev->int_ena.rxfifo_full = 1;
uart->dev->int_ena.frm_err = 1;
uart->dev->int_ena.rxfifo_tout = 1;
uart->dev->int_clr.val = 0xffffffff;
UART_MUTEX_UNLOCK();
}
#endif
uint8_t uartRead(uart_t* uart)
{
if(uart == NULL || uart->queue == NULL) {
if(uart == NULL) {
return 0;
}
uint8_t c;
#ifdef UART_READ_RX_FIFO
if ((uxQueueMessagesWaiting(uart->queue) == 0) && (uart->dev->status.rxfifo_cnt > 0))
{
uartRxFifoToQueue(uart);
uint8_t c = 0;
UART_MUTEX_LOCK();
if (uart->has_peek) {
uart->has_peek = false;
c = uart->peek_byte;
} else {
int len = uart_read_bytes(uart->num, &c, 1, 20 / portTICK_RATE_MS);
if (len == 0) {
c = 0;
}
#endif
if(xQueueReceive(uart->queue, &c, 0)) {
}
UART_MUTEX_UNLOCK();
return c;
}
return 0;
}
uint8_t uartPeek(uart_t* uart)
{
if(uart == NULL || uart->queue == NULL) {
if(uart == NULL) {
return 0;
}
uint8_t c;
#ifdef UART_READ_RX_FIFO
if ((uxQueueMessagesWaiting(uart->queue) == 0) && (uart->dev->status.rxfifo_cnt > 0))
{
uartRxFifoToQueue(uart);
uint8_t c = 0;
UART_MUTEX_LOCK();
if (uart->has_peek) {
c = uart->peek_byte;
} else {
int len = uart_read_bytes(uart->num, &c, 1, 20 / portTICK_RATE_MS);
if (len == 0) {
c = 0;
} else {
uart->has_peek = true;
uart->peek_byte = c;
}
#endif
if(xQueuePeek(uart->queue, &c, 0)) {
}
UART_MUTEX_UNLOCK();
return c;
}
return 0;
}
void uartWrite(uart_t* uart, uint8_t c)
{
@ -422,12 +263,7 @@ void uartWrite(uart_t* uart, uint8_t c)
return;
}
UART_MUTEX_LOCK();
while(uart->dev->status.txfifo_cnt >= 0x7E);
#if CONFIG_IDF_TARGET_ESP32
uart->dev->fifo.rw_byte = c;
#else
ESP_REG(UART_FIFO_AHB_REG(uart->num)) = c;
#endif
uart_write_bytes(uart->num, &c, 1);
UART_MUTEX_UNLOCK();
}
@ -436,19 +272,9 @@ void uartWriteBuf(uart_t* uart, const uint8_t * data, size_t len)
if(uart == NULL) {
return;
}
UART_MUTEX_LOCK();
#ifndef CONFIG_IDF_TARGET_ESP32
uint32_t fifo_reg = UART_FIFO_AHB_REG(uart->num);
#endif
while(len) {
while(uart->dev->status.txfifo_cnt >= 0x7E);
#if CONFIG_IDF_TARGET_ESP32
uart->dev->fifo.rw_byte = *data++;
#else
ESP_REG(fifo_reg) = *data++;
#endif
len--;
}
uart_write_bytes(uart->num, data, len);
UART_MUTEX_UNLOCK();
}
@ -464,26 +290,11 @@ void uartFlushTxOnly(uart_t* uart, bool txOnly)
}
UART_MUTEX_LOCK();
#if CONFIG_IDF_TARGET_ESP32
while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out);
ESP_ERROR_CHECK(uart_wait_tx_done(uart->num, portMAX_DELAY));
if ( !txOnly ) {
//Due to hardware issue, we can not use fifo_rst to reset uart fifo.
//See description about UART_TXFIFO_RST and UART_RXFIFO_RST in <<esp32_technical_reference_manual>> v2.6 or later.
// we read the data out and make `fifo_len == 0 && rd_addr == wr_addr`.
while(uart->dev->status.rxfifo_cnt != 0 || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
READ_PERI_REG(UART_FIFO_REG(uart->num));
ESP_ERROR_CHECK(uart_flush_input(uart->num));
}
xQueueReset(uart->queue);
}
#else
while(uart->dev->status.txfifo_cnt);
uart->dev->conf0.txfifo_rst = 1;
uart->dev->conf0.txfifo_rst = 0;
#endif
UART_MUTEX_UNLOCK();
}
@ -493,100 +304,41 @@ void uartSetBaudRate(uart_t* uart, uint32_t baud_rate)
return;
}
UART_MUTEX_LOCK();
uart_ll_set_baudrate(uart->dev, baud_rate);
uart_ll_set_baudrate(UART_LL_GET_HW(uart->num), baud_rate);
UART_MUTEX_UNLOCK();
}
static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb)
{
uart_t* uart = (uart_t*)arg;
if(ev_type == APB_BEFORE_CHANGE){
UART_MUTEX_LOCK();
//disabple interrupt
uart->dev->int_ena.val = 0;
uart->dev->int_clr.val = 0xffffffff;
// read RX fifo
uint8_t c;
// BaseType_t xHigherPriorityTaskWoken;
#if CONFIG_IDF_TARGET_ESP32
while(uart->dev->status.rxfifo_cnt != 0 || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
c = uart->dev->fifo.rw_byte;
#else
uint32_t fifo_reg = UART_FIFO_AHB_REG(uart->num);
while(uart->dev->status.rxfifo_cnt != 0) {
c = ESP_REG(fifo_reg);
#endif
if(uart->queue != NULL ) {
xQueueSend(uart->queue, &c, 1); //&xHigherPriorityTaskWoken);
}
}
UART_MUTEX_UNLOCK();
// wait TX empty
#if CONFIG_IDF_TARGET_ESP32
while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out);
#else
while(uart->dev->status.txfifo_cnt);
#endif
} else {
//todo:
// set baudrate
UART_MUTEX_LOCK();
uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F);
uint32_t baud_rate = ((old_apb<<4)/clk_div);
clk_div = ((new_apb<<4)/baud_rate);
uart->dev->clk_div.div_int = clk_div>>4 ;
uart->dev->clk_div.div_frag = clk_div & 0xf;
//enable interrupts
uart->dev->int_ena.rxfifo_full = 1;
uart->dev->int_ena.frm_err = 1;
uart->dev->int_ena.rxfifo_tout = 1;
uart->dev->int_clr.val = 0xffffffff;
UART_MUTEX_UNLOCK();
}
}
uint32_t uartGetBaudRate(uart_t* uart)
{
if(uart == NULL) {
return 0;
}
uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F);
if(!clk_div) {
return 0;
}
return ((getApbFrequency()<<4)/clk_div);
UART_MUTEX_LOCK();
uint32_t baud_rate = uart_ll_get_baudrate(UART_LL_GET_HW(uart->num));
UART_MUTEX_UNLOCK();
return baud_rate;
}
static void ARDUINO_ISR_ATTR uart0_write_char(char c)
{
#if CONFIG_IDF_TARGET_ESP32
while(((ESP_REG(0x01C+DR_REG_UART_BASE) >> UART_TXFIFO_CNT_S) & 0x7F) >= 0x7E);
ESP_REG(DR_REG_UART_BASE) = c;
#else
while(UART0.status.txfifo_cnt == 0x7F);
WRITE_PERI_REG(UART_FIFO_AHB_REG(0), c);
#endif
while (uart_ll_get_txfifo_len(&UART0) == 0);
uart_ll_write_txfifo(&UART0, (const uint8_t *) &c, 1);
}
#if SOC_UART_NUM > 1
static void ARDUINO_ISR_ATTR uart1_write_char(char c)
{
#if CONFIG_IDF_TARGET_ESP32
while(((ESP_REG(0x01C+DR_REG_UART1_BASE) >> UART_TXFIFO_CNT_S) & 0x7F) >= 0x7E);
ESP_REG(DR_REG_UART1_BASE) = c;
#else
while(UART1.status.txfifo_cnt == 0x7F);
WRITE_PERI_REG(UART_FIFO_AHB_REG(1), c);
#endif
while (uart_ll_get_txfifo_len(&UART1) == 0);
uart_ll_write_txfifo(&UART1, (const uint8_t *) &c, 1);
}
#endif
#if CONFIG_IDF_TARGET_ESP32
#if SOC_UART_NUM > 2
static void ARDUINO_ISR_ATTR uart2_write_char(char c)
{
while(((ESP_REG(0x01C+DR_REG_UART2_BASE) >> UART_TXFIFO_CNT_S) & 0x7F) >= 0x7E);
ESP_REG(DR_REG_UART2_BASE) = c;
while (uart_ll_get_txfifo_len(&UART2) == 0);
uart_ll_write_txfifo(&UART2, (const uint8_t *) &c, 1);
}
#endif
@ -596,10 +348,12 @@ void uart_install_putc()
case 0:
ets_install_putc1((void (*)(char)) &uart0_write_char);
break;
#if SOC_UART_NUM > 1
case 1:
ets_install_putc1((void (*)(char)) &uart1_write_char);
break;
#if CONFIG_IDF_TARGET_ESP32
#endif
#if SOC_UART_NUM > 2
case 2:
ets_install_putc1((void (*)(char)) &uart2_write_char);
break;
@ -612,7 +366,7 @@ void uart_install_putc()
void uartSetDebug(uart_t* uart)
{
if(uart == NULL || uart->num >= UART_PORTS_NUM) {
if(uart == NULL || uart->num >= SOC_UART_NUM) {
s_uart_debug_nr = -1;
//ets_install_putc1(NULL);
//return;
@ -646,17 +400,19 @@ int log_printf(const char *format, ...)
return 0;
}
}
vsnprintf(temp, len+1, format, arg);
#if !CONFIG_DISABLE_HAL_LOCKS
if(s_uart_debug_nr != -1 && _uart_bus_array[s_uart_debug_nr].lock){
xSemaphoreTake(_uart_bus_array[s_uart_debug_nr].lock, portMAX_DELAY);
ets_printf("%s", temp);
xSemaphoreGive(_uart_bus_array[s_uart_debug_nr].lock);
} else {
ets_printf("%s", temp);
}
#else
#endif
vsnprintf(temp, len+1, format, arg);
ets_printf("%s", temp);
#if !CONFIG_DISABLE_HAL_LOCKS
if(s_uart_debug_nr != -1 && _uart_bus_array[s_uart_debug_nr].lock){
xSemaphoreGive(_uart_bus_array[s_uart_debug_nr].lock);
}
#endif
va_end(arg);
if(len >= sizeof(loc_buf)){
@ -665,48 +421,126 @@ int log_printf(const char *format, ...)
return len;
}
static void log_print_buf_line(const uint8_t *b, size_t len, size_t total_len){
for(size_t i = 0; i<len; i++){
log_printf("%s0x%02x,",i?" ":"", b[i]);
}
if(total_len > 16){
for(size_t i = len; i<16; i++){
log_printf(" ");
}
log_printf(" // ");
} else {
log_printf(" // ");
}
for(size_t i = 0; i<len; i++){
log_printf("%c",((b[i] >= 0x20) && (b[i] < 0x80))?b[i]:'.');
}
log_printf("\n");
}
void log_print_buf(const uint8_t *b, size_t len){
if(!len || !b){
return;
}
for(size_t i = 0; i<len; i+=16){
if(len > 16){
log_printf("/* 0x%04X */ ", i);
}
log_print_buf_line(b+i, ((len-i)<16)?(len - i):16, len);
}
}
/*
* if enough pulses are detected return the minimum high pulse duration + minimum low pulse duration divided by two.
* This equals one bit period. If flag is true the function return inmediately, otherwise it waits for enough pulses.
*/
unsigned long uartBaudrateDetect(uart_t *uart, bool flg)
{
while(uart->dev->rxd_cnt.edge_cnt < 30) { // UART_PULSE_NUM(uart_num)
if(uart == NULL) {
return 0;
}
uart_dev_t *hw = UART_LL_GET_HW(uart->num);
while(hw->rxd_cnt.edge_cnt < 30) { // UART_PULSE_NUM(uart_num)
if(flg) return 0;
ets_delay_us(1000);
}
UART_MUTEX_LOCK();
unsigned long ret = ((uart->dev->lowpulse.min_cnt + uart->dev->highpulse.min_cnt) >> 1) + 12;
//log_i("lowpulse_min_cnt = %d hightpulse_min_cnt = %d", hw->lowpulse.min_cnt, hw->highpulse.min_cnt);
unsigned long ret = ((hw->lowpulse.min_cnt + hw->highpulse.min_cnt) >> 1);
UART_MUTEX_UNLOCK();
return ret;
}
/*
* To start detection of baud rate with the uart the auto_baud.en bit needs to be cleared and set. The bit period is
* detected calling uartBadrateDetect(). The raw baudrate is computed using the UART_CLK_FREQ. The raw baudrate is
* rounded to the closed real baudrate.
*
* ESP32-C3 reports wrong baud rate detection as shown below:
*
* This will help in a future recall for the C3.
* Baud Sent: Baud Read:
* 300 --> 19536
* 2400 --> 19536
* 4800 --> 19536
* 9600 --> 28818
* 19200 --> 57678
* 38400 --> 115440
* 57600 --> 173535
* 115200 --> 347826
* 230400 --> 701754
*
*
*/
void uartStartDetectBaudrate(uart_t *uart) {
if(!uart) return;
#ifndef CONFIG_IDF_TARGET_ESP32C3
uart->dev->auto_baud.glitch_filt = 0x08;
uart->dev->auto_baud.en = 0;
uart->dev->auto_baud.en = 1;
if(uart == NULL) {
return;
}
uart_dev_t *hw = UART_LL_GET_HW(uart->num);
#ifdef CONFIG_IDF_TARGET_ESP32C3
// ESP32-C3 requires further testing
// Baud rate detection returns wrong values
log_e("ESP32-C3 baud rate detection is not supported.");
return;
// Code bellow for C3 kept for future recall
//hw->rx_filt.glitch_filt = 0x08;
//hw->rx_filt.glitch_filt_en = 1;
//hw->conf0.autobaud_en = 0;
//hw->conf0.autobaud_en = 1;
#else
hw->auto_baud.glitch_filt = 0x08;
hw->auto_baud.en = 0;
hw->auto_baud.en = 1;
#endif
}
unsigned long
uartDetectBaudrate(uart_t *uart)
{
#ifndef CONFIG_IDF_TARGET_ESP32C3
if(uart == NULL) {
return 0;
}
#ifndef CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3 requires further testing - Baud rate detection returns wrong values
static bool uartStateDetectingBaudrate = false;
uart_dev_t *hw = UART_LL_GET_HW(uart->num);
if(!uartStateDetectingBaudrate) {
uart->dev->auto_baud.glitch_filt = 0x08;
uart->dev->auto_baud.en = 0;
uart->dev->auto_baud.en = 1;
uartStartDetectBaudrate(uart);
uartStateDetectingBaudrate = true;
}
@ -714,11 +548,21 @@ uartDetectBaudrate(uart_t *uart)
if (!divisor) {
return 0;
}
// log_i(...) below has been used to check C3 baud rate detection results
//log_i("Divisor = %d\n", divisor);
//log_i("BAUD RATE based on Positive Pulse %d\n", getApbFrequency()/((hw->pospulse.min_cnt + 1)/2));
//log_i("BAUD RATE based on Negative Pulse %d\n", getApbFrequency()/((hw->negpulse.min_cnt + 1)/2));
uart->dev->auto_baud.en = 0;
#ifdef CONFIG_IDF_TARGET_ESP32C3
//hw->conf0.autobaud_en = 0;
#else
hw->auto_baud.en = 0;
#endif
uartStateDetectingBaudrate = false; // Initialize for the next round
unsigned long baudrate = getApbFrequency() / divisor;
//log_i("APB_FREQ = %d\nraw baudrate detected = %d", getApbFrequency(), baudrate);
static const unsigned long default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400};
@ -736,17 +580,7 @@ uartDetectBaudrate(uart_t *uart)
return default_rates[i];
#else
return 0;
#endif
}
/*
* Returns the status of the RX state machine, if the value is non-zero the state machine is active.
*/
bool uartRxActive(uart_t* uart) {
#if CONFIG_IDF_TARGET_ESP32
return uart->dev->status.st_urx_out != 0;
#else
log_e("ESP32-C3 baud rate detection is not supported.");
return 0;
#endif
}

View File

@ -52,7 +52,7 @@ struct uart_struct_t;
typedef struct uart_struct_t uart_t;
uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t queueLen, bool inverted, uint8_t rxfifo_full_thrhd);
void uartEnd(uart_t* uart, uint8_t rxPin, uint8_t txPin);
void uartEnd(uart_t* uart);
uint32_t uartAvailable(uart_t* uart);
uint32_t uartAvailableForWrite(uart_t* uart);
@ -68,17 +68,17 @@ void uartFlushTxOnly(uart_t* uart, bool txOnly );
void uartSetBaudRate(uart_t* uart, uint32_t baud_rate);
uint32_t uartGetBaudRate(uart_t* uart);
size_t uartResizeRxBuffer(uart_t* uart, size_t new_size);
void uartSetRxInvert(uart_t* uart, bool invert);
void uartSetDebug(uart_t* uart);
int uartGetDebug();
bool uartIsDriverInstalled(uart_t* uart);
void uartSetPins(uart_t* uart, uint8_t rxPin, uint8_t txPin);
void uartStartDetectBaudrate(uart_t *uart);
unsigned long uartDetectBaudrate(uart_t *uart);
bool uartRxActive(uart_t* uart);
#ifdef __cplusplus
}

View File

@ -0,0 +1,204 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "firmware_msc_fat.h"
//copy up to max_len chars from src to dst and do not terminate
static size_t cplstr(void *dst, const void * src, size_t max_len){
if(!src || !dst || !max_len){
return 0;
}
size_t l = strlen((const char *)src);
if(l > max_len){
l = max_len;
}
memcpy(dst, src, l);
return l;
}
//copy up to max_len chars from src to dst, adding spaces up to max_len. do not terminate
static void cplstrsp(void *dst, const void * src, size_t max_len){
size_t l = cplstr(dst, src, max_len);
for(; l < max_len; l++){
((uint8_t*)dst)[l] = 0x20;
}
}
// FAT12
static const char * FAT12_FILE_SYSTEM_TYPE = "FAT12";
static uint16_t fat12_sectors_per_alloc_table(uint32_t sector_num){
uint32_t required_bytes = (((sector_num * 3)+1)/2);
return (required_bytes / DISK_SECTOR_SIZE) + ((required_bytes & DISK_SECTOR_SIZE)?1:0);
}
static uint8_t * fat12_add_table(uint8_t * dst, fat_boot_sector_t * boot){
memset(dst+DISK_SECTOR_SIZE, 0, boot->sectors_per_alloc_table * DISK_SECTOR_SIZE);
uint8_t * d = dst + DISK_SECTOR_SIZE;
d[0] = 0xF8;
d[1] = 0xFF;
d[2] = 0xFF;
return d;
}
static void fat12_set_table_index(uint8_t * table, uint16_t index, uint16_t value){
uint16_t offset = (index >> 1) * 3;
uint8_t * data = table + offset;
if(index & 1){
data[2] = (value >> 4) & 0xFF;
data[1] = (data[1] & 0xF) | ((value & 0xF) << 4);
} else {
data[0] = value & 0xFF;
data[1] = (data[1] & 0xF0) | ((value >> 8) & 0xF);
}
}
//FAT16
static const char * FAT16_FILE_SYSTEM_TYPE = "FAT16";
static uint16_t fat16_sectors_per_alloc_table(uint32_t sector_num){
uint32_t required_bytes = sector_num * 2;
return (required_bytes / DISK_SECTOR_SIZE) + ((required_bytes & DISK_SECTOR_SIZE)?1:0);
}
static uint8_t * fat16_add_table(uint8_t * dst, fat_boot_sector_t * boot){
memset(dst+DISK_SECTOR_SIZE, 0, boot->sectors_per_alloc_table * DISK_SECTOR_SIZE);
uint16_t * d = (uint16_t *)(dst + DISK_SECTOR_SIZE);
d[0] = 0xFFF8;
d[1] = 0xFFFF;
return (uint8_t *)d;
}
static void fat16_set_table_index(uint8_t * table, uint16_t index, uint16_t value){
uint16_t offset = index * 2;
*(uint16_t *)(table + offset) = value;
}
//Interface
const char * fat_file_system_type(bool fat16) {
return ((fat16)?FAT16_FILE_SYSTEM_TYPE:FAT12_FILE_SYSTEM_TYPE);
}
uint16_t fat_sectors_per_alloc_table(uint32_t sector_num, bool fat16){
if(fat16){
return fat16_sectors_per_alloc_table(sector_num);
}
return fat12_sectors_per_alloc_table(sector_num);
}
uint8_t * fat_add_table(uint8_t * dst, fat_boot_sector_t * boot, bool fat16){
if(fat16){
return fat16_add_table(dst, boot);
}
return fat12_add_table(dst, boot);
}
void fat_set_table_index(uint8_t * table, uint16_t index, uint16_t value, bool fat16){
if(fat16){
fat16_set_table_index(table, index, value);
} else {
fat12_set_table_index(table, index, value);
}
}
fat_boot_sector_t * fat_add_boot_sector(uint8_t * dst, uint16_t sector_num, uint16_t table_sectors, const char * file_system_type, const char * volume_label, uint32_t serial_number){
fat_boot_sector_t *boot = (fat_boot_sector_t*)dst;
boot->jump_instruction[0] = 0xEB;
boot->jump_instruction[1] = 0x3C;
boot->jump_instruction[2] = 0x90;
cplstr(boot->oem_name, "MSDOS5.0", 8);
boot->bytes_per_sector = DISK_SECTOR_SIZE;
boot->sectors_per_cluster = 1;
boot->reserved_sectors_count = 1;
boot->file_alloc_tables_num = 1;
boot->max_root_dir_entries = 16;
boot->fat12_sector_num = sector_num;
boot->media_descriptor = 0xF8;
boot->sectors_per_alloc_table = table_sectors;
boot->sectors_per_track = 1;
boot->num_heads = 1;
boot->hidden_sectors_count = 0;
boot->total_sectors_32 = 0;
boot->physical_drive_number = 0x00;
boot->reserved0 = 0x00;
boot->extended_boot_signature = 0x29;
boot->serial_number = serial_number;
cplstrsp(boot->volume_label, volume_label, 11);
memset(boot->reserved, 0, 448);
cplstrsp(boot->file_system_type, file_system_type, 8);
boot->signature = 0xAA55;
return boot;
}
fat_dir_entry_t * fat_add_label(uint8_t * dst, const char * volume_label){
fat_boot_sector_t * boot = (fat_boot_sector_t *)dst;
fat_dir_entry_t * entry = (fat_dir_entry_t *)(dst + ((boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE));
memset(entry, 0, sizeof(fat_dir_entry_t));
cplstrsp(entry->volume_label, volume_label, 11);
entry->file_attr = FAT_FILE_ATTR_VOLUME_LABEL;
return entry;
}
fat_dir_entry_t * fat_add_root_file(uint8_t * dst, uint8_t index, const char * file_name, const char * file_extension, size_t file_size, uint16_t data_start_sector, bool is_fat16){
fat_boot_sector_t * boot = (fat_boot_sector_t *)dst;
uint8_t * table = dst + DISK_SECTOR_SIZE;
fat_dir_entry_t * entry = (fat_dir_entry_t *)(dst + ((boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE) + (index * sizeof(fat_dir_entry_t)));
memset(entry, 0, sizeof(fat_dir_entry_t));
cplstrsp(entry->file_name, file_name, 8);
cplstrsp(entry->file_extension, file_extension, 3);
entry->file_attr = FAT_FILE_ATTR_ARCHIVE;
entry->file_size = file_size;
entry->data_start_sector = data_start_sector;
entry->extended_attr = 0;
uint16_t file_sectors = file_size / DISK_SECTOR_SIZE;
if(file_size % DISK_SECTOR_SIZE){
file_sectors++;
}
uint16_t data_end_sector = data_start_sector + file_sectors;
for(uint16_t i=data_start_sector; i<(data_end_sector-1); i++){
fat_set_table_index(table, i, i+1, is_fat16);
}
fat_set_table_index(table, data_end_sector-1, 0xFFFF, is_fat16);
//Set Firmware Date based on the build time
static const char * month_names_short[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
char mstr[8] = {'\0',};
const char *str = __DATE__ " " __TIME__;
int ms=0, seconds=0, minutes=0, hours=0, year=0, date=0, month=0;
int r = sscanf(str,"%s %d %d %d:%d:%d", mstr, &date, &year, &hours, &minutes, &seconds);
if(r >= 0){
for(int i=0; i<12; i++){
if(!strcmp(mstr, month_names_short[i])){
month = i;
break;
}
}
entry->creation_time_ms = FAT_MS2V(seconds, ms);
entry->creation_time_hms = FAT_HMS2V(hours, minutes, seconds);
entry->creation_time_ymd = FAT_YMD2V(year, month, date);
entry->last_access_ymd = entry->creation_time_ymd;
entry->last_modified_hms = entry->creation_time_hms;
entry->last_modified_ymd = entry->creation_time_ymd;
}
return entry;
}
uint8_t fat_lfn_checksum(const uint8_t *short_filename){
uint8_t sum = 0;
for (uint8_t i = 11; i; i--) {
sum = ((sum & 1) << 7) + (sum >> 1) + *short_filename++;
}
return sum;
}

View File

@ -0,0 +1,141 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#define FAT_U8(v) ((v) & 0xFF)
#define FAT_U16(v) FAT_U8(v), FAT_U8((v) >> 8)
#define FAT_U32(v) FAT_U8(v), FAT_U8((v) >> 8), FAT_U8((v) >> 16), FAT_U8((v) >> 24)
#define FAT12_TBL2B(l,h) FAT_U8(l), FAT_U8(((l >> 8) & 0xF) | ((h << 4) & 0xF0)), FAT_U8(h >> 4)
#define FAT_MS2B(s,ms) FAT_U8(((((s) & 0x1) * 1000) + (ms)) / 10)
#define FAT_HMS2B(h,m,s) FAT_U8(((s) >> 1)|(((m) & 0x7) << 5)), FAT_U8((((m) >> 3) & 0x7)|((h) << 3))
#define FAT_YMD2B(y,m,d) FAT_U8(((d) & 0x1F)|(((m) & 0x7) << 5)), FAT_U8((((m) >> 3) & 0x1)|((((y) - 1980) & 0x7F) << 1))
#define FAT_MS2V(s,ms) FAT_U8(((((s) & 0x1) * 1000) + (ms)) / 10)
#define FAT_HMS2V(h,m,s) (FAT_U8(((s) >> 1)|(((m) & 0x7) << 5)) | (FAT_U8((((m) >> 3) & 0x7)|((h) << 3)) << 8))
#define FAT_YMD2V(y,m,d) (FAT_U8(((d) & 0x1F)|(((m) & 0x7) << 5)) | (FAT_U8((((m) >> 3) & 0x1)|((((y) - 1980) & 0x7F) << 1)) << 8))
#define FAT_B2HMS(hms) ((hms >> 11) & 0x1F), ((hms >> 5) & 0x3F), ((hms & 0x1F) << 1)
#define FAT_B2YMD(ymd) (((ymd >> 9) & 0x7F) + 1980), ((ymd >> 5) & 0x0F), (ymd & 0x1F)
#define FAT_FILE_ATTR_READ_ONLY 0x01
#define FAT_FILE_ATTR_HIDDEN 0x02
#define FAT_FILE_ATTR_SYSTEM 0x04
#define FAT_FILE_ATTR_VOLUME_LABEL 0x08
#define FAT_FILE_ATTR_SUBDIRECTORY 0x10
#define FAT_FILE_ATTR_ARCHIVE 0x20
#define FAT_FILE_ATTR_DEVICE 0x40
static const uint16_t DISK_SECTOR_SIZE = 512;
#define FAT_SIZE_TO_SECTORS(bytes) ((bytes) / DISK_SECTOR_SIZE) + (((bytes) % DISK_SECTOR_SIZE)?1:0)
typedef struct __attribute__ ((packed)) {
uint8_t jump_instruction[3];
char oem_name[8];//padded with spaces (0x20)
uint16_t bytes_per_sector;//DISK_SECTOR_SIZE usually 512
uint8_t sectors_per_cluster;//Allowed values are 1, 2, 4, 8, 16, 32, 64, and 128
uint16_t reserved_sectors_count;//At least 1 for this sector, usually 32 for FAT32
uint8_t file_alloc_tables_num;//Almost always 2; RAM disks might use 1
uint16_t max_root_dir_entries;//FAT12 and FAT16
uint16_t fat12_sector_num;//DISK_SECTOR_NUM FAT12 and FAT16
uint8_t media_descriptor;
uint16_t sectors_per_alloc_table;//FAT12 and FAT16
uint16_t sectors_per_track;//A value of 0 may indicate LBA-only access
uint16_t num_heads;
uint32_t hidden_sectors_count;
uint32_t total_sectors_32;
uint8_t physical_drive_number;//0x00 for (first) removable media, 0x80 for (first) fixed disk
uint8_t reserved0;
uint8_t extended_boot_signature;//should be 0x29
uint32_t serial_number;//0x1234 => 1234
char volume_label[11];//padded with spaces (0x20)
char file_system_type[8];//padded with spaces (0x20)
uint8_t reserved[448];
uint16_t signature;//should be 0xAA55
} fat_boot_sector_t;
typedef struct __attribute__ ((packed)) {
union {
struct {
char file_name[8];//padded with spaces (0x20)
char file_extension[3];//padded with spaces (0x20)
};
struct {
uint8_t file_magic;// 0xE5:deleted, 0x05:will_be_deleted, 0x00:end_marker, 0x2E:dot_marker(. or ..)
char file_magic_data[10];
};
char volume_label[11];//padded with spaces (0x20)
};
uint8_t file_attr;//mask of FAT_FILE_ATTR_*
uint8_t reserved;//always 0
uint8_t creation_time_ms;//ms * 10; max 1990 (1s 990ms)
uint16_t creation_time_hms; // [5:6:5] => h:m:(s/2)
uint16_t creation_time_ymd; // [7:4:5] => (y+1980):m:d
uint16_t last_access_ymd;
uint16_t extended_attr;
uint16_t last_modified_hms;
uint16_t last_modified_ymd;
uint16_t data_start_sector;
uint32_t file_size;
} fat_dir_entry_t;
typedef struct __attribute__ ((packed)) {
union {
struct {
uint8_t number:5;
uint8_t reserved0:1;
uint8_t llfp:1;
uint8_t reserved1:1;
} seq;
uint8_t seq_num; //0xE5: Deleted Entry
};
uint16_t name0[5];
uint8_t attr; //ALWAYS 0x0F
uint8_t type; //ALWAYS 0x00
uint8_t dos_checksum;
uint16_t name1[6];
uint16_t first_cluster; //ALWAYS 0x0000
uint16_t name2[2];
} fat_lfn_entry_t;
typedef union {
fat_dir_entry_t dir;
fat_lfn_entry_t lfn;
} fat_entry_t;
const char * fat_file_system_type(bool fat16);
uint16_t fat_sectors_per_alloc_table(uint32_t sector_num, bool fat16);
uint8_t * fat_add_table(uint8_t * dst, fat_boot_sector_t * boot, bool fat16);
void fat_set_table_index(uint8_t * table, uint16_t index, uint16_t value, bool fat16);
fat_boot_sector_t * fat_add_boot_sector(uint8_t * dst, uint16_t sector_num, uint16_t table_sectors, const char * file_system_type, const char * volume_label, uint32_t serial_number);
fat_dir_entry_t * fat_add_label(uint8_t * dst, const char * volume_label);
fat_dir_entry_t * fat_add_root_file(uint8_t * dst, uint8_t index, const char * file_name, const char * file_extension, size_t file_size, uint16_t data_start_sector, bool is_fat16);
uint8_t fat_lfn_checksum(const uint8_t *short_filename);
#ifdef __cplusplus
}
#endif

View File

@ -2,8 +2,11 @@
#include "freertos/task.h"
#include "esp_task_wdt.h"
#include "Arduino.h"
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
#if (ARDUINO_USB_CDC_ON_BOOT|ARDUINO_USB_MSC_ON_BOOT|ARDUINO_USB_DFU_ON_BOOT)
#include "USB.h"
#if ARDUINO_USB_MSC_ON_BOOT
#include "FirmwareMSC.h"
#endif
#endif
#ifndef ARDUINO_LOOP_STACK_SIZE
@ -47,9 +50,17 @@ void loopTask(void *pvParameters)
extern "C" void app_main()
{
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
USB.begin();
#if ARDUINO_USB_CDC_ON_BOOT
Serial.begin();
#endif
#if ARDUINO_USB_MSC_ON_BOOT
MSC_Update.begin();
#endif
#if ARDUINO_USB_DFU_ON_BOOT
USB.enableDFU();
#endif
#if ARDUINO_USB_ON_BOOT
USB.begin();
#endif
loopTaskWDTEnabled = false;
initArduino();

View File

@ -30,8 +30,7 @@
#include "stdlib_noniso.h"
#include "esp_system.h"
#if !CONFIG_DSP_ANSI && !CONFIG_DSP_OPTIMIZED
void reverse(char* begin, char* end) {
static void reverse(char* begin, char* end) {
char *is = begin;
char *ie = end - 1;
while(is < ie) {
@ -42,9 +41,6 @@ void reverse(char* begin, char* end) {
--ie;
}
}
#else
void reverse(char* begin, char* end);
#endif
char* ltoa(long value, char* result, int base) {
if(base < 2 || base > 16) {

View File

@ -17,13 +17,11 @@
//#include <limits.h>
#include "wiring_private.h"
#include "pins_arduino.h"
extern uint32_t xthal_get_ccount();
#include <hal/cpu_hal.h>
#define WAIT_FOR_PIN_STATE(state) \
while (digitalRead(pin) != (state)) { \
if (xthal_get_ccount() - start_cycle_count > timeout_cycles) { \
if (cpu_hal_get_cycle_count() - start_cycle_count > timeout_cycles) { \
return 0; \
} \
}
@ -36,12 +34,12 @@ unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
timeout = max_timeout_us;
}
const uint32_t timeout_cycles = microsecondsToClockCycles(timeout);
const uint32_t start_cycle_count = xthal_get_ccount();
const uint32_t start_cycle_count = cpu_hal_get_cycle_count();
WAIT_FOR_PIN_STATE(!state);
WAIT_FOR_PIN_STATE(state);
const uint32_t pulse_start_cycle_count = xthal_get_ccount();
const uint32_t pulse_start_cycle_count = cpu_hal_get_cycle_count();
WAIT_FOR_PIN_STATE(!state);
return clockCyclesToMicroseconds(xthal_get_ccount() - pulse_start_cycle_count);
return clockCyclesToMicroseconds(cpu_hal_get_cycle_count() - pulse_start_cycle_count);
}
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout)

View File

@ -81,6 +81,12 @@ Add here the third party boards, listed by vendors.
creating an `issue on GitHub <https://github.com/espressif/arduino-esp32/issues>`_ and directly
link/mention the vendor in the issue description.
LOLIN
*****
* |board_lolin_d32|
* |board_lolin_d32_pro|
Generic Vendor
**************
@ -104,3 +110,11 @@ Resources
.. _ESP32 Datasheet: https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf
.. _ESP32-S2 Datasheet: https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf
.. _ESP32-C3 Datasheet: https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf
.. |board_lolin_d32| raw:: html
<a href="https://www.wemos.cc/en/latest/d32/d32.html" target="_blank">LOLIN D32</a>
.. |board_lolin_d32_pro| raw:: html
<a href="https://www.wemos.cc/en/latest/d32/d32_pro.html" target="_blank">LOLIN D32 Pro</a>

View File

@ -56,7 +56,7 @@ exclude_patterns = []
# a list of builtin themes.
#
html_theme = 'default'
html_logo = 'logo_espressif.png'
html_logo = '_static/logo_espressif.png'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,

View File

@ -1,5 +1,5 @@
###############
Troubleshotting
Troubleshooting
###############
Common Issues
@ -18,10 +18,36 @@ Building
Flashing
--------
* The board is not flashing.
Why is my board not flashing/uploading when I try to upload my sketch?
**********************************************************************
If you are trying to upload a new sketch and your board isn't responding, there are some possible reasons.
To be able to upload the sketch via serial interface, the ESP32 must be in the download mode. The download mode allows you to upload the sketch over the serial port and to get into it, you need to keep the **GPIO0** in LOW while a resetting (**EN** pin) cycle.
Possible fatal error message from the Arduino IDE:
*A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header*
Here are some steps that you can try to:
* Check your USB cable and try a new one.
* Change the USB port.
* Check your power supply.
* In some instances, you must keep **GPIO0** LOW during the uploading process via serial interface.
* Hold-down the **“BOOT”** button in your ESP32 board while uploading/flashing.
In some development boards, you can try adding the reset delay circuit, as decribed in the *Power-on Sequence* section on the `ESP32 Hardware Design Guidelines <https://www.espressif.com/sites/default/files/documentation/esp32_hardware_design_guidelines_en.pdf>`_ in order to get into the download mode automatically.
Hardware
--------
* Power Source
* Bad USB cable or charging only cables
Why is my computer not detecting my board?
**************************************************
If your board is not detected after connecting into the USB, you can try to:
* Check if the USB driver is missing. - `USB Driver Download Link <https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers>`_
* Check your USB cable and try a new one.
* Change the USB port.
* Check your power supply.
* Check if the board is damaged or defective.

View File

@ -347,7 +347,7 @@ gatts_event_handler BLEDevice::m_customGattsHandler = nullptr;
return;
}
#ifndef CLASSIC_BT_ENABLED
#ifndef CONFIG_BT_CLASSIC_ENABLED
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
#endif
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
@ -357,7 +357,7 @@ gatts_event_handler BLEDevice::m_customGattsHandler = nullptr;
return;
}
#ifndef CLASSIC_BT_ENABLED
#ifndef CONFIG_BT_CLASSIC_ENABLED
errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (errRc != ESP_OK) {
log_e("esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));

View File

@ -148,6 +148,10 @@ void setup(){
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println("Contacting Time Server");
/*
Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
*/
configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
struct tm tmstruct ;
delay(2000);

View File

@ -1264,6 +1264,8 @@ int HTTPClient::handleHeaderResponse()
log_d("Transfer-Encoding: %s", transferEncoding.c_str());
if(transferEncoding.equalsIgnoreCase("chunked")) {
_transferEncoding = HTTPC_TE_CHUNKED;
} else if(transferEncoding.equalsIgnoreCase("identity")) {
_transferEncoding = HTTPC_TE_IDENTITY;
} else {
return HTTPC_ERROR_ENCODING;
}

View File

@ -17,6 +17,10 @@ WiFiMulti WiFiMulti;
// Set time via NTP, as required for x.509 validation
void setClock() {
/*
Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
*/
configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // UTC
Serial.print(F("Waiting for NTP time sync: "));

View File

@ -160,6 +160,10 @@ void setup(){
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println("Contacting Time Server");
/*
Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
*/
configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
struct tm tmstruct ;
delay(2000);

View File

@ -164,6 +164,10 @@ void setup(){
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println("Contacting Time Server");
/*
Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
*/
configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
struct tm tmstruct ;
delay(2000);

View File

@ -164,6 +164,10 @@ void setup(){
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println("Contacting Time Server");
/*
Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
*/
configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
struct tm tmstruct ;
delay(2000);

View File

@ -148,6 +148,10 @@ void setup(){
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println("Contacting Time Server");
/*
Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
*/
configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
struct tm tmstruct ;
delay(2000);

View File

@ -0,0 +1,213 @@
#include "USB.h"
#include "USBHIDMouse.h"
#include "USBHIDKeyboard.h"
#include "USBHIDGamepad.h"
#include "USBHIDConsumerControl.h"
#include "USBHIDSystemControl.h"
#include "USBHIDVendor.h"
#include "FirmwareMSC.h"
#if !ARDUINO_USB_MSC_ON_BOOT
FirmwareMSC MSC_Update;
#endif
#if ARDUINO_USB_CDC_ON_BOOT
#define HWSerial Serial0
#define USBSerial Serial
#else
#define HWSerial Serial
USBCDC USBSerial;
#endif
USBHID HID;
USBHIDKeyboard Keyboard;
USBHIDMouse Mouse;
USBHIDGamepad Gamepad;
USBHIDConsumerControl ConsumerControl;
USBHIDSystemControl SystemControl;
USBHIDVendor Vendor;
const int buttonPin = 0;
int previousButtonState = HIGH;
static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
if(event_base == ARDUINO_USB_EVENTS){
arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data;
switch (event_id){
case ARDUINO_USB_STARTED_EVENT:
HWSerial.println("USB PLUGGED");
break;
case ARDUINO_USB_STOPPED_EVENT:
HWSerial.println("USB UNPLUGGED");
break;
case ARDUINO_USB_SUSPEND_EVENT:
HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en);
break;
case ARDUINO_USB_RESUME_EVENT:
HWSerial.println("USB RESUMED");
break;
default:
break;
}
} else if(event_base == ARDUINO_USB_CDC_EVENTS){
arduino_usb_cdc_event_data_t * data = (arduino_usb_cdc_event_data_t*)event_data;
switch (event_id){
case ARDUINO_USB_CDC_CONNECTED_EVENT:
HWSerial.println("CDC CONNECTED");
break;
case ARDUINO_USB_CDC_DISCONNECTED_EVENT:
HWSerial.println("CDC DISCONNECTED");
break;
case ARDUINO_USB_CDC_LINE_STATE_EVENT:
HWSerial.printf("CDC LINE STATE: dtr: %u, rts: %u\n", data->line_state.dtr, data->line_state.rts);
break;
case ARDUINO_USB_CDC_LINE_CODING_EVENT:
HWSerial.printf("CDC LINE CODING: bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u\n", data->line_coding.bit_rate, data->line_coding.data_bits, data->line_coding.stop_bits, data->line_coding.parity);
break;
case ARDUINO_USB_CDC_RX_EVENT:
HWSerial.printf("CDC RX [%u]:", data->rx.len);
{
uint8_t buf[data->rx.len];
size_t len = USBSerial.read(buf, data->rx.len);
HWSerial.write(buf, len);
}
HWSerial.println();
break;
default:
break;
}
} else if(event_base == ARDUINO_FIRMWARE_MSC_EVENTS){
arduino_firmware_msc_event_data_t * data = (arduino_firmware_msc_event_data_t*)event_data;
switch (event_id){
case ARDUINO_FIRMWARE_MSC_START_EVENT:
HWSerial.println("MSC Update Start");
break;
case ARDUINO_FIRMWARE_MSC_WRITE_EVENT:
//HWSerial.printf("MSC Update Write %u bytes at offset %u\n", data->write.size, data->write.offset);
HWSerial.print(".");
break;
case ARDUINO_FIRMWARE_MSC_END_EVENT:
HWSerial.printf("\nMSC Update End: %u bytes\n", data->end.size);
break;
case ARDUINO_FIRMWARE_MSC_ERROR_EVENT:
HWSerial.printf("MSC Update ERROR! Progress: %u bytes\n", data->error.size);
break;
case ARDUINO_FIRMWARE_MSC_POWER_EVENT:
HWSerial.printf("MSC Update Power: power: %u, start: %u, eject: %u\n", data->power.power_condition, data->power.start, data->power.load_eject);
break;
default:
break;
}
} else if(event_base == ARDUINO_USB_HID_EVENTS){
arduino_usb_hid_event_data_t * data = (arduino_usb_hid_event_data_t*)event_data;
switch (event_id){
case ARDUINO_USB_HID_SET_PROTOCOL_EVENT:
HWSerial.printf("HID SET PROTOCOL: %s\n", data->set_protocol.protocol?"REPORT":"BOOT");
break;
case ARDUINO_USB_HID_SET_IDLE_EVENT:
HWSerial.printf("HID SET IDLE: %u\n", data->set_idle.idle_rate);
break;
default:
break;
}
} else if(event_base == ARDUINO_USB_HID_KEYBOARD_EVENTS){
arduino_usb_hid_keyboard_event_data_t * data = (arduino_usb_hid_keyboard_event_data_t*)event_data;
switch (event_id){
case ARDUINO_USB_HID_KEYBOARD_LED_EVENT:
HWSerial.printf("HID KEYBOARD LED: NumLock:%u, CapsLock:%u, ScrollLock:%u\n", data->numlock, data->capslock, data->scrolllock);
break;
default:
break;
}
} else if(event_base == ARDUINO_USB_HID_VENDOR_EVENTS){
arduino_usb_hid_vendor_event_data_t * data = (arduino_usb_hid_vendor_event_data_t*)event_data;
switch (event_id){
case ARDUINO_USB_HID_VENDOR_GET_FEATURE_EVENT:
HWSerial.printf("HID VENDOR GET FEATURE: len:%u\n", data->len);
for(uint16_t i=0; i<data->len; i++){
HWSerial.write(data->buffer[i]?data->buffer[i]:'.');
}
HWSerial.println();
break;
case ARDUINO_USB_HID_VENDOR_SET_FEATURE_EVENT:
HWSerial.printf("HID VENDOR SET FEATURE: len:%u\n", data->len);
for(uint16_t i=0; i<data->len; i++){
HWSerial.write(data->buffer[i]?data->buffer[i]:'.');
}
HWSerial.println();
break;
case ARDUINO_USB_HID_VENDOR_OUTPUT_EVENT:
HWSerial.printf("HID VENDOR OUTPUT: len:%u\n", data->len);
for(uint16_t i=0; i<data->len; i++){
HWSerial.write(Vendor.read());
}
HWSerial.println();
break;
default:
break;
}
}
}
void setup() {
HWSerial.begin(115200);
HWSerial.setDebugOutput(true);
USB.onEvent(usbEventCallback);
USBSerial.onEvent(usbEventCallback);
MSC_Update.onEvent(usbEventCallback);
HID.onEvent(usbEventCallback);
Keyboard.onEvent(usbEventCallback);
Vendor.onEvent(usbEventCallback);
USBSerial.begin();
MSC_Update.begin();
Vendor.begin();
Mouse.begin();
Keyboard.begin();
Gamepad.begin();
ConsumerControl.begin();
SystemControl.begin();
USB.begin();
}
void loop() {
int buttonState = digitalRead(buttonPin);
if (HID.ready() && buttonState != previousButtonState) {
previousButtonState = buttonState;
if (buttonState == LOW) {
HWSerial.println("Button Pressed");
USBSerial.println("Button Pressed");
Vendor.println("Button Pressed");
Mouse.move(10,10);
Keyboard.pressRaw(HID_KEY_CAPS_LOCK);
Gamepad.leftStick(100,100);
ConsumerControl.press(CONSUMER_CONTROL_VOLUME_INCREMENT);
//SystemControl.press(SYSTEM_CONTROL_POWER_OFF);
} else {
Keyboard.releaseRaw(HID_KEY_CAPS_LOCK);
Gamepad.leftStick(0,0);
ConsumerControl.release();
//SystemControl.release();
Vendor.println("Button Released");
USBSerial.println("Button Released");
HWSerial.println("Button Released");
}
delay(100);
}
while(HWSerial.available()){
size_t l = HWSerial.available();
uint8_t b[l];
l = HWSerial.read(b, l);
USBSerial.write(b, l);
if(HID.ready()){
Vendor.write(b,l);
}
}
}

View File

@ -0,0 +1,21 @@
#include "USB.h"
#include "USBHIDConsumerControl.h"
USBHIDConsumerControl ConsumerControl;
const int buttonPin = 0;
int previousButtonState = HIGH;
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
ConsumerControl.begin();
USB.begin();
}
void loop() {
int buttonState = digitalRead(buttonPin);
if ((buttonState != previousButtonState) && (buttonState == LOW)) {
ConsumerControl.press(CONSUMER_CONTROL_VOLUME_INCREMENT);
ConsumerControl.release();
}
previousButtonState = buttonState;
}

View File

@ -0,0 +1,79 @@
#include "USB.h"
#include "USBHID.h"
USBHID HID;
static const uint8_t report_descriptor[] = { // 8 axis
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x04, // Usage (Joystick)
0xa1, 0x01, // Collection (Application)
0xa1, 0x00, // Collection (Physical)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x33, // Usage (Rx)
0x09, 0x34, // Usage (Ry)
0x09, 0x35, // Usage (Rz)
0x09, 0x36, // Usage (Slider)
0x09, 0x36, // Usage (Slider)
0x15, 0x81, // Logical Minimum (-127)
0x25, 0x7f, // Logical Maximum (127)
0x75, 0x08, // Report Size (8)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xC0, // End Collection
};
class CustomHIDDevice: public USBHIDDevice {
public:
CustomHIDDevice(void){
static bool initialized = false;
if(!initialized){
initialized = true;
HID.addDevice(this, sizeof(report_descriptor));
}
}
void begin(void){
HID.begin();
}
uint16_t _onGetDescriptor(uint8_t* buffer){
memcpy(buffer, report_descriptor, sizeof(report_descriptor));
return sizeof(report_descriptor);
}
bool send(uint8_t * value){
return HID.SendReport(0, value, 8);
}
};
CustomHIDDevice Device;
const int buttonPin = 0;
int previousButtonState = HIGH;
uint8_t axis[8];
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
pinMode(buttonPin, INPUT_PULLUP);
Device.begin();
USB.begin();
}
void loop() {
int buttonState = digitalRead(buttonPin);
if (HID.ready() && buttonState != previousButtonState) {
previousButtonState = buttonState;
if (buttonState == LOW) {
Serial.println("Button Pressed");
axis[0] = random() & 0xFF;
Device.send(axis);
} else {
Serial.println("Button Released");
}
delay(100);
}
}

View File

@ -0,0 +1,74 @@
#include "USB.h"
#include "FirmwareMSC.h"
#if !ARDUINO_USB_MSC_ON_BOOT
FirmwareMSC MSC_Update;
#endif
#if ARDUINO_USB_CDC_ON_BOOT
#define HWSerial Serial0
#define USBSerial Serial
#else
#define HWSerial Serial
USBCDC USBSerial;
#endif
static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
if(event_base == ARDUINO_USB_EVENTS){
arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data;
switch (event_id){
case ARDUINO_USB_STARTED_EVENT:
HWSerial.println("USB PLUGGED");
break;
case ARDUINO_USB_STOPPED_EVENT:
HWSerial.println("USB UNPLUGGED");
break;
case ARDUINO_USB_SUSPEND_EVENT:
HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en);
break;
case ARDUINO_USB_RESUME_EVENT:
HWSerial.println("USB RESUMED");
break;
default:
break;
}
} else if(event_base == ARDUINO_FIRMWARE_MSC_EVENTS){
arduino_firmware_msc_event_data_t * data = (arduino_firmware_msc_event_data_t*)event_data;
switch (event_id){
case ARDUINO_FIRMWARE_MSC_START_EVENT:
HWSerial.println("MSC Update Start");
break;
case ARDUINO_FIRMWARE_MSC_WRITE_EVENT:
//HWSerial.printf("MSC Update Write %u bytes at offset %u\n", data->write.size, data->write.offset);
HWSerial.print(".");
break;
case ARDUINO_FIRMWARE_MSC_END_EVENT:
HWSerial.printf("\nMSC Update End: %u bytes\n", data->end.size);
break;
case ARDUINO_FIRMWARE_MSC_ERROR_EVENT:
HWSerial.printf("MSC Update ERROR! Progress: %u bytes\n", data->error.size);
break;
case ARDUINO_FIRMWARE_MSC_POWER_EVENT:
HWSerial.printf("MSC Update Power: power: %u, start: %u, eject: %u", data->power.power_condition, data->power.start, data->power.load_eject);
break;
default:
break;
}
}
}
void setup() {
HWSerial.begin(115200);
HWSerial.setDebugOutput(true);
USB.onEvent(usbEventCallback);
MSC_Update.onEvent(usbEventCallback);
MSC_Update.begin();
USBSerial.begin();
USB.begin();
}
void loop() {
// put your main code here, to run repeatedly
}

View File

@ -0,0 +1,21 @@
#include "USB.h"
#include "USBHIDGamepad.h"
USBHIDGamepad Gamepad;
const int buttonPin = 0;
int previousButtonState = HIGH;
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
Gamepad.begin();
USB.begin();
}
void loop() {
int buttonState = digitalRead(buttonPin);
if ((buttonState != previousButtonState) && (buttonState == LOW)) {
Gamepad.pressButton(BUTTON_START);
Gamepad.releaseButton(BUTTON_START);
}
previousButtonState = buttonState;
}

View File

@ -0,0 +1,52 @@
#include "USB.h"
#include "USBHIDVendor.h"
USBHIDVendor Vendor;
const int buttonPin = 0;
int previousButtonState = HIGH;
static void vendorEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
if(event_base == ARDUINO_USB_HID_VENDOR_EVENTS){
arduino_usb_hid_vendor_event_data_t * data = (arduino_usb_hid_vendor_event_data_t*)event_data;
switch (event_id){
case ARDUINO_USB_HID_VENDOR_GET_FEATURE_EVENT:
Serial.printf("HID VENDOR GET FEATURE: len:%u\n", data->len);
break;
case ARDUINO_USB_HID_VENDOR_SET_FEATURE_EVENT:
Serial.printf("HID VENDOR SET FEATURE: len:%u\n", data->len);
for(uint16_t i=0; i<data->len; i++){
Serial.printf("0x%02X ",data->buffer);
}
Serial.println();
break;
case ARDUINO_USB_HID_VENDOR_OUTPUT_EVENT:
Serial.printf("HID VENDOR OUTPUT: len:%u\n", data->len);
// for(uint16_t i=0; i<data->len; i++){
// Serial.write(Vendor.read());
// }
break;
default:
break;
}
}
}
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
Serial.begin(115200);
Vendor.onEvent(vendorEventCallback);
Vendor.begin();
USB.begin();
}
void loop() {
int buttonState = digitalRead(buttonPin);
if ((buttonState != previousButtonState) && (buttonState == LOW)) {
Vendor.println("Hello World!");
}
previousButtonState = buttonState;
while(Vendor.available()){
Serial.write(Vendor.read());
}
}

View File

@ -0,0 +1,92 @@
/*
Keyboard logout
This sketch demonstrates the Keyboard library.
When you connect pin 2 to ground, it performs a logout.
It uses keyboard combinations to do this, as follows:
On Windows, CTRL-ALT-DEL followed by ALT-l
On Ubuntu, CTRL-ALT-DEL, and ENTER
On OSX, CMD-SHIFT-q
To wake: Spacebar.
Circuit:
- Arduino Leonardo or Micro
- wire to connect D2 to ground
created 6 Mar 2012
modified 27 Mar 2012
by Tom Igoe
This example is in the public domain.
http://www.arduino.cc/en/Tutorial/KeyboardLogout
*/
#define OSX 0
#define WINDOWS 1
#define UBUNTU 2
#include "USB.h"
#include "USBHIDKeyboard.h"
USBHIDKeyboard Keyboard;
// change this to match your platform:
int platform = OSX;
void setup() {
// make pin 0 an input and turn on the pull-up resistor so it goes high unless
// connected to ground:
pinMode(0, INPUT_PULLUP);
Keyboard.begin();
USB.begin();
}
void loop() {
while (digitalRead(0) == HIGH) {
// do nothing until pin 2 goes low
delay(500);
}
delay(1000);
switch (platform) {
case OSX:
Keyboard.press(KEY_LEFT_GUI);
// Shift-Q logs out:
Keyboard.press(KEY_LEFT_SHIFT);
Keyboard.press('Q');
delay(100);
Keyboard.releaseAll();
// enter:
Keyboard.write(KEY_RETURN);
break;
case WINDOWS:
// CTRL-ALT-DEL:
Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press(KEY_LEFT_ALT);
Keyboard.press(KEY_DELETE);
delay(100);
Keyboard.releaseAll();
// ALT-l:
delay(2000);
Keyboard.press(KEY_LEFT_ALT);
Keyboard.press('l');
Keyboard.releaseAll();
break;
case UBUNTU:
// CTRL-ALT-DEL:
Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press(KEY_LEFT_ALT);
Keyboard.press(KEY_DELETE);
delay(1000);
Keyboard.releaseAll();
// Enter to confirm logout:
Keyboard.write(KEY_RETURN);
break;
}
// do nothing:
while (true) delay(1000);
}

View File

@ -0,0 +1,55 @@
/*
Keyboard Message test
For the Arduino Leonardo and Micro.
Sends a text string when a button is pressed.
The circuit:
- pushbutton attached from pin 0 to ground
- 10 kilohm resistor attached from pin 0 to +5V
created 24 Oct 2011
modified 27 Mar 2012
by Tom Igoe
modified 11 Nov 2013
by Scott Fitzgerald
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/KeyboardMessage
*/
#include "USB.h"
#include "USBHIDKeyboard.h"
USBHIDKeyboard Keyboard;
const int buttonPin = 0; // input pin for pushbutton
int previousButtonState = HIGH; // for checking the state of a pushButton
int counter = 0; // button push counter
void setup() {
// make the pushButton pin an input:
pinMode(buttonPin, INPUT_PULLUP);
// initialize control over the keyboard:
Keyboard.begin();
USB.begin();
}
void loop() {
// read the pushbutton:
int buttonState = digitalRead(buttonPin);
// if the button state has changed,
if ((buttonState != previousButtonState)
// and it's currently pressed:
&& (buttonState == LOW)) {
// increment the button counter
counter++;
// type out a message
Keyboard.print("You pressed the button ");
Keyboard.print(counter);
Keyboard.println(" times.");
}
// save the current button state for comparison next time:
previousButtonState = buttonState;
}

View File

@ -0,0 +1,106 @@
/*
Arduino Programs Blink
This sketch demonstrates the Keyboard library.
For Leonardo and Due boards only.
When you connect pin 2 to ground, it creates a new window with a key
combination (CTRL-N), then types in the Blink sketch, then auto-formats the
text using another key combination (CTRL-T), then uploads the sketch to the
currently selected Arduino using a final key combination (CTRL-U).
Circuit:
- Arduino Leonardo, Micro, Due, LilyPad USB, or Yún
- wire to connect D2 to ground
created 5 Mar 2012
modified 29 Mar 2012
by Tom Igoe
modified 3 May 2014
by Scott Fitzgerald
This example is in the public domain.
http://www.arduino.cc/en/Tutorial/KeyboardReprogram
*/
#include "USB.h"
#include "USBHIDKeyboard.h"
USBHIDKeyboard Keyboard;
// use this option for OSX.
// Comment it out if using Windows or Linux:
char ctrlKey = KEY_LEFT_GUI;
// use this option for Windows and Linux.
// leave commented out if using OSX:
// char ctrlKey = KEY_LEFT_CTRL;
void setup() {
// make pin 0 an input and turn on the pull-up resistor so it goes high unless
// connected to ground:
pinMode(0, INPUT_PULLUP);
// initialize control over the keyboard:
Keyboard.begin();
USB.begin();
}
void loop() {
while (digitalRead(0) == HIGH) {
// do nothing until pin 0 goes low
delay(500);
}
delay(1000);
// new document:
Keyboard.press(ctrlKey);
Keyboard.press('n');
delay(100);
Keyboard.releaseAll();
// wait for new window to open:
delay(1000);
// versions of the Arduino IDE after 1.5 pre-populate new sketches with
// setup() and loop() functions let's clear the window before typing anything new
// select all
Keyboard.press(ctrlKey);
Keyboard.press('a');
delay(500);
Keyboard.releaseAll();
// delete the selected text
Keyboard.write(KEY_BACKSPACE);
delay(500);
// Type out "blink":
Keyboard.println("void setup() {");
Keyboard.println("pinMode(13, OUTPUT);");
Keyboard.println("}");
Keyboard.println();
Keyboard.println("void loop() {");
Keyboard.println("digitalWrite(13, HIGH);");
Keyboard.print("delay(3000);");
// 3000 ms is too long. Delete it:
for (int keystrokes = 0; keystrokes < 6; keystrokes++) {
delay(500);
Keyboard.write(KEY_BACKSPACE);
}
// make it 1000 instead:
Keyboard.println("1000);");
Keyboard.println("digitalWrite(13, LOW);");
Keyboard.println("delay(1000);");
Keyboard.println("}");
// tidy up:
Keyboard.press(ctrlKey);
Keyboard.press('t');
delay(100);
Keyboard.releaseAll();
delay(3000);
// upload code:
Keyboard.press(ctrlKey);
Keyboard.press('u');
delay(100);
Keyboard.releaseAll();
// wait for the sweet oblivion of reprogramming:
while (true)delay(1000);
}

View File

@ -0,0 +1,40 @@
/*
Keyboard test
Reads a byte from the serial port, sends a keystroke back.
The sent keystroke is one higher than what's received, e.g. if you send a,
you get b, send A you get B, and so forth.
The circuit:
- none
created 21 Oct 2011
modified 27 Mar 2012
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/KeyboardSerial
*/
#include "USB.h"
#include "USBHIDKeyboard.h"
USBHIDKeyboard Keyboard;
void setup() {
// open the serial port:
Serial.begin(115200);
// initialize control over the keyboard:
Keyboard.begin();
USB.begin();
}
void loop() {
// check for incoming serial data:
if (Serial.available() > 0) {
// read incoming serial data:
char inChar = Serial.read();
// Type the next ASCII value from what you received:
Keyboard.write(inChar + 1);
}
}

View File

@ -0,0 +1,95 @@
/*
KeyboardAndMouseControl
Hardware:
- five pushbuttons attached to D12, D13, D14, D15, D0
The mouse movement is always relative. This sketch reads four pushbuttons, and
uses them to set the movement of the mouse.
WARNING: When you use the Mouse.move() command, the Arduino takes over your
mouse! Make sure you have control before you use the mouse commands.
created 15 Mar 2012
modified 27 Mar 2012
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/KeyboardAndMouseControl
*/
#include "USB.h"
#include "USBHIDMouse.h"
#include "USBHIDKeyboard.h"
USBHIDMouse Mouse;
USBHIDKeyboard Keyboard;
// set pin numbers for the five buttons:
const int upButton = 12;
const int downButton = 13;
const int leftButton = 14;
const int rightButton = 15;
const int mouseButton = 0;
void setup() { // initialize the buttons' inputs:
pinMode(upButton, INPUT_PULLUP);
pinMode(downButton, INPUT_PULLUP);
pinMode(leftButton, INPUT_PULLUP);
pinMode(rightButton, INPUT_PULLUP);
pinMode(mouseButton, INPUT_PULLUP);
Serial.begin(115200);
// initialize mouse control:
Mouse.begin();
Keyboard.begin();
USB.begin();
}
void loop() {
// use serial input to control the mouse:
if (Serial.available() > 0) {
char inChar = Serial.read();
switch (inChar) {
case 'u':
// move mouse up
Mouse.move(0, -40);
break;
case 'd':
// move mouse down
Mouse.move(0, 40);
break;
case 'l':
// move mouse left
Mouse.move(-40, 0);
break;
case 'r':
// move mouse right
Mouse.move(40, 0);
break;
case 'm':
// perform mouse left click
Mouse.click(MOUSE_LEFT);
break;
}
}
// use the pushbuttons to control the keyboard:
if (digitalRead(upButton) == LOW) {
Keyboard.write('u');
}
if (digitalRead(downButton) == LOW) {
Keyboard.write('d');
}
if (digitalRead(leftButton) == LOW) {
Keyboard.write('l');
}
if (digitalRead(rightButton) == LOW) {
Keyboard.write('r');
}
if (digitalRead(mouseButton) == LOW) {
Keyboard.write('m');
}
delay(5);
}

View File

@ -0,0 +1,86 @@
/*
ButtonMouseControl
Controls the mouse from five pushbuttons on an Arduino Leonardo, Micro or Due.
Hardware:
- five pushbuttons attached to D12, D13, D14, D15, D0
The mouse movement is always relative. This sketch reads four pushbuttons,
and uses them to set the movement of the mouse.
WARNING: When you use the Mouse.move() command, the Arduino takes over your
mouse! Make sure you have control before you use the mouse commands.
created 15 Mar 2012
modified 27 Mar 2012
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/ButtonMouseControl
*/
#include "USB.h"
#include "USBHIDMouse.h"
USBHIDMouse Mouse;
// set pin numbers for the five buttons:
const int upButton = 12;
const int downButton = 13;
const int leftButton = 14;
const int rightButton = 15;
const int mouseButton = 0;
int range = 5; // output range of X or Y movement; affects movement speed
int responseDelay = 10; // response delay of the mouse, in ms
void setup() {
// initialize the buttons' inputs:
pinMode(upButton, INPUT_PULLUP);
pinMode(downButton, INPUT_PULLUP);
pinMode(leftButton, INPUT_PULLUP);
pinMode(rightButton, INPUT_PULLUP);
pinMode(mouseButton, INPUT_PULLUP);
// initialize mouse control:
Mouse.begin();
USB.begin();
}
void loop() {
// read the buttons:
int upState = digitalRead(upButton);
int downState = digitalRead(downButton);
int rightState = digitalRead(rightButton);
int leftState = digitalRead(leftButton);
int clickState = digitalRead(mouseButton);
// calculate the movement distance based on the button states:
int xDistance = (leftState - rightState) * range;
int yDistance = (upState - downState) * range;
// if X or Y is non-zero, move:
if ((xDistance != 0) || (yDistance != 0)) {
Mouse.move(xDistance, yDistance, 0);
}
// if the mouse button is pressed:
if (clickState == LOW) {
// if the mouse is not pressed, press it:
if (!Mouse.isPressed(MOUSE_LEFT)) {
Mouse.press(MOUSE_LEFT);
}
}
// else the mouse button is not pressed:
else {
// if the mouse is pressed, release it:
if (Mouse.isPressed(MOUSE_LEFT)) {
Mouse.release(MOUSE_LEFT);
}
}
// a delay so the mouse doesn't move too fast:
delay(responseDelay);
}

View File

@ -0,0 +1,21 @@
#include "USB.h"
#include "USBHIDSystemControl.h"
USBHIDSystemControl SystemControl;
const int buttonPin = 0;
int previousButtonState = HIGH;
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
SystemControl.begin();
USB.begin();
}
void loop() {
int buttonState = digitalRead(buttonPin);
if ((buttonState != previousButtonState) && (buttonState == LOW)) {
SystemControl.press(SYSTEM_CONTROL_POWER_OFF);
SystemControl.release();
}
previousButtonState = buttonState;
}

View File

@ -0,0 +1,192 @@
#include "USB.h"
#include "USBMSC.h"
#if ARDUINO_USB_CDC_ON_BOOT
#define HWSerial Serial0
#define USBSerial Serial
#else
#define HWSerial Serial
USBCDC USBSerial;
#endif
USBMSC MSC;
#define FAT_U8(v) ((v) & 0xFF)
#define FAT_U16(v) FAT_U8(v), FAT_U8((v) >> 8)
#define FAT_U32(v) FAT_U8(v), FAT_U8((v) >> 8), FAT_U8((v) >> 16), FAT_U8((v) >> 24)
#define FAT_MS2B(s,ms) FAT_U8(((((s) & 0x1) * 1000) + (ms)) / 10)
#define FAT_HMS2B(h,m,s) FAT_U8(((s) >> 1)|(((m) & 0x7) << 5)), FAT_U8((((m) >> 3) & 0x7)|((h) << 3))
#define FAT_YMD2B(y,m,d) FAT_U8(((d) & 0x1F)|(((m) & 0x7) << 5)), FAT_U8((((m) >> 3) & 0x1)|((((y) - 1980) & 0x7F) << 1))
#define FAT_TBL2B(l,h) FAT_U8(l), FAT_U8(((l >> 8) & 0xF) | ((h << 4) & 0xF0)), FAT_U8(h >> 4)
#define README_CONTENTS "This is tinyusb's MassStorage Class demo.\r\n\r\nIf you find any bugs or get any questions, feel free to file an\r\nissue at github.com/hathach/tinyusb"
static const uint32_t DISK_SECTOR_COUNT = 2 * 8; // 8KB is the smallest size that windows allow to mount
static const uint16_t DISK_SECTOR_SIZE = 512; // Should be 512
static const uint16_t DISC_SECTORS_PER_TABLE = 1; //each table sector can fit 170KB (340 sectors)
static uint8_t msc_disk[DISK_SECTOR_COUNT][DISK_SECTOR_SIZE] =
{
//------------- Block0: Boot Sector -------------//
{
// Header (62 bytes)
0xEB, 0x3C, 0x90, //jump_instruction
'M' , 'S' , 'D' , 'O' , 'S' , '5' , '.' , '0' , //oem_name
FAT_U16(DISK_SECTOR_SIZE), //bytes_per_sector
FAT_U8(1), //sectors_per_cluster
FAT_U16(1), //reserved_sectors_count
FAT_U8(1), //file_alloc_tables_num
FAT_U16(16), //max_root_dir_entries
FAT_U16(DISK_SECTOR_COUNT), //fat12_sector_num
0xF8, //media_descriptor
FAT_U16(DISC_SECTORS_PER_TABLE), //sectors_per_alloc_table;//FAT12 and FAT16
FAT_U16(1), //sectors_per_track;//A value of 0 may indicate LBA-only access
FAT_U16(1), //num_heads
FAT_U32(0), //hidden_sectors_count
FAT_U32(0), //total_sectors_32
0x00, //physical_drive_number;0x00 for (first) removable media, 0x80 for (first) fixed disk
0x00, //reserved
0x29, //extended_boot_signature;//should be 0x29
FAT_U32(0x1234), //serial_number: 0x1234 => 1234
'T' , 'i' , 'n' , 'y' , 'U' , 'S' , 'B' , ' ' , 'M' , 'S' , 'C' , //volume_label padded with spaces (0x20)
'F' , 'A' , 'T' , '1' , '2' , ' ' , ' ' , ' ' , //file_system_type padded with spaces (0x20)
// Zero up to 2 last bytes of FAT magic code (448 bytes)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
//boot signature (2 bytes)
0x55, 0xAA
},
//------------- Block1: FAT12 Table -------------//
{
FAT_TBL2B(0xFF8, 0xFFF), FAT_TBL2B(0xFFF, 0x000) // first 2 entries must be 0xFF8 0xFFF, third entry is cluster end of readme file
},
//------------- Block2: Root Directory -------------//
{
// first entry is volume label
'E' , 'S' , 'P' , '3' , '2' , 'S' , '2' , ' ' ,
'M' , 'S' , 'C' ,
0x08, //FILE_ATTR_VOLUME_LABEL
0x00,
FAT_MS2B(0,0),
FAT_HMS2B(0,0,0),
FAT_YMD2B(0,0,0),
FAT_YMD2B(0,0,0),
FAT_U16(0),
FAT_HMS2B(13,42,30), //last_modified_hms
FAT_YMD2B(2018,11,5), //last_modified_ymd
FAT_U16(0),
FAT_U32(0),
// second entry is readme file
'R' , 'E' , 'A' , 'D' , 'M' , 'E' , ' ' , ' ' ,//file_name[8]; padded with spaces (0x20)
'T' , 'X' , 'T' , //file_extension[3]; padded with spaces (0x20)
0x20, //file attributes: FILE_ATTR_ARCHIVE
0x00, //ignore
FAT_MS2B(1,980), //creation_time_10_ms (max 199x10 = 1s 990ms)
FAT_HMS2B(13,42,36), //create_time_hms [5:6:5] => h:m:(s/2)
FAT_YMD2B(2018,11,5), //create_time_ymd [7:4:5] => (y+1980):m:d
FAT_YMD2B(2020,11,5), //last_access_ymd
FAT_U16(0), //extended_attributes
FAT_HMS2B(13,44,16), //last_modified_hms
FAT_YMD2B(2019,11,5), //last_modified_ymd
FAT_U16(2), //start of file in cluster
FAT_U32(sizeof(README_CONTENTS) - 1) //file size
},
//------------- Block3: Readme Content -------------//
README_CONTENTS
};
static int32_t onWrite(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize){
HWSerial.printf("MSC WRITE: lba: %u, offset: %u, bufsize: %u\n", lba, offset, bufsize);
memcpy(msc_disk[lba] + offset, buffer, bufsize);
return bufsize;
}
static int32_t onRead(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize){
HWSerial.printf("MSC READ: lba: %u, offset: %u, bufsize: %u\n", lba, offset, bufsize);
memcpy(buffer, msc_disk[lba] + offset, bufsize);
return bufsize;
}
static bool onStartStop(uint8_t power_condition, bool start, bool load_eject){
HWSerial.printf("MSC START/STOP: power: %u, start: %u, eject: %u\n", power_condition, start, load_eject);
return true;
}
static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
if(event_base == ARDUINO_USB_EVENTS){
arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data;
switch (event_id){
case ARDUINO_USB_STARTED_EVENT:
HWSerial.println("USB PLUGGED");
break;
case ARDUINO_USB_STOPPED_EVENT:
HWSerial.println("USB UNPLUGGED");
break;
case ARDUINO_USB_SUSPEND_EVENT:
HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en);
break;
case ARDUINO_USB_RESUME_EVENT:
HWSerial.println("USB RESUMED");
break;
default:
break;
}
}
}
void setup() {
HWSerial.begin(115200);
HWSerial.setDebugOutput(true);
USB.onEvent(usbEventCallback);
MSC.vendorID("ESP32");//max 8 chars
MSC.productID("USB_MSC");//max 16 chars
MSC.productRevision("1.0");//max 4 chars
MSC.onStartStop(onStartStop);
MSC.onRead(onRead);
MSC.onWrite(onWrite);
MSC.mediaPresent(true);
MSC.begin(DISK_SECTOR_COUNT, DISK_SECTOR_SIZE);
USBSerial.begin();
USB.begin();
}
void loop() {
// put your main code here, to run repeatedly:
}

View File

@ -1,6 +1,6 @@
#include "USB.h"
#if ARDUINO_SERIAL_PORT
#if ARDUINO_USB_CDC_ON_BOOT
#define HWSerial Serial0
#define USBSerial Serial
#else
@ -66,14 +66,8 @@ void setup() {
USB.onEvent(usbEventCallback);
USBSerial.onEvent(usbEventCallback);
#if !ARDUINO_SERIAL_PORT
USB.enableDFU();
USB.webUSB(true);
USB.webUSBURL("http://localhost/webusb");
USB.productName("ESP32S2-USB");
USB.begin();
USBSerial.begin();
#endif
USB.begin();
}
void loop() {

View File

@ -0,0 +1,191 @@
#include "USB.h"
#include "USBVendor.h"
#if ARDUINO_USB_CDC_ON_BOOT
#define HWSerial Serial0
#else
#define HWSerial Serial
#endif
USBVendor Vendor;
const int buttonPin = 0;
//CDC Control Requests
#define REQUEST_SET_LINE_CODING 0x20
#define REQUEST_GET_LINE_CODING 0x21
#define REQUEST_SET_CONTROL_LINE_STATE 0x22
//CDC Line Coding Control Request Structure
typedef struct __attribute__ ((packed)) {
uint32_t bit_rate;
uint8_t stop_bits; //0: 1 stop bit, 1: 1.5 stop bits, 2: 2 stop bits
uint8_t parity; //0: None, 1: Odd, 2: Even, 3: Mark, 4: Space
uint8_t data_bits; //5, 6, 7, 8 or 16
} request_line_coding_t;
static request_line_coding_t vendor_line_coding = {9600, 0, 0, 8};
// Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
static uint8_t vendor_line_state = 0;
//USB and Vendor events
static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
if (event_base == ARDUINO_USB_EVENTS) {
arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data;
switch (event_id) {
case ARDUINO_USB_STARTED_EVENT:
HWSerial.println("USB PLUGGED");
break;
case ARDUINO_USB_STOPPED_EVENT:
HWSerial.println("USB UNPLUGGED");
break;
case ARDUINO_USB_SUSPEND_EVENT:
HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en);
break;
case ARDUINO_USB_RESUME_EVENT:
HWSerial.println("USB RESUMED");
break;
default:
break;
}
} else if (event_base == ARDUINO_USB_VENDOR_EVENTS) {
arduino_usb_vendor_event_data_t * data = (arduino_usb_vendor_event_data_t*)event_data;
switch (event_id) {
case ARDUINO_USB_VENDOR_DATA_EVENT:
HWSerial.printf("Vendor RX: len:%u\n", data->data.len);
for (uint16_t i = 0; i < data->data.len; i++) {
HWSerial.write(Vendor.read());
}
HWSerial.println();
break;
default:
break;
}
}
}
static const char * strRequestDirections[] = {"OUT", "IN"};
static const char * strRequestTypes[] = {"STANDARD", "CLASS", "VENDOR", "INVALID"};
static const char * strRequestRecipients[] = {"DEVICE", "INTERFACE", "ENDPOINT", "OTHER"};
static const char * strRequestStages[] = {"SETUP", "DATA", "ACK"};
//Handle USB requests to the vendor interface
bool vendorRequestCallback(uint8_t rhport, uint8_t requestStage, arduino_usb_control_request_t const * request) {
HWSerial.printf("Vendor Request: Stage: %5s, Direction: %3s, Type: %8s, Recipient: %9s, bRequest: 0x%02x, wValue: 0x%04x, wIndex: %u, wLength: %u\n",
strRequestStages[requestStage],
strRequestDirections[request->bmRequestDirection],
strRequestTypes[request->bmRequestType],
strRequestRecipients[request->bmRequestRecipient],
request->bRequest, request->wValue, request->wIndex, request->wLength);
bool result = false;
if (request->bmRequestDirection == REQUEST_DIRECTION_OUT &&
request->bmRequestType == REQUEST_TYPE_STANDARD &&
request->bmRequestRecipient == REQUEST_RECIPIENT_INTERFACE &&
request->bRequest == 0x0b
) {
if (requestStage == REQUEST_STAGE_SETUP) {
// response with status OK
result = Vendor.sendResponse(rhport, request);
} else {
result = true;
}
} else
//Implement CDC Control Requests
if (request->bmRequestType == REQUEST_TYPE_CLASS && request->bmRequestRecipient == REQUEST_RECIPIENT_DEVICE) {
switch (request->bRequest) {
case REQUEST_SET_LINE_CODING: //0x20
// Accept only direction OUT with data size 7
if (request->wLength != sizeof(request_line_coding_t) || request->bmRequestDirection != REQUEST_DIRECTION_OUT) {
break;
}
if (requestStage == REQUEST_STAGE_SETUP) {
//Send the response in setup stage (it will write the data to vendor_line_coding in the DATA stage)
result = Vendor.sendResponse(rhport, request, (void*) &vendor_line_coding, sizeof(request_line_coding_t));
} else if (requestStage == REQUEST_STAGE_ACK) {
//In the ACK stage the response is complete
HWSerial.printf("Vendor Line Coding: bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u\n", vendor_line_coding.bit_rate, vendor_line_coding.data_bits, vendor_line_coding.stop_bits, vendor_line_coding.parity);
}
result = true;
break;
case REQUEST_GET_LINE_CODING: //0x21
// Accept only direction IN with data size 7
if (request->wLength != sizeof(request_line_coding_t) || request->bmRequestDirection != REQUEST_DIRECTION_IN) {
break;
}
if (requestStage == REQUEST_STAGE_SETUP) {
//Send the response in setup stage (it will write the data to vendor_line_coding in the DATA stage)
result = Vendor.sendResponse(rhport, request, (void*) &vendor_line_coding, sizeof(request_line_coding_t));
}
result = true;
break;
case REQUEST_SET_CONTROL_LINE_STATE: //0x22
// Accept only direction OUT with data size 0
if (request->wLength != 0 || request->bmRequestDirection != REQUEST_DIRECTION_OUT) {
break;
}
if (requestStage == REQUEST_STAGE_SETUP) {
//Send the response in setup stage
vendor_line_state = request->wValue;
result = Vendor.sendResponse(rhport, request);
} else if (requestStage == REQUEST_STAGE_ACK) {
//In the ACK stage the response is complete
bool dtr = (vendor_line_state & 1) != 0;
bool rts = (vendor_line_state & 2) != 0;
HWSerial.printf("Vendor Line State: dtr: %u, rts: %u\n", dtr, rts);
}
result = true;
break;
default:
// stall unknown request
break;
}
}
return result;
}
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
HWSerial.begin(115200);
HWSerial.setDebugOutput(true);
Vendor.onEvent(usbEventCallback);
Vendor.onRequest(vendorRequestCallback);
Vendor.begin();
USB.onEvent(usbEventCallback);
USB.webUSB(true);
USB.webUSBURL("http://localhost/webusb");
USB.begin();
}
void loop() {
static int previousButtonState = HIGH;
int buttonState = digitalRead(buttonPin);
if (buttonState != previousButtonState) {
previousButtonState = buttonState;
if (buttonState == LOW) {
HWSerial.println("Button Pressed");
Vendor.println("Button Pressed");
} else {
Vendor.println("Button Released");
HWSerial.println("Button Released");
}
delay(100);
}
while (HWSerial.available()) {
size_t l = HWSerial.available();
uint8_t b[l];
l = HWSerial.read(b, l);
Vendor.write(b, l);
}
}

View File

@ -8,6 +8,7 @@
USB KEYWORD1
USBCDC KEYWORD1
USBMSC KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
@ -18,6 +19,14 @@ end KEYWORD2
onEvent KEYWORD2
enableReset KEYWORD2
vendorID KEYWORD2
productID KEYWORD2
productRevision KEYWORD2
mediaPresent KEYWORD2
onStartStop KEYWORD2
onRead KEYWORD2
onWrite KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View File

@ -0,0 +1,365 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "USBHID.h"
#if CONFIG_TINYUSB_HID_ENABLED
#include "esp32-hal-tinyusb.h"
#include "USB.h"
#include "esp_hid_common.h"
#define USB_HID_DEVICES_MAX 10
ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_EVENTS);
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
typedef struct {
USBHIDDevice * device;
uint8_t reports_num;
uint8_t * report_ids;
} tinyusb_hid_device_t;
static tinyusb_hid_device_t tinyusb_hid_devices[USB_HID_DEVICES_MAX];
static uint8_t tinyusb_hid_devices_num = 0;
static bool tinyusb_hid_devices_is_initialized = false;
static xSemaphoreHandle tinyusb_hid_device_input_sem = NULL;
static xSemaphoreHandle tinyusb_hid_device_input_mutex = NULL;
static bool tinyusb_hid_is_initialized = false;
static uint8_t tinyusb_loaded_hid_devices_num = 0;
static uint16_t tinyusb_hid_device_descriptor_len = 0;
static uint8_t * tinyusb_hid_device_descriptor = NULL;
static const char * tinyusb_hid_device_report_types[4] = {"INVALID", "INPUT", "OUTPUT", "FEATURE"};
static bool tinyusb_enable_hid_device(uint16_t descriptor_len, USBHIDDevice * device){
if(tinyusb_hid_is_initialized){
log_e("TinyUSB HID has already started! Device not enabled");
return false;
}
if(tinyusb_loaded_hid_devices_num >= USB_HID_DEVICES_MAX){
log_e("Maximum devices already enabled! Device not enabled");
return false;
}
tinyusb_hid_device_descriptor_len += descriptor_len;
tinyusb_hid_devices[tinyusb_loaded_hid_devices_num++].device = device;
log_d("Device[%u] len: %u", tinyusb_loaded_hid_devices_num-1, descriptor_len);
return true;
}
USBHIDDevice * tinyusb_get_device_by_report_id(uint8_t report_id){
for(uint8_t i=0; i<tinyusb_loaded_hid_devices_num; i++){
tinyusb_hid_device_t * device = &tinyusb_hid_devices[i];
if(device->device && device->reports_num){
for(uint8_t r=0; r<device->reports_num; r++){
if(report_id == device->report_ids[r]){
return device->device;
}
}
}
}
return NULL;
}
static uint16_t tinyusb_on_get_feature(uint8_t report_id, uint8_t* buffer, uint16_t reqlen){
USBHIDDevice * device = tinyusb_get_device_by_report_id(report_id);
if(device){
return device->_onGetFeature(report_id, buffer, reqlen);
}
return 0;
}
static bool tinyusb_on_set_feature(uint8_t report_id, const uint8_t* buffer, uint16_t reqlen){
USBHIDDevice * device = tinyusb_get_device_by_report_id(report_id);
if(device){
device->_onSetFeature(report_id, buffer, reqlen);
return true;
}
return false;
}
static bool tinyusb_on_set_output(uint8_t report_id, const uint8_t* buffer, uint16_t reqlen){
USBHIDDevice * device = tinyusb_get_device_by_report_id(report_id);
if(device){
device->_onOutput(report_id, buffer, reqlen);
return true;
}
return false;
}
static uint16_t tinyusb_on_add_descriptor(uint8_t device_index, uint8_t * dst){
uint16_t res = 0;
uint8_t report_id = 0, reports_num = 0;
tinyusb_hid_device_t * device = &tinyusb_hid_devices[device_index];
if(device->device){
res = device->device->_onGetDescriptor(dst);
if(res){
esp_hid_report_map_t *hid_report_map = esp_hid_parse_report_map(dst, res);
if(hid_report_map){
if(device->report_ids){
free(device->report_ids);
}
device->reports_num = hid_report_map->reports_len;
device->report_ids = (uint8_t*)malloc(device->reports_num);
memset(device->report_ids, 0, device->reports_num);
reports_num = device->reports_num;
for(uint8_t i=0; i<device->reports_num; i++){
if(hid_report_map->reports[i].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT){
report_id = hid_report_map->reports[i].report_id;
for(uint8_t r=0; r<device->reports_num; r++){
if(!report_id){
//todo: handle better when device has no report ID set
break;
} else if(report_id == device->report_ids[r]){
//already added
reports_num--;
break;
} else if(!device->report_ids[r]){
//empty slot
device->report_ids[r] = report_id;
break;
}
}
} else {
reports_num--;
}
}
device->reports_num = reports_num;
esp_hid_free_report_map(hid_report_map);
}
}
}
return res;
}
static bool tinyusb_load_enabled_hid_devices(){
if(tinyusb_hid_device_descriptor != NULL){
return true;
}
tinyusb_hid_device_descriptor = (uint8_t *)malloc(tinyusb_hid_device_descriptor_len);
if (tinyusb_hid_device_descriptor == NULL) {
log_e("HID Descriptor Malloc Failed");
return false;
}
uint8_t * dst = tinyusb_hid_device_descriptor;
for(uint8_t i=0; i<tinyusb_loaded_hid_devices_num; i++){
uint16_t len = tinyusb_on_add_descriptor(i, dst);
if (!len) {
break;
} else {
dst += len;
}
}
esp_hid_report_map_t *hid_report_map = esp_hid_parse_report_map(tinyusb_hid_device_descriptor, tinyusb_hid_device_descriptor_len);
if(hid_report_map){
log_d("Loaded HID Desriptor with the following reports:");
for(uint8_t i=0; i<hid_report_map->reports_len; i++){
if(hid_report_map->reports[i].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT){
log_d(" ID: %3u, Type: %7s, Size: %2u, Usage: %8s",
hid_report_map->reports[i].report_id,
esp_hid_report_type_str(hid_report_map->reports[i].report_type),
hid_report_map->reports[i].value_len,
esp_hid_usage_str(hid_report_map->reports[i].usage)
);
}
}
esp_hid_free_report_map(hid_report_map);
} else {
log_e("Failed to parse the hid report descriptor!");
return false;
}
return true;
}
extern "C" uint16_t tusb_hid_load_descriptor(uint8_t * dst, uint8_t * itf)
{
if(tinyusb_hid_is_initialized){
return 0;
}
tinyusb_hid_is_initialized = true;
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB HID");
uint8_t ep_in = tinyusb_get_free_in_endpoint();
TU_VERIFY (ep_in != 0);
uint8_t ep_out = tinyusb_get_free_out_endpoint();
TU_VERIFY (ep_out != 0);
uint8_t descriptor[TUD_HID_INOUT_DESC_LEN] = {
// HID Input & Output descriptor
// Interface number, string index, protocol, report descriptor len, EP OUT & IN address, size & polling interval
TUD_HID_INOUT_DESCRIPTOR(*itf, str_index, HID_ITF_PROTOCOL_NONE, tinyusb_hid_device_descriptor_len, ep_out, (uint8_t)(0x80 | ep_in), 64, 1)
};
*itf+=1;
memcpy(dst, descriptor, TUD_HID_INOUT_DESC_LEN);
return TUD_HID_INOUT_DESC_LEN;
}
// Invoked when received GET HID REPORT DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance){
log_v("instance: %u", instance);
if(!tinyusb_load_enabled_hid_devices()){
return NULL;
}
return tinyusb_hid_device_descriptor;
}
// Invoked when received SET_PROTOCOL request
// protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol){
log_v("instance: %u, protocol:%u", instance, protocol);
arduino_usb_hid_event_data_t p = {0};
p.instance = instance;
p.set_protocol.protocol = protocol;
arduino_usb_event_post(ARDUINO_USB_HID_EVENTS, ARDUINO_USB_HID_SET_PROTOCOL_EVENT, &p, sizeof(arduino_usb_hid_event_data_t), portMAX_DELAY);
}
// Invoked when received SET_IDLE request. return false will stall the request
// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication
// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms).
bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate){
log_v("instance: %u, idle_rate:%u", instance, idle_rate);
arduino_usb_hid_event_data_t p = {0};
p.instance = instance;
p.set_idle.idle_rate = idle_rate;
arduino_usb_event_post(ARDUINO_USB_HID_EVENTS, ARDUINO_USB_HID_SET_IDLE_EVENT, &p, sizeof(arduino_usb_hid_event_data_t), portMAX_DELAY);
return true;
}
// Invoked when received GET_REPORT control request
// Application must fill buffer report's content and return its length.
// Return zero will cause the stack to STALL request
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen){
uint16_t res = tinyusb_on_get_feature(report_id, buffer, reqlen);
if(!res){
log_d("instance: %u, report_id: %u, report_type: %s, reqlen: %u", instance, report_id, tinyusb_hid_device_report_types[report_type], reqlen);
}
return res;
}
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize){
if(!report_id && !report_type){
if(!tinyusb_on_set_output(0, buffer, bufsize) && !tinyusb_on_set_output(buffer[0], buffer+1, bufsize-1)){
log_d("instance: %u, report_id: %u, report_type: %s, bufsize: %u", instance, buffer[0], tinyusb_hid_device_report_types[HID_REPORT_TYPE_OUTPUT], bufsize-1);
}
} else {
if(!tinyusb_on_set_feature(report_id, buffer, bufsize)){
log_d("instance: %u, report_id: %u, report_type: %s, bufsize: %u", instance, report_id, tinyusb_hid_device_report_types[report_type], bufsize);
}
}
}
USBHID::USBHID(){
if(!tinyusb_hid_devices_is_initialized){
tinyusb_hid_devices_is_initialized = true;
for(uint8_t i=0; i<USB_HID_DEVICES_MAX; i++){
memset(&tinyusb_hid_devices[i], 0, sizeof(tinyusb_hid_device_t));
}
tinyusb_hid_devices_num = 0;
tinyusb_enable_interface(USB_INTERFACE_HID, TUD_HID_INOUT_DESC_LEN, tusb_hid_load_descriptor);
}
}
void USBHID::begin(){
if(tinyusb_hid_device_input_sem == NULL){
tinyusb_hid_device_input_sem = xSemaphoreCreateBinary();
}
if(tinyusb_hid_device_input_mutex == NULL){
tinyusb_hid_device_input_mutex = xSemaphoreCreateMutex();
}
}
void USBHID::end(){
if (tinyusb_hid_device_input_sem != NULL) {
vSemaphoreDelete(tinyusb_hid_device_input_sem);
tinyusb_hid_device_input_sem = NULL;
}
if (tinyusb_hid_device_input_mutex != NULL) {
vSemaphoreDelete(tinyusb_hid_device_input_mutex);
tinyusb_hid_device_input_mutex = NULL;
}
}
bool USBHID::ready(void){
return tud_hid_n_ready(0);
}
void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint8_t len){
if (tinyusb_hid_device_input_sem) {
xSemaphoreGive(tinyusb_hid_device_input_sem);
}
}
bool USBHID::SendReport(uint8_t id, const void* data, size_t len, uint32_t timeout_ms){
if(!tinyusb_hid_device_input_sem || !tinyusb_hid_device_input_mutex){
log_e("TX Semaphore is NULL. You must call USBHID::begin() before you can send reports");
return false;
}
if(xSemaphoreTake(tinyusb_hid_device_input_mutex, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
log_e("report %u mutex failed", id);
return false;
}
bool res = ready();
if(!res){
log_e("not ready");
} else {
res = tud_hid_n_report(0, id, data, len);
if(!res){
log_e("report %u failed", id);
} else {
xSemaphoreTake(tinyusb_hid_device_input_sem, 0);
if(xSemaphoreTake(tinyusb_hid_device_input_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
log_e("report %u wait failed", id);
res = false;
}
}
}
xSemaphoreGive(tinyusb_hid_device_input_mutex);
return res;
}
bool USBHID::addDevice(USBHIDDevice * device, uint16_t descriptor_len){
if(device && tinyusb_loaded_hid_devices_num < USB_HID_DEVICES_MAX){
if(!tinyusb_enable_hid_device(descriptor_len, device)){
return false;
}
return true;
}
return false;
}
void USBHID::onEvent(esp_event_handler_t callback){
onEvent(ARDUINO_USB_HID_ANY_EVENT, callback);
}
void USBHID::onEvent(arduino_usb_hid_event_t event, esp_event_handler_t callback){
arduino_usb_event_handler_register_with(ARDUINO_USB_HID_EVENTS, event, callback, this);
}
#endif /* CONFIG_TINYUSB_HID_ENABLED */

View File

@ -0,0 +1,79 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "sdkconfig.h"
#if CONFIG_TINYUSB_HID_ENABLED
#include "esp_event.h"
#include "class/hid/hid.h"
#include "class/hid/hid_device.h"
// Used by the included TinyUSB drivers
enum {
HID_REPORT_ID_NONE,
HID_REPORT_ID_KEYBOARD,
HID_REPORT_ID_MOUSE,
HID_REPORT_ID_GAMEPAD,
HID_REPORT_ID_CONSUMER_CONTROL,
HID_REPORT_ID_SYSTEM_CONTROL,
HID_REPORT_ID_VENDOR
};
ESP_EVENT_DECLARE_BASE(ARDUINO_USB_HID_EVENTS);
typedef enum {
ARDUINO_USB_HID_ANY_EVENT = ESP_EVENT_ANY_ID,
ARDUINO_USB_HID_SET_PROTOCOL_EVENT = 0,
ARDUINO_USB_HID_SET_IDLE_EVENT,
ARDUINO_USB_HID_MAX_EVENT,
} arduino_usb_hid_event_t;
typedef struct {
uint8_t instance;
union {
struct {
uint8_t protocol;
} set_protocol;
struct {
uint8_t idle_rate;
} set_idle;
};
} arduino_usb_hid_event_data_t;
class USBHIDDevice
{
public:
virtual uint16_t _onGetDescriptor(uint8_t* buffer){return 0;}
virtual uint16_t _onGetFeature(uint8_t report_id, uint8_t* buffer, uint16_t len){return 0;}
virtual void _onSetFeature(uint8_t report_id, const uint8_t* buffer, uint16_t len){}
virtual void _onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len){}
};
class USBHID
{
public:
USBHID(void);
void begin(void);
void end(void);
bool ready(void);
bool SendReport(uint8_t report_id, const void* data, size_t len, uint32_t timeout_ms = 100);
void onEvent(esp_event_handler_t callback);
void onEvent(arduino_usb_hid_event_t event, esp_event_handler_t callback);
static bool addDevice(USBHIDDevice * device, uint16_t descriptor_len);
};
#endif /* CONFIG_TINYUSB_HID_ENABLED */

View File

@ -0,0 +1,57 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "USBHID.h"
#if CONFIG_TINYUSB_HID_ENABLED
#include "USBHIDConsumerControl.h"
static const uint8_t report_descriptor[] = {
TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(HID_REPORT_ID_CONSUMER_CONTROL))
};
USBHIDConsumerControl::USBHIDConsumerControl(): hid(){
static bool initialized = false;
if(!initialized){
initialized = true;
hid.addDevice(this, sizeof(report_descriptor));
}
}
uint16_t USBHIDConsumerControl::_onGetDescriptor(uint8_t* dst){
memcpy(dst, report_descriptor, sizeof(report_descriptor));
return sizeof(report_descriptor);
}
void USBHIDConsumerControl::begin(){
hid.begin();
}
void USBHIDConsumerControl::end(){
}
bool USBHIDConsumerControl::send(uint16_t value){
return hid.SendReport(HID_REPORT_ID_CONSUMER_CONTROL, &value, 2);
}
size_t USBHIDConsumerControl::press(uint16_t k){
return send(k);
}
size_t USBHIDConsumerControl::release(){
return send(0);
}
#endif /* CONFIG_TINYUSB_HID_ENABLED */

View File

@ -0,0 +1,85 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "USBHID.h"
#if CONFIG_TINYUSB_HID_ENABLED
// Power Control
#define CONSUMER_CONTROL_POWER 0x0030
#define CONSUMER_CONTROL_RESET 0x0031
#define CONSUMER_CONTROL_SLEEP 0x0032
// Screen Brightness
#define CONSUMER_CONTROL_BRIGHTNESS_INCREMENT 0x006F
#define CONSUMER_CONTROL_BRIGHTNESS_DECREMENT 0x0070
// These HID usages operate only on mobile systems (battery powered) and
// require Windows 8 (build 8302 or greater).
#define CONSUMER_CONTROL_WIRELESS_RADIO_CONTROLS 0x000C
#define CONSUMER_CONTROL_WIRELESS_RADIO_BUTTONS 0x00C6
#define CONSUMER_CONTROL_WIRELESS_RADIO_LED 0x00C7
#define CONSUMER_CONTROL_WIRELESS_RADIO_SLIDER_SWITCH 0x00C8
// Media Control
#define CONSUMER_CONTROL_PLAY_PAUSE 0x00CD
#define CONSUMER_CONTROL_SCAN_NEXT 0x00B5
#define CONSUMER_CONTROL_SCAN_PREVIOUS 0x00B6
#define CONSUMER_CONTROL_STOP 0x00B7
#define CONSUMER_CONTROL_VOLUME 0x00E0
#define CONSUMER_CONTROL_MUTE 0x00E2
#define CONSUMER_CONTROL_BASS 0x00E3
#define CONSUMER_CONTROL_TREBLE 0x00E4
#define CONSUMER_CONTROL_BASS_BOOST 0x00E5
#define CONSUMER_CONTROL_VOLUME_INCREMENT 0x00E9
#define CONSUMER_CONTROL_VOLUME_DECREMENT 0x00EA
#define CONSUMER_CONTROL_BASS_INCREMENT 0x0152
#define CONSUMER_CONTROL_BASS_DECREMENT 0x0153
#define CONSUMER_CONTROL_TREBLE_INCREMENT 0x0154
#define CONSUMER_CONTROL_TREBLE_DECREMENT 0x0155
// Application Launcher
#define CONSUMER_CONTROL_CONFIGURATION 0x0183
#define CONSUMER_CONTROL_EMAIL_READER 0x018A
#define CONSUMER_CONTROL_CALCULATOR 0x0192
#define CONSUMER_CONTROL_LOCAL_BROWSER 0x0194
// Browser/Explorer Specific
#define CONSUMER_CONTROL_SEARCH 0x0221
#define CONSUMER_CONTROL_HOME 0x0223
#define CONSUMER_CONTROL_BACK 0x0224
#define CONSUMER_CONTROL_FORWARD 0x0225
#define CONSUMER_CONTROL_BR_STOP 0x0226
#define CONSUMER_CONTROL_REFRESH 0x0227
#define CONSUMER_CONTROL_BOOKMARKS 0x022A
// Mouse Horizontal scroll
#define CONSUMER_CONTROL_PAN 0x0238
class USBHIDConsumerControl: public USBHIDDevice {
private:
USBHID hid;
bool send(uint16_t value);
public:
USBHIDConsumerControl(void);
void begin(void);
void end(void);
size_t press(uint16_t k);
size_t release();
// internal use
uint16_t _onGetDescriptor(uint8_t* buffer);
};
#endif

View File

@ -0,0 +1,121 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "USBHID.h"
#if CONFIG_TINYUSB_HID_ENABLED
#include "USBHIDGamepad.h"
static const uint8_t report_descriptor[] = {
TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(HID_REPORT_ID_GAMEPAD))
};
USBHIDGamepad::USBHIDGamepad(): hid(), _x(0), _y(0), _z(0), _rz(0), _rx(0), _ry(0), _hat(0), _buttons(0){
static bool initialized = false;
if(!initialized){
initialized = true;
hid.addDevice(this, sizeof(report_descriptor));
}
}
uint16_t USBHIDGamepad::_onGetDescriptor(uint8_t* dst){
memcpy(dst, report_descriptor, sizeof(report_descriptor));
return sizeof(report_descriptor);
}
void USBHIDGamepad::begin(){
hid.begin();
}
void USBHIDGamepad::end(){
}
bool USBHIDGamepad::write(){
hid_gamepad_report_t report = {
.x = _x,
.y = _y,
.z = _z,
.rz = _rz,
.rx = _rx,
.ry = _ry,
.hat = _hat,
.buttons = _buttons
};
return hid.SendReport(HID_REPORT_ID_GAMEPAD, &report, sizeof(report));
}
bool USBHIDGamepad::leftStick(int8_t x, int8_t y){
_x = x;
_y = y;
return write();
}
bool USBHIDGamepad::rightStick(int8_t z, int8_t rz){
_z = z;
_rz = rz;
return write();
}
bool USBHIDGamepad::leftTrigger(int8_t rx){
_rx = rx;
return write();
}
bool USBHIDGamepad::rightTrigger(int8_t ry){
_ry = ry;
return write();
}
bool USBHIDGamepad::hat(uint8_t hat){
if(hat > 9){
return false;
}
_hat = hat;
return write();
}
bool USBHIDGamepad::pressButton(uint8_t button){
if(button > 31){
return false;
}
_buttons |= (1 << button);
return write();
}
bool USBHIDGamepad::releaseButton(uint8_t button){
if(button > 31){
return false;
}
_buttons &= ~(1 << button);
return write();
}
bool USBHIDGamepad::send(int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons){
if(hat > 9){
return false;
}
_x = x;
_y = y;
_z = z;
_rz = rz;
_rx = rx;
_ry = ry;
_hat = hat;
_buttons = buttons;
return write();
}
#endif /* CONFIG_TINYUSB_HID_ENABLED */

View File

@ -0,0 +1,87 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "USBHID.h"
#if CONFIG_TINYUSB_HID_ENABLED
/// Standard Gamepad Buttons Naming from Linux input event codes
/// https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h
#define BUTTON_A 0
#define BUTTON_B 1
#define BUTTON_C 2
#define BUTTON_X 3
#define BUTTON_Y 4
#define BUTTON_Z 5
#define BUTTON_TL 6
#define BUTTON_TR 7
#define BUTTON_TL2 8
#define BUTTON_TR2 9
#define BUTTON_SELECT 10
#define BUTTON_START 11
#define BUTTON_MODE 12
#define BUTTON_THUMBL 13
#define BUTTON_THUMBR 14
#define BUTTON_SOUTH BUTTON_A
#define BUTTON_EAST BUTTON_B
#define BUTTON_NORTH BUTTON_X
#define BUTTON_WEST BUTTON_Y
/// Standard Gamepad HAT/DPAD Buttons (from Linux input event codes)
#define HAT_CENTER 0
#define HAT_UP 1
#define HAT_UP_RIGHT 2
#define HAT_RIGHT 3
#define HAT_DOWN_RIGHT 4
#define HAT_DOWN 5
#define HAT_DOWN_LEFT 6
#define HAT_LEFT 7
#define HAT_UP_LEFT 8
class USBHIDGamepad: public USBHIDDevice {
private:
USBHID hid;
int8_t _x; ///< Delta x movement of left analog-stick
int8_t _y; ///< Delta y movement of left analog-stick
int8_t _z; ///< Delta z movement of right analog-joystick
int8_t _rz; ///< Delta Rz movement of right analog-joystick
int8_t _rx; ///< Delta Rx movement of analog left trigger
int8_t _ry; ///< Delta Ry movement of analog right trigger
uint8_t _hat; ///< Buttons mask for currently pressed buttons in the DPad/hat
uint32_t _buttons; ///< Buttons mask for currently pressed buttons
bool write();
public:
USBHIDGamepad(void);
void begin(void);
void end(void);
bool leftStick(int8_t x, int8_t y);
bool rightStick(int8_t z, int8_t rz);
bool leftTrigger(int8_t rx);
bool rightTrigger(int8_t ry);
bool hat(uint8_t hat);
bool pressButton(uint8_t button);
bool releaseButton(uint8_t button);
bool send(int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
// internal use
uint16_t _onGetDescriptor(uint8_t* buffer);
};
#endif

View File

@ -0,0 +1,354 @@
/*
Keyboard.cpp
Copyright (c) 2015, Arduino LLC
Original code (pre-library): Copyright (c) 2011, Peter Barrett
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "USBHID.h"
#if CONFIG_TINYUSB_HID_ENABLED
#include "USBHIDKeyboard.h"
ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_KEYBOARD_EVENTS);
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
static const uint8_t report_descriptor[] = {
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(HID_REPORT_ID_KEYBOARD))
};
USBHIDKeyboard::USBHIDKeyboard(): hid(){
static bool initialized = false;
if(!initialized){
initialized = true;
hid.addDevice(this, sizeof(report_descriptor));
}
}
uint16_t USBHIDKeyboard::_onGetDescriptor(uint8_t* dst){
memcpy(dst, report_descriptor, sizeof(report_descriptor));
return sizeof(report_descriptor);
}
void USBHIDKeyboard::begin(){
hid.begin();
}
void USBHIDKeyboard::end(){
}
void USBHIDKeyboard::onEvent(esp_event_handler_t callback){
onEvent(ARDUINO_USB_HID_KEYBOARD_ANY_EVENT, callback);
}
void USBHIDKeyboard::onEvent(arduino_usb_hid_keyboard_event_t event, esp_event_handler_t callback){
arduino_usb_event_handler_register_with(ARDUINO_USB_HID_KEYBOARD_EVENTS, event, callback, this);
}
void USBHIDKeyboard::_onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len){
if(report_id == HID_REPORT_ID_KEYBOARD){
arduino_usb_hid_keyboard_event_data_t p = {0};
p.leds = buffer[0];
arduino_usb_event_post(ARDUINO_USB_HID_KEYBOARD_EVENTS, ARDUINO_USB_HID_KEYBOARD_LED_EVENT, &p, sizeof(arduino_usb_hid_keyboard_event_data_t), portMAX_DELAY);
}
}
void USBHIDKeyboard::sendReport(KeyReport* keys)
{
hid_keyboard_report_t report;
report.reserved = 0;
report.modifier = keys->modifiers;
if (keys->keys) {
memcpy(report.keycode, keys->keys, 6);
} else {
memset(report.keycode, 0, 6);
}
hid.SendReport(HID_REPORT_ID_KEYBOARD, &report, sizeof(report));
}
#define SHIFT 0x80
const uint8_t _asciimap[128] =
{
0x00, // NUL
0x00, // SOH
0x00, // STX
0x00, // ETX
0x00, // EOT
0x00, // ENQ
0x00, // ACK
0x00, // BEL
0x2a, // BS Backspace
0x2b, // TAB Tab
0x28, // LF Enter
0x00, // VT
0x00, // FF
0x00, // CR
0x00, // SO
0x00, // SI
0x00, // DEL
0x00, // DC1
0x00, // DC2
0x00, // DC3
0x00, // DC4
0x00, // NAK
0x00, // SYN
0x00, // ETB
0x00, // CAN
0x00, // EM
0x00, // SUB
0x00, // ESC
0x00, // FS
0x00, // GS
0x00, // RS
0x00, // US
0x2c, // ' '
0x1e|SHIFT, // !
0x34|SHIFT, // "
0x20|SHIFT, // #
0x21|SHIFT, // $
0x22|SHIFT, // %
0x24|SHIFT, // &
0x34, // '
0x26|SHIFT, // (
0x27|SHIFT, // )
0x25|SHIFT, // *
0x2e|SHIFT, // +
0x36, // ,
0x2d, // -
0x37, // .
0x38, // /
0x27, // 0
0x1e, // 1
0x1f, // 2
0x20, // 3
0x21, // 4
0x22, // 5
0x23, // 6
0x24, // 7
0x25, // 8
0x26, // 9
0x33|SHIFT, // :
0x33, // ;
0x36|SHIFT, // <
0x2e, // =
0x37|SHIFT, // >
0x38|SHIFT, // ?
0x1f|SHIFT, // @
0x04|SHIFT, // A
0x05|SHIFT, // B
0x06|SHIFT, // C
0x07|SHIFT, // D
0x08|SHIFT, // E
0x09|SHIFT, // F
0x0a|SHIFT, // G
0x0b|SHIFT, // H
0x0c|SHIFT, // I
0x0d|SHIFT, // J
0x0e|SHIFT, // K
0x0f|SHIFT, // L
0x10|SHIFT, // M
0x11|SHIFT, // N
0x12|SHIFT, // O
0x13|SHIFT, // P
0x14|SHIFT, // Q
0x15|SHIFT, // R
0x16|SHIFT, // S
0x17|SHIFT, // T
0x18|SHIFT, // U
0x19|SHIFT, // V
0x1a|SHIFT, // W
0x1b|SHIFT, // X
0x1c|SHIFT, // Y
0x1d|SHIFT, // Z
0x2f, // [
0x31, // bslash
0x30, // ]
0x23|SHIFT, // ^
0x2d|SHIFT, // _
0x35, // `
0x04, // a
0x05, // b
0x06, // c
0x07, // d
0x08, // e
0x09, // f
0x0a, // g
0x0b, // h
0x0c, // i
0x0d, // j
0x0e, // k
0x0f, // l
0x10, // m
0x11, // n
0x12, // o
0x13, // p
0x14, // q
0x15, // r
0x16, // s
0x17, // t
0x18, // u
0x19, // v
0x1a, // w
0x1b, // x
0x1c, // y
0x1d, // z
0x2f|SHIFT, // {
0x31|SHIFT, // |
0x30|SHIFT, // }
0x35|SHIFT, // ~
0 // DEL
};
size_t USBHIDKeyboard::pressRaw(uint8_t k)
{
uint8_t i;
if (k >= 0xE0 && k < 0xE8) {
// it's a modifier key
_keyReport.modifiers |= (1<<(k-0x80));
} else if (k && k < 0xA5) {
// Add k to the key report only if it's not already present
// and if there is an empty slot.
if (_keyReport.keys[0] != k && _keyReport.keys[1] != k &&
_keyReport.keys[2] != k && _keyReport.keys[3] != k &&
_keyReport.keys[4] != k && _keyReport.keys[5] != k) {
for (i=0; i<6; i++) {
if (_keyReport.keys[i] == 0x00) {
_keyReport.keys[i] = k;
break;
}
}
if (i == 6) {
return 0;
}
}
} else {
//not a modifier and not a key
return 0;
}
sendReport(&_keyReport);
return 1;
}
size_t USBHIDKeyboard::releaseRaw(uint8_t k)
{
uint8_t i;
if (k >= 0xE0 && k < 0xE8) {
// it's a modifier key
_keyReport.modifiers &= ~(1<<(k-0x80));
} else if (k && k < 0xA5) {
// Test the key report to see if k is present. Clear it if it exists.
// Check all positions in case the key is present more than once (which it shouldn't be)
for (i=0; i<6; i++) {
if (0 != k && _keyReport.keys[i] == k) {
_keyReport.keys[i] = 0x00;
}
}
} else {
//not a modifier and not a key
return 0;
}
sendReport(&_keyReport);
return 1;
}
// press() adds the specified key (printing, non-printing, or modifier)
// to the persistent key report and sends the report. Because of the way
// USB HID works, the host acts like the key remains pressed until we
// call release(), releaseAll(), or otherwise clear the report and resend.
size_t USBHIDKeyboard::press(uint8_t k)
{
uint8_t i;
if (k >= 0x88) { // it's a non-printing key (not a modifier)
k = k - 0x88;
} else if (k >= 0x80) { // it's a modifier key
_keyReport.modifiers |= (1<<(k-0x80));
k = 0;
} else { // it's a printing key
k = _asciimap[k];
if (!k) {
return 0;
}
if (k & 0x80) { // it's a capital letter or other character reached with shift
_keyReport.modifiers |= 0x02; // the left shift modifier
k &= 0x7F;
}
}
return pressRaw(k);
}
// release() takes the specified key out of the persistent key report and
// sends the report. This tells the OS the key is no longer pressed and that
// it shouldn't be repeated any more.
size_t USBHIDKeyboard::release(uint8_t k)
{
uint8_t i;
if (k >= 0x88) { // it's a non-printing key (not a modifier)
k = k - 0x88;
} else if (k >= 0x80) { // it's a modifier key
_keyReport.modifiers &= ~(1<<(k-0x80));
k = 0;
} else { // it's a printing key
k = _asciimap[k];
if (!k) {
return 0;
}
if (k & 0x80) { // it's a capital letter or other character reached with shift
_keyReport.modifiers &= ~(0x02); // the left shift modifier
k &= 0x7F;
}
}
return releaseRaw(k);
}
void USBHIDKeyboard::releaseAll(void)
{
_keyReport.keys[0] = 0;
_keyReport.keys[1] = 0;
_keyReport.keys[2] = 0;
_keyReport.keys[3] = 0;
_keyReport.keys[4] = 0;
_keyReport.keys[5] = 0;
_keyReport.modifiers = 0;
sendReport(&_keyReport);
}
size_t USBHIDKeyboard::write(uint8_t c)
{
uint8_t p = press(c); // Keydown
release(c); // Keyup
return p; // just return the result of press() since release() almost always returns 1
}
size_t USBHIDKeyboard::write(const uint8_t *buffer, size_t size) {
size_t n = 0;
while (size--) {
if (*buffer != '\r') {
if (write(*buffer)) {
n++;
} else {
break;
}
}
buffer++;
}
return n;
}
#endif /* CONFIG_TINYUSB_HID_ENABLED */

View File

@ -0,0 +1,140 @@
/*
Keyboard.h
Copyright (c) 2015, Arduino LLC
Original code (pre-library): Copyright (c) 2011, Peter Barrett
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "Print.h"
#include "USBHID.h"
#if CONFIG_TINYUSB_HID_ENABLED
#include "esp_event.h"
ESP_EVENT_DECLARE_BASE(ARDUINO_USB_HID_KEYBOARD_EVENTS);
typedef enum {
ARDUINO_USB_HID_KEYBOARD_ANY_EVENT = ESP_EVENT_ANY_ID,
ARDUINO_USB_HID_KEYBOARD_LED_EVENT = 0,
ARDUINO_USB_HID_KEYBOARD_MAX_EVENT,
} arduino_usb_hid_keyboard_event_t;
typedef union {
struct {
uint8_t numlock:1;
uint8_t capslock:1;
uint8_t scrolllock:1;
uint8_t compose:1;
uint8_t kana:1;
uint8_t reserved:3;
};
uint8_t leds;
} arduino_usb_hid_keyboard_event_data_t;
#define KEY_LEFT_CTRL 0x80
#define KEY_LEFT_SHIFT 0x81
#define KEY_LEFT_ALT 0x82
#define KEY_LEFT_GUI 0x83
#define KEY_RIGHT_CTRL 0x84
#define KEY_RIGHT_SHIFT 0x85
#define KEY_RIGHT_ALT 0x86
#define KEY_RIGHT_GUI 0x87
#define KEY_UP_ARROW 0xDA
#define KEY_DOWN_ARROW 0xD9
#define KEY_LEFT_ARROW 0xD8
#define KEY_RIGHT_ARROW 0xD7
#define KEY_BACKSPACE 0xB2
#define KEY_TAB 0xB3
#define KEY_RETURN 0xB0
#define KEY_ESC 0xB1
#define KEY_INSERT 0xD1
#define KEY_DELETE 0xD4
#define KEY_PAGE_UP 0xD3
#define KEY_PAGE_DOWN 0xD6
#define KEY_HOME 0xD2
#define KEY_END 0xD5
#define KEY_CAPS_LOCK 0xC1
#define KEY_F1 0xC2
#define KEY_F2 0xC3
#define KEY_F3 0xC4
#define KEY_F4 0xC5
#define KEY_F5 0xC6
#define KEY_F6 0xC7
#define KEY_F7 0xC8
#define KEY_F8 0xC9
#define KEY_F9 0xCA
#define KEY_F10 0xCB
#define KEY_F11 0xCC
#define KEY_F12 0xCD
#define KEY_F13 0xF0
#define KEY_F14 0xF1
#define KEY_F15 0xF2
#define KEY_F16 0xF3
#define KEY_F17 0xF4
#define KEY_F18 0xF5
#define KEY_F19 0xF6
#define KEY_F20 0xF7
#define KEY_F21 0xF8
#define KEY_F22 0xF9
#define KEY_F23 0xFA
#define KEY_F24 0xFB
#define LED_NUMLOCK 0x01
#define LED_CAPSLOCK 0x02
#define LED_SCROLLLOCK 0x04
#define LED_COMPOSE 0x08
#define LED_KANA 0x10
// Low level key report: up to 6 keys and shift, ctrl etc at once
typedef struct
{
uint8_t modifiers;
uint8_t reserved;
uint8_t keys[6];
} KeyReport;
class USBHIDKeyboard: public USBHIDDevice, public Print
{
private:
USBHID hid;
KeyReport _keyReport;
void sendReport(KeyReport* keys);
public:
USBHIDKeyboard(void);
void begin(void);
void end(void);
size_t write(uint8_t k);
size_t write(const uint8_t *buffer, size_t size);
size_t press(uint8_t k);
size_t release(uint8_t k);
void releaseAll(void);
//raw functions work with TinyUSB's HID_KEY_* macros
size_t pressRaw(uint8_t k);
size_t releaseRaw(uint8_t k);
void onEvent(esp_event_handler_t callback);
void onEvent(arduino_usb_hid_keyboard_event_t event, esp_event_handler_t callback);
// internal use
uint16_t _onGetDescriptor(uint8_t* buffer);
void _onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len);
};
#endif

View File

@ -0,0 +1,92 @@
/*
Mouse.cpp
Copyright (c) 2015, Arduino LLC
Original code (pre-library): Copyright (c) 2011, Peter Barrett
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "USBHID.h"
#if CONFIG_TINYUSB_HID_ENABLED
#include "USBHIDMouse.h"
static const uint8_t report_descriptor[] = {
TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(HID_REPORT_ID_MOUSE))
};
USBHIDMouse::USBHIDMouse(): hid(), _buttons(0){
static bool initialized = false;
if(!initialized){
initialized = true;
hid.addDevice(this, sizeof(report_descriptor));
}
}
uint16_t USBHIDMouse::_onGetDescriptor(uint8_t* dst){
memcpy(dst, report_descriptor, sizeof(report_descriptor));
return sizeof(report_descriptor);
}
void USBHIDMouse::begin(){
hid.begin();
}
void USBHIDMouse::end(){
}
void USBHIDMouse::move(int8_t x, int8_t y, int8_t wheel, int8_t pan){
hid_mouse_report_t report = {
.buttons = _buttons,
.x = x,
.y = y,
.wheel = wheel,
.pan = pan
};
hid.SendReport(HID_REPORT_ID_MOUSE, &report, sizeof(report));
}
void USBHIDMouse::click(uint8_t b){
_buttons = b;
move(0,0);
_buttons = 0;
move(0,0);
}
void USBHIDMouse::buttons(uint8_t b){
if (b != _buttons){
_buttons = b;
move(0,0);
}
}
void USBHIDMouse::press(uint8_t b){
buttons(_buttons | b);
}
void USBHIDMouse::release(uint8_t b){
buttons(_buttons & ~b);
}
bool USBHIDMouse::isPressed(uint8_t b){
if ((b & _buttons) > 0) {
return true;
}
return false;
}
#endif /* CONFIG_TINYUSB_HID_ENABLED */

View File

@ -0,0 +1,54 @@
/*
Mouse.h
Copyright (c) 2015, Arduino LLC
Original code (pre-library): Copyright (c) 2011, Peter Barrett
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "USBHID.h"
#if CONFIG_TINYUSB_HID_ENABLED
#define MOUSE_LEFT 0x01
#define MOUSE_RIGHT 0x02
#define MOUSE_MIDDLE 0x04
#define MOUSE_BACKWARD 0x08
#define MOUSE_FORWARD 0x10
#define MOUSE_ALL 0x1F
class USBHIDMouse: public USBHIDDevice {
private:
USBHID hid;
uint8_t _buttons;
void buttons(uint8_t b);
bool write(int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
public:
USBHIDMouse(void);
void begin(void);
void end(void);
void click(uint8_t b = MOUSE_LEFT);
void move(int8_t x, int8_t y, int8_t wheel = 0, int8_t pan = 0);
void press(uint8_t b = MOUSE_LEFT); // press LEFT by default
void release(uint8_t b = MOUSE_LEFT); // release LEFT by default
bool isPressed(uint8_t b = MOUSE_LEFT); // check LEFT by default
// internal use
uint16_t _onGetDescriptor(uint8_t* buffer);
};
#endif

View File

@ -0,0 +1,59 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "USBHID.h"
#if CONFIG_TINYUSB_HID_ENABLED
#include "USBHIDSystemControl.h"
static const uint8_t report_descriptor[] = {
TUD_HID_REPORT_DESC_SYSTEM_CONTROL(HID_REPORT_ID(HID_REPORT_ID_SYSTEM_CONTROL))
};
USBHIDSystemControl::USBHIDSystemControl(): hid(){
static bool initialized = false;
if(!initialized){
initialized = true;
hid.addDevice(this, sizeof(report_descriptor));
}
}
uint16_t USBHIDSystemControl::_onGetDescriptor(uint8_t* dst){
memcpy(dst, report_descriptor, sizeof(report_descriptor));
return sizeof(report_descriptor);
}
void USBHIDSystemControl::begin(){
hid.begin();
}
void USBHIDSystemControl::end(){
}
bool USBHIDSystemControl::send(uint8_t value){
return hid.SendReport(HID_REPORT_ID_SYSTEM_CONTROL, &value, 1);
}
size_t USBHIDSystemControl::press(uint8_t k){
if(k > 3){
return 0;
}
return send(k);
}
size_t USBHIDSystemControl::release(){
return send(0);
}
#endif /* CONFIG_TINYUSB_HID_ENABLED */

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