Merge pull request #28 from nilclass/adc-samples-via-hid

Continously sample ADCs and expose samples via a USB Interrupt endpoint
This commit is contained in:
Kevin Santo Cappuccio 2024-02-16 09:57:17 -08:00 committed by GitHub
commit aa6590a1fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
226 changed files with 48803 additions and 29 deletions

View File

@ -65,14 +65,18 @@ extern "C" {
// Device Configuration // Device Configuration
//-------------------------------------------------------------------- //--------------------------------------------------------------------
#define CFG_TUD_ENDOINT0_SIZE 64 #define CFG_TUD_ENDOINT0_SIZE 256
#define CFG_TUD_CDC 2 #define CFG_TUD_CDC 2
#define CFG_TUD_MSC 1 #define CFG_TUD_MSC 0
#define CFG_TUD_HID 1 #define CFG_TUD_HID 0
#define CFG_TUD_MIDI 1 #define CFG_TUD_MIDI 0
#define CFG_TUD_VENDOR 1 #define CFG_TUD_VENDOR 0
#define CFG_TUD_JUMPERLESS 1
// max is 64
#define CFG_TUD_JUMPERLESS_EP_BUFSIZE 8
// CDC FIFO size of TX and RX // CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE 256 #define CFG_TUD_CDC_RX_BUFSIZE 256

View File

@ -0,0 +1,6 @@
synopsys
sie
inout
busses
thre

View File

@ -0,0 +1,10 @@
# See: https://github.com/codespell-project/codespell#using-a-config-file
[codespell]
# In the event of a false positive, add the problematic word, in all lowercase, to 'ignore-words.txt' (one word per line).
# Or copy & paste the whole problematic line to 'exclude-file.txt'
ignore-words = .codespell/ignore-words.txt
exclude-file = .codespell/exclude-file.txt
check-filenames =
check-hidden =
count =
skip = .git

View File

@ -0,0 +1,102 @@
name: Bug Report
description: Report a problem with TinyUSB Library
labels: 'Bug'
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
It's okay to leave some blank if it doesn't apply to your problem.
- type: dropdown
attributes:
label: Operating System
options:
- Linux
- MacOS
- RaspberryPi OS
- Windows 7
- Windows 10
- Windows 11
- Others
validations:
required: true
- type: input
attributes:
label: Arduino IDE version
placeholder: e.g Arduino 1.8.15
validations:
required: true
- type: input
attributes:
label: Board
placeholder: e.g Feather nRF52840 Express
validations:
required: true
- type: input
attributes:
label: ArduinoCore version
description: Can be found under "Board Manager" menu
validations:
required: true
- type: input
attributes:
label: TinyUSB Library version
placeholder: "Release version or github latest"
validations:
required: true
- type: textarea
attributes:
label: Sketch as ATTACHED TXT
placeholder: |
e.g examples/MassStorage/msc_ramdisk.
If it is custom sketch, please provide it as **ATTACHED** files or link to it.
Pasting raw long code that hurts readability can get your issue **closed**
validations:
required: true
- type: textarea
attributes:
label: Compiled Log as ATTACHED TXT
placeholder: |
Compiled log from Arduino IDE as **ATTACHED** txt.
Pasting raw long log that hurts readability can get your issue **closed**
validations:
required: true
- type: textarea
attributes:
label: What happened ?
placeholder: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
attributes:
label: How to reproduce ?
placeholder: |
1. Go to '...'
2. Click on '....'
3. See error
validations:
required: true
- type: textarea
attributes:
label: Debug Log
placeholder: |
TinyUSB debug log where the issue occurred as attached txt file, best with comments to explain the actual events.
validations:
required: false
- type: textarea
attributes:
label: Screenshots
description: If applicable, add screenshots to help explain your problem.
validations:
required: false

View File

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Adafruit Support Forum
url: https://forums.adafruit.com
about: If you have other questions or need help, post it here.
- name: TinyUSB Arduino Discussion
url: https://github.com/adafruit/Adafruit_TinyUSB_Arduino/discussions
about: If you have other questions or need help, post it here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: 'Feature'
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -0,0 +1,85 @@
name: Build
on: [pull_request, push, repository_dispatch]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Checkout code
uses: actions/checkout@v3
- name: Run pre-commit
uses: pre-commit/action@v3.0.0
- name: Checkout adafruit/ci-arduino
uses: actions/checkout@v3
with:
repository: adafruit/ci-arduino
path: ci
- name: pre-install
run: bash ci/actions_install.sh
# - name: clang
# run: python3 ci/run-clang-format.py -r src/arduino
- name: doxygen
env:
GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }}
PRETTYNAME : "Adafruit TinyUSB Library"
run: bash ci/doxy_gen_and_deploy.sh
build:
runs-on: ubuntu-latest
needs: pre-commit
strategy:
fail-fast: false
matrix:
arduino-platform:
# ESP32S3
- 'feather_esp32s3'
# ESP32S2
- 'feather_esp32s2'
# nRF52
- 'cpb'
- 'nrf52840'
# RP2040
- 'feather_rp2040_tinyusb'
# SAMD
- 'feather_m4_can_tinyusb'
- 'metro_m0_tinyusb'
- 'metro_m4_tinyusb'
steps:
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Checkout code
uses: actions/checkout@v3
- name: Checkout adafruit/ci-arduino
uses: actions/checkout@v3
with:
repository: adafruit/ci-arduino
path: ci
- name: pre-install
run: bash ci/actions_install.sh
- name: Install Libraries for building examples
run: arduino-cli lib install "Adafruit SPIFlash" "MIDI Library" "Adafruit seesaw Library" "Adafruit NeoPixel" "SdFat - Adafruit Fork" "SD" "Adafruit Circuit Playground" "Adafruit InternalFlash" "Pico PIO USB"
- name: test platforms
run: python3 ci/build_platform.py ${{ matrix.arduino-platform }}

View File

@ -0,0 +1,4 @@
/examples/**/build/
/.development
.idea
platformio.ini

View File

@ -0,0 +1,29 @@
# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò
#
# SPDX-License-Identifier: Unlicense
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v15.0.7
hooks:
- id: clang-format
exclude: |
(?x)^(
examples/|
src/class|
src/common|
src/device|
src/host|
src/osal|
src/portable|
src/tusb_option.h|
src/tusb.c|
src/tusb.h
)
types_or: [c++, c, header]
- repo: https://github.com/codespell-project/codespell
rev: v2.2.4
hooks:
- id: codespell
args: [-w]

View File

@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at . All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2019 Ha Thach for Adafruit Industries
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,82 @@
# Adafruit TinyUSB Library for Arduino
[![Build Status](https://github.com/adafruit/Adafruit_TinyUSB_Arduino/workflows/Build/badge.svg)](https://github.com/adafruit/Adafruit_TinyUSB_Arduino/actions) [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)
This library is a Arduino-friendly version of [TinyUSB](https://github.com/hathach/tinyusb) stack.
It is designed with structure and APIs that are easily integrated to an Arduino Core.
## Features
### Device Stack
Supported device class drivers are:
- Communication (CDC): which is used to implement `Serial` monitor
- Human Interface Device (HID): Generic (In & Out), Keyboard, Mouse, Gamepad etc ...
- Mass Storage Class (MSC): with multiple LUNs
- Musical Instrument Digital Interface (MIDI)
- WebUSB with vendor specific class
### Host Stack
Host support is still work-in-progress but currently available with rp2040 core thanks to [Pico-PIO-USB](https://github.com/sekigon-gonnoc/Pico-PIO-USB). Supported class driver are:
- Communication (CDC)
- MassStorage class
## Supported Cores
There are 2 type of supported cores: with and without built-in support for TinyUSB. Built-in support provide seamless integration but requires extra code added to core's source code. Unfortunately it is not always easy or possible to make those modification.
### Cores with built-in support
Following core has TinyUSB as either the primary usb stack or selectable via menu `Tools->USB Stack`. You only need to include `<Adafruit_TinyUSB.h>` in your sketch to use.
- [adafruit/Adafruit_nRF52_Arduino](https://github.com/adafruit/Adafruit_nRF52_Arduino)
- [adafruit/ArduinoCore-samd](https://github.com/adafruit/ArduinoCore-samd)
- [earlephilhower/arduino-pico](https://github.com/earlephilhower/arduino-pico)
- [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
ESP32 port relies on Espressif's [esp32-hal-tinyusb.c](https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-tinyusb.c) for building usb descriptors which requires all descriptors must be specified in usb objects declaration i.e constructors. Therefore all descriptor-related fields must be part of object declaration and descriptor-related API have no effect afterwards for this port.
### Cores without built-in support
Following is cores without built-in support
- **mbed_rp2040**
It is still possible to use TinyUSB but with some limits such as:
- `TinyUSB_Device_Init()` need to be manually called in setup()
- `TinyUSB_Device_Task()` and/or `TinyUSB_Device_FlushCDC()` may (or not) need to be manually called in loop()
- Use `SerialTinyUSB` name instead of Serial for serial monitor
- And there could be more other issues, using on these cores should be considered as experimental
## Class Driver API
More document to write ...
## Porting Guide
To integrate TinyUSB library to a Arduino core, you will need to make changes to the core for built-in support and library for porting the mcu/platform.
### Arduino Core Changes
If possible, making changes to core will allow it to have built-in which make it almost transparent to user sketch
1. Add this repo as submodule (or have local copy) at your ArduioCore/libraries/Adafruit_TinyUSB_Arduino (much like SPI).
2. Since Serial as CDC is considered as part of the core, we need to have `#include "Adafruit_USBD_CDC.h"` within your `Arduino.h`. For this to work, your `platform.txt` include path need to have `"-I{runtime.platform.path}/libraries/Adafruit_TinyUSB_Arduino/src/arduino"`.
3. In your `main.cpp` before setup() invoke the `TinyUSB_Device_Init(rhport)`. This will initialize usb device hardware and tinyusb stack and also include Serial as an instance of CDC class.
4. `TinyUSB_Device_Task()` must be called whenever there is new USB event. Depending on your core and MCU with or without RTOS. There are many ways to run the task. For example:
- Use USB IRQn to set flag then invoke function later on after exiting IRQ.
- Just invoke function after the loop(), within yield(), and delay()
5. `TinyUSB_Device_FlushCDC()` should also be called often to send out Serial data as well.
6. Note: For low power platform that make use of WFI()/WFE(), extra care is required before mcu go into low power mode. Check out my PR to circuipython for reference https://github.com/adafruit/circuitpython/pull/2956
### Library Changes
In addition to core changes, library need to be ported to your platform. Don't worry, tinyusb stack has already done most of heavy-lifting. You only need to write a few APIs
1. `TinyUSB_Port_InitDevice()` hardware specific (clock, phy) to enable usb hardware then call tud_init(). This API is called as part of TinyUSB_Device_Init() invocation.
2. `TinyUSB_Port_EnterDFU()` which is called when device need to enter DFU mode, usually by touch1200 feature
3. `TinyUSB_Port_GetSerialNumber()` which is called to get unique MCU Serial ID to used as Serial string descriptor.

View File

@ -0,0 +1,158 @@
# Adafruit TinyUSB Arduino Library Changelog
## 1.6.0
- Update TinyUSB to post 0.12.0 at commit https://github.com/hathach/tinyusb/commit/b4a0f0b273eee32ead7acbd44ca9554c58a2adfa
- Fix build with ESP32S2 v2.0.1rc
- Fix MIDI Control Change message sending is corrupted
## 1.5.0 - 2021.09.29
- Add support for ESP32-S2 core version 2.0.0
- ESP32 port relies on Espressif's [esp32-hal-tinyusb.c](https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-tinyusb.c) for building usb descriptors which requires all descriptors must be specified in usb objects declaration i.e constructors. Therefore all descriptor-related fields must be part of object declaration and descriptor-related API have no effect afterwards for this port.
- Add new constructor for Adafruit_USBD_HID(desc_report, len, protocol, interval_ms, has_out_endpoint)
## 1.4.4 - 2021.08.18
- Update tinyusb stack
- Fully support nRF5x suspend, resume & remote wakeup
- Update hid_keyboard/mouse example to hid boot keyboard/mouse
## 1.4.3 - 2021.08.11
- Fix HID generic inout example python/js script cause missing 1st byte in report
- Use correct HID code for newline 0x28
## 1.4.2 - 2021.08.03
- Update tinyusb to match upstream
## 1.4.1 - 2021.07.27
- Fix nRF race condition when initialize usb stack
## 1.4.0 - 2021.07.22
- Support rp2040 mbed core as non built-in support
## 1.3.2 - 2021.07.07
- revert CFG_TUSB_DEBUG = 0 on SAMD port
## 1.3.1 - 2021.07.06
- Fix warning with ci build for rp2040 core
- Fix example build for esp32s2
- Use ARDUINO_NRF52_ADAFRUIT instead of ARDUINO_ARCH_NRF52
## 1.3.0 - 2021.06.29
- Move tusb_config for each ports into library to make it more portable
- Adding support for ESP32S2 (still need PR to be merged from esp32-arduino)
- Update CDC to have instance and getInstanceCount()
- Allow USB_VID/PID to be declared in variant pins_arduino.h
- Use bug report form template
## 1.1.0 - 2021.06.21
- Add support for multiple CDC ports (need to modify tusb_config.h)
- fix #86 when calling midi API when device is not fully enumerated
- fix Serial connection issue with nrf52 on windows
- Update device driver of rp2040 to match tinyusb upstream
- Added feather rp2040 to ci build (skipped the external flash example for now due to SdFat compilation error)
- Add optional debug log for stack with Serial1 on samd core (will expand later to other core).
- lost more bug fixes to the stack
## 1.0.3 - 2021.05.26
- Update tinyusb stack to latest
- Added HID Gamepad example with Dhat support
- Fix warnings with -Wall -Wextra
- Fix warnings with cast function type for nrf
## 1.0.1 - 2021.05.21
Warn user to select TinyUSB via menu on ports where it is optional e.g SAMD and RP2040
## 1.0.0 - 2021.05.19
Rework to work as independent libraries, sources from https://github.com/adafruit/Adafruit_TinyUSB_ArduinoCore is now integrated as part of this libraries. Require
- Adafruit SAMD core with version at least 1.7.0
- Adafruit nRF52 core with version at least 0.22.0
- Support [earlephilhower/arduino-pico](https://github.com/earlephilhower/arduino-pico) version released after https://github.com/earlephilhower/arduino-pico/pull/127
## 0.9.0 - 2020.04.23
- Fixed mouseButtonRelease() error
- Supported multiple cables for USB MIDI (requires BSP nRF52 0.20.0 and SAMD 1.5.12 )
- Added consumer control support to HID/hid_composite example
- Added Adafruit_USBD_HID send report helper: sendReport8(), sendReport16(), sendReport32()
**Minor Breaking Changes**
- Removed trailing comma in hid report descriptor, this is required to use with BSP nRF52 0.20.0 and SAMD 1.5.12 e.g
from
```
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(RID_KEYBOARD), ),
TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(RID_MOUSE ), ),
};
```
to
```
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(RID_KEYBOARD) /*, no more trailing comma */ ),
TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(RID_MOUSE ) /*, no more trailing comma */ ),
};
```
## 0.8.2 - 2020.04.06
- Removed package-lock.json in hid generic inout example due to security warning from github
## 0.8.1 - 2020.01.08
- More CI migrating work, no function changes
## 0.8.0 - 2019.12.30
- Correct USB BCD version to 2.1 for webUSB
- Migrate CI from travis to github actions
## 0.7.1 - 2019.10.12
- Fixed MIDI build failed since it is under development
## 0.7.0 - 2019.10.09
- Added MIDI virtual wires/plugs
## 0.6.0 - 2019.08.05
- Added webUSB support with 2 example: webusb-serial, webusb-rgb
- Aligned mouse examples, added newer hid in/out example from main repo, added new composite example for ramdisk and hid in/out. PR #19 thanks to @PTS93
## 0.5.0 - 2019.07.17
- Added travis build
- Fixed msc setID
- Added itfnum to internal API getDescriptor()
- Added MIDI support
- Added midi_test example
- Added pizza box dj example for cplayground express
- Mass Storage
- Added msc_sdfat, msc dual lun (external flash + sd card) example
- Updated msc example to use new SPIFlash 3.x API
- Update msc example to print root contents
- HID
- Added hid_mouse, hid_keyboard
- Added hid_composite_joy_featherwing
- Added Composite: mouse_ramdisk, mouse_external_flash example
## 0.0.1 Initial Release

View File

@ -0,0 +1,75 @@
.main-content {
width: 1440px;
margin: auto;
font-size: 14px;
}
.connect-container {
margin: 20px 0;
}
.button::before {
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
-webkit-box-shadow: #959595 0 2px 5px;
-moz-box-shadow: #959595 0 2px 5px;
border-radius: 3px;
box-shadow: #959595 0 2px 5px;
content: "";
display: block;
left: 0;
padding: 2px 0 0;
position: absolute;
top: 0;
}
.button:active::before { padding: 1px 0 0; }
.button.black {
background: #656565;
background: -webkit-gradient(linear, 0 0, 0 bottom, from(#656565), to(#444));
background: -moz-linear-gradient(#656565, #444);
background: linear-gradient(#656565, #444);
border: solid 1px #535353;
border-bottom: solid 3px #414141;
box-shadow: inset 0 0 0 1px #939393;
color: #fff;
text-shadow: 0 1px 0 #2f2f2f;
padding: 8px 16px;
outline: none;
}
.button.black:hover {
background: #4c4c4c;
background: -webkit-gradient(linear, 0 0, 0 bottom, from(#4c4c4c), to(#565656));
background: -moz-linear-gradient(#4c4c4c, #565656);
background: linear-gradient(#4c4c4c, #565656);
border: solid 1px #464646;
border-bottom: solid 3px #414141;
box-shadow: inset 0 0 0 1px #818181;
}
.button.black:active {
background: #474747;
background: -webkit-gradient(linear, 0 0, 0 bottom, from(#474747), to(#444));
background: -moz-linear-gradient(#474747, #444);
background: linear-gradient(#474747, #444);
border: solid 1px #2f2f2f;
box-shadow: inset 0 10px 15px 0 #3e3e3e;
}
.color-picker-container {
width: 100px;
height: 100px;
border-radius: 50px;
overflow: hidden;
}
.color-picker {
padding: 0;
border: none;
width: 200px;
height: 200px;
outline: none;
transform: translate(-25%, -25%)
}

View File

@ -0,0 +1,54 @@
(function() {
'use strict';
document.addEventListener('DOMContentLoaded', event => {
let connectButton = document.querySelector("#connect");
let statusDisplay = document.querySelector('#status');
let port;
function connect() {
port.connect().then(() => {
statusDisplay.textContent = '';
connectButton.textContent = 'Disconnect';
port.onReceiveError = error => {
console.error(error);
};
}, error => {
statusDisplay.textContent = error;
});
}
connectButton.addEventListener('click', function() {
if (port) {
port.disconnect();
connectButton.textContent = 'Connect';
statusDisplay.textContent = '';
port = null;
} else {
serial.requestPort().then(selectedPort => {
port = selectedPort;
connect();
}).catch(error => {
statusDisplay.textContent = error;
});
}
});
serial.getPorts().then(ports => {
if (ports.length === 0) {
statusDisplay.textContent = 'No device found.';
} else {
statusDisplay.textContent = 'Connecting...';
port = ports[0];
connect();
}
});
let colorPicker = document.getElementById("color_picker");
colorPicker.addEventListener("change", function(event) {
port.send(new TextEncoder("utf-8").encode(colorPicker.value));
});
});
})();

View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title>TinyUSB</title>
<script src="serial.js"></script>
<script src="application.js"></script>
<link rel="stylesheet" href="application.css">
</head>
<body>
<div class="main-content">
<h1>TinyUSB - WebUSB RGB Example</h1>
<div class="connect-container">
<button id="connect" class="button black">Connect</button>
<span id="status"></span>
</div>
<div class="color-picker-container">
<input type="color" id="color_picker" value="#00ff00" class="color-picker">
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,92 @@
var serial = {};
(function() {
'use strict';
serial.getPorts = function() {
return navigator.usb.getDevices().then(devices => {
return devices.map(device => new serial.Port(device));
});
};
serial.requestPort = function() {
const filters = [
{ 'vendorId': 0xcafe }, // TinyUSB
{ 'vendorId': 0x239a }, // Adafruit
{ 'vendorId': 0x2e8a }, // Raspberry Pi
{ 'vendorId': 0x303a }, // Espressif
{ 'vendorId': 0x2341 }, // Arduino
];
return navigator.usb.requestDevice({ 'filters': filters }).then(
device => new serial.Port(device)
);
}
serial.Port = function(device) {
this.device_ = device;
this.interfaceNumber = 0;
this.endpointIn = 0;
this.endpointOut = 0;
};
serial.Port.prototype.connect = function() {
let readLoop = () => {
this.device_.transferIn(this.endpointIn, 64).then(result => {
this.onReceive(result.data);
readLoop();
}, error => {
this.onReceiveError(error);
});
};
return this.device_.open()
.then(() => {
if (this.device_.configuration === null) {
return this.device_.selectConfiguration(1);
}
})
.then(() => {
var interfaces = this.device_.configuration.interfaces;
interfaces.forEach(element => {
element.alternates.forEach(elementalt => {
if (elementalt.interfaceClass==0xFF) {
this.interfaceNumber = element.interfaceNumber;
elementalt.endpoints.forEach(elementendpoint => {
if (elementendpoint.direction == "out") {
this.endpointOut = elementendpoint.endpointNumber;
}
if (elementendpoint.direction=="in") {
this.endpointIn =elementendpoint.endpointNumber;
}
})
}
})
})
})
.then(() => this.device_.claimInterface(this.interfaceNumber))
.then(() => this.device_.selectAlternateInterface(this.interfaceNumber, 0))
.then(() => this.device_.controlTransferOut({
'requestType': 'class',
'recipient': 'interface',
'request': 0x22,
'value': 0x01,
'index': this.interfaceNumber}))
.then(() => {
readLoop();
});
};
serial.Port.prototype.disconnect = function() {
return this.device_.controlTransferOut({
'requestType': 'class',
'recipient': 'interface',
'request': 0x22,
'value': 0x00,
'index': this.interfaceNumber})
.then(() => this.device_.close());
};
serial.Port.prototype.send = function(data) {
return this.device_.transferOut(this.endpointOut, data);
};
})();

View File

@ -0,0 +1,107 @@
.main-content {
width: 1440px;
margin: auto;
font-size: 14px;
}
.connect-container {
margin: 20px 0;
}
.container {
display: flex;
}
.sender, .receiver {
flex: 1;
}
.sender {
margin-right: 8px;
}
.receiver {
margin-left: 8px;
}
.lines-header {
height: 30px;
width: 100%;
box-sizing: border-box;
background-color: #444;
line-height: 30px;
color: white;
padding-left: 10px;
}
.lines-body {
width: 100%;
background-color: #222;
min-height: 300px;
padding: 10px 0 20px 0;
}
.line, .command-line {
box-sizing: border-box;
width: 100%;
color: #f1f1f1;
background-color: #222;
outline: none;
border: none;
padding: 5px 10px;
font-size: 14px;
}
.line:hover {
background-color: #444;
}
.button::before {
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
-webkit-box-shadow: #959595 0 2px 5px;
-moz-box-shadow: #959595 0 2px 5px;
border-radius: 3px;
box-shadow: #959595 0 2px 5px;
content: "";
display: block;
left: 0;
padding: 2px 0 0;
position: absolute;
top: 0;
}
.button:active::before { padding: 1px 0 0; }
.button.black {
background: #656565;
background: -webkit-gradient(linear, 0 0, 0 bottom, from(#656565), to(#444));
background: -moz-linear-gradient(#656565, #444);
background: linear-gradient(#656565, #444);
border: solid 1px #535353;
border-bottom: solid 3px #414141;
box-shadow: inset 0 0 0 1px #939393;
color: #fff;
text-shadow: 0 1px 0 #2f2f2f;
padding: 8px 16px;
outline: none;
}
.button.black:hover {
background: #4c4c4c;
background: -webkit-gradient(linear, 0 0, 0 bottom, from(#4c4c4c), to(#565656));
background: -moz-linear-gradient(#4c4c4c, #565656);
background: linear-gradient(#4c4c4c, #565656);
border: solid 1px #464646;
border-bottom: solid 3px #414141;
box-shadow: inset 0 0 0 1px #818181;
}
.button.black:active {
background: #474747;
background: -webkit-gradient(linear, 0 0, 0 bottom, from(#474747), to(#444));
background: -moz-linear-gradient(#474747, #444);
background: linear-gradient(#474747, #444);
border: solid 1px #2f2f2f;
box-shadow: inset 0 10px 15px 0 #3e3e3e;
}

View File

@ -0,0 +1,96 @@
(function() {
'use strict';
document.addEventListener('DOMContentLoaded', event => {
let connectButton = document.querySelector("#connect");
let statusDisplay = document.querySelector('#status');
let port;
function addLine(linesId, text) {
var senderLine = document.createElement("div");
senderLine.className = 'line';
var textnode = document.createTextNode(text);
senderLine.appendChild(textnode);
document.getElementById(linesId).appendChild(senderLine);
return senderLine;
}
let currentReceiverLine;
function appendLines(linesId, text) {
const lines = text.split('\r');
if (currentReceiverLine) {
currentReceiverLine.innerHTML = currentReceiverLine.innerHTML + lines[0];
for (let i = 1; i < lines.length; i++) {
currentReceiverLine = addLine(linesId, lines[i]);
}
} else {
for (let i = 0; i < lines.length; i++) {
currentReceiverLine = addLine(linesId, lines[i]);
}
}
}
function connect() {
port.connect().then(() => {
statusDisplay.textContent = '';
connectButton.textContent = 'Disconnect';
port.onReceive = data => {
let textDecoder = new TextDecoder();
console.log(textDecoder.decode(data));
if (data.getInt8() === 13) {
currentReceiverLine = null;
} else {
appendLines('receiver_lines', textDecoder.decode(data));
}
};
port.onReceiveError = error => {
console.error(error);
};
}, error => {
statusDisplay.textContent = error;
});
}
connectButton.addEventListener('click', function() {
if (port) {
port.disconnect();
connectButton.textContent = 'Connect';
statusDisplay.textContent = '';
port = null;
} else {
serial.requestPort().then(selectedPort => {
port = selectedPort;
connect();
}).catch(error => {
statusDisplay.textContent = error;
});
}
});
serial.getPorts().then(ports => {
if (ports.length === 0) {
statusDisplay.textContent = 'No device found.';
} else {
statusDisplay.textContent = 'Connecting...';
port = ports[0];
connect();
}
});
let commandLine = document.getElementById("command_line");
commandLine.addEventListener("keypress", function(event) {
if (event.keyCode === 13) {
if (commandLine.value.length > 0) {
addLine('sender_lines', commandLine.value);
commandLine.value = '';
}
}
port.send(new TextEncoder('utf-8').encode(String.fromCharCode(event.which || event.keyCode)));
});
});
})();

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<title>TinyUSB</title>
<script src="serial.js"></script>
<script src="application.js"></script>
<link rel="stylesheet" href="application.css">
</head>
<body>
<div class="main-content">
<h1>TinyUSB - WebUSB Serial Example</h1>
<div class="connect-container">
<button id="connect" class="button black">Connect</button>
<span id="status"></span>
</div>
<div class="container">
<div class="sender">
<div class="lines-header">Sender</div>
<div class="lines-body">
<div id="sender_lines" class="lines"></div>
<input id="command_line" class="command-line" placeholder="Start typing ...." />
</div>
</div>
<div class="receiver">
<div class="lines-header">Receiver</div>
<div class="lines-body">
<div id="receiver_lines" class="lines"></div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,92 @@
var serial = {};
(function() {
'use strict';
serial.getPorts = function() {
return navigator.usb.getDevices().then(devices => {
return devices.map(device => new serial.Port(device));
});
};
serial.requestPort = function() {
const filters = [
{ 'vendorId': 0xcafe }, // TinyUSB
{ 'vendorId': 0x239a }, // Adafruit
{ 'vendorId': 0x2e8a }, // Raspberry Pi
{ 'vendorId': 0x303a }, // Espressif
{ 'vendorId': 0x2341 }, // Arduino
];
return navigator.usb.requestDevice({ 'filters': filters }).then(
device => new serial.Port(device)
);
}
serial.Port = function(device) {
this.device_ = device;
this.interfaceNumber = 0;
this.endpointIn = 0;
this.endpointOut = 0;
};
serial.Port.prototype.connect = function() {
let readLoop = () => {
this.device_.transferIn(this.endpointIn, 64).then(result => {
this.onReceive(result.data);
readLoop();
}, error => {
this.onReceiveError(error);
});
};
return this.device_.open()
.then(() => {
if (this.device_.configuration === null) {
return this.device_.selectConfiguration(1);
}
})
.then(() => {
var interfaces = this.device_.configuration.interfaces;
interfaces.forEach(element => {
element.alternates.forEach(elementalt => {
if (elementalt.interfaceClass==0xFF) {
this.interfaceNumber = element.interfaceNumber;
elementalt.endpoints.forEach(elementendpoint => {
if (elementendpoint.direction == "out") {
this.endpointOut = elementendpoint.endpointNumber;
}
if (elementendpoint.direction=="in") {
this.endpointIn =elementendpoint.endpointNumber;
}
})
}
})
})
})
.then(() => this.device_.claimInterface(this.interfaceNumber))
.then(() => this.device_.selectAlternateInterface(this.interfaceNumber, 0))
.then(() => this.device_.controlTransferOut({
'requestType': 'class',
'recipient': 'interface',
'request': 0x22,
'value': 0x01,
'index': this.interfaceNumber}))
.then(() => {
readLoop();
});
};
serial.Port.prototype.disconnect = function() {
return this.device_.controlTransferOut({
'requestType': 'class',
'recipient': 'interface',
'request': 0x22,
'value': 0x00,
'index': this.interfaceNumber})
.then(() => this.device_.close());
};
serial.Port.prototype.send = function(data) {
return this.device_.transferOut(this.endpointOut, data);
};
})();

View File

@ -0,0 +1,116 @@
/*
This example demonstrates the use of multiple USB CDC/ACM "Virtual
Serial" ports
Written by Bill Westfield (aka WestfW), June 2021.
Copyright 2021 by Bill Westfield
MIT license, check LICENSE for more information
*/
/* The example creates two virtual serial ports. Text entered on
* any of the ports will be echoed to the all ports with
* - all lower case in port0 (Serial)
* - all upper case in port1
*
* Requirement:
* The max number of CDC ports (CFG_TUD_CDC) has to be changed to at least 2.
* Config file is located in Adafruit_TinyUSB_Arduino/src/arduino/ports/{platform}/tusb_config_{platform}.h
* where platform is one of: nrf, rp2040, samd
*
* NOTE: Currnetly multiple CDCs on ESP32-Sx is not yet supported.
* An PR to update core/esp32/USBCDC and/or pre-built libusb are needed.
* We would implement this later when we could.
*/
#include <Adafruit_TinyUSB.h>
#define LED LED_BUILTIN
// Create 2nd instance of CDC Ports.
#ifdef ARDUINO_ARCH_ESP32
#error "Currnetly multiple CDCs on ESP32-Sx is not yet supported. An PR to update core/esp32/USBCDC and/or pre-built libusb are needed."
// for ESP32, we need to specify instance number when declaring object
Adafruit_USBD_CDC USBSer1(1);
#else
Adafruit_USBD_CDC USBSer1;
#endif
void setup() {
pinMode(LED, OUTPUT);
Serial.begin(115200);
// check to see if multiple CDCs are enabled
if ( CFG_TUD_CDC < 2 ) {
digitalWrite(LED, HIGH); // LED on for error indicator
while(1) {
Serial.printf("CFG_TUD_CDC must be at least 2, current value is %u\n", CFG_TUD_CDC);
Serial.println(" Config file is located in Adafruit_TinyUSB_Arduino/src/arduino/ports/{platform}/tusb_config_{platform}.h");
Serial.println(" where platform is one of: nrf, rp2040, samd");
delay(1000);
}
}
// initialize 2nd CDC interface
USBSer1.begin(115200);
while (!Serial || !USBSer1) {
if (Serial) {
Serial.println("Waiting for other USB ports");
}
if (USBSer1) {
USBSer1.println("Waiting for other USB ports");
}
delay(1000);
}
Serial.print("You are port 0\n\r\n0> ");
USBSer1.print("You are port 1\n\r\n1> ");
}
int LEDstate = 0;
void loop() {
int ch;
ch = Serial.read();
if (ch > 0) {
printAll(ch);
}
ch = USBSer1.read();
if (ch > 0) {
printAll(ch);
}
if (delay_without_delaying(500)) {
LEDstate = !LEDstate;
digitalWrite(LED, LEDstate);
}
}
// print to all CDC ports
void printAll(int ch) {
// always lower case
Serial.write(tolower(ch));
// always upper case
USBSer1.write(toupper(ch));
}
// Helper: non-blocking "delay" alternative.
boolean delay_without_delaying(unsigned long time) {
// return false if we're still "delaying", true if time ms has passed.
// this should look a lot like "blink without delay"
static unsigned long previousmillis = 0;
unsigned long currentmillis = millis();
if (currentmillis - previousmillis >= time) {
previousmillis = currentmillis;
return true;
}
return false;
}

View File

@ -0,0 +1,36 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include "Adafruit_TinyUSB.h"
/* This sketch demonstrates USB CDC Serial can be dropped by simply
* call Serial.end() within setup(). This must be called before any
* other USB interfaces (MSC / HID) begin to have a clean configuration
*
* Note: this will cause device to loose the touch1200 and require
* user manual interaction to put device into bootloader/DFU mode.
*/
int led = LED_BUILTIN;
void setup()
{
Serial.end();
pinMode(led, OUTPUT);
}
void loop()
{
digitalWrite(led, HIGH);
delay(1000);
digitalWrite(led, LOW);
delay(1000);
}

View File

@ -0,0 +1,192 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This sketch demonstrates USB Mass Storage and HID mouse (and CDC)
* - Enumerated as disk using on-board external flash
* - Press button pin will move mouse toward bottom right of monitor
*/
#include "SPI.h"
#include "SdFat.h"
#include "Adafruit_SPIFlash.h"
#include "Adafruit_TinyUSB.h"
//--------------------------------------------------------------------+
// MSC External Flash Config
//--------------------------------------------------------------------+
// Un-comment to run example with custom SPI SPI and SS e.g with FRAM breakout
// #define CUSTOM_CS A5
// #define CUSTOM_SPI SPI
#if defined(CUSTOM_CS) && defined(CUSTOM_SPI)
Adafruit_FlashTransport_SPI flashTransport(CUSTOM_CS, CUSTOM_SPI);
#elif defined(ARDUINO_ARCH_ESP32)
// ESP32 use same flash device that store code.
// Therefore there is no need to specify the SPI and SS
Adafruit_FlashTransport_ESP32 flashTransport;
#elif defined(ARDUINO_ARCH_RP2040)
// RP2040 use same flash device that store code.
// Therefore there is no need to specify the SPI and SS
// Use default (no-args) constructor to be compatible with CircuitPython partition scheme
Adafruit_FlashTransport_RP2040 flashTransport;
// For generic usage:
// Adafruit_FlashTransport_RP2040 flashTransport(start_address, size)
// If start_address and size are both 0, value that match filesystem setting in
// 'Tools->Flash Size' menu selection will be used
#else
// On-board external flash (QSPI or SPI) macros should already
// defined in your board variant if supported
// - EXTERNAL_FLASH_USE_QSPI
// - EXTERNAL_FLASH_USE_CS/EXTERNAL_FLASH_USE_SPI
#if defined(EXTERNAL_FLASH_USE_QSPI)
Adafruit_FlashTransport_QSPI flashTransport;
#elif defined(EXTERNAL_FLASH_USE_SPI)
Adafruit_FlashTransport_SPI flashTransport(EXTERNAL_FLASH_USE_CS, EXTERNAL_FLASH_USE_SPI);
#else
#error No QSPI/SPI flash are defined on your board variant.h !
#endif
#endif
Adafruit_SPIFlash flash(&flashTransport);
Adafruit_USBD_MSC usb_msc;
//--------------------------------------------------------------------+
// HID Config
//--------------------------------------------------------------------+
// HID report descriptor using TinyUSB's template
// Single Report (no ID) descriptor
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_MOUSE()
};
// USB HID object. For ESP32 these values cannot be changed after this declaration
// desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_NONE, 2, false);
#if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(ARDUINO_NRF52840_CIRCUITPLAY)
const int pin = 4; // Left Button
bool activeState = true;
#elif defined(ARDUINO_FUNHOUSE_ESP32S2)
const int pin = BUTTON_DOWN;
bool activeState = true;
#elif defined PIN_BUTTON1
const int pin = PIN_BUTTON1;
bool activeState = false;
#else
const int pin = 12;
bool activeState = false;
#endif
// the setup function runs once when you press reset or power the board
void setup()
{
flash.begin();
pinMode(LED_BUILTIN, OUTPUT);
// Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively
usb_msc.setID("Adafruit", "External Flash", "1.0");
// Set callback
usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb);
// Set disk size, block size should be 512 regardless of spi flash page size
usb_msc.setCapacity(flash.size()/512, 512);
// MSC is ready for read/write
usb_msc.setUnitReady(true);
usb_msc.begin();
// Set up button
pinMode(pin, activeState ? INPUT_PULLDOWN : INPUT_PULLUP);
// Notes: following commented-out functions has no affect on ESP32
// usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
usb_hid.begin();
Serial.begin(115200);
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Adafruit TinyUSB Mouse + Mass Storage (external flash) example");
}
void loop()
{
// poll gpio once each 10 ms
delay(10);
// button is active low
uint32_t const btn = (digitalRead(pin) == activeState);
// Remote wakeup
if ( TinyUSBDevice.suspended() && btn )
{
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
}
/*------------- Mouse -------------*/
if ( usb_hid.ready() )
{
if ( btn )
{
int8_t const delta = 5;
usb_hid.mouseMove(0, delta, delta); // no ID: right + down
// delay a bit before attempt to send keyboard report
delay(10);
}
}
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and
// return number of copied bytes (must be multiple of block size)
int32_t msc_read_cb (uint32_t lba, void* buffer, uint32_t bufsize)
{
// Note: SPIFLash Bock API: readBlocks/writeBlocks/syncBlocks
// already include 4K sector caching internally. We don't need to cache it, yahhhh!!
return flash.readBlocks(lba, (uint8_t*) buffer, bufsize/512) ? bufsize : -1;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t msc_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize)
{
// Note: SPIFLash Bock API: readBlocks/writeBlocks/syncBlocks
// already include 4K sector caching internally. We don't need to cache it, yahhhh!!
return flash.writeBlocks(lba, buffer, bufsize/512) ? bufsize : -1;
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void msc_flush_cb (void)
{
flash.syncBlocks();
}

View File

@ -0,0 +1,155 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This sketch demonstrates USB Mass Storage and HID mouse (and CDC)
* - Enumerated as 8KB flash disk
* - Press button pin will move mouse toward bottom right of monitor
*/
#include "Adafruit_TinyUSB.h"
//--------------------------------------------------------------------+
// MSC RAM Disk Config
//--------------------------------------------------------------------+
// 8KB is the smallest size that windows allow to mount
#define DISK_BLOCK_NUM 16
#define DISK_BLOCK_SIZE 512
#include "ramdisk.h"
Adafruit_USBD_MSC usb_msc;
//--------------------------------------------------------------------+
// HID Config
//--------------------------------------------------------------------+
// HID report descriptor using TinyUSB's template
// Single Report (no ID) descriptor
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_MOUSE()
};
// USB HID object. For ESP32 these values cannot be changed after this declaration
// desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_NONE, 2, false);
#if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(ARDUINO_NRF52840_CIRCUITPLAY)
const int pin = 4; // Left Button
bool activeState = true;
#elif defined(ARDUINO_FUNHOUSE_ESP32S2)
const int pin = BUTTON_DOWN;
bool activeState = true;
#elif defined PIN_BUTTON1
const int pin = PIN_BUTTON1;
bool activeState = false;
#else
const int pin = 12;
bool activeState = false;
#endif
// the setup function runs once when you press reset or power the board
void setup()
{
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
// Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040
TinyUSB_Device_Init(0);
#endif
// Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively
usb_msc.setID("Adafruit", "Mass Storage", "1.0");
// Set disk size
usb_msc.setCapacity(DISK_BLOCK_NUM, DISK_BLOCK_SIZE);
// Set callback
usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb);
// Set Lun ready (RAM disk is always ready)
usb_msc.setUnitReady(true);
usb_msc.begin();
// Set up button
pinMode(pin, activeState ? INPUT_PULLDOWN : INPUT_PULLUP);
// Notes: following commented-out functions has no affect on ESP32
// usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
usb_hid.begin();
Serial.begin(115200);
while( !TinyUSBDevice.mounted() ) delay(1); // wait for native usb
Serial.println("Adafruit TinyUSB Mouse + Mass Storage (ramdisk) example");
}
void loop()
{
// poll gpio once each 10 ms
delay(10);
// button is active low
uint32_t const btn = (digitalRead(pin) == activeState);
// Remote wakeup
if ( TinyUSBDevice.suspended() && btn )
{
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
}
/*------------- Mouse -------------*/
if ( usb_hid.ready() )
{
if ( btn )
{
int8_t const delta = 5;
usb_hid.mouseMove(0, delta, delta); // no ID: right + down
// delay a bit before attempt to send keyboard report
delay(10);
}
}
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and
// return number of copied bytes (must be multiple of block size)
int32_t msc_read_cb (uint32_t lba, void* buffer, uint32_t bufsize)
{
uint8_t const* addr = msc_disk[lba];
memcpy(buffer, addr, bufsize);
return bufsize;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t msc_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize)
{
uint8_t* addr = msc_disk[lba];
memcpy(addr, buffer, bufsize);
return bufsize;
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void msc_flush_cb (void)
{
// nothing to do
}

View File

@ -0,0 +1,113 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef RAMDISK_H_
#define RAMDISK_H_
#define README_CONTENTS \
"This is TinyUSB MassStorage device demo for Arduino on RAM disk."
uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = {
//------------- Block0: Boot Sector -------------//
// byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16 =
// DISK_BLOCK_NUM; sector_per_cluster = 1; reserved_sectors = 1; fat_num =
// 1; fat12_root_entry_num = 16; sector_per_fat = 1; sector_per_track =
// 1; head_num = 1; hidden_sectors = 0; drive_number = 0x80;
// media_type = 0xf8; extended_boot_signature = 0x29; filesystem_type =
// "FAT12 "; volume_serial_number = 0x1234; volume_label = "TinyUSB MSC";
// FAT magic code at offset 510-511
{0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00,
0x02, 0x01, 0x01, 0x00, 0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00,
0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T', 'i', 'n', 'y', 'U', 'S',
'B', ' ', 'M', 'S', 'C', 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20,
0x00, 0x00,
// Zero up to 2 last bytes of FAT magic code
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x55, 0xAA},
//------------- Block1: FAT12 Table -------------//
{
0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third
// entry is cluster end of readme file
},
//------------- Block2: Root Directory -------------//
{
// first entry is volume label
'T', 'i', 'n', 'y', 'U', 'S', 'B', ' ', 'M', 'S', 'C', 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// second entry is readme file
'R', 'E', 'A', 'D', 'M', 'E', ' ', ' ', 'T', 'X', 'T', 0x20, 0x00, 0xC6,
0x52, 0x6D, 0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43,
0x02, 0x00, sizeof(README_CONTENTS) - 1, 0x00, 0x00,
0x00 // readme's files size (4 Bytes)
},
//------------- Block3: Readme Content -------------//
README_CONTENTS};
#endif /* RAMDISK_H_ */

View File

@ -0,0 +1,159 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example demonstrates use of Host Serial (CDC). SerialHost (declared below) is
* an object to manage an CDC peripheral connected to our USB Host connector. This example
* will forward all characters from Serial to SerialHost and vice versa.
*
* Note:
* - Device run on native usb controller (controller0)
* - Host run on bit-banging 2 GPIOs with the help of Pico-PIO-USB library (controller1)
* Requirements:
* - [Pico-PIO-USB](https://github.com/sekigon-gonnoc/Pico-PIO-USB) library
* - 2 consecutive GPIOs: D+ is defined by PIN_USB_HOST_DP, D- = D+ +1
* - Provide VBus (5v) and GND for peripheral
* - CPU Speed must be either 120 or 240 Mhz. Selected via "Menu -> CPU Speed"
*/
// pio-usb is required for rp2040 host
#include "pio_usb.h"
// TinyUSB lib
#include "Adafruit_TinyUSB.h"
// Pin D+ for host, D- = D+ + 1
#ifndef PIN_USB_HOST_DP
#define PIN_USB_HOST_DP 16
#endif
// Pin for enabling Host VBUS. comment out if not used
#ifndef PIN_5V_EN
#define PIN_5V_EN 18
#endif
#ifndef PIN_5V_EN_STATE
#define PIN_5V_EN_STATE 1
#endif
// USB Host object
Adafruit_USBH_Host USBHost;
// CDC Host object
Adafruit_USBH_CDC SerialHost;
//--------------------------------------------------------------------+
// Setup and Loop on Core0
//--------------------------------------------------------------------+
void setup() {
Serial.begin(115200);
// while ( !Serial ) delay(10); // wait for native usb
Serial.println("TinyUSB Host Serial Echo Example");
}
void loop()
{
uint8_t buf[64];
// Serial -> SerialHost
if (Serial.available()) {
size_t count = Serial.read(buf, sizeof(buf));
if ( SerialHost && SerialHost.connected() ) {
SerialHost.write(buf, count);
SerialHost.flush();
}
}
// SerialHost -> Serial
if ( SerialHost.connected() && SerialHost.available() ) {
size_t count = SerialHost.read(buf, sizeof(buf));
Serial.write(buf, count);
}
}
//--------------------------------------------------------------------+
// Setup and Loop on Core1
//--------------------------------------------------------------------+
void setup1() {
// while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if ( cpu_hz != 120000000UL && cpu_hz != 240000000UL ) {
while ( !Serial ) {
delay(10); // wait for native usb
}
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while(1) {
delay(1);
}
}
#ifdef PIN_5V_EN
pinMode(PIN_5V_EN, OUTPUT);
// power off first
digitalWrite(PIN_5V_EN, 1-PIN_5V_EN_STATE);
delay(1);
// power on
digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE);
delay(10);
#endif
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
pio_cfg.pin_dp = PIN_USB_HOST_DP;
USBHost.configure_pio_usb(1, &pio_cfg);
// run host stack on controller (rhport) 1
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
// host bit-banging processing works done in core1 to free up core0 for other works
USBHost.begin(1);
SerialHost.begin(115200);
}
void loop1()
{
USBHost.task();
// periodically flush SerialHost if connected
if ( SerialHost && SerialHost.connected() ) {
SerialHost.flush();
}
}
//--------------------------------------------------------------------+
// TinyUSB Host callbacks
//--------------------------------------------------------------------+
extern "C" {
// Invoked when a device with CDC interface is mounted
// idx is index of cdc interface in the internal pool.
void tuh_cdc_mount_cb(uint8_t idx) {
// bind SerialHost object to this interface index
SerialHost.mount(idx);
Serial.println("SerialHost is connected to a new CDC device");
}
// Invoked when a device with CDC interface is unmounted
void tuh_cdc_umount_cb(uint8_t idx) {
SerialHost.umount(idx);
Serial.println("SerialHost is disconnected");
}
}

View File

@ -0,0 +1,140 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example demonstrates use of both device and host, where
* - Device run on native usb controller (controller0)
* - Host run on bit-banging 2 GPIOs with the help of Pico-PIO-USB library (controller1)
*
* Requirements:
* - [Pico-PIO-USB](https://github.com/sekigon-gonnoc/Pico-PIO-USB) library
* - 2 consecutive GPIOs: D+ is defined by PIN_USB_HOST_DP, D- = D+ +1
* - Provide VBus (5v) and GND for peripheral
* - CPU Speed must be either 120 or 240 Mhz. Selected via "Menu -> CPU Speed"
*/
// pio-usb is required for rp2040 host
#include "pio_usb.h"
#include "Adafruit_TinyUSB.h"
// Pin D+ for host, D- = D+ + 1
#ifndef PIN_USB_HOST_DP
#define PIN_USB_HOST_DP 16
#endif
// Pin for enabling Host VBUS. comment out if not used
#ifndef PIN_5V_EN
#define PIN_5V_EN 18
#endif
#ifndef PIN_5V_EN_STATE
#define PIN_5V_EN_STATE 1
#endif
// Language ID: English
#define LANGUAGE_ID 0x0409
// USB Host object
Adafruit_USBH_Host USBHost;
//--------------------------------------------------------------------+
// Setup and Loop on Core0
//--------------------------------------------------------------------+
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // wait for native usb
Serial.println("TinyUSB Dual Device Info Example");
}
void loop()
{
Serial.flush();
}
//--------------------------------------------------------------------+
// Setup and Loop on Core1
//--------------------------------------------------------------------+
void setup1() {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if ( cpu_hz != 120000000UL && cpu_hz != 240000000UL ) {
while ( !Serial ) delay(10); // wait for native usb
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while(1) delay(1);
}
#ifdef PIN_5V_EN
pinMode(PIN_5V_EN, OUTPUT);
digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE);
#endif
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
pio_cfg.pin_dp = PIN_USB_HOST_DP;
USBHost.configure_pio_usb(1, &pio_cfg);
// run host stack on controller (rhport) 1
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
// host bit-banging processing works done in core1 to free up core0 for other works
USBHost.begin(1);
}
void loop1()
{
USBHost.task();
}
extern "C" {
// Invoked when device with hid interface is mounted
// Report descriptor is also available for use.
// tuh_hid_parse_report_descriptor() can be used to parse common/simple enough
// descriptor. Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE,
// it will be skipped therefore report_desc = NULL, desc_len = 0
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
(void) desc_report;
(void) desc_len;
uint16_t vid, pid;
tuh_vid_pid_get(dev_addr, &vid, &pid);
Serial.printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
Serial.printf("VID = %04x, PID = %04x\r\n", vid, pid);
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.printf("Error: cannot request to receive report\r\n");
}
}
// Invoked when device with hid interface is un-mounted
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
Serial.printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
}
// Invoked when received report from device via interrupt endpoint
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
Serial.printf("HIDreport : ");
for (uint16_t i = 0; i < len; i++) {
Serial.printf("0x%02X ", report[i]);
}
Serial.println();
// continue to request to receive report
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.printf("Error: cannot request to receive report\r\n");
}
}
} // extern C

View File

@ -0,0 +1,213 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2023 Bill Binko for Adafruit Industries
Based on tremor_filter example by Thach Ha
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example demonstrates use of both device and host, where
* - Device run on native usb controller (controller0)
* - Host run on bit-banging 2 GPIOs with the help of Pico-PIO-USB library (controller1)
*
* Example sketch receive mouse report from host interface (from e.g consumer mouse)
* and reduce large motions due to tremors by applying the natural log function.
* It handles negative values and a dead zone where small values will not be adjusted.
* Adjusted mouse movement are send via device interface (to PC).
*
* Requirements:
* - [Pico-PIO-USB](https://github.com/sekigon-gonnoc/Pico-PIO-USB) library
* - 2 consecutive GPIOs: D+ is defined by PIN_PIO_USB_HOST_DP, D- = D+ +1
* - Provide VBus (5v) and GND for peripheral
* - CPU Speed must be either 120 or 240 Mhz. Selected via "Menu -> CPU Speed"
*/
// pio-usb is required for rp2040 host
#include "pio_usb.h"
#include "Adafruit_TinyUSB.h"
#include <math.h>
// Pin D+ for host, D- = D+ + 1
#ifndef PIN_USB_HOST_DP
#define PIN_USB_HOST_DP 16
#endif
// Pin for enabling Host VBUS. comment out if not used
#ifndef PIN_5V_EN
#define PIN_5V_EN 18
#endif
#ifndef PIN_5V_EN_STATE
#define PIN_5V_EN_STATE 1
#endif
// Language ID: English
#define LANGUAGE_ID 0x0409
// USB Host object
Adafruit_USBH_Host USBHost;
// HID report descriptor using TinyUSB's template
// Single Report (no ID) descriptor
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_MOUSE()
};
// USB HID object. For ESP32 these values cannot be changed after this declaration
// desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_MOUSE, 2, false);
/* Adjustable parameters for the log_filter() method. Adjust for each user (would be ideal to have this
* adjustable w/o recompiling
*/
#define PRESCALE 8.0 // Must be > 0, Higher numbers increase rate of attenuation
#define POSTSCALE 1.5 // Must be > 0, Higher numbers compensate for PRESCALE attenuation
#define DEADZONE 1.0 // Must be > 1, Movements < this magnitude will not be filtered
//--------------------------------------------------------------------+
// Setup and Loop on Core0
//--------------------------------------------------------------------+
void setup()
{
Serial.begin(115200);
usb_hid.begin();
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("ATMakers Logarithm Tremor Filter Example");
}
void loop()
{
Serial.flush();
}
//--------------------------------------------------------------------+
// Setup and Loop on Core1
//--------------------------------------------------------------------+
void setup1() {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if ( cpu_hz != 120000000UL && cpu_hz != 240000000UL ) {
while ( !Serial ) delay(10); // wait for native usb
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while(1) delay(1);
}
#ifdef PIN_5V_EN
pinMode(PIN_5V_EN, OUTPUT);
digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE);
#endif
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
pio_cfg.pin_dp = PIN_USB_HOST_DP;
USBHost.configure_pio_usb(1, &pio_cfg);
// run host stack on controller (rhport) 1
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
// host bit-banging processing works done in core1 to free up core0 for other works
USBHost.begin(1);
}
void loop1()
{
USBHost.task();
}
extern "C"
{
// Invoked when device with hid interface is mounted
// Report descriptor is also available for use.
// tuh_hid_parse_report_descriptor() can be used to parse common/simple enough
// descriptor. Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE,
// it will be skipped therefore report_desc = NULL, desc_len = 0
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
(void) desc_report;
(void) desc_len;
uint16_t vid, pid;
tuh_vid_pid_get(dev_addr, &vid, &pid);
Serial.printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
Serial.printf("VID = %04x, PID = %04x\r\n", vid, pid);
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
if (itf_protocol == HID_ITF_PROTOCOL_MOUSE) {
Serial.printf("HID Mouse\r\n");
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.printf("Error: cannot request to receive report\r\n");
}
}
}
// Invoked when device with hid interface is un-mounted
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
Serial.printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
}
// Invoked when received report from device via interrupt endpoint
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
filter_report((hid_mouse_report_t const *) report);
// continue to request to receive report
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.printf("Error: cannot request to receive report\r\n");
}
}
} // extern C
//--------------------------------------------------------------------+
// Low pass filter Functions
//--------------------------------------------------------------------+
/*
* log_filter: Reduce large motions due to tremors by applying the natural log function
* Handles negative values and a dead zone where small values will not be adjusted
*/
int8_t log_filter(int8_t val)
{
if (val < -1*DEADZONE)
{
return (int8_t) (-1.0 * POSTSCALE * logf(-1.0 * PRESCALE * (float)val));
}
else if (val > DEADZONE)
{
return (int8_t) (POSTSCALE * logf(PRESCALE * (float)val));
}
else
{
return val;
}
}
/*
* Adjust HID report by applying log_filter
*/
void filter_report(hid_mouse_report_t const* report) {
int8_t old_x = report->x;
int8_t old_y = report->y;
hid_mouse_report_t filtered_report = *report;
filtered_report.x = log_filter(old_x);
filtered_report.y = log_filter(old_y);
//Serial.printf("%d,%d,%d,%d\n", old_x, filtered_report.x, old_y, filtered_report.y);
usb_hid.sendReport(0, &filtered_report, sizeof(filtered_report));
}

View File

@ -0,0 +1,224 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example demonstrates use of both device and host, where
* - Device run on native usb controller (controller0)
* - Host run on bit-banging 2 GPIOs with the help of Pico-PIO-USB library (controller1)
*
* Example sketch receive mouse report from host interface (from e.g consumer mouse)
* and apply a butterworth low pass filter with a specific CUTOFF_FREQUENCY on hid mouse movement report.
* Filtered report are send via device interface (to PC) acting as a "Mouse Tremor Filter".
*
* Requirements:
* - [Pico-PIO-USB](https://github.com/sekigon-gonnoc/Pico-PIO-USB) library
* - 2 consecutive GPIOs: D+ is defined by PIN_PIO_USB_HOST_DP, D- = D+ +1
* - Provide VBus (5v) and GND for peripheral
* - CPU Speed must be either 120 or 240 Mhz. Selected via "Menu -> CPU Speed"
*/
// pio-usb is required for rp2040 host
#include "pio_usb.h"
#include "Adafruit_TinyUSB.h"
// Pin D+ for host, D- = D+ + 1
#ifndef PIN_USB_HOST_DP
#define PIN_USB_HOST_DP 16
#endif
// Pin for enabling Host VBUS. comment out if not used
#ifndef PIN_5V_EN
#define PIN_5V_EN 18
#endif
#ifndef PIN_5V_EN_STATE
#define PIN_5V_EN_STATE 1
#endif
// Language ID: English
#define LANGUAGE_ID 0x0409
// USB Host object
Adafruit_USBH_Host USBHost;
// HID report descriptor using TinyUSB's template
// Single Report (no ID) descriptor
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_MOUSE()
};
// USB HID object. For ESP32 these values cannot be changed after this declaration
// desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_MOUSE, 2, false);
//------------- Low pass filter with Butterworth -------------//
// Butterworth low-pass filter coefficients
typedef struct {
float b0, b1, b2, a1, a2;
} butterworth_coeffs_t;
#define SAMPLING_FREQUENCY 100.0 // Hz
#define CUTOFF_FREQUENCY 10.0 // Hz
// x, y
butterworth_coeffs_t coeffs[2];
butterworth_coeffs_t butterworth_lowpass(float cutoff_frequency, float sampling_frequency);
void filter_report(hid_mouse_report_t const* report);
//--------------------------------------------------------------------+
// Setup and Loop on Core0
//--------------------------------------------------------------------+
void setup()
{
Serial.begin(115200);
usb_hid.begin();
coeffs[0] = butterworth_lowpass(CUTOFF_FREQUENCY, SAMPLING_FREQUENCY);
coeffs[1] = butterworth_lowpass(CUTOFF_FREQUENCY, SAMPLING_FREQUENCY);
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("TinyUSB Mouse Tremor Filter Example");
}
void loop()
{
Serial.flush();
}
//--------------------------------------------------------------------+
// Setup and Loop on Core1
//--------------------------------------------------------------------+
void setup1() {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if ( cpu_hz != 120000000UL && cpu_hz != 240000000UL ) {
while ( !Serial ) delay(10); // wait for native usb
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while(1) delay(1);
}
#ifdef PIN_5V_EN
pinMode(PIN_5V_EN, OUTPUT);
digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE);
#endif
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
pio_cfg.pin_dp = PIN_USB_HOST_DP;
USBHost.configure_pio_usb(1, &pio_cfg);
// run host stack on controller (rhport) 1
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
// host bit-banging processing works done in core1 to free up core0 for other works
USBHost.begin(1);
}
void loop1()
{
USBHost.task();
}
extern "C"
{
// Invoked when device with hid interface is mounted
// Report descriptor is also available for use.
// tuh_hid_parse_report_descriptor() can be used to parse common/simple enough
// descriptor. Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE,
// it will be skipped therefore report_desc = NULL, desc_len = 0
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
(void) desc_report;
(void) desc_len;
uint16_t vid, pid;
tuh_vid_pid_get(dev_addr, &vid, &pid);
Serial.printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
Serial.printf("VID = %04x, PID = %04x\r\n", vid, pid);
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
if (itf_protocol == HID_ITF_PROTOCOL_MOUSE) {
Serial.printf("HID Mouse\r\n");
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.printf("Error: cannot request to receive report\r\n");
}
}
}
// Invoked when device with hid interface is un-mounted
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
Serial.printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
}
// Invoked when received report from device via interrupt endpoint
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
filter_report((hid_mouse_report_t const *) report);
// continue to request to receive report
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.printf("Error: cannot request to receive report\r\n");
}
}
} // extern C
//--------------------------------------------------------------------+
// Low pass filter Functions
//--------------------------------------------------------------------+
butterworth_coeffs_t butterworth_lowpass(float cutoff_frequency, float sampling_frequency) {
butterworth_coeffs_t coe;
float omega = 2.0 * PI * cutoff_frequency / sampling_frequency;
float c = cos(omega);
float s = sin(omega);
float t = tan(omega / 2.0);
float alpha = s / (2.0 * t);
coe.b0 = 1.0 / (1.0 + 2.0 * alpha + 2.0 * alpha * alpha);
coe.b1 = 2.0 * coe.b0;
coe.b2 = coe.b0;
coe.a1 = 2.0 * (alpha * alpha - 1.0) * coe.b0;
coe.a2 = (1.0 - 2.0 * alpha + 2.0 * alpha * alpha) * coe.b0;
return coe;
}
float butterworth_filter(float data, butterworth_coeffs_t *coeffs, float *filtered, float *prev1, float *prev2) {
float output = coeffs->b0 * data + coeffs->b1 * (*prev1) + coeffs->b2 * (*prev2) - coeffs->a1 * (*filtered) - coeffs->a2 * (*prev1);
*prev2 = *prev1;
*prev1 = data;
*filtered = output;
return output;
}
void filter_report(hid_mouse_report_t const* report) {
static float filtered[2] = { 0.0, 0.0 };
static float prev1[2] = { 0.0, 0.0 };
static float prev2[2] = { 0.0, 0.0 };
butterworth_filter(report->x, &coeffs[0], &filtered[0], &prev1[0], &prev2[0]);
butterworth_filter(report->y, &coeffs[1], &filtered[1], &prev1[1], &prev2[1]);
hid_mouse_report_t filtered_report = *report;
filtered_report.x = (int8_t) filtered[0];
filtered_report.y = (int8_t) filtered[1];
usb_hid.sendReport(0, &filtered_report, sizeof(filtered_report));
}

View File

@ -0,0 +1,185 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example demonstrates use of both device and host, where
* - Device run on native usb controller (controller0)
* - Host run on bit-banging 2 GPIOs with the help of Pico-PIO-USB library (controller1)
*
* Example sketch receive keyboard report from host interface (from e.g consumer keyboard)
* and remap it to another key and send it via device interface (to PC). For simplicity,
* this example only toggle shift key to the report, effectively remap:
* - all character key <-> upper case
* - number <-> its symbol (with shift)
*
* Requirements:
* - [Pico-PIO-USB](https://github.com/sekigon-gonnoc/Pico-PIO-USB) library
* - 2 consecutive GPIOs: D+ is defined by PIN_USB_HOST_DP, D- = D+ +1
* - Provide VBus (5v) and GND for peripheral
* - CPU Speed must be either 120 or 240 Mhz. Selected via "Menu -> CPU Speed"
*/
// pio-usb is required for rp2040 host
#include "pio_usb.h"
#include "Adafruit_TinyUSB.h"
// Pin D+ for host, D- = D+ + 1
#ifndef PIN_USB_HOST_DP
#define PIN_USB_HOST_DP 16
#endif
// Pin for enabling Host VBUS. comment out if not used
#ifndef PIN_5V_EN
#define PIN_5V_EN 18
#endif
#ifndef PIN_5V_EN_STATE
#define PIN_5V_EN_STATE 1
#endif
// Language ID: English
#define LANGUAGE_ID 0x0409
// USB Host object
Adafruit_USBH_Host USBHost;
// HID report descriptor using TinyUSB's template
// Single Report (no ID) descriptor
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_KEYBOARD()
};
// USB HID object. For ESP32 these values cannot be changed after this declaration
// desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_KEYBOARD, 2, false);
//--------------------------------------------------------------------+
// Setup and Loop on Core0
//--------------------------------------------------------------------+
void setup()
{
Serial.begin(115200);
usb_hid.begin();
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("TinyUSB Host HID Remap Example");
}
void loop()
{
}
//--------------------------------------------------------------------+
// Setup and Loop on Core1
//--------------------------------------------------------------------+
void setup1() {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if ( cpu_hz != 120000000UL && cpu_hz != 240000000UL ) {
while ( !Serial ) delay(10); // wait for native usb
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while(1) delay(1);
}
#ifdef PIN_5V_EN
pinMode(PIN_5V_EN, OUTPUT);
digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE);
#endif
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
pio_cfg.pin_dp = PIN_USB_HOST_DP;
USBHost.configure_pio_usb(1, &pio_cfg);
// run host stack on controller (rhport) 1
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
// host bit-banging processing works done in core1 to free up core0 for other works
USBHost.begin(1);
}
void loop1()
{
USBHost.task();
}
// Invoked when device with hid interface is mounted
// Report descriptor is also available for use.
// tuh_hid_parse_report_descriptor() can be used to parse common/simple enough
// descriptor. Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE,
// it will be skipped therefore report_desc = NULL, desc_len = 0
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
(void)desc_report;
(void)desc_len;
uint16_t vid, pid;
tuh_vid_pid_get(dev_addr, &vid, &pid);
Serial.printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
Serial.printf("VID = %04x, PID = %04x\r\n", vid, pid);
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD) {
Serial.printf("HID Keyboard\r\n");
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.printf("Error: cannot request to receive report\r\n");
}
}
}
// Invoked when device with hid interface is un-mounted
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
Serial.printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
}
void remap_key(hid_keyboard_report_t const* original_report, hid_keyboard_report_t* remapped_report)
{
memcpy(remapped_report, original_report, sizeof(hid_keyboard_report_t));
// only remap if not empty report i.e key released
for(uint8_t i=0; i<6; i++) {
if (remapped_report->keycode[i] != 0) {
// Note: we ignore right shift here
remapped_report->modifier ^= KEYBOARD_MODIFIER_LEFTSHIFT;
break;
}
}
}
// Invoked when received report from device via interrupt endpoint
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
if ( len != 8 ) {
Serial.printf("report len = %u NOT 8, probably something wrong !!\r\n", len);
}else {
hid_keyboard_report_t remapped_report;
remap_key((hid_keyboard_report_t const*) report, &remapped_report);
// send remapped report to PC
// NOTE: for better performance you should save/queue remapped report instead of
// blocking wait for usb_hid ready here
while ( !usb_hid.ready() ) {
yield();
}
usb_hid.sendReport(0, &remapped_report, sizeof(hid_keyboard_report_t));
}
// continue to request to receive report
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.printf("Error: cannot request to receive report\r\n");
}
}

View File

@ -0,0 +1,212 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example demonstrates use of both device and host, where
* - Device run on native usb controller (controller0)
* - Host run on bit-banging 2 GPIOs with the help of Pico-PIO-USB library (controller1)
*
* Example will log CPU temperature periodically (ms,value) to USB thumbdrive
*
* Requirements:
* - [Pico-PIO-USB](https://github.com/sekigon-gonnoc/Pico-PIO-USB) library
* - 2 consecutive GPIOs: D+ is defined by PIN_USB_HOST_DP, D- = D+ +1
* - Provide VBus (5v) and GND for peripheral
* - CPU Speed must be either 120 or 240 Mhz. Selected via "Menu -> CPU Speed"
*/
// pio-usb is required for rp2040 host
#include "pio_usb.h"
// SdFat is required for using Adafruit_USBH_MSC_SdFatDevice
#include "SdFat.h"
// TinyUSB lib
#include "Adafruit_TinyUSB.h"
// Pin D+ for host, D- = D+ + 1
#ifndef PIN_USB_HOST_DP
#define PIN_USB_HOST_DP 16
#endif
// Pin for enabling Host VBUS. comment out if not used
#ifndef PIN_5V_EN
#define PIN_5V_EN 18
#endif
#ifndef PIN_5V_EN_STATE
#define PIN_5V_EN_STATE 1
#endif
#define LOG_FILE "cpu_temp.csv"
#define LOG_INTERVAL 5000
// USB Host object
Adafruit_USBH_Host USBHost;
// USB Host MSC Block Device object which implemented API for use with SdFat
Adafruit_USBH_MSC_BlockDevice msc_block_dev;
// file system object from SdFat
FatVolume fatfs;
File32 f_log;
// if file system is successfully mounted on usb block device
volatile bool is_mounted = false;
//--------------------------------------------------------------------+
// Setup and Loop on Core0
//--------------------------------------------------------------------+
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("TinyUSB Host MassStorage Data Logger Example");
}
void loop()
{
if (!is_mounted) {
// nothing to do
delay(1000);
return;
}
// Turn on LED when start writing
digitalWrite(LED_BUILTIN, HIGH);
f_log = fatfs.open(LOG_FILE, O_WRITE | O_APPEND | O_CREAT);
if (!f_log) {
Serial.println("Cannot create file: " LOG_FILE);
}else {
float cpu_temp = analogReadTemp();
uint32_t ms = millis();
Serial.printf("%u,%.02f\r\n", millis(), cpu_temp);
f_log.printf("%u,%.02f\r\n", millis(), cpu_temp);
f_log.close();
}
delay(LOG_INTERVAL);
}
//--------------------------------------------------------------------+
// Setup and Loop on Core1
//--------------------------------------------------------------------+
void setup1() {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if ( cpu_hz != 120000000UL && cpu_hz != 240000000UL ) {
while ( !Serial ) {
delay(10); // wait for native usb
}
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while(1) {
delay(1);
}
}
#ifdef PIN_5V_EN
pinMode(PIN_5V_EN, OUTPUT);
digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE);
#endif
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
pio_cfg.pin_dp = PIN_USB_HOST_DP;
USBHost.configure_pio_usb(1, &pio_cfg);
// run host stack on controller (rhport) 1
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
// host bit-banging processing works done in core1 to free up core0 for other works
USBHost.begin(1);
}
void loop1()
{
USBHost.task();
Serial.flush();
}
//--------------------------------------------------------------------+
// TinyUSB Host callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted (configured)
void tuh_mount_cb (uint8_t daddr)
{
(void) daddr;
}
/// Invoked when device is unmounted (bus reset/unplugged)
void tuh_umount_cb(uint8_t daddr)
{
(void) daddr;
}
// Invoked when a device with MassStorage interface is mounted
void tuh_msc_mount_cb(uint8_t dev_addr)
{
// Initialize block device with MSC device address
msc_block_dev.begin(dev_addr);
// For simplicity this example only support LUN 0
msc_block_dev.setActiveLUN(0);
msc_block_dev.setWriteCompleteCallback(write_complete_callback);
is_mounted = fatfs.begin(&msc_block_dev);
if (is_mounted) {
fatfs.ls(&Serial, LS_SIZE);
}else {
Serial.println("Failed to mount mass storage device. Make sure it is formatted as FAT");
}
}
// Invoked when a device with MassStorage interface is unmounted
void tuh_msc_umount_cb(uint8_t dev_addr)
{
(void) dev_addr;
// unmount file system
is_mounted = false;
fatfs.end();
// end block device
msc_block_dev.end();
}
bool write_complete_callback(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data)
{
(void) dev_addr;
(void) cb_data;
// turn off LED after write is complete
// Note this only marks the usb transfer is complete, device can take longer to actual
// write data to physical flash
digitalWrite(LED_BUILTIN, LOW);
return true;
}

View File

@ -0,0 +1,160 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example demonstrates use of both device and host, where
* - Device run on native usb controller (controller0)
* - Host run on bit-banging 2 GPIOs with the help of Pico-PIO-USB library (controller1)
*
* Requirements:
* - [Pico-PIO-USB](https://github.com/sekigon-gonnoc/Pico-PIO-USB) library
* - 2 consecutive GPIOs: D+ is defined by PIN_USB_HOST_DP, D- = D+ +1
* - Provide VBus (5v) and GND for peripheral
* - CPU Speed must be either 120 or 240 Mhz. Selected via "Menu -> CPU Speed"
*/
// pio-usb is required for rp2040 host
#include "pio_usb.h"
// SdFat is required for using Adafruit_USBH_MSC_SdFatDevice
#include "SdFat.h"
// TinyUSB lib
#include "Adafruit_TinyUSB.h"
// Pin D+ for host, D- = D+ + 1
#ifndef PIN_USB_HOST_DP
#define PIN_USB_HOST_DP 16
#endif
// Pin for enabling Host VBUS. comment out if not used
#ifndef PIN_5V_EN
#define PIN_5V_EN 18
#endif
#ifndef PIN_5V_EN_STATE
#define PIN_5V_EN_STATE 1
#endif
// USB Host object
Adafruit_USBH_Host USBHost;
// USB Host MSC Block Device object which implemented API for use with SdFat
Adafruit_USBH_MSC_BlockDevice msc_block_dev;
// file system object from SdFat
FatVolume fatfs;
// if file system is successfully mounted on usb block device
bool is_mounted = false;
//--------------------------------------------------------------------+
// Setup and Loop on Core0
//--------------------------------------------------------------------+
void setup()
{
Serial.begin(115200);
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("TinyUSB Host Mass Storage File Explorer Example");
}
void loop()
{
}
//--------------------------------------------------------------------+
// Setup and Loop on Core1
//--------------------------------------------------------------------+
void setup1() {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if ( cpu_hz != 120000000UL && cpu_hz != 240000000UL ) {
while ( !Serial ) {
delay(10); // wait for native usb
}
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while(1) {
delay(1);
}
}
#ifdef PIN_5V_EN
pinMode(PIN_5V_EN, OUTPUT);
digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE);
#endif
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
pio_cfg.pin_dp = PIN_USB_HOST_DP;
USBHost.configure_pio_usb(1, &pio_cfg);
// run host stack on controller (rhport) 1
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
// host bit-banging processing works done in core1 to free up core0 for other works
USBHost.begin(1);
}
void loop1()
{
USBHost.task();
}
//--------------------------------------------------------------------+
// TinyUSB Host callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted (configured)
void tuh_mount_cb (uint8_t daddr)
{
(void) daddr;
}
/// Invoked when device is unmounted (bus reset/unplugged)
void tuh_umount_cb(uint8_t daddr)
{
(void) daddr;
}
// Invoked when a device with MassStorage interface is mounted
void tuh_msc_mount_cb(uint8_t dev_addr)
{
// Initialize block device with MSC device address
msc_block_dev.begin(dev_addr);
// For simplicity this example only support LUN 0
msc_block_dev.setActiveLUN(0);
is_mounted = fatfs.begin(&msc_block_dev);
if (is_mounted) {
fatfs.ls(&Serial, LS_SIZE);
}
}
// Invoked when a device with MassStorage interface is unmounted
void tuh_msc_umount_cb(uint8_t dev_addr)
{
(void) dev_addr;
// unmount file system
is_mounted = false;
fatfs.end();
// end block device
msc_block_dev.end();
}

View File

@ -0,0 +1,252 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example demonstrates use of both device and host, where
* - Device run on native usb controller (controller0)
* - Host run on bit-banging 2 GPIOs with the help of Pico-PIO-USB library (controller1)
*
* Requirements:
* - [Pico-PIO-USB](https://github.com/sekigon-gonnoc/Pico-PIO-USB) library
* - 2 consecutive GPIOs: D+ is defined by PIN_USB_HOST_DP, D- = D+ +1
* - Provide VBus (5v) and GND for peripheral
* - CPU Speed must be either 120 or 240 Mhz. Selected via "Menu -> CPU Speed"
*
* RP2040 host stack will get device descriptors of attached devices and print it out via
* device cdc (Serial) as follows:
* Device 1: ID 046d:c52f
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 0200
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x046d
idProduct 0xc52f
bcdDevice 2200
iManufacturer 1 Logitech
iProduct 2 USB Receiver
iSerialNumber 0
bNumConfigurations 1
*
*/
// pio-usb is required for rp2040 host
#include "pio_usb.h"
#include "Adafruit_TinyUSB.h"
// Pin D+ for host, D- = D+ + 1
#ifndef PIN_USB_HOST_DP
#define PIN_USB_HOST_DP 16
#endif
// Pin for enabling Host VBUS. comment out if not used
#ifndef PIN_5V_EN
#define PIN_5V_EN 18
#endif
#ifndef PIN_5V_EN_STATE
#define PIN_5V_EN_STATE 1
#endif
// Language ID: English
#define LANGUAGE_ID 0x0409
// USB Host object
Adafruit_USBH_Host USBHost;
// holding device descriptor
tusb_desc_device_t desc_device;
//--------------------------------------------------------------------+
// Setup and Loop on Core0
//--------------------------------------------------------------------+
// the setup function runs once when you press reset or power the board
void setup()
{
Serial1.begin(115200);
Serial.begin(115200);
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("TinyUSB Dual Device Info Example");
}
void loop()
{
}
//--------------------------------------------------------------------+
// Setup and Loop on Core1
//--------------------------------------------------------------------+
void setup1() {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if ( cpu_hz != 120000000UL && cpu_hz != 240000000UL ) {
while ( !Serial ) {
delay(10); // wait for native usb
}
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while(1) {
delay(1);
}
}
#ifdef PIN_5V_EN
pinMode(PIN_5V_EN, OUTPUT);
digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE);
#endif
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
pio_cfg.pin_dp = PIN_USB_HOST_DP;
USBHost.configure_pio_usb(1, &pio_cfg);
// run host stack on controller (rhport) 1
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
// host bit-banging processing works done in core1 to free up core0 for other works
USBHost.begin(1);
}
void loop1()
{
USBHost.task();
}
//--------------------------------------------------------------------+
// TinyUSB Host callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted (configured)
void tuh_mount_cb (uint8_t daddr)
{
Serial.printf("Device attached, address = %d\r\n", daddr);
// Get Device Descriptor
tuh_descriptor_get_device(daddr, &desc_device, 18, print_device_descriptor, 0);
}
/// Invoked when device is unmounted (bus reset/unplugged)
void tuh_umount_cb(uint8_t daddr)
{
Serial.printf("Device removed, address = %d\r\n", daddr);
}
void print_device_descriptor(tuh_xfer_t* xfer)
{
if ( XFER_RESULT_SUCCESS != xfer->result )
{
Serial.printf("Failed to get device descriptor\r\n");
return;
}
uint8_t const daddr = xfer->daddr;
Serial.printf("Device %u: ID %04x:%04x\r\n", daddr, desc_device.idVendor, desc_device.idProduct);
Serial.printf("Device Descriptor:\r\n");
Serial.printf(" bLength %u\r\n" , desc_device.bLength);
Serial.printf(" bDescriptorType %u\r\n" , desc_device.bDescriptorType);
Serial.printf(" bcdUSB %04x\r\n" , desc_device.bcdUSB);
Serial.printf(" bDeviceClass %u\r\n" , desc_device.bDeviceClass);
Serial.printf(" bDeviceSubClass %u\r\n" , desc_device.bDeviceSubClass);
Serial.printf(" bDeviceProtocol %u\r\n" , desc_device.bDeviceProtocol);
Serial.printf(" bMaxPacketSize0 %u\r\n" , desc_device.bMaxPacketSize0);
Serial.printf(" idVendor 0x%04x\r\n" , desc_device.idVendor);
Serial.printf(" idProduct 0x%04x\r\n" , desc_device.idProduct);
Serial.printf(" bcdDevice %04x\r\n" , desc_device.bcdDevice);
// Get String descriptor using Sync API
uint16_t temp_buf[128];
Serial.printf(" iManufacturer %u " , desc_device.iManufacturer);
if (XFER_RESULT_SUCCESS == tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, temp_buf, sizeof(temp_buf)) )
{
print_utf16(temp_buf, TU_ARRAY_SIZE(temp_buf));
}
Serial.printf("\r\n");
Serial.printf(" iProduct %u " , desc_device.iProduct);
if (XFER_RESULT_SUCCESS == tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, temp_buf, sizeof(temp_buf)))
{
print_utf16(temp_buf, TU_ARRAY_SIZE(temp_buf));
}
Serial.printf("\r\n");
Serial.printf(" iSerialNumber %u " , desc_device.iSerialNumber);
if (XFER_RESULT_SUCCESS == tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, temp_buf, sizeof(temp_buf)))
{
print_utf16(temp_buf, TU_ARRAY_SIZE(temp_buf));
}
Serial.printf("\r\n");
Serial.printf(" bNumConfigurations %u\r\n" , desc_device.bNumConfigurations);
}
//--------------------------------------------------------------------+
// String Descriptor Helper
//--------------------------------------------------------------------+
static void _convert_utf16le_to_utf8(const uint16_t *utf16, size_t utf16_len, uint8_t *utf8, size_t utf8_len) {
// TODO: Check for runover.
(void)utf8_len;
// Get the UTF-16 length out of the data itself.
for (size_t i = 0; i < utf16_len; i++) {
uint16_t chr = utf16[i];
if (chr < 0x80) {
*utf8++ = chr & 0xff;
} else if (chr < 0x800) {
*utf8++ = (uint8_t)(0xC0 | (chr >> 6 & 0x1F));
*utf8++ = (uint8_t)(0x80 | (chr >> 0 & 0x3F));
} else {
// TODO: Verify surrogate.
*utf8++ = (uint8_t)(0xE0 | (chr >> 12 & 0x0F));
*utf8++ = (uint8_t)(0x80 | (chr >> 6 & 0x3F));
*utf8++ = (uint8_t)(0x80 | (chr >> 0 & 0x3F));
}
// TODO: Handle UTF-16 code points that take two entries.
}
}
// Count how many bytes a utf-16-le encoded string will take in utf-8.
static int _count_utf8_bytes(const uint16_t *buf, size_t len) {
size_t total_bytes = 0;
for (size_t i = 0; i < len; i++) {
uint16_t chr = buf[i];
if (chr < 0x80) {
total_bytes += 1;
} else if (chr < 0x800) {
total_bytes += 2;
} else {
total_bytes += 3;
}
// TODO: Handle UTF-16 code points that take two entries.
}
return total_bytes;
}
static void print_utf16(uint16_t *temp_buf, size_t buf_len) {
size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t);
size_t utf8_len = _count_utf8_bytes(temp_buf + 1, utf16_len);
_convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t *) temp_buf, sizeof(uint16_t) * buf_len);
((uint8_t*) temp_buf)[utf8_len] = '\0';
Serial.printf((char*)temp_buf);
}

View File

@ -0,0 +1,205 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include "Adafruit_TinyUSB.h"
#include <Adafruit_NeoPixel.h>
/* This sketch demonstrates USB HID keyboard.
* - PIN A0-A5 is used to send digit '0' to '5' respectively
* (On the RP2040, pins D0-D5 used)
* - LED and/or Neopixels will be used as Capslock indicator
*/
// HID report descriptor using TinyUSB's template
// Single Report (no ID) descriptor
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_KEYBOARD()
};
// USB HID object. For ESP32 these values cannot be changed after this declaration
// desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_KEYBOARD, 2, false);
//------------- Input Pins -------------//
// Array of pins and its keycode.
// Notes: these pins can be replaced by PIN_BUTTONn if defined in setup()
#ifdef ARDUINO_ARCH_RP2040
uint8_t pins[] = { D0, D1, D2, D3 };
#else
uint8_t pins[] = { A0, A1, A2, A3 };
#endif
// number of pins
uint8_t pincount = sizeof(pins)/sizeof(pins[0]);
// For keycode definition check out https://github.com/hathach/tinyusb/blob/master/src/class/hid/hid.h
uint8_t hidcode[] = { HID_KEY_ARROW_RIGHT, HID_KEY_ARROW_LEFT, HID_KEY_ARROW_DOWN, HID_KEY_ARROW_UP };
#if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(ARDUINO_NRF52840_CIRCUITPLAY) || defined(ARDUINO_FUNHOUSE_ESP32S2)
bool activeState = true;
#else
bool activeState = false;
#endif
//------------- Neopixel -------------//
// #define PIN_NEOPIXEL 8
#ifdef PIN_NEOPIXEL
// How many NeoPixels are attached to the Arduino?
// use on-board defined NEOPIXEL_NUM if existed
#ifndef NEOPIXEL_NUM
#define NEOPIXEL_NUM 10
#endif
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NEOPIXEL_NUM, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
#endif
// the setup function runs once when you press reset or power the board
void setup()
{
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
// Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040
TinyUSB_Device_Init(0);
#endif
// Notes: following commented-out functions has no affect on ESP32
// usb_hid.setBootProtocol(HID_ITF_PROTOCOL_KEYBOARD);
// usb_hid.setPollInterval(2);
// usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
// usb_hid.setStringDescriptor("TinyUSB Keyboard");
// Set up output report (on control endpoint) for Capslock indicator
usb_hid.setReportCallback(NULL, hid_report_callback);
usb_hid.begin();
// led pin
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
// neopixel if existed
#ifdef PIN_NEOPIXEL
pixels.begin();
pixels.setBrightness(50);
#ifdef NEOPIXEL_POWER
pinMode(NEOPIXEL_POWER, OUTPUT);
digitalWrite(NEOPIXEL_POWER, NEOPIXEL_POWER_ON);
#endif
#endif
// overwrite input pin with PIN_BUTTONx
#ifdef PIN_BUTTON1
pins[0] = PIN_BUTTON1;
#endif
#ifdef PIN_BUTTON2
pins[1] = PIN_BUTTON2;
#endif
#ifdef PIN_BUTTON3
pins[2] = PIN_BUTTON3;
#endif
#ifdef PIN_BUTTON4
pins[3] = PIN_BUTTON4;
#endif
// Set up pin as input
for (uint8_t i=0; i<pincount; i++)
{
pinMode(pins[i], activeState ? INPUT_PULLDOWN : INPUT_PULLUP);
}
// wait until device mounted
while( !TinyUSBDevice.mounted() ) delay(1);
}
void loop()
{
// poll gpio once each 2 ms
delay(2);
// used to avoid send multiple consecutive zero report for keyboard
static bool keyPressedPreviously = false;
uint8_t count=0;
uint8_t keycode[6] = { 0 };
// scan normal key and send report
for(uint8_t i=0; i < pincount; i++)
{
if ( activeState == digitalRead(pins[i]) )
{
// if pin is active (low), add its hid code to key report
keycode[count++] = hidcode[i];
// 6 is max keycode per report
if (count == 6) break;
}
}
if ( TinyUSBDevice.suspended() && count )
{
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
TinyUSBDevice.remoteWakeup();
}
// skip if hid is not ready e.g still transferring previous report
if ( !usb_hid.ready() ) return;
if ( count )
{
// Send report if there is key pressed
uint8_t const report_id = 0;
uint8_t const modifier = 0;
keyPressedPreviously = true;
usb_hid.keyboardReport(report_id, modifier, keycode);
}else
{
// Send All-zero report to indicate there is no keys pressed
// Most of the time, it is, though we don't need to send zero report
// every loop(), only a key is pressed in previous loop()
if ( keyPressedPreviously )
{
keyPressedPreviously = false;
usb_hid.keyboardRelease(0);
}
}
}
// Output report callback for LED indicator such as Caplocks
void hid_report_callback(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
{
(void) report_id;
(void) bufsize;
// LED indicator is output report with only 1 byte length
if ( report_type != HID_REPORT_TYPE_OUTPUT ) return;
// The LED bit map is as follows: (also defined by KEYBOARD_LED_* )
// Kana (4) | Compose (3) | ScrollLock (2) | CapsLock (1) | Numlock (0)
uint8_t ledIndicator = buffer[0];
// turn on LED if capslock is set
digitalWrite(LED_BUILTIN, ledIndicator & KEYBOARD_LED_CAPSLOCK);
#ifdef PIN_NEOPIXEL
pixels.fill(ledIndicator & KEYBOARD_LED_CAPSLOCK ? 0xff0000 : 0x000000);
pixels.show();
#endif
}

View File

@ -0,0 +1,102 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include "Adafruit_TinyUSB.h"
/* This sketch demonstrates USB HID mouse
* Press button pin will move
* - mouse toward bottom right of monitor
*
* Depending on the board, the button pin
* and its active state (when pressed) are different
*/
#if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(ARDUINO_NRF52840_CIRCUITPLAY)
const int pin = 4; // Left Button
bool activeState = true;
#elif defined(ARDUINO_FUNHOUSE_ESP32S2)
const int pin = BUTTON_DOWN;
bool activeState = true;
#elif defined PIN_BUTTON1
const int pin = PIN_BUTTON1;
bool activeState = false;
#else
const int pin = 12;
bool activeState = false;
#endif
// HID report descriptor using TinyUSB's template
// Single Report (no ID) descriptor
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_MOUSE()
};
// USB HID object. For ESP32 these values cannot be changed after this declaration
// desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_MOUSE, 2, false);
// the setup function runs once when you press reset or power the board
void setup()
{
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
// Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040
TinyUSB_Device_Init(0);
#endif
// Set up button, pullup opposite to active state
pinMode(pin, activeState ? INPUT_PULLDOWN : INPUT_PULLUP);
// Notes: following commented-out functions has no affect on ESP32
// usb_hid.setBootProtocol(HID_ITF_PROTOCOL_MOUSE);
// usb_hid.setPollInterval(2);
// usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
// usb_hid.setStringDescriptor("TinyUSB Mouse");
usb_hid.begin();
Serial.begin(115200);
// wait until device mounted
while( !TinyUSBDevice.mounted() ) delay(1);
Serial.println("Adafruit TinyUSB HID Mouse example");
}
void loop()
{
// poll gpio once each 10 ms
delay(10);
// Whether button is pressed
bool btn_pressed = (digitalRead(pin) == activeState);
// nothing to do if button is not pressed
if (!btn_pressed) return;
// Remote wakeup
if ( TinyUSBDevice.suspended() )
{
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
TinyUSBDevice.remoteWakeup();
}
if ( usb_hid.ready() )
{
uint8_t const report_id = 0; // no ID
int8_t const delta = 5;
usb_hid.mouseMove(report_id, delta, delta); // right + down
}
}

View File

@ -0,0 +1,154 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include "Adafruit_TinyUSB.h"
/* This sketch demonstrates multiple report USB HID.
* Press button pin will move
* - mouse toward bottom right of monitor
* - send 'a' key
*
* Depending on the board, the button pin
* and its active state (when pressed) are different
*/
#if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(ARDUINO_NRF52840_CIRCUITPLAY)
const int pin = 4; // Left Button
bool activeState = true;
#elif defined(ARDUINO_FUNHOUSE_ESP32S2)
const int pin = BUTTON_DOWN;
bool activeState = true;
#elif defined PIN_BUTTON1
const int pin = PIN_BUTTON1;
bool activeState = false;
#else
const int pin = 12;
bool activeState = false;
#endif
// Report ID
enum
{
RID_KEYBOARD = 1,
RID_MOUSE,
RID_CONSUMER_CONTROL, // Media, volume etc ..
};
// HID report descriptor using TinyUSB's template
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(RID_KEYBOARD) ),
TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(RID_MOUSE) ),
TUD_HID_REPORT_DESC_CONSUMER( HID_REPORT_ID(RID_CONSUMER_CONTROL) )
};
// USB HID object. For ESP32 these values cannot be changed after this declaration
// desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_NONE, 2, false);
// the setup function runs once when you press reset or power the board
void setup()
{
// Notes: following commented-out functions has no affect on ESP32
// usb_hid.setPollInterval(2);
// usb_hid.setReportDescriptor();
// usb_hid.setStringDescriptor("TinyUSB HID Composite");
usb_hid.begin();
// Set up button, pullup opposite to active state
pinMode(pin, activeState ? INPUT_PULLDOWN : INPUT_PULLUP);
Serial.begin(115200);
// wait until device mounted
while( !TinyUSBDevice.mounted() ) delay(1);
Serial.println("Adafruit TinyUSB HID Composite example");
}
void loop()
{
// poll gpio once each 10 ms
delay(10);
// Whether button is pressed
bool btn_pressed = (digitalRead(pin) == activeState);
// Remote wakeup
if ( TinyUSBDevice.suspended() && btn_pressed )
{
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
TinyUSBDevice.remoteWakeup();
}
/*------------- Mouse -------------*/
if ( usb_hid.ready() && btn_pressed )
{
int8_t const delta = 5;
usb_hid.mouseMove(RID_MOUSE, delta, delta); // right + down
// delay a bit before attempt to send keyboard report
delay(10);
}
/*------------- Keyboard -------------*/
if ( usb_hid.ready() )
{
// use to send key release report
static bool has_key = false;
if ( btn_pressed )
{
uint8_t keycode[6] = { 0 };
keycode[0] = HID_KEY_A;
usb_hid.keyboardReport(RID_KEYBOARD, 0, keycode);
has_key = true;
}else
{
// send empty key report if previously has key pressed
if (has_key) usb_hid.keyboardRelease(RID_KEYBOARD);
has_key = false;
}
// delay a bit before attempt to send consumer report
delay(10);
}
/*------------- Consumer Control -------------*/
if ( usb_hid.ready() )
{
// Consumer Control is used to control Media playback, Volume, Brightness etc ...
// Consumer report is 2-byte containing the control code of the key
// For list of control check out https://github.com/hathach/tinyusb/blob/master/src/class/hid/hid.h
// use to send consumer release report
static bool has_consumer_key = false;
if ( btn_pressed )
{
// send volume down (0x00EA)
usb_hid.sendReport16(RID_CONSUMER_CONTROL, HID_USAGE_CONSUMER_VOLUME_DECREMENT);
has_consumer_key = true;
}else
{
// release the consume key by sending zero (0x0000)
if (has_consumer_key) usb_hid.sendReport16(RID_CONSUMER_CONTROL, 0);
has_consumer_key = false;
}
}
}

View File

@ -0,0 +1,156 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This sketch demonstrates USB HID Mouse and Keyboard with Joy Feather Wing.
* - The analog stick move mouse cursor
* - Button A, B, X, Y will send character a, b, x, y
* - Any actions will wake up PC host if it is in suspended (standby) mode.
*
* Joy Feather Wing: https://www.adafruit.com/product/3632
*
* Following library is required
* - Adafruit_seesaw
*/
#include "Adafruit_TinyUSB.h"
#include "Adafruit_seesaw.h"
#define BUTTON_A 6
#define BUTTON_B 7
#define BUTTON_Y 9
#define BUTTON_X 10
uint32_t button_mask = (1 << BUTTON_A) | (1 << BUTTON_B) |
(1 << BUTTON_Y) | (1 << BUTTON_X);
Adafruit_seesaw ss;
// Report ID
enum
{
RID_KEYBOARD = 1,
RID_MOUSE
};
// HID report descriptor using TinyUSB's template
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(RID_KEYBOARD) ),
TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(RID_MOUSE) )
};
// USB HID object. For ESP32 these values cannot be changed after this declaration
// desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_NONE, 2, false);
int last_x, last_y;
// the setup function runs once when you press reset or power the board
void setup()
{
// Notes: following commented-out functions has no affect on ESP32
// usb_hid.setPollInterval(2);
// usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
usb_hid.begin();
Serial.begin(115200);
Serial.println("Adafruit TinyUSB HID Mouse with Joy FeatherWing example");
if(!ss.begin(0x49)){
Serial.println("ERROR! seesaw not found");
while(1);
} else {
Serial.println("seesaw started");
Serial.print("version: ");
Serial.println(ss.getVersion(), HEX);
}
ss.pinModeBulk(button_mask, INPUT_PULLUP);
ss.setGPIOInterrupts(button_mask, 1);
last_y = ss.analogRead(2);
last_x = ss.analogRead(3);
// wait until device mounted
while( !TinyUSBDevice.mounted() ) delay(1);
}
void loop()
{
// poll gpio once each 10 ms
delay(10);
// If either analog stick or any buttons is pressed
bool has_action = false;
/*------------- Mouse -------------*/
int y = ss.analogRead(2);
int x = ss.analogRead(3);
// reduce the delta by half to slow down the cursor move
int dx = (x - last_x) / 2;
int dy = (y - last_y) / 2;
if ( (abs(dx) > 3) || (abs(dy) > 3) )
{
has_action = true;
if ( usb_hid.ready() )
{
usb_hid.mouseMove(RID_MOUSE, dx, dy); // no ID: right + down
last_x = x;
last_y = y;
// delay a bit before attempt to send keyboard report
delay(10);
}
}
/*------------- Keyboard -------------*/
// button is active low, invert read value for convenience
uint32_t buttons = ~ss.digitalReadBulk(button_mask);
if ( usb_hid.ready() )
{
// use to prevent sending multiple consecutive zero report
static bool has_key = false;
if ( buttons & button_mask )
{
has_action = true;
has_key = true;
uint8_t keycode[6] = { 0 };
if ( buttons & (1 << BUTTON_A) ) keycode[0] = HID_KEY_A;
if ( buttons & (1 << BUTTON_B) ) keycode[0] = HID_KEY_B;
if ( buttons & (1 << BUTTON_X) ) keycode[0] = HID_KEY_X;
if ( buttons & (1 << BUTTON_Y) ) keycode[0] = HID_KEY_Y;
usb_hid.keyboardReport(RID_KEYBOARD, 0, keycode);
}else
{
// send empty key report if previously has key pressed
if (has_key) usb_hid.keyboardRelease(RID_KEYBOARD);
has_key = false;
}
}
/*------------- Remote Wakeup -------------*/
// Remote wakeup if PC is suspended and we has user interaction with joy feather wing
if ( has_action && TinyUSBDevice.suspended() )
{
// Wake up only works if REMOTE_WAKEUP feature is enable by host
// Usually this is the case with Mouse/Keyboard device
TinyUSBDevice.remoteWakeup();
}
}

View File

@ -0,0 +1,123 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include "Adafruit_TinyUSB.h"
/* This sketch demonstrates multiple USB HID interfaces. Pressing the button will
* - mouse toward bottom right of monitor
* - send 'a' key
*
* Depending on the board, the button pin
* and its active state (when pressed) are different
*/
#if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(ARDUINO_NRF52840_CIRCUITPLAY)
const int pin = 4; // Left Button
bool activeState = true;
#elif defined(ARDUINO_FUNHOUSE_ESP32S2)
const int pin = BUTTON_DOWN;
bool activeState = true;
#elif defined PIN_BUTTON1
const int pin = PIN_BUTTON1;
bool activeState = false;
#elif defined PIN_BUTTON
const int pin = PIN_BUTTON;
bool activeState = false;
#else
const int pin = 12;
bool activeState = false;
#endif
// HID report descriptor using TinyUSB's template
uint8_t const desc_keyboard_report[] =
{
TUD_HID_REPORT_DESC_KEYBOARD()
};
uint8_t const desc_mouse_report[] =
{
TUD_HID_REPORT_DESC_MOUSE()
};
// USB HID object. For ESP32 these values cannot be changed after this declaration
// desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_keyboard(desc_keyboard_report, sizeof(desc_keyboard_report), HID_ITF_PROTOCOL_KEYBOARD, 2, false);
Adafruit_USBD_HID usb_mouse(desc_mouse_report, sizeof(desc_mouse_report), HID_ITF_PROTOCOL_MOUSE, 2, false);
// the setup function runs once when you press reset or power the board
void setup()
{
// Notes: following commented-out functions has no affect on ESP32
// usb_keyboard.setPollInterval(2);
// usb_keyboard.setReportDescriptor();
// usb_keyboard.setStringDescriptor("TinyUSB HID Composite");
usb_keyboard.begin();
usb_mouse.begin();
// Set up button, pullup opposite to active state
pinMode(pin, activeState ? INPUT_PULLDOWN : INPUT_PULLUP);
Serial.begin(115200);
// wait until device mounted
//while( !TinyUSBDevice.mounted() ) delay(1);
Serial.println("Adafruit TinyUSB HID Composite example");
}
void loop()
{
// poll gpio once each 10 ms
delay(10);
// Whether button is pressed
bool btn_pressed = (digitalRead(pin) == activeState);
// Remote wakeup
if ( TinyUSBDevice.suspended() && btn_pressed )
{
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
TinyUSBDevice.remoteWakeup();
}
/*------------- Mouse -------------*/
if (usb_mouse.ready() && btn_pressed )
{
int8_t const delta = 5;
usb_mouse.mouseMove(0, delta, delta); // right + down
}
/*------------- Keyboard -------------*/
if ( usb_keyboard.ready() )
{
// use to send key release report
static bool has_key = false;
if ( btn_pressed )
{
uint8_t keycode[6] = { 0 };
keycode[0] = HID_KEY_A;
usb_keyboard.keyboardReport(0, 0, keycode);
has_key = true;
}else
{
// send empty key report if previously has key pressed
if (has_key) usb_keyboard.keyboardRelease(0);
has_key = false;
}
}
}

View File

@ -0,0 +1,275 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2021 NeKuNeKo for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include "Adafruit_TinyUSB.h"
/* This sketch demonstrates USB HID gamepad use.
* This sketch is only valid on boards which have native USB support
* and compatibility with Adafruit TinyUSB library.
* For example SAMD21, SAMD51, nRF52840.
*
* Make sure you select the TinyUSB USB stack if you have a SAMD board.
* You can test the gamepad on a Windows system by pressing WIN+R, writing Joy.cpl and pressing Enter.
*/
// HID report descriptor using TinyUSB's template
// Single Report (no ID) descriptor
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_GAMEPAD()
};
// USB HID object. For ESP32 these values cannot be changed after this declaration
// desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_NONE, 2, false);
// Report payload defined in src/class/hid/hid.h
// - For Gamepad Button Bit Mask see hid_gamepad_button_bm_t
// - For Gamepad Hat Bit Mask see hid_gamepad_hat_t
hid_gamepad_report_t gp;
void setup()
{
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
// Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040
TinyUSB_Device_Init(0);
#endif
Serial.begin(115200);
// Notes: following commented-out functions has no affect on ESP32
// usb_hid.setPollInterval(2);
// usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
usb_hid.begin();
// wait until device mounted
while( !TinyUSBDevice.mounted() ) delay(1);
Serial.println("Adafruit TinyUSB HID Gamepad example");
}
void loop()
{
// // Remote wakeup
// if ( TinyUSBDevice.suspended() && btn )
// {
// // Wake up host if we are in suspend mode
// // and REMOTE_WAKEUP feature is enabled by host
// TinyUSBDevice.remoteWakeup();
// }
if ( !usb_hid.ready() ) return;
// Reset buttons
Serial.println("No pressing buttons");
gp.x = 0;
gp.y = 0;
gp.z = 0;
gp.rz = 0;
gp.rx = 0;
gp.ry = 0;
gp.hat = 0;
gp.buttons = 0;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Hat/DPAD UP
Serial.println("Hat/DPAD UP");
gp.hat = 1; // GAMEPAD_HAT_UP;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Hat/DPAD UP RIGHT
Serial.println("Hat/DPAD UP RIGHT");
gp.hat = 2; // GAMEPAD_HAT_UP_RIGHT;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Hat/DPAD RIGHT
Serial.println("Hat/DPAD RIGHT");
gp.hat = 3; // GAMEPAD_HAT_RIGHT;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Hat/DPAD DOWN RIGHT
Serial.println("Hat/DPAD DOWN RIGHT");
gp.hat = 4; // GAMEPAD_HAT_DOWN_RIGHT;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Hat/DPAD DOWN
Serial.println("Hat/DPAD DOWN");
gp.hat = 5; // GAMEPAD_HAT_DOWN;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Hat/DPAD DOWN LEFT
Serial.println("Hat/DPAD DOWN LEFT");
gp.hat = 6; // GAMEPAD_HAT_DOWN_LEFT;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Hat/DPAD LEFT
Serial.println("Hat/DPAD LEFT");
gp.hat = 7; // GAMEPAD_HAT_LEFT;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Hat/DPAD UP LEFT
Serial.println("Hat/DPAD UP LEFT");
gp.hat = 8; // GAMEPAD_HAT_UP_LEFT;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Hat/DPAD CENTER
Serial.println("Hat/DPAD CENTER");
gp.hat = 0; // GAMEPAD_HAT_CENTERED;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Joystick 1 UP
Serial.println("Joystick 1 UP");
gp.x = 0;
gp.y = -127;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Joystick 1 DOWN
Serial.println("Joystick 1 DOWN");
gp.x = 0;
gp.y = 127;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Joystick 1 RIGHT
Serial.println("Joystick 1 RIGHT");
gp.x = 127;
gp.y = 0;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Joystick 1 LEFT
Serial.println("Joystick 1 LEFT");
gp.x = -127;
gp.y = 0;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Joystick 1 CENTER
Serial.println("Joystick 1 CENTER");
gp.x = 0;
gp.y = 0;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Joystick 2 UP
Serial.println("Joystick 2 UP");
gp.z = 0;
gp.rz = 127;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Joystick 2 DOWN
Serial.println("Joystick 2 DOWN");
gp.z = 0;
gp.rz = -127;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Joystick 2 RIGHT
Serial.println("Joystick 2 RIGHT");
gp.z = 127;
gp.rz = 0;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Joystick 2 LEFT
Serial.println("Joystick 2 LEFT");
gp.z = -127;
gp.rz = 0;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Joystick 2 CENTER
Serial.println("Joystick 2 CENTER");
gp.z = 0;
gp.rz = 0;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Analog Trigger 1 UP
Serial.println("Analog Trigger 1 UP");
gp.rx = 127;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Analog Trigger 1 DOWN
Serial.println("Analog Trigger 1 DOWN");
gp.rx = -127;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Analog Trigger 1 CENTER
Serial.println("Analog Trigger 1 CENTER");
gp.rx = 0;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Analog Trigger 2 UP
Serial.println("Analog Trigger 2 UP");
gp.ry = 127;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Analog Trigger 2 DOWN
Serial.println("Analog Trigger 2 DOWN");
gp.ry = -127;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Analog Trigger 2 CENTER
Serial.println("Analog Trigger 2 CENTER");
gp.ry = 0;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// Test buttons (up to 32 buttons)
for (int i=0; i<32; ++i)
{
Serial.print("Pressing button "); Serial.println(i);
gp.buttons = (1U << i);
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(1000);
}
// Random touch
Serial.println("Random touch");
gp.x = random(-127, 128);
gp.y = random(-127, 128);
gp.z = random(-127, 128);
gp.rz = random(-127, 128);
gp.rx = random(-127, 128);
gp.ry = random(-127, 128);
gp.hat = random(0, 9);
gp.buttons = random(0, 0xffff);
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2000);
// */
}

View File

@ -0,0 +1,10 @@
Instructions for node.js based example
===Setup===
1. Upload example code to your board
2. Install node.js if you haven't already
3. Run `npm install` to install the dependencies
4. If this should fail on windows try installing the build tools via `npm i -g windows-build-tools`
5. While the board is connected run `node hid_test.js`
6. If this should fail make sure the VID and PID of your board is listed in boards.js

View File

@ -0,0 +1,4 @@
module.exports = {
"Adafruit Boards":[0x239A,0xFFFF],
"TinyUSB example":[0xCAFE,0xFFFF]
}

View File

@ -0,0 +1,104 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example demonstrate HID Generic raw Input & Output.
* It will receive data from Host (In endpoint) and echo back (Out endpoint).
* HID Report descriptor use vendor for usage page (using template TUD_HID_REPORT_DESC_GENERIC_INOUT)
*
* There are 2 ways to test the sketch
* 1. Using nodejs
* - Install nodejs and npm to your PC
*
* - Install excellent node-hid (https://github.com/node-hid/node-hid) by
* $ npm install node-hid
*
* - Run provided hid test script
* $ node hid_test.js
*
* 2. Using python
* - Install `hid` package (https://pypi.org/project/hid/) by
* $ pip install hid
*
* - hid package replies on hidapi (https://github.com/libusb/hidapi) for backend,
* which already available in Linux. However on windows, you may need to download its dlls from their release page and
* copy it over to folder where python is installed.
*
* - Run provided hid test script to send and receive data to this device.
* $ python3 hid_test.py
*/
#include "Adafruit_TinyUSB.h"
// HID report descriptor using TinyUSB's template
// Generic In Out with 64 bytes report (max)
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_GENERIC_INOUT(64)
};
// USB HID object. For ESP32 these values cannot be changed after this declaration
// desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_NONE, 2, true);
// the setup function runs once when you press reset or power the board
void setup()
{
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
// Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040
TinyUSB_Device_Init(0);
#endif
// Notes: following commented-out functions has no affect on ESP32
// usb_hid.enableOutEndpoint(true);
// usb_hid.setPollInterval(2);
// usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
// usb_hid.setStringDescriptor("TinyUSB HID Generic");
usb_hid.setReportCallback(get_report_callback, set_report_callback);
usb_hid.begin();
Serial.begin(115200);
// wait until device mounted
while( !TinyUSBDevice.mounted() ) delay(1);
Serial.println("Adafruit TinyUSB HID Generic In Out example");
}
void loop()
{
// nothing to do
}
// 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 get_report_callback (uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
{
// not used in this example
(void) report_id;
(void) report_type;
(void) buffer;
(void) reqlen;
return 0;
}
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
void set_report_callback(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
{
// This example doesn't use multiple report and report ID
(void) report_id;
(void) report_type;
// echo back anything we received from host
usb_hid.sendReport(0, buffer, bufsize);
}

View File

@ -0,0 +1,68 @@
// IMPORTANT: install the dependency via 'npm i node-hid' in the same location as the script
// If the install fails on windows you may need to run 'npm i -g windows-build-tools' first to be able to compile native code needed for this library
var HID = require('node-hid');
var os = require('os')
// list of supported devices
var boards = require('./boards.js')
var devices = HID.devices();
// this will choose any device found in the boards.js file
var deviceInfo = devices.find(anySupportedBoard);
var reportLen = 64;
var message = "Hello World!"
// Turn our string into an array of integers e.g. 'ascii codes', though charCodeAt spits out UTF-16
// This means if you have characters in your string that are not Latin-1 you will have to add additional logic for character codes above 255
var messageBuffer = Array.from(message, function(c){return c.charCodeAt(0)});
// HIDAPI requires us to prepend a 0 for single hid report as dummy reportID
messageBuffer.unshift(0)
// Some OSes expect that you always send a buffer that equals your report length
// So lets fill up the rest of the buffer with zeros
var paddingBuf = Array(reportLen-messageBuffer.length);
paddingBuf.fill(0)
messageBuffer = messageBuffer.concat(paddingBuf)
// check if we actually found a device and if so send our messageBuffer to it
if( deviceInfo ) {
console.log(deviceInfo)
var device = new HID.HID( deviceInfo.path );
// register an event listener for data coming from the device
device.on("data", function(data) {
// Print what we get from the device
console.log(data.toString('ascii'));
});
// the same for any error that occur
device.on("error", function(err) {console.log(err)});
// send our message to the device every 500ms
setInterval(function () {
device.write(messageBuffer);
},500)
}
function anySupportedBoard(d) {
for (var key in boards) {
if (boards.hasOwnProperty(key)) {
if (isDevice(boards[key],d)) {
console.log("Found " + d.product);
return true;
}
}
}
return false;
}
function isDevice(board,d){
// product id 0xff is matches all
return d.vendorId==board[0] && (d.productId==board[1] || board[1] == 0xFFFF);
}

View File

@ -0,0 +1,22 @@
# Install python3 HID package https://pypi.org/project/hid/
import hid
# default is TinyUSB (0xcafe), Adafruit (0x239a), RaspberryPi (0x2e8a), Espressif (0x303a) VID
USB_VID = (0xcafe, 0x239a, 0x2e8a, 0x303a)
print("VID list: " + ", ".join('%02x' % v for v in USB_VID))
for vid in USB_VID:
for dict in hid.enumerate(vid):
print(dict)
dev = hid.Device(dict['vendor_id'], dict['product_id'])
if dev:
while True:
# Get input from console and encode to UTF8 for array of chars.
# hid generic inout is single report therefore by HIDAPI requirement
# it must be preceded with 0x00 as dummy reportID
str_out = b'\x00'
str_out += input("Send text to HID Device : ").encode('utf-8')
dev.write(str_out)
str_in = dev.read(64)
print("Received from HID Device:", str_in, '\n')

View File

@ -0,0 +1,14 @@
{
"name": "hid_example",
"version": "1.0.0",
"description": "Test application for hid_generic_inout example sketch",
"main": "hid_test.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Timon, Tod E. Kurt",
"license": "MIT",
"dependencies": {
"node-hid": "^0.7.9"
}
}

View File

@ -0,0 +1,46 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
// This sketch is enumerated as USB MIDI device with multiple ports
// and how to set their name
#include <Arduino.h>
#include <Adafruit_TinyUSB.h>
#include <MIDI.h>
// USB MIDI object with 3 ports
Adafruit_USBD_MIDI usb_midi(3);
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
// Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040
TinyUSB_Device_Init(0);
#endif
// Set name for each cable, must be done before usb_midi.begin()
usb_midi.setCableName(1, "Keyboard");
usb_midi.setCableName(2, "Drum Pads");
usb_midi.setCableName(3, "Lights");
usb_midi.begin();
}
void loop()
{
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}

View File

@ -0,0 +1,427 @@
//Circuit Playground PZ-1 Pizza Box DJ
// by John Park
// a.k.a. DJ Sternocleidomastoid
// for Adafruit Industries
// MIT License
///////////////////////////////////////////////////////////////////////////////
// MIDI controller for Traktor, Mixxx and other DJ software
// Uses TinyUSB to send MIDI over USB
//
// MIDI controller command mapping must be set up in your DJ software, check
// below for MIDI signal outputs
//
// For detail tutorial https://learn.adafruit.com/circuit-playground-pizza-box-dj-controller
///////////////////////////////////////////////////////////////////////////////
#include <Adafruit_CircuitPlayground.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_TinyUSB.h>
#include <MIDI.h>
// USB MIDI object
Adafruit_USBD_MIDI usb_midi;
// Create a new instance of the Arduino MIDI Library,
// and attach usb_midi as the transport.
MIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, usbMIDI);
///////////////////////////////////////////////////////////////////////////////
//MIDI CC (change control) assignments
const int CHANNEL = 14; //MIDI channel
const int LEFT_BUTTON_CC = 4; //Play/Pause Deck A
const int RIGHT_BUTTON_CC = 19; //MIDI CC for left button: FX 1 On
const int CAP1_CONTROL = 1; //Select/Set+Store Hotcue 2 (1 is load point)
const int CAP0_CONTROL = 0; //Left fader mixes Deck B volume down
const int CAP2_CONTROL = 2; //Delete hotcue 2 point
const int CAP3_CONTROL = 3; //Remix Trigger 6-1 or Loop Size /2
const int CAP12_CONTROL = 12; //Right fader mixes Deck A volume down
const int CAP6_CONTROL = 6; //Scratch (jog turn) Deck B
const int CAP9_CONTROL = 9; //Play/Pause Deck B
const int CAP10_CONTROL = 10; //Remix Trigger 6-4 or Loop Set 4 bar
int leftButtonState = 0;
int rightButtonState = 0;
int leftButtonLastState = 0;
int rightButtonLastState = 0;
int cap0State = 0;
int cap0LastState = 0;
int cap1State = 0;
int cap1LastState = 0;
int cap2State = 0;
int cap2LastState = 0;
int cap3State = 0;
int cap3LastState = 0;
int cap12State = 0;
int cap12LastState = 0;
int cap6State = 0;
int cap6LastState = 0;
int cap9State = 0;
int cap9LastState = 0;
int cap10State = 0;
int cap10LastState = 0;
bool cap1ON = false; //prevents off command from being spewed
bool cap12ON = false; //prevents off command from being spewed
bool cap6ON = false; //prevents off command from being spewed
const int CAPMIN = 25; //lowest value that's considered an intentional touch to minimize crosstalk
const int CAPSLOP = 0; //value of capacitive pad variance that's considered noise
///////////////////////////////////////////////////////////////////////////////
//mic_meter code
// To keep the display 'lively,' an upper and lower range of volume
// levels are dynamically adjusted based on recent audio history, and
// the graph is fit into this range.
#define FRAMES 8
uint16_t lvl[FRAMES], // Audio level for the prior #FRAMES frames
avgLo = 6, // Audio volume lower end of range
avgHi = 512, // Audio volume upper end of range
sum = 256 * FRAMES; // Sum of lvl[] array
uint8_t lvlIdx = 0; // Counter into lvl[] array
int16_t peak = 0; // Falling dot shows recent max
int8_t peakV = 0; // Velocity of peak dot
///////////////////////////////////////////////////////////////////////////////
void setup() {
usbMIDI.begin();
Serial.begin(115200);
CircuitPlayground.begin();
CircuitPlayground.setBrightness(30); //make brighter for performance, up to 255
CircuitPlayground.clearPixels();
for(uint8_t i=0; i<FRAMES; i++) lvl[i] = 256;
// Wait until device is enumerated properly before sending MIDI message
while( !TinyUSBDevice.mounted() ) delay(1);
}
///////////////////////////////////////////////////////////////////////////////
// COLOR TABLES for animation ----------------------------------------------
const uint8_t PROGMEM
reds[] = { 0x9A, 0x75, 0x00, 0x00, 0x00, 0x65, 0x84, 0x9A, 0xAD, 0xAD },
greens[] = { 0x00, 0x00, 0x00, 0x87, 0xB1, 0x9E, 0x87, 0x66, 0x00, 0x00 },
blues[] = { 0x95, 0xD5, 0xFF, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
gamma8[] = { // Gamma correction improves the appearance of midrange colors
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06,
0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09,
0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0E,
0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14,
0x14, 0x15, 0x15, 0x16, 0x16, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1B,
0x1B, 0x1C, 0x1D, 0x1D, 0x1E, 0x1F, 0x1F, 0x20, 0x21, 0x22, 0x22, 0x23,
0x24, 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2A, 0x2B, 0x2C, 0x2D,
0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x44, 0x45, 0x46,
0x47, 0x48, 0x49, 0x4B, 0x4C, 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x54, 0x55,
0x56, 0x58, 0x59, 0x5A, 0x5C, 0x5D, 0x5E, 0x60, 0x61, 0x63, 0x64, 0x66,
0x67, 0x69, 0x6A, 0x6C, 0x6D, 0x6F, 0x70, 0x72, 0x73, 0x75, 0x77, 0x78,
0x7A, 0x7C, 0x7D, 0x7F, 0x81, 0x82, 0x84, 0x86, 0x88, 0x89, 0x8B, 0x8D,
0x8F, 0x91, 0x92, 0x94, 0x96, 0x98, 0x9A, 0x9C, 0x9E, 0xA0, 0xA2, 0xA4,
0xA6, 0xA8, 0xAA, 0xAC, 0xAE, 0xB0, 0xB2, 0xB4, 0xB6, 0xB8, 0xBA, 0xBC,
0xBF, 0xC1, 0xC3, 0xC5, 0xC7, 0xCA, 0xCC, 0xCE, 0xD1, 0xD3, 0xD5, 0xD7,
0xDA, 0xDC, 0xDF, 0xE1, 0xE3, 0xE6, 0xE8, 0xEB, 0xED, 0xF0, 0xF2, 0xF5,
0xF7, 0xFA, 0xFC, 0xFF };
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void loop() {
//Sound activated lights
uint8_t i, r, g, b;
uint16_t minLvl, maxLvl, a, scaled;
a = CircuitPlayground.mic.peak(10); // 10 ms of audio
sum -= lvl[lvlIdx];
lvl[lvlIdx] = a;
sum += a; // Sum of lvl[] array
minLvl = maxLvl = lvl[0]; // Calc min, max of lvl[]...
for(i=1; i<FRAMES; i++) {
if(lvl[i] < minLvl) minLvl = lvl[i];
else if(lvl[i] > maxLvl) maxLvl = lvl[i];
}
// Keep some minimum distance between min & max levels,
// else the display gets "jumpy."
if((maxLvl - minLvl) < 40) {
maxLvl = (minLvl < (512-40)) ? minLvl + 40 : 512;
}
avgLo = (avgLo * 7 + minLvl + 2) / 8; // Dampen min/max levels
avgHi = (maxLvl >= avgHi) ? // (fake rolling averages)
(avgHi * 3 + maxLvl + 1) / 4 : // Fast rise
(avgHi * 31 + maxLvl + 8) / 32; // Slow decay
a = sum / FRAMES; // Average of lvl[] array
if(a <= avgLo) { // Below min?
scaled = 0; // Bargraph = zero
} else { // Else scale to fixed-point coordspace 0-2560
scaled = 2560L * (a - avgLo) / (avgHi - avgLo);
if(scaled > 2560) scaled = 2560;
}
if(scaled >= peak) { // New peak
peakV = (scaled - peak) / 4; // Give it an upward nudge
peak = scaled;
}
uint8_t whole = scaled / 256, // Full-brightness pixels (0-10)
frac = scaled & 255; // Brightness of fractional pixel
int whole2 = peak / 256, // Index of peak pixel
frac2 = peak & 255; // Between-pixels position of peak
uint16_t a1, a2; // Scaling factors for color blending
for(i=0; i<10; i++) { // For each NeoPixel...
if(i <= whole) { // In currently-lit area?
r = pgm_read_byte(&reds[i]), // Look up pixel color
g = pgm_read_byte(&greens[i]),
b = pgm_read_byte(&blues[i]);
if(i == whole) { // Fraction pixel at top of range?
a1 = (uint16_t)frac + 1; // Fade toward black
r = (r * a1) >> 8;
g = (g * a1) >> 8;
b = (b * a1) >> 8;
}
} else {
r = g = b = 0; // In unlit area
}
// Composite the peak pixel atop whatever else is happening...
if(i == whole2) { // Peak pixel?
a1 = 256 - frac2; // Existing pixel blend factor 1-256
a2 = frac2 + 1; // Peak pixel blend factor 1-256
r = ((r * a1) + (0x84 * a2)) >> 8; // Will
g = ((g * a1) + (0x87 * a2)) >> 8; // it
b = ((b * a1) + (0xC3 * a2)) >> 8; // blend?
} else if(i == (whole2-1)) { // Just below peak pixel
a1 = frac2 + 1; // Opposite blend ratios to above,
a2 = 256 - frac2; // but same idea
r = ((r * a1) + (0x84 * a2)) >> 8;
g = ((g * a1) + (0x87 * a2)) >> 8;
b = ((b * a1) + (0xC3 * a2)) >> 8;
}
CircuitPlayground.strip.setPixelColor(i,
pgm_read_byte(&gamma8[r]),
pgm_read_byte(&gamma8[g]),
pgm_read_byte(&gamma8[b]));
}
CircuitPlayground.strip.show();
peak += peakV;
if(peak <= 0) {
peak = 0;
peakV = 0;
} else if(peakV >= -126) {
peakV -= 2;
}
if(++lvlIdx >= FRAMES) lvlIdx = 0;
///////////////////////////////////////////////////////////////////////////////
//BUTTONS
//
//read the buttons
leftButtonState = (CircuitPlayground.leftButton());
//not pressed = 0, pressed = 1
rightButtonState = (CircuitPlayground.rightButton());
//Left Button
//compare current button states to previous button states
if(leftButtonState != leftButtonLastState){ //the state has changed
if(leftButtonState == 1){ //went from off to on, it's pressed
usbMIDI.sendControlChange(LEFT_BUTTON_CC, 1, CHANNEL); //send MIDI note ON command
CircuitPlayground.redLED(HIGH); //turn on the red LED
}
else{ //the button went from on to off, it isn't pressed
usbMIDI.sendControlChange(LEFT_BUTTON_CC, 0, CHANNEL); //send MIDI note OFF
CircuitPlayground.redLED(LOW); //turn off the red LED
}
leftButtonLastState = leftButtonState; //toggle
}
//Right Button
if(rightButtonState != rightButtonLastState){
if(rightButtonState == 1){
usbMIDI.sendControlChange(RIGHT_BUTTON_CC, 1, CHANNEL);
CircuitPlayground.redLED(HIGH);
}
else{
usbMIDI.sendControlChange(RIGHT_BUTTON_CC, 0, CHANNEL);
CircuitPlayground.redLED(LOW);
}
rightButtonLastState = rightButtonState;
}
///////////////////////////////////////////////////////////////////////////////
//Cap sense pads
//
//read the capacative pads
int cap1 = CircuitPlayground.readCap(1);
int cap0 = CircuitPlayground.readCap(0);
int cap2 = CircuitPlayground.readCap(2);
int cap3 = CircuitPlayground.readCap(3);
int cap12 = CircuitPlayground.readCap(12);
int cap6 = CircuitPlayground.readCap(6);
int cap9 = CircuitPlayground.readCap(9);
int cap10 = CircuitPlayground.readCap(10);
//cap1 Crossfader Deck B
if(cap1 > CAPMIN){
cap1State=1;
}
else{
cap1State=0;
}
if(cap1State != cap1LastState){
if(cap1State==1){
usbMIDI.sendControlChange(CAP1_CONTROL, 127, CHANNEL);
CircuitPlayground.redLED(HIGH);
}
else {
usbMIDI.sendControlChange(CAP1_CONTROL, 0, CHANNEL);
CircuitPlayground.redLED(LOW);
}
cap1LastState = cap1State;
}
//cap0
if(cap0 > CAPMIN){
cap0State=1;
}
else{
cap0State=0;
}
if(cap0State != cap0LastState){
if(cap0State==1){
usbMIDI.sendControlChange(CAP0_CONTROL, 1, CHANNEL);
CircuitPlayground.redLED(HIGH);
}
else {
usbMIDI.sendControlChange(CAP0_CONTROL, 0, CHANNEL);
CircuitPlayground.redLED(LOW);
}
cap0LastState = cap0State;
}
//cap2
if(cap2 > CAPMIN){
cap2State=1;
}
else{
cap2State=0;
}
if(cap2State != cap2LastState){
if(cap2State==1){
usbMIDI.sendControlChange(CAP2_CONTROL, 1, CHANNEL);
CircuitPlayground.redLED(HIGH);
}
else {
usbMIDI.sendControlChange(CAP2_CONTROL, 0, CHANNEL);
CircuitPlayground.redLED(LOW);
}
cap2LastState = cap2State;
}
//cap3
if(cap3 > CAPMIN){
cap3State=1;
}
else{
cap3State=0;
}
if(cap3State != cap3LastState){
if(cap3State==1){
usbMIDI.sendControlChange(CAP3_CONTROL, 1, CHANNEL);
CircuitPlayground.redLED(HIGH);
}
else {
usbMIDI.sendControlChange(CAP3_CONTROL, 0, CHANNEL);
CircuitPlayground.redLED(LOW);
}
cap3LastState = cap3State;
}
//cap12 Crossfader Deck B
if(cap12 > CAPMIN){
cap12State=1;
}
else{
cap12State=0;
}
if(cap12State != cap12LastState){
if(cap12State==1){
usbMIDI.sendControlChange(CAP12_CONTROL, 127, CHANNEL);
CircuitPlayground.redLED(HIGH);
}
else {
usbMIDI.sendControlChange(CAP12_CONTROL, 0, CHANNEL);
CircuitPlayground.redLED(LOW);
}
cap12LastState = cap12State;
}
//cap6
if (cap6>CAPMIN){
int cap6NewVal = map(CircuitPlayground.readCap(6),0,200,0,127);
if (abs(cap6 - cap6NewVal>CAPSLOP)) {
cap6 = cap6NewVal;
usbMIDI.sendControlChange(CAP6_CONTROL, cap6, CHANNEL);
CircuitPlayground.redLED(HIGH);
cap6ON = true;
}
else{
if (cap6ON==true){
usbMIDI.sendControlChange(CAP6_CONTROL, 0, CHANNEL); //send a 0
CircuitPlayground.redLED(LOW);
cap6ON=false;
}
}
}
//cap9
if(cap9 > CAPMIN){
cap9State=1;
}
else{
cap9State=0;
}
if(cap9State != cap9LastState){
if(cap9State==1){
usbMIDI.sendControlChange(CAP9_CONTROL, 1, CHANNEL);
CircuitPlayground.redLED(HIGH);
}
else {
usbMIDI.sendControlChange(CAP9_CONTROL, 0, CHANNEL);
CircuitPlayground.redLED(LOW);
}
cap9LastState = cap9State;
}
//cap10
if(cap10 > CAPMIN){
cap10State=1;
}
else{
cap10State=0;
}
if(cap10State != cap10LastState){
if(cap10State==1){
usbMIDI.sendControlChange(CAP10_CONTROL, 1, CHANNEL);
CircuitPlayground.redLED(HIGH);
}
else {
usbMIDI.sendControlChange(CAP10_CONTROL, 0, CHANNEL);
CircuitPlayground.redLED(LOW);
}
cap10LastState = cap10State;
}
// MIDI Controllers should discard incoming MIDI messages.
while (usbMIDI.read()) {
}
}

View File

@ -0,0 +1,127 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This sketch is enumerated as USB MIDI device.
* Following library is required
* - MIDI Library by Forty Seven Effects
* https://github.com/FortySevenEffects/arduino_midi_library
*/
#include <Arduino.h>
#include <Adafruit_TinyUSB.h>
#include <MIDI.h>
// USB MIDI object
Adafruit_USBD_MIDI usb_midi;
// Create a new instance of the Arduino MIDI Library,
// and attach usb_midi as the transport.
MIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, MIDI);
// Variable that holds the current position in the sequence.
uint32_t position = 0;
// Store example melody as an array of note values
byte note_sequence[] = {
74,78,81,86,90,93,98,102,57,61,66,69,73,78,81,85,88,92,97,100,97,92,88,85,81,78,
74,69,66,62,57,62,66,69,74,78,81,86,90,93,97,102,97,93,90,85,81,78,73,68,64,61,
56,61,64,68,74,78,81,86,90,93,98,102
};
void setup()
{
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
// Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040
TinyUSB_Device_Init(0);
#endif
pinMode(LED_BUILTIN, OUTPUT);
//usb_midi.setStringDescriptor("TinyUSB MIDI");
// Initialize MIDI, and listen to all MIDI channels
// This will also call usb_midi's begin()
MIDI.begin(MIDI_CHANNEL_OMNI);
// Attach the handleNoteOn function to the MIDI Library. It will
// be called whenever the Bluefruit receives MIDI Note On messages.
MIDI.setHandleNoteOn(handleNoteOn);
// Do the same for MIDI Note Off messages.
MIDI.setHandleNoteOff(handleNoteOff);
Serial.begin(115200);
// wait until device mounted
while( !TinyUSBDevice.mounted() ) delay(1);
}
void loop()
{
static uint32_t start_ms = 0;
if ( millis() - start_ms > 266 )
{
start_ms += 266;
// Setup variables for the current and previous
// positions in the note sequence.
int previous = position - 1;
// If we currently are at position 0, set the
// previous position to the last note in the sequence.
if (previous < 0) {
previous = sizeof(note_sequence) - 1;
}
// Send Note On for current position at full velocity (127) on channel 1.
MIDI.sendNoteOn(note_sequence[position], 127, 1);
// Send Note Off for previous note.
MIDI.sendNoteOff(note_sequence[previous], 0, 1);
// Increment position
position++;
// If we are at the end of the sequence, start over.
if (position >= sizeof(note_sequence)) {
position = 0;
}
}
// read any new MIDI messages
MIDI.read();
}
void handleNoteOn(byte channel, byte pitch, byte velocity)
{
// Log when a note is pressed.
Serial.print("Note on: channel = ");
Serial.print(channel);
Serial.print(" pitch = ");
Serial.print(pitch);
Serial.print(" velocity = ");
Serial.println(velocity);
}
void handleNoteOff(byte channel, byte pitch, byte velocity)
{
// Log when a note is released.
Serial.print("Note off: channel = ");
Serial.print(channel);
Serial.print(" pitch = ");
Serial.print(pitch);
Serial.print(" velocity = ");
Serial.println(velocity);
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('8 H(t){K 21.1Y(t)}8 9(a,b){a.1S(b)}8 1O(b,t,w,h,l,m,d,f,j){6(Q b==="S"||!b)K 1f;6(Q d==="S"||!d)d=1P;w=(w>1b)?w:1b;h=(h<1N)?1N:h;4 k=1M 1L();4 n="V";4 o=(Q f==="S"&&f)?"1J":f;4 p="#1I";4 q=(Q j==="S"&&j)?1f:j;4 r=m;4 s=0;4 u=1;4 x=l;4 y=h+20;b.7.1H="1G";b.7.1F="#1s";b.7.1c="12 11 "+p;b.7.1D="N";b.7.1C="N";b.7.P=(w+2)+"1B";4 g=H("1a");g.7.1c="12 11 "+p;g.7.1z="N";g.P=w;g.1w=y;4 c=g.1v("2d");4 z=H("1j");z.M="1u: ";z.7.1m="N";4 A=H("1p");A.1i="1r";A.1e=3;A.I=u;A.1h=8(e){u=A.I};4 B=H("T");B.M="-";B.U=8(e){6(u>1)u--;A.I=u};4 C=H("T");C.M="+";C.U=8(e){u++;A.I=1n(u)};4 D=H("1j");D.M="1Q: ";D.7.1m="N";4 E=H("1p");E.1i="1r";E.1e=5;E.I=x;E.1h=8(e){x=1n(E.I)};4 F=H("T");F.M="-";F.U=8(e){6(x>l)x--;E.I=x};4 G=H("T");G.M="+";G.U=8(e){6(x<m)x++;E.I=x};9(b,z);9(b,B);9(b,A);9(b,C);9(b,D);9(b,F);9(b,E);9(b,G);9(b,H("1E"));9(b,g);8 R(v){6(v<=x)K 0;6(v>(((m-l)/u)+x))K h;K 1d.1o((v-x)*(h/((m-l)/u)))}g.1x=8(v){6(q)v=q(v);k.1y(v);6(v<r)r=v;6(v>s)s=v;6(k.L>w)k.1A();c.1a.P=w;c.1g=1;c.17=2;c.Y="V";c.O=p;c.J(0,0,w,20);c.O=o;c.X="1q Z";4 a=t+": "+(k[k.L-1])+" | 1U: "+r+" | 23: "+s;c.W(a,5,15);c.O=n;c.J(0,20,w,y);c.1K=o;18(4 i=0;i<k.L;i++){6(d){c.13(i,y);c.14(i,y-R(k[i]));c.16();1R}6(i===0){c.13(0,y-R(k[0]));c.14(0,y-R(k[0]));c.16()}19{c.13((i-1),y-R(k[i-1]));c.14(i,y-R(k[i]));c.16()}}c.O=o;c.X="1T Z";c.W(x,2,y-5);c.W(1d.1o((((m-l)/u)+x)*10)/10,2,1V)};K g}8 1W(j,t,w,h,m,k){6(Q j==="S"||!j)K 1f;w=(w>1b)?w:1b;h=(h<20)?20:h;4 l=1M 1L();4 n="V";4 o=(Q k==="S"&&k)?"1X":k;4 q="#1I";4 r=1;4 u=h+20;j.7.1H="1G";j.7.1F="#1s";j.7.1c="12 11 "+q;j.7.1D="N";j.7.1C="N";j.7.P=(w+2)+"1B";4 g=H("1a");g.7.1c="12 11 "+q;g.7.1z="N";g.P=w;g.1w=u;4 c=g.1v("2d");4 x=H("1j");x.M="1u: ";x.7.1m="N";4 y=H("1p");y.1i="1r";y.1e=3;y.I=r;y.1h=8(e){r=y.I};4 z=H("T");z.M="-";z.U=8(e){6(r>1)r--;y.I=r};4 A=H("T");A.M="+";A.U=8(e){r++;y.I=1n(r)};9(j,x);9(j,z);9(j,y);9(j,A);9(j,H("1E"));9(j,g);8 1l(p){4 a=1d.1Z(((h-((m.L-1)*2))/m.L));4 s=(p*2)+(p*a);K[s,s+a]}8 1k(i,p){K((l[i]&(1<<m[p]))!==0)}4 B=["22","1J","#1t","#24","25","26","27","#28","#29","#2a","#2b","#2c","#2e","#2f","#1t","#2g","#2h"];g.1x=8(v){l.1y(v);6(l.L>w)l.1A();c.1a.P=w;c.1g=1;c.17=2;c.Y="V";c.O=q;c.J(0,0,w,20);c.O=o;c.X="1q Z";c.W(t,5,15);c.O=n;c.J(0,20,w,u);c.1K=q;c.1g=1;c.17=0;c.Y="";18(4 p=0;p<m.L;p++){4 a=1l(p);c.13(0,20+a[1]+1);c.14(w,20+a[1]+1);c.16()}c.17=2;c.X="1q Z";4 b=1d.1o(w/r);4 d=w-b-(w-l.L);6(d<0)d=0;18(4 i=d;i<l.L;i++){18(4 p=0;p<m.L;p++){4 a=1l(p);4 e=1k(i,p);c.O=B[p];c.Y="V";c.X="2i Z";c.W(""+m[p],2,a[0]+2j);c.Y=B[p];4 f=((i-d)*r)-(r/2);4 g=1k(i-1,p);6(i&&e!=g){6(!g){c.J(f,20+(a[1]-2),r/2,2);c.J(f+(r/2),20+a[0],2,a[1]-a[0]);c.J(f+(r/2),20+a[0],r/2,2)}19{c.J(f,20+a[0],r/2,2);c.J(f+(r/2),20+a[0],2,a[1]-a[0]);c.J(f+(r/2),20+(a[1]-2),r/2,2)}}19 6(e){c.J(f,20+a[0],r,2)}19{c.J(f,20+(a[1]-2),r,2)}}}};K g}',62,144,'||||var||if|style|function|ac||||||||||||||||||||||||||||||||||ce|value|fillRect|return|length|innerText|5px|fillStyle|width|typeof|scv|undefined|button|onclick|black|fillText|font|shadowColor|Verdana||solid|1px|moveTo|lineTo||stroke|shadowBlur|for|else|canvas|360|border|Math|size|null|lineWidth|onchange|type|span|gact|gpos|paddingLeft|parseInt|round|input|14px|text|eee|FF4500|Scale|getContext|height|add|push|marginTop|shift|px|margin|padding|br|backgroundColor|block|display|444|yellow|strokeStyle|Array|new|100|createGraph|false|Offset|continue|appendChild|10px|Min|30|createDigiGraph|pink|createElement|floor||document|aqua|Max|00FF7F|orange|cyan|magenta|7FFF00|FAF0E6|00CED1|FFD700|EE82EE||00FF00|00BFFF|EEE8AA|FF1493|12px|32'.split('|'),0,{}))

View File

@ -0,0 +1,97 @@
<!--
FSWebServer - Example Index Page
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the WebServer library for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-->
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>ESP Monitor</title>
<script type="text/javascript" src="graphs.js"></script>
<script type="text/javascript">
var heap,temp,digi;
var reloadPeriod = 1000;
var running = false;
function loadValues(){
if(!running) return;
var xh = new XMLHttpRequest();
xh.onreadystatechange = function(){
if (xh.readyState == 4){
if(xh.status == 200) {
var res = JSON.parse(xh.responseText);
heap.add(res.heap);
temp.add(res.analog);
digi.add(res.gpio);
if(running) setTimeout(loadValues, reloadPeriod);
} else running = false;
}
};
xh.open("GET", "/all", true);
xh.send(null);
};
function run(){
if(!running){
running = true;
loadValues();
}
}
function onBodyLoad(){
var refreshInput = document.getElementById("refresh-rate");
refreshInput.value = reloadPeriod;
refreshInput.onchange = function(e){
var value = parseInt(e.target.value);
reloadPeriod = (value > 0)?value:0;
e.target.value = reloadPeriod;
}
var stopButton = document.getElementById("stop-button");
stopButton.onclick = function(e){
running = false;
}
var startButton = document.getElementById("start-button");
startButton.onclick = function(e){
run();
}
// Example with 10K thermistor
//function calcThermistor(v) {
// var t = Math.log(((10230000 / v) - 10000));
// t = (1/(0.001129148+(0.000234125*t)+(0.0000000876741*t*t*t)))-273.15;
// return (t>120)?0:Math.round(t*10)/10;
//}
//temp = createGraph(document.getElementById("analog"), "Temperature", 100, 128, 10, 40, false, "cyan", calcThermistor);
temp = createGraph(document.getElementById("analog"), "Analog Input", 100, 128, 0, 1023, false, "cyan");
heap = createGraph(document.getElementById("heap"), "Current Heap", 100, 125, 0, 30000, true, "orange");
digi = createDigiGraph(document.getElementById("digital"), "GPIO", 100, 146, [0, 4, 5, 16], "gold");
run();
}
</script>
</head>
<body id="index" style="margin:0; padding:0;" onload="onBodyLoad()">
<div id="controls" style="display: block; border: 1px solid rgb(68, 68, 68); padding: 5px; margin: 5px; width: 362px; background-color: rgb(238, 238, 238);">
<label>Period (ms):</label>
<input type="number" id="refresh-rate"/>
<input type="button" id="start-button" value="Start"/>
<input type="button" id="stop-button" value="Stop"/>
</div>
<div id="heap"></div>
<div id="analog"></div>
<div id="digital"></div>
</body>
</html>

View File

@ -0,0 +1,437 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example is based on "arduino-esp32/libraries/WebServer/examples/FSBrowser"
* to expose on-board external Flash as USB Mass Storage and webserver. Both interfaces
* can make changes to file system
*
* How to run this example
* 1. Create secrets.h and define your "SECRET_SSID" and "SECRET_PASSWORD"
* 2. Compile and upload this sketch
* 3. Your ESP will be expose as MassStorage device.
* 4. If it is your first run (otherwise skip this step):
* - you may need to format the drive as FAT. Note: If your PC failed to format, you could format
* it using follow sketch "https://github.com/adafruit/Adafruit_SPIFlash/tree/master/examples/SdFat_format"
* - Copy all files in 'data/' folder of this example to the root directory of the MassStorage disk drive
* 5. When prompted, open http://esp32fs.local/edit to access the file browser
* 6. Modify/Update USB drive then refresh your browser to see if the change is updated
* 7. Upload/Edit a file using web browser then see if the USB Drive is updated. Note: the usb drive could
* briefly disappear and reappear to force PC to refresh its cache
*
* NOTE: Following library is required
* - Adafruit_SPIFlash https://github.com/adafruit/Adafruit_SPIFlash
* - SdFat https://github.com/adafruit/SdFat
*/
#include "SPI.h"
#include "SdFat.h"
#include "Adafruit_SPIFlash.h"
#include "Adafruit_TinyUSB.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
// check if secrets.h is includable, if not please
// create one with SSDI & PASSWORD macro as following example:
#if __has_include("secrets.h")
#include "secrets.h"
#else
#warning "Please create secrets.h with SSID & PASSWORD defined"
#define SECRET_SSID "your-ssid"
#define SECRET_PASSWORD "your-password"
#endif
// Debug with FTDI (Serial0) or USBCDC (Serial)
#define DBG_SERIAL Serial
// ESP32 use same flash device that store code.
// Therefore there is no need to specify the SPI and SS
Adafruit_FlashTransport_ESP32 flashTransport;
Adafruit_SPIFlash flash(&flashTransport);
// file system object from SdFat
FatVolume fatfs;
// USB Mass Storage object
Adafruit_USBD_MSC usb_msc;
bool fs_formatted; // Check if flash is formatted
bool fs_changed; // Set to true when browser write to flash
const char* host = "esp32fs";
WebServer server(80);
//holds the current upload
File32 fsUploadFile;
//--------------------------------------------------------------------+
// Setup
//--------------------------------------------------------------------+
void setupMassStorage(void)
{
flash.begin();
// Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively
usb_msc.setID("Adafruit", "External Flash", "1.0");
// Set callback
usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb);
// Set disk size, block size should be 512 regardless of spi flash page size
usb_msc.setCapacity(flash.size()/512, 512);
// MSC is ready for read/write
fs_changed = false;
usb_msc.setReadyCallback(0, msc_ready_callback);
usb_msc.begin();
// Init file system on the flash
fs_formatted = fatfs.begin(&flash);
if ( !fs_formatted )
{
DBG_SERIAL.println("Failed to init files system, flash may not be formatted");
}
}
void refreshMassStorage(void)
{
fs_changed = true;
}
void setupServer(void)
{
//WIFI INIT
DBG_SERIAL.printf("Connecting to %s\n", SECRET_SSID);
if (String(WiFi.SSID()) != String(SECRET_SSID)) {
WiFi.mode(WIFI_STA);
WiFi.begin(SECRET_SSID, SECRET_PASSWORD);
}
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DBG_SERIAL.print(".");
}
DBG_SERIAL.println("");
DBG_SERIAL.print("Connected! IP address: ");
DBG_SERIAL.println(WiFi.localIP());
MDNS.begin(host);
DBG_SERIAL.print("Open http://");
DBG_SERIAL.print(host);
DBG_SERIAL.println(".local/edit to access the file browser");
//SERVER INIT
//list directory
server.on("/list", HTTP_GET, handleFileList);
//load editor
server.on("/edit", HTTP_GET, []() {
if (!handleFileRead("/edit.htm")) {
server.send(404, "text/plain", "FileNotFound");
}
});
//create file
server.on("/edit", HTTP_PUT, handleFileCreate);
//delete file
server.on("/edit", HTTP_DELETE, handleFileDelete);
//first callback is called after the request has ended with all parsed arguments
//second callback handles file uploads at that location
server.on("/edit", HTTP_POST, []() {
server.send(200, "text/plain", "");
}, handleFileUpload);
//called when the url is not defined here
//use it to load content from fatfs
server.onNotFound([]() {
if (!handleFileRead(server.uri())) {
server.send(404, "text/plain", "FileNotFound");
}
});
//get heap status, analog input value and all GPIO statuses in one json call
server.on("/all", HTTP_GET, []() {
String json = "{";
json += "\"heap\":" + String(ESP.getFreeHeap());
json += ", \"analog\":" + String(analogRead(A0));
json += ", \"gpio\":" + String((uint32_t)(0));
json += "}";
server.send(200, "text/json", json);
json = String();
});
server.begin();
DBG_SERIAL.println("HTTP server started");
}
void setup()
{
#ifdef LED_BUILTIN
pinMode(LED_BUILTIN, OUTPUT);
#endif
DBG_SERIAL.begin(115200);
setupMassStorage();
// while ( !DBG_SERIAL ) delay(10); // wait for native usb
DBG_SERIAL.println("TinyUSB Mass Storage with ESP32 File Browser example");
DBG_SERIAL.print("JEDEC ID: 0x"); DBG_SERIAL.println(flash.getJEDECID(), HEX);
DBG_SERIAL.print("Flash size: "); DBG_SERIAL.print(flash.size() / 1024); DBG_SERIAL.println(" KB");
setupServer();
}
//--------------------------------------------------------------------+
// Handle requests
//--------------------------------------------------------------------+
//format bytes
String formatBytes(size_t bytes) {
if (bytes < 1024) {
return String(bytes) + "B";
} else if (bytes < (1024 * 1024)) {
return String(bytes / 1024.0) + "KB";
} else if (bytes < (1024 * 1024 * 1024)) {
return String(bytes / 1024.0 / 1024.0) + "MB";
} else {
return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB";
}
}
String getContentType(String filename) {
if (server.hasArg("download")) {
return "application/octet-stream";
} else if (filename.endsWith(".htm")) {
return "text/html";
} else if (filename.endsWith(".html")) {
return "text/html";
} else if (filename.endsWith(".css")) {
return "text/css";
} else if (filename.endsWith(".js")) {
return "application/javascript";
} else if (filename.endsWith(".png")) {
return "image/png";
} else if (filename.endsWith(".gif")) {
return "image/gif";
} else if (filename.endsWith(".jpg")) {
return "image/jpeg";
} else if (filename.endsWith(".ico")) {
return "image/x-icon";
} else if (filename.endsWith(".xml")) {
return "text/xml";
} else if (filename.endsWith(".pdf")) {
return "application/x-pdf";
} else if (filename.endsWith(".zip")) {
return "application/x-zip";
} else if (filename.endsWith(".gz")) {
return "application/x-gzip";
}
return "text/plain";
}
bool exists(String path){
bool yes = false;
File32 file = fatfs.open(path, O_READ);
if(file && !file.isDirectory()){
yes = true;
}
file.close();
return yes;
}
bool handleFileRead(String path) {
DBG_SERIAL.println("handleFileRead: " + path);
if (path.endsWith("/")) {
path += "index.htm";
}
String contentType = getContentType(path);
// String pathWithGz = path + ".gz";
if ( /*exists(pathWithGz) ||*/ exists(path)) {
// if (exists(pathWithGz)) {
// path += ".gz";
// }
File32 file = fatfs.open(path, O_READ);
server.streamFile(file, contentType);
file.close();
return true;
}
return false;
}
void handleFileUpload() {
if (server.uri() != "/edit") {
return;
}
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
String filename = upload.filename;
if (!filename.startsWith("/")) {
filename = "/" + filename;
}
DBG_SERIAL.print("handleFileUpload Name: "); DBG_SERIAL.println(filename);
fsUploadFile = fatfs.open(filename, O_WRITE | O_CREAT | O_TRUNC);
filename = String();
} else if (upload.status == UPLOAD_FILE_WRITE) {
DBG_SERIAL.print("handleFileUpload Data: "); DBG_SERIAL.println(upload.currentSize);
if (fsUploadFile) {
fsUploadFile.write(upload.buf, upload.currentSize);
}else
{
DBG_SERIAL.print("handleFileUpload file is not opened !!!");
}
} else if (upload.status == UPLOAD_FILE_END) {
if (fsUploadFile) {
fsUploadFile.close();
refreshMassStorage();
}
DBG_SERIAL.print("handleFileUpload Size: "); DBG_SERIAL.println(upload.totalSize);
}
}
void handleFileDelete() {
if (server.args() == 0) {
return server.send(500, "text/plain", "BAD ARGS");
}
String path = server.arg(0);
DBG_SERIAL.println("handleFileDelete: " + path);
if (path == "/") {
return server.send(500, "text/plain", "BAD PATH");
}
if (!exists(path)) {
return server.send(404, "text/plain", "FileNotFound");
}
fatfs.remove(path.c_str());
refreshMassStorage();
server.send(200, "text/plain", "");
path = String();
}
void handleFileCreate() {
if (server.args() == 0) {
return server.send(500, "text/plain", "BAD ARGS");
}
String path = server.arg(0);
DBG_SERIAL.println("handleFileCreate: " + path);
if (path == "/") {
return server.send(500, "text/plain", "BAD PATH");
}
if (exists(path)) {
return server.send(500, "text/plain", "FILE EXISTS");
}
File32 file = fatfs.open(path, O_WRITE | O_CREAT);
if (file) {
file.close();
} else {
return server.send(500, "text/plain", "CREATE FAILED");
}
server.send(200, "text/plain", "");
path = String();
}
void handleFileList() {
if (!server.hasArg("dir")) {
server.send(500, "text/plain", "BAD ARGS");
return;
}
String path = server.arg("dir");
DBG_SERIAL.println("handleFileList: " + path);
File32 root = fatfs.open(path);
path = String();
String output = "[";
if(root.isDirectory()){
File32 file = root.openNextFile();
char fname[256];
while(file){
if (output != "[") {
output += ',';
}
output += "{\"type\":\"";
output += (file.isDirectory()) ? "dir" : "file";
output += "\",\"name\":\"";
//output += String(file.path()).substring(1);
file.getName(fname, sizeof(fname));
output += fname;
output += "\"}";
file = root.openNextFile();
}
}
output += "]";
server.send(200, "text/json", output);
}
//--------------------------------------------------------------------+
// Loop
//--------------------------------------------------------------------+
void loop()
{
server.handleClient();
delay(2);//allow the cpu to switch to other tasks
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and
// return number of copied bytes (must be multiple of block size)
int32_t msc_read_cb (uint32_t lba, void* buffer, uint32_t bufsize)
{
// Note: SPIFLash Bock API: readBlocks/writeBlocks/syncBlocks
// already include 4K sector caching internally. We don't need to cache it, yahhhh!!
return flash.readBlocks(lba, (uint8_t*) buffer, bufsize/512) ? bufsize : -1;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t msc_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize)
{
#ifdef LED_BUILTIN
digitalWrite(LED_BUILTIN, HIGH);
#endif
// Note: SPIFLash Bock API: readBlocks/writeBlocks/syncBlocks
// already include 4K sector caching internally. We don't need to cache it, yahhhh!!
return flash.writeBlocks(lba, buffer, bufsize/512) ? bufsize : -1;
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void msc_flush_cb (void)
{
// sync with flash
flash.syncBlocks();
// clear file system's cache to force refresh
fatfs.cacheClear();
#ifdef LED_BUILTIN
digitalWrite(LED_BUILTIN, LOW);
#endif
}
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool msc_ready_callback(void)
{
// if fs has changed, mark unit as not ready temporarily to force PC to flush cache
bool ret = !fs_changed;
fs_changed = false;
return ret;
}

View File

@ -0,0 +1,79 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Ha Thach (tinyusb.org) for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FLASH_CONFIG_H_
#define FLASH_CONFIG_H_
// Un-comment to run example with custom SPI and SS e.g with FRAM breakout
// #define CUSTOM_CS A5
// #define CUSTOM_SPI SPI
#if defined(CUSTOM_CS) && defined(CUSTOM_SPI)
Adafruit_FlashTransport_SPI flashTransport(CUSTOM_CS, CUSTOM_SPI);
#elif defined(ARDUINO_ARCH_ESP32)
// ESP32 use same flash device that store code for file system.
// SPIFlash will parse partition.cvs to detect FATFS partition to use
Adafruit_FlashTransport_ESP32 flashTransport;
#elif defined(ARDUINO_ARCH_RP2040)
// RP2040 use same flash device that store code for file system. Therefore we
// only need to specify start address and size (no need SPI or SS)
// By default (start=0, size=0), values that match file system setting in
// 'Tools->Flash Size' menu selection will be used.
Adafruit_FlashTransport_RP2040 flashTransport;
// To be compatible with CircuitPython partition scheme (start_address = 1 MB,
// size = total flash - 1 MB) use const value (CPY_START_ADDR, CPY_SIZE) or
// subclass Adafruit_FlashTransport_RP2040_CPY. Un-comment either of the
// following line:
// Adafruit_FlashTransport_RP2040
// flashTransport(Adafruit_FlashTransport_RP2040::CPY_START_ADDR,
// Adafruit_FlashTransport_RP2040::CPY_SIZE);
// Adafruit_FlashTransport_RP2040_CPY flashTransport;
#else
// On-board external flash (QSPI or SPI) macros should already
// defined in your board variant if supported
// - EXTERNAL_FLASH_USE_QSPI
// - EXTERNAL_FLASH_USE_CS/EXTERNAL_FLASH_USE_SPI
#if defined(EXTERNAL_FLASH_USE_QSPI)
Adafruit_FlashTransport_QSPI flashTransport;
#elif defined(EXTERNAL_FLASH_USE_SPI)
Adafruit_FlashTransport_SPI flashTransport(EXTERNAL_FLASH_USE_CS,
EXTERNAL_FLASH_USE_SPI);
#else
#error No (Q)SPI flash are defined for your board !
#endif
#endif
#endif

View File

@ -0,0 +1,174 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example demo how to expose on-board external Flash as USB Mass Storage.
* Following library is required
* - Adafruit_SPIFlash https://github.com/adafruit/Adafruit_SPIFlash
* - SdFat https://github.com/adafruit/SdFat
*
* Note: Adafruit fork of SdFat enabled ENABLE_EXTENDED_TRANSFER_CLASS and FAT12_SUPPORT
* in SdFatConfig.h, which is needed to run SdFat on external flash. You can use original
* SdFat library and manually change those macros
*
* Note2: If your flash is not formatted as FAT12 previously, you could format it using
* follow sketch https://github.com/adafruit/Adafruit_SPIFlash/tree/master/examples/SdFat_format
*/
#include "SPI.h"
#include "SdFat.h"
#include "Adafruit_SPIFlash.h"
#include "Adafruit_TinyUSB.h"
// for flashTransport definition
#include "flash_config.h"
Adafruit_SPIFlash flash(&flashTransport);
// file system object from SdFat
FatVolume fatfs;
FatFile root;
FatFile file;
// USB Mass Storage object
Adafruit_USBD_MSC usb_msc;
// Check if flash is formatted
bool fs_formatted = false;
// Set to true when PC write to flash
bool fs_changed = true;;
// the setup function runs once when you press reset or power the board
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
flash.begin();
// Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively
usb_msc.setID("Adafruit", "External Flash", "1.0");
// Set callback
usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb);
// Set disk size, block size should be 512 regardless of spi flash page size
usb_msc.setCapacity(flash.size()/512, 512);
// MSC is ready for read/write
usb_msc.setUnitReady(true);
usb_msc.begin();
// Init file system on the flash
fs_formatted = fatfs.begin(&flash);
Serial.begin(115200);
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Adafruit TinyUSB Mass Storage External Flash example");
Serial.print("JEDEC ID: 0x"); Serial.println(flash.getJEDECID(), HEX);
Serial.print("Flash size: "); Serial.print(flash.size() / 1024); Serial.println(" KB");
}
void loop()
{
// check if formatted
if ( !fs_formatted )
{
fs_formatted = fatfs.begin(&flash);
if (!fs_formatted)
{
Serial.println("Failed to init files system, flash may not be formatted");
Serial.println("Please format it as FAT12 with your PC or using Adafruit_SPIFlash's SdFat_format example:");
Serial.println("- https://github.com/adafruit/Adafruit_SPIFlash/tree/master/examples/SdFat_format");
Serial.println();
delay(1000);
return;
}
}
if ( fs_changed )
{
fs_changed = false;
Serial.println("Opening root");
if ( !root.open("/") )
{
Serial.println("open root failed");
return;
}
Serial.println("Flash contents:");
// Open next file in root.
// Warning, openNext starts at the current directory position
// so a rewind of the directory may be required.
while ( file.openNext(&root, O_RDONLY) )
{
file.printFileSize(&Serial);
Serial.write(' ');
file.printName(&Serial);
if ( file.isDir() )
{
// Indicate a directory.
Serial.write('/');
}
Serial.println();
file.close();
}
root.close();
Serial.println();
delay(1000); // refresh every 1 second
}
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and
// return number of copied bytes (must be multiple of block size)
int32_t msc_read_cb (uint32_t lba, void* buffer, uint32_t bufsize)
{
// Note: SPIFLash Block API: readBlocks/writeBlocks/syncBlocks
// already include 4K sector caching internally. We don't need to cache it, yahhhh!!
return flash.readBlocks(lba, (uint8_t*) buffer, bufsize/512) ? bufsize : -1;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t msc_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize)
{
digitalWrite(LED_BUILTIN, HIGH);
// Note: SPIFLash Block API: readBlocks/writeBlocks/syncBlocks
// already include 4K sector caching internally. We don't need to cache it, yahhhh!!
return flash.writeBlocks(lba, buffer, bufsize/512) ? bufsize : -1;
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void msc_flush_cb (void)
{
// sync with flash
flash.syncBlocks();
// clear file system's cache to force refresh
fatfs.cacheClear();
fs_changed = true;
digitalWrite(LED_BUILTIN, LOW);
}

View File

@ -0,0 +1,79 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Ha Thach (tinyusb.org) for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FLASH_CONFIG_H_
#define FLASH_CONFIG_H_
// Un-comment to run example with custom SPI and SS e.g with FRAM breakout
// #define CUSTOM_CS A5
// #define CUSTOM_SPI SPI
#if defined(CUSTOM_CS) && defined(CUSTOM_SPI)
Adafruit_FlashTransport_SPI flashTransport(CUSTOM_CS, CUSTOM_SPI);
#elif defined(ARDUINO_ARCH_ESP32)
// ESP32 use same flash device that store code for file system.
// SPIFlash will parse partition.cvs to detect FATFS partition to use
Adafruit_FlashTransport_ESP32 flashTransport;
#elif defined(ARDUINO_ARCH_RP2040)
// RP2040 use same flash device that store code for file system. Therefore we
// only need to specify start address and size (no need SPI or SS)
// By default (start=0, size=0), values that match file system setting in
// 'Tools->Flash Size' menu selection will be used.
Adafruit_FlashTransport_RP2040 flashTransport;
// To be compatible with CircuitPython partition scheme (start_address = 1 MB,
// size = total flash - 1 MB) use const value (CPY_START_ADDR, CPY_SIZE) or
// subclass Adafruit_FlashTransport_RP2040_CPY. Un-comment either of the
// following line:
// Adafruit_FlashTransport_RP2040
// flashTransport(Adafruit_FlashTransport_RP2040::CPY_START_ADDR,
// Adafruit_FlashTransport_RP2040::CPY_SIZE);
// Adafruit_FlashTransport_RP2040_CPY flashTransport;
#else
// On-board external flash (QSPI or SPI) macros should already
// defined in your board variant if supported
// - EXTERNAL_FLASH_USE_QSPI
// - EXTERNAL_FLASH_USE_CS/EXTERNAL_FLASH_USE_SPI
#if defined(EXTERNAL_FLASH_USE_QSPI)
Adafruit_FlashTransport_QSPI flashTransport;
#elif defined(EXTERNAL_FLASH_USE_SPI)
Adafruit_FlashTransport_SPI flashTransport(EXTERNAL_FLASH_USE_CS,
EXTERNAL_FLASH_USE_SPI);
#else
#error No (Q)SPI flash are defined for your board !
#endif
#endif
#endif

View File

@ -0,0 +1,327 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example exposes both external flash and SD card as mass storage (dual LUNs)
* Following library is required
* - Adafruit_SPIFlash https://github.com/adafruit/Adafruit_SPIFlash
* - SdFat https://github.com/adafruit/SdFat
*
* Note: Adafruit fork of SdFat enabled ENABLE_EXTENDED_TRANSFER_CLASS and FAT12_SUPPORT
* in SdFatConfig.h, which is needed to run SdFat on external flash. You can use original
* SdFat library and manually change those macros
*
* Note2: If your flash is not formatted as FAT12 previously, you could format it using
* follow sketch https://github.com/adafruit/Adafruit_SPIFlash/tree/master/examples/SdFat_format
*/
#include "SPI.h"
#include "SdFat.h"
#include "Adafruit_SPIFlash.h"
#include "Adafruit_TinyUSB.h"
//--------------------------------------------------------------------+
// External Flash Config
//--------------------------------------------------------------------+
// for flashTransport definition
#include "flash_config.h"
Adafruit_SPIFlash flash(&flashTransport);
// External Flash File system
FatVolume fatfs;
//--------------------------------------------------------------------+
// SDCard Config
//--------------------------------------------------------------------+
#if defined(ARDUINO_PYPORTAL_M4) || defined(ARDUINO_PYPORTAL_M4_TITANO)
// PyPortal has on-board card reader
#define SDCARD_CS 32
#define SDCARD_DETECT 33
#else
#define SDCARD_CS 10
// no detect
#endif
// SDCard File system
SdFat sd;
// USB Mass Storage object
Adafruit_USBD_MSC usb_msc;
// Set to true when PC write to flash
bool sd_changed = false;
bool sd_inited = false;
bool flash_formatted = false;
bool flash_changed = false;
// the setup function runs once when you press reset or power the board
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
// MSC with 2 Logical Units: LUN0: External Flash, LUN1: SDCard
usb_msc.setMaxLun(2);
usb_msc.setID(0, "Adafruit", "External Flash", "1.0");
usb_msc.setID(1, "Adafruit", "SD Card", "1.0");
// Since initialize both external flash and SD card can take time.
// If it takes too long, our board could be enumerated as CDC device only
// i.e without Mass Storage. To prevent this, we call Mass Storage begin first
// LUN readiness will always be set later on
usb_msc.begin();
//------------- Lun 0 for external flash -------------//
flash.begin();
flash_formatted = fatfs.begin(&flash);
usb_msc.setCapacity(0, flash.size()/512, 512);
usb_msc.setReadWriteCallback(0, external_flash_read_cb, external_flash_write_cb, external_flash_flush_cb);
usb_msc.setUnitReady(0, true);
flash_changed = true; // to print contents initially
//------------- Lun 1 for SD card -------------//
#ifdef SDCARD_DETECT
// DETECT pin is available, detect card present on the fly with test unit ready
pinMode(SDCARD_DETECT, INPUT);
usb_msc.setReadyCallback(1, sdcard_ready_callback);
#else
// no DETECT pin, card must be inserted when powered on
init_sdcard();
sd_inited = true;
usb_msc.setUnitReady(1, true);
#endif
// while ( !Serial ) delay(10); // wait for native usb
Serial.println("Adafruit TinyUSB Mass Storage External Flash + SD Card example");
delay(1000);
}
bool init_sdcard(void)
{
Serial.print("Init SDCard ... ");
if ( !sd.begin(SDCARD_CS, SD_SCK_MHZ(50)) )
{
Serial.print("Failed ");
sd.errorPrint("sd.begin() failed");
return false;
}
uint32_t block_count;
#if SD_FAT_VERSION >= 20000
block_count = sd.card()->sectorCount();
#else
block_count = sd.card()->cardSize();
#endif
usb_msc.setCapacity(1, block_count, 512);
usb_msc.setReadWriteCallback(1, sdcard_read_cb, sdcard_write_cb, sdcard_flush_cb);
sd_changed = true; // to print contents initially
Serial.print("OK, Card size = ");
Serial.print((block_count / (1024*1024)) * 512);
Serial.println(" MB");
return true;
}
void print_rootdir(File32* rdir)
{
File32 file;
// Open next file in root.
// Warning, openNext starts at the current directory position
// so a rewind of the directory may be required.
while ( file.openNext(rdir, O_RDONLY) )
{
file.printFileSize(&Serial);
Serial.write(' ');
file.printName(&Serial);
if ( file.isDir() )
{
// Indicate a directory.
Serial.write('/');
}
Serial.println();
file.close();
}
}
void loop()
{
if ( flash_changed )
{
if (!flash_formatted)
{
flash_formatted = fatfs.begin(&flash);
}
// skip if still not formatted
if (flash_formatted)
{
File32 root;
root = fatfs.open("/");
Serial.println("Flash contents:");
print_rootdir(&root);
Serial.println();
root.close();
}
flash_changed = false;
}
if ( sd_changed )
{
File32 root;
root = sd.open("/");
Serial.println("SD contents:");
print_rootdir(&root);
Serial.println();
root.close();
sd_changed = false;
}
delay(1000); // refresh every 1 second
}
//--------------------------------------------------------------------+
// SD Card callbacks
//--------------------------------------------------------------------+
int32_t sdcard_read_cb (uint32_t lba, void* buffer, uint32_t bufsize)
{
bool rc;
#if SD_FAT_VERSION >= 20000
rc = sd.card()->readSectors(lba, (uint8_t*) buffer, bufsize/512);
#else
rc = sd.card()->readBlocks(lba, (uint8_t*) buffer, bufsize/512);
#endif
return rc ? bufsize : -1;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t sdcard_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize)
{
bool rc;
digitalWrite(LED_BUILTIN, HIGH);
#if SD_FAT_VERSION >= 20000
rc = sd.card()->writeSectors(lba, buffer, bufsize/512);
#else
rc = sd.card()->writeBlocks(lba, buffer, bufsize/512);
#endif
return rc ? bufsize : -1;
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void sdcard_flush_cb (void)
{
#if SD_FAT_VERSION >= 20000
sd.card()->syncDevice();
#else
sd.card()->syncBlocks();
#endif
// clear file system's cache to force refresh
sd.cacheClear();
sd_changed = true;
digitalWrite(LED_BUILTIN, LOW);
}
#ifdef SDCARD_DETECT
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool sdcard_ready_callback(void)
{
// Card is inserted
if ( digitalRead(SDCARD_DETECT) == HIGH )
{
// init SD card if not already
if ( !sd_inited )
{
sd_inited = init_sdcard();
}
}else
{
sd_inited = false;
usb_msc.setReadWriteCallback(1, NULL, NULL, NULL);
}
Serial.println(sd_inited);
return sd_inited;
}
#endif
//--------------------------------------------------------------------+
// External Flash callbacks
//--------------------------------------------------------------------+
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and
// return number of copied bytes (must be multiple of block size)
int32_t external_flash_read_cb (uint32_t lba, void* buffer, uint32_t bufsize)
{
// Note: SPIFLash Bock API: readBlocks/writeBlocks/syncBlocks
// already include 4K sector caching internally. We don't need to cache it, yahhhh!!
return flash.readBlocks(lba, (uint8_t*) buffer, bufsize/512) ? bufsize : -1;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t external_flash_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize)
{
digitalWrite(LED_BUILTIN, HIGH);
// Note: SPIFLash Bock API: readBlocks/writeBlocks/syncBlocks
// already include 4K sector caching internally. We don't need to cache it, yahhhh!!
return flash.writeBlocks(lba, buffer, bufsize/512) ? bufsize : -1;
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void external_flash_flush_cb (void)
{
flash.syncBlocks();
// clear file system's cache to force refresh
fatfs.cacheClear();
flash_changed = true;
digitalWrite(LED_BUILTIN, LOW);
}

View File

@ -0,0 +1,144 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include "SPI.h"
#include "SdFat.h"
#include "Adafruit_InternalFlash.h"
#include "Adafruit_TinyUSB.h"
// Start address and size should matches value in the CircuitPython (INTERNAL_FLASH_FILESYSTEM = 1)
// to make it easier to switch between Arduino and CircuitPython
#define INTERNAL_FLASH_FILESYSTEM_START_ADDR (0x00040000 - 256 - 0 - INTERNAL_FLASH_FILESYSTEM_SIZE)
#define INTERNAL_FLASH_FILESYSTEM_SIZE (64*1024)
// Internal Flash object
Adafruit_InternalFlash flash(INTERNAL_FLASH_FILESYSTEM_START_ADDR, INTERNAL_FLASH_FILESYSTEM_SIZE);
// file system object from SdFat
FatVolume fatfs;
FatFile root;
FatFile file;
// USB MSC object
Adafruit_USBD_MSC usb_msc;
// Set to true when PC write to flash
bool fs_changed;
// the setup function runs once when you press reset or power the board
void setup()
{
// Initialize internal flash
flash.begin();
// Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively
usb_msc.setID("Adafruit", "Internal Flash", "1.0");
// Set callback
usb_msc.setReadWriteCallback(msc_read_callback, msc_write_callback, msc_flush_callback);
usb_msc.setWritableCallback(msc_writable_callback);
// Set disk size, block size should be 512 regardless of flash page size
usb_msc.setCapacity(flash.size()/512, 512);
// Set Lun ready
usb_msc.setUnitReady(true);
usb_msc.begin();
// Init file system on the flash
fatfs.begin(&flash);
Serial.begin(115200);
//while ( !Serial ) delay(10); // wait for native usb
fs_changed = true; // to print contents initially
}
void loop()
{
if ( fs_changed )
{
fs_changed = false;
if ( !root.open("/") )
{
Serial.println("open root failed");
return;
}
Serial.println("Flash contents:");
// Open next file in root.
// Warning, openNext starts at the current directory position
// so a rewind of the directory may be required.
while ( file.openNext(&root, O_RDONLY) )
{
file.printFileSize(&Serial);
Serial.write(' ');
file.printName(&Serial);
if ( file.isDir() )
{
// Indicate a directory.
Serial.write('/');
}
Serial.println();
file.close();
}
root.close();
Serial.println();
delay(1000); // refresh every 1 second
}
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and
// return number of copied bytes (must be multiple of block size)
int32_t msc_read_callback (uint32_t lba, void* buffer, uint32_t bufsize)
{
// Note: InternalFlash Block API: readBlocks/writeBlocks/syncBlocks
// already include sector caching (if needed). We don't need to cache it, yahhhh!!
return flash.readBlocks(lba, (uint8_t*) buffer, bufsize/512) ? bufsize : -1;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t msc_write_callback (uint32_t lba, uint8_t* buffer, uint32_t bufsize)
{
// Note: InternalFlash Block API: readBlocks/writeBlocks/syncBlocks
// already include sector caching (if needed). We don't need to cache it, yahhhh!!
return flash.writeBlocks(lba, buffer, bufsize/512) ? bufsize : -1;
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void msc_flush_callback (void)
{
// sync with flash
flash.syncBlocks();
// clear file system's cache to force refresh
fatfs.cacheClear();
fs_changed = true;
}
// Invoked to check if device is writable as part of SCSI WRITE10
// Default mode is writable
bool msc_writable_callback(void)
{
// true for writable, false for read-only
return true;
}

View File

@ -0,0 +1,114 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include "Adafruit_TinyUSB.h"
// 8KB is the smallest size that windows allow to mount
#define DISK_BLOCK_NUM 16
#define DISK_BLOCK_SIZE 512
#include "ramdisk.h"
Adafruit_USBD_MSC usb_msc;
// Eject button to demonstrate medium is not ready e.g SDCard is not present
// whenever this button is pressed and hold, it will report to host as not ready
#if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(ARDUINO_NRF52840_CIRCUITPLAY)
#define BTN_EJECT 4 // Left Button
bool activeState = true;
#elif defined(ARDUINO_FUNHOUSE_ESP32S2)
#define BTN_EJECT BUTTON_DOWN
bool activeState = true;
#elif defined PIN_BUTTON1
#define BTN_EJECT PIN_BUTTON1
bool activeState = false;
#endif
// the setup function runs once when you press reset or power the board
void setup()
{
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
// Manual begin() is required on core without built-in support for TinyUSB such as
// - mbed rp2040
TinyUSB_Device_Init(0);
#endif
// Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively
usb_msc.setID("Adafruit", "Mass Storage", "1.0");
// Set disk size
usb_msc.setCapacity(DISK_BLOCK_NUM, DISK_BLOCK_SIZE);
// Set callback
usb_msc.setReadWriteCallback(msc_read_callback, msc_write_callback, msc_flush_callback);
// Set Lun ready (RAM disk is always ready)
usb_msc.setUnitReady(true);
#ifdef BTN_EJECT
pinMode(BTN_EJECT, activeState ? INPUT_PULLDOWN : INPUT_PULLUP);
usb_msc.setReadyCallback(msc_ready_callback);
#endif
usb_msc.begin();
Serial.begin(115200);
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Adafruit TinyUSB Mass Storage RAM Disk example");
}
void loop()
{
// nothing to do
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and
// return number of copied bytes (must be multiple of block size)
int32_t msc_read_callback (uint32_t lba, void* buffer, uint32_t bufsize)
{
uint8_t const* addr = msc_disk[lba];
memcpy(buffer, addr, bufsize);
return bufsize;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t msc_write_callback (uint32_t lba, uint8_t* buffer, uint32_t bufsize)
{
uint8_t* addr = msc_disk[lba];
memcpy(addr, buffer, bufsize);
return bufsize;
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void msc_flush_callback (void)
{
// nothing to do
}
#ifdef BTN_EJECT
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool msc_ready_callback(void)
{
// button not active --> medium ready
return digitalRead(BTN_EJECT) != activeState;
}
#endif

View File

@ -0,0 +1,119 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef RAMDISK_H_
#define RAMDISK_H_
#define README_CONTENTS \
"This is TinyUSB MassStorage device demo for Arduino on RAM disk."
uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = {
//------------- Block0: Boot Sector -------------//
// byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16 =
// DISK_BLOCK_NUM; sector_per_cluster = 1; reserved_sectors = 1; fat_num =
// 1; fat12_root_entry_num = 16; sector_per_fat = 1; sector_per_track =
// 1; head_num = 1; hidden_sectors = 0; drive_number = 0x80;
// media_type = 0xf8; extended_boot_signature = 0x29; filesystem_type =
// "FAT12 "; volume_serial_number = 0x1234; volume_label = "TinyUSB MSC";
// FAT magic code at offset 510-511
{0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00,
0x02, 0x01, 0x01, 0x00, 0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00,
0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T', 'i', 'n', 'y', 'U', 'S',
'B', ' ', 'M', 'S', 'C', 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20,
0x00, 0x00,
// Zero up to 2 last bytes of FAT magic code
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x55, 0xAA},
//------------- Block1: FAT12 Table -------------//
{
0xF8, 0xFF, 0x00, // The first 2 12bit entries of the FAT linked list are reserved. Therefore the first 3 bytes are reserved.
// The 2 12 bit values must be 0xF8 and 0xFF
// [Entry2 | Entry3]
0xFF, 0x0F, 0x00 // <<< These 3 bytes represents the 12 bit entries 2 and 3 of the FAT linked list.
// The README.TXT file first block is 2 (see line 112), therefore to find the next block, we go to this section Entry[2]
// to read the address of the next block. Since the size of the README.TXT is 65 bytes and less then one sector (512) byte.
// There is no next block to read therefore the Entry[2] contains the value 0xFFF, which is the EOF.
},
//------------- Block2: Root Directory -------------//
{
// first entry is volume label
'T', 'i', 'n', 'y', 'U', 'S', 'B', ' ', 'M', 'S', 'C', 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// second entry is readme file
'R', 'E', 'A', 'D', 'M', 'E', ' ', ' ', 'T', 'X', 'T', 0x20, 0x00, 0xC6,
0x52, 0x6D, 0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43,
0x02, 0x00, sizeof(README_CONTENTS) - 1, 0x00, 0x00,
0x00 // readme's files size (4 Bytes)
},
//------------- Block3: Readme Content -------------//
README_CONTENTS};
#endif /* RAMDISK_H_ */

View File

@ -0,0 +1,122 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include "Adafruit_TinyUSB.h"
// 8KB is the smallest size that windows allow to mount
#define DISK_BLOCK_NUM 16
#define DISK_BLOCK_SIZE 512
#include "ramdisk.h"
Adafruit_USBD_MSC usb_msc;
// the setup function runs once when you press reset or power the board
void setup()
{
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
// Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040
TinyUSB_Device_Init(0);
#endif
usb_msc.setMaxLun(2);
// Set disk size and callback for Logical Unit 0 (LUN 0)
usb_msc.setID(0, "Adafruit", "Lun0", "1.0");
usb_msc.setCapacity(0, DISK_BLOCK_NUM, DISK_BLOCK_SIZE);
usb_msc.setReadWriteCallback(0, ram0_read_cb, ram0_write_cb, ram0_flush_cb);
usb_msc.setUnitReady(0, true);
// Set disk size and callback for Logical Unit 1 (LUN 1)
usb_msc.setID(1, "Adafruit", "Lun1", "1.0");
usb_msc.setCapacity(1, DISK_BLOCK_NUM, DISK_BLOCK_SIZE);
usb_msc.setReadWriteCallback(1, ram1_read_cb, ram1_write_cb, ram1_flush_cb);
usb_msc.setUnitReady(1, true);
usb_msc.begin();
Serial.begin(115200);
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Adafruit TinyUSB Mass Storage Dual RAM Disks example");
}
void loop()
{
// nothing to do
}
//--------------------------------------------------------------------+
// LUN 0 callback
//--------------------------------------------------------------------+
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and
// return number of copied bytes (must be multiple of block size)
int32_t ram0_read_cb (uint32_t lba, void* buffer, uint32_t bufsize)
{
uint8_t const* addr = msc_disk0[lba];
memcpy(buffer, addr, bufsize);
return bufsize;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t ram0_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize)
{
uint8_t* addr = msc_disk0[lba];
memcpy(addr, buffer, bufsize);
return bufsize;
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void ram0_flush_cb (void)
{
// nothing to do
}
//--------------------------------------------------------------------+
// LUN 1 callback
//--------------------------------------------------------------------+
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and
// return number of copied bytes (must be multiple of block size)
int32_t ram1_read_cb (uint32_t lba, void* buffer, uint32_t bufsize)
{
uint8_t const* addr = msc_disk1[lba];
memcpy(buffer, addr, bufsize);
return bufsize;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t ram1_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize)
{
uint8_t* addr = msc_disk1[lba];
memcpy(addr, buffer, bufsize);
return bufsize;
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void ram1_flush_cb (void)
{
// nothing to do
}

View File

@ -0,0 +1,204 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef RAMDISK_H_
#define RAMDISK_H_
//--------------------------------------------------------------------+
// LUN 0
//--------------------------------------------------------------------+
#define README0_CONTENTS \
"LUN0: This is TinyUSB MassStorage device demo for Arduino on RAM disk."
uint8_t msc_disk0[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = {
//------------- Block0: Boot Sector -------------//
// byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16 =
// DISK_BLOCK_NUM; sector_per_cluster = 1; reserved_sectors = 1; fat_num =
// 1; fat12_root_entry_num = 16; sector_per_fat = 1; sector_per_track =
// 1; head_num = 1; hidden_sectors = 0; drive_number = 0x80;
// media_type = 0xf8; extended_boot_signature = 0x29; filesystem_type =
// "FAT12 "; volume_serial_number = 0x1234; volume_label = "TinyUSB 0 ";
// FAT magic code at offset 510-511
{0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00,
0x02, 0x01, 0x01, 0x00, 0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00,
0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T', 'i', 'n', 'y', 'U', 'S',
'B', ' ', '0', ' ', ' ', 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20,
0x00, 0x00,
// Zero up to 2 last bytes of FAT magic code
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x55, 0xAA},
//------------- Block1: FAT12 Table -------------//
{
0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third
// entry is cluster end of readme file
},
//------------- Block2: Root Directory -------------//
{
// first entry is volume label
'T', 'i', 'n', 'y', 'U', 'S', 'B', ' ', '0', ' ', ' ', 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// second entry is readme file
'R', 'E', 'A', 'D', 'M', 'E', '0', ' ', 'T', 'X', 'T', 0x20, 0x00, 0xC6,
0x52, 0x6D, 0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43,
0x02, 0x00, sizeof(README0_CONTENTS) - 1, 0x00, 0x00,
0x00 // readme's files size (4 Bytes)
},
//------------- Block3: Readme Content -------------//
README0_CONTENTS};
//--------------------------------------------------------------------+
// LUN 1
//--------------------------------------------------------------------+
#define README1_CONTENTS \
"LUN1: This is TinyUSB MassStorage device demo for Arduino on RAM disk."
uint8_t msc_disk1[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = {
//------------- Block0: Boot Sector -------------//
// byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16 =
// DISK_BLOCK_NUM; sector_per_cluster = 1; reserved_sectors = 1; fat_num =
// 1; fat12_root_entry_num = 16; sector_per_fat = 1; sector_per_track =
// 1; head_num = 1; hidden_sectors = 0; drive_number = 0x80;
// media_type = 0xf8; extended_boot_signature = 0x29; filesystem_type =
// "FAT12 "; volume_serial_number = 0x5678; volume_label = "TinyUSB 1 ";
// FAT magic code at offset 510-511
{0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00,
0x02, 0x01, 0x01, 0x00, 0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00,
0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0x00, 0x29, 0x78, 0x56, 0x00, 0x00, 'T', 'i', 'n', 'y', 'U', 'S',
'B', ' ', '1', ' ', ' ', 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20,
0x00, 0x00,
// Zero up to 2 last bytes of FAT magic code
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x55, 0xAA},
//------------- Block1: FAT12 Table -------------//
{
0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third
// entry is cluster end of readme file
},
//------------- Block2: Root Directory -------------//
{
// first entry is volume label
'T', 'i', 'n', 'y', 'U', 'S', 'B', ' ', '1', ' ', ' ', 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// second entry is readme file
'R', 'E', 'A', 'D', 'M', 'E', '1', ' ', 'T', 'X', 'T', 0x20, 0x00, 0xC6,
0x52, 0x6D, 0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43,
0x02, 0x00, sizeof(README1_CONTENTS) - 1, 0x00, 0x00,
0x00 // readme's files size (4 Bytes)
},
//------------- Block3: Readme Content -------------//
README1_CONTENTS};
#endif /* RAMDISK_H_ */

View File

@ -0,0 +1,102 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example expose SD card as mass storage using
* default SD Library
*/
#include "SD.h"
#include "Adafruit_TinyUSB.h"
const int chipSelect = 10;
Adafruit_USBD_MSC usb_msc;
Sd2Card card;
SdVolume volume;
// the setup function runs once when you press reset or power the board
void setup()
{
// Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively
usb_msc.setID("Adafruit", "SD Card", "1.0");
// Set read write callback
usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb);
// Still initialize MSC but tell usb stack that MSC is not ready to read/write
// If we don't initialize, board will be enumerated as CDC only
usb_msc.setUnitReady(false);
usb_msc.begin();
Serial.begin(115200);
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Adafruit TinyUSB Mass Storage SD Card example");
Serial.println("\nInitializing SD card...");
if ( !card.init(SPI_HALF_SPEED, chipSelect) )
{
Serial.println("initialization failed. Things to check:");
Serial.println("* is a card inserted?");
Serial.println("* is your wiring correct?");
Serial.println("* did you change the chipSelect pin to match your shield or module?");
while (1) delay(1);
}
// Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
if (!volume.init(card)) {
Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
while (1) delay(1);
}
uint32_t block_count = volume.blocksPerCluster()*volume.clusterCount();
Serial.print("Volume size (MB): ");
Serial.println((block_count/2) / 1024);
// Set disk size, SD block size is always 512
usb_msc.setCapacity(block_count, 512);
// MSC is ready for read/write
usb_msc.setUnitReady(true);
}
void loop()
{
// nothing to do
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and
// return number of copied bytes (must be multiple of block size)
int32_t msc_read_cb (uint32_t lba, void* buffer, uint32_t bufsize)
{
(void) bufsize;
return card.readBlock(lba, (uint8_t*) buffer) ? 512 : -1;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t msc_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize)
{
(void) bufsize;
return card.writeBlock(lba, buffer) ? 512 : -1;
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void msc_flush_cb (void)
{
// nothing to do
}

View File

@ -0,0 +1,169 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example expose SD card as mass storage using
* SdFat Library
*/
#include "SPI.h"
#include "SdFat.h"
#include "Adafruit_TinyUSB.h"
const int chipSelect = 10;
// File system on SD Card
SdFat sd;
SdFile root;
SdFile file;
// USB Mass Storage object
Adafruit_USBD_MSC usb_msc;
// Set to true when PC write to flash
bool fs_changed;
// the setup function runs once when you press reset or power the board
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
// Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively
usb_msc.setID("Adafruit", "SD Card", "1.0");
// Set read write callback
usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb);
// Still initialize MSC but tell usb stack that MSC is not ready to read/write
// If we don't initialize, board will be enumerated as CDC only
usb_msc.setUnitReady(false);
usb_msc.begin();
Serial.begin(115200);
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Adafruit TinyUSB Mass Storage SD Card example");
Serial.print("\nInitializing SD card ... ");
Serial.print("CS = "); Serial.println(chipSelect);
if ( !sd.begin(chipSelect, SD_SCK_MHZ(50)) )
{
Serial.println("initialization failed. Things to check:");
Serial.println("* is a card inserted?");
Serial.println("* is your wiring correct?");
Serial.println("* did you change the chipSelect pin to match your shield or module?");
while (1) delay(1);
}
// Size in blocks (512 bytes)
#if SD_FAT_VERSION >= 20000
uint32_t block_count = sd.card()->sectorCount();
#else
uint32_t block_count = sd.card()->cardSize();
#endif
Serial.print("Volume size (MB): ");
Serial.println((block_count/2) / 1024);
// Set disk size, SD block size is always 512
usb_msc.setCapacity(block_count, 512);
// MSC is ready for read/write
usb_msc.setUnitReady(true);
fs_changed = true; // to print contents initially
}
void loop()
{
if ( fs_changed )
{
root.open("/");
Serial.println("SD contents:");
// Open next file in root.
// Warning, openNext starts at the current directory position
// so a rewind of the directory may be required.
while ( file.openNext(&root, O_RDONLY) )
{
file.printFileSize(&Serial);
Serial.write(' ');
file.printName(&Serial);
if ( file.isDir() )
{
// Indicate a directory.
Serial.write('/');
}
Serial.println();
file.close();
}
root.close();
Serial.println();
fs_changed = false;
delay(1000); // refresh every 0.5 second
}
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and
// return number of copied bytes (must be multiple of block size)
int32_t msc_read_cb (uint32_t lba, void* buffer, uint32_t bufsize)
{
bool rc;
#if SD_FAT_VERSION >= 20000
rc = sd.card()->readSectors(lba, (uint8_t*) buffer, bufsize/512);
#else
rc = sd.card()->readBlocks(lba, (uint8_t*) buffer, bufsize/512);
#endif
return rc ? bufsize : -1;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t msc_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize)
{
bool rc;
digitalWrite(LED_BUILTIN, HIGH);
#if SD_FAT_VERSION >= 20000
rc = sd.card()->writeSectors(lba, buffer, bufsize/512);
#else
rc = sd.card()->writeBlocks(lba, buffer, bufsize/512);
#endif
return rc ? bufsize : -1;
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void msc_flush_cb (void)
{
#if SD_FAT_VERSION >= 20000
sd.card()->syncDevice();
#else
sd.card()->syncBlocks();
#endif
// clear file system's cache to force refresh
sd.cacheClear();
fs_changed = true;
digitalWrite(LED_BUILTIN, LOW);
}

View File

@ -0,0 +1,211 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2023 Ha Thach (tinyusb.org) for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Reference:
* - https://github.com/torvalds/linux/blob/master/drivers/i2c/busses/i2c-tiny-usb.c
* - https://github.com/harbaum/I2C-Tiny-USB
*
* Requirement:
* - Install i2c-tools with
* sudo apt install i2c-tools
*
* How to test example:
* - Compile and flash this sketch on your board with an i2c device, it should enumerated as
* ID 1c40:0534 EZPrototypes i2c-tiny-usb interface
*
* - Run "i2cdetect -l" to find our bus ID e.g
* i2c-8 i2c i2c-tiny-usb at bus 003 device 039 I2C adapter
*
* - Run "i2cdetect -y 8" to scan for on-board device (8 is the above bus ID)
* 0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77
- You can then interact with sensor using following commands:
i2cget i2cset i2cdump i2ctransfer or using any driver/tools that work on i2c device.
*/
#include "Adafruit_USBD_I2C.h"
Adafruit_USBD_I2C::Adafruit_USBD_I2C(TwoWire* wire) {
_wire = wire;
_buf = NULL;
_bufsize = 0; // not used to verify length yet
_state = I2C_STATUS_IDLE;
_functionality = 0x8eff0001; // check out _functionality_* defines
setStringDescriptor("I2C Interface");
}
uint16_t Adafruit_USBD_I2C::getInterfaceDescriptor(uint8_t itfnum, uint8_t* buf, uint16_t bufsize) {
uint8_t desc[] = { TUD_VENDOR_DESCRIPTOR(itfnum, 0, 0x00, 0x80, 64) };
uint16_t const len = sizeof(desc);
if (buf) {
if (bufsize < len) {
return 0;
}
memcpy(buf, desc, len);
}
return len;
}
bool Adafruit_USBD_I2C::begin(uint8_t* buffer, size_t bufsize) {
_buf = buffer;
_bufsize = (uint16_t) bufsize;
if (!_wire || !_buf || !_bufsize) return false;
// needed to identify as a device for i2c_tiny_usb (EZPrototypes VID/PID)
TinyUSBDevice.setID(0x1c40, 0x0534);
if (!TinyUSBDevice.addInterface(*this)) return false;
_wire->begin();
return true;
}
uint16_t Adafruit_USBD_I2C::i2c_read(uint8_t addr, uint8_t* buf, uint16_t len, bool stop_bit)
{
uint16_t const rd_count = (uint16_t) _wire->requestFrom(addr, len, stop_bit);
_state = (len && !rd_count) ? I2C_STATUS_NAK : I2C_STATUS_ACK;
// Serial.printf("I2C Read: addr = 0x%02X, len = %u, rd_count %u bytes, status = %u\r\n", addr, len, rd_count, i2c_state);
for(uint16_t i = 0; i <rd_count; i++)
{
buf[i] = (uint8_t) _wire->read();
}
return rd_count;
}
uint16_t Adafruit_USBD_I2C::i2c_write(uint8_t addr, uint8_t const* buf, uint16_t len, bool stop_bit)
{
_wire->beginTransmission(addr);
uint16_t wr_count = (uint16_t) _wire->write(buf, len);
uint8_t const sts = _wire->endTransmission(stop_bit);
_state = (sts == 0) ? I2C_STATUS_ACK : I2C_STATUS_NAK;
// Serial.printf("I2C Write: addr = 0x%02X, len = %u, wr_count = %u, status = %u\r\n", addr, len, wr_count, i2c_state);
return wr_count;
}
bool Adafruit_USBD_I2C::handleControlTransfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) {
uint8_t const cmd = request->bRequest;
if ( stage == CONTROL_STAGE_SETUP )
{
switch ( cmd )
{
case CMD_ECHO:
// echo
return tud_control_xfer(rhport, request, (void*) &request->wValue, sizeof(request->wValue));
case CMD_GET_FUNC:
// capabilities
return tud_control_xfer(rhport, request, (void*) &_functionality, sizeof(_functionality));
case CMD_SET_DELAY:
if ( request->wValue == 0 )
{
_wire->setClock(115200);
}
else
{
int baudrate = 1000000 / request->wValue;
if ( baudrate > 400000 ) baudrate = 400000;
_wire->setClock(baudrate);
}
return tud_control_status(rhport, request);
case CMD_GET_STATUS:
return tud_control_xfer(rhport, request, (void*) &_state, sizeof(_state));
case CMD_I2C_IO:
case CMD_I2C_IO | CMD_I2C_IO_BEGIN:
case CMD_I2C_IO | CMD_I2C_IO_END:
case CMD_I2C_IO | CMD_I2C_IO_BEGIN | CMD_I2C_IO_END:
{
uint8_t const addr = (uint8_t) request->wIndex;
// uint16_t const flags = request->wValue;
uint16_t const len = tu_min16(request->wLength, _bufsize);
bool const stop_bit = (cmd & CMD_I2C_IO_END) ? true : false;
if (request->bmRequestType_bit.direction == TUSB_DIR_OUT)
{
if (len == 0)
{
// zero write: do it here since there will be no data stage for len = 0
i2c_write(addr, _buf, len, stop_bit);
}
return tud_control_xfer(rhport, request, _buf, len);
}else
{
uint16_t const rd_count = i2c_read(addr, _buf, len, stop_bit);
return tud_control_xfer(rhport, request, rd_count ? _buf : NULL, rd_count);
}
}
break;
default: return true;
}
}
else if ( stage == CONTROL_STAGE_DATA )
{
switch ( cmd )
{
case CMD_I2C_IO:
case CMD_I2C_IO | CMD_I2C_IO_BEGIN:
case CMD_I2C_IO | CMD_I2C_IO_END:
case CMD_I2C_IO | CMD_I2C_IO_BEGIN | CMD_I2C_IO_END:
if (request->bmRequestType_bit.direction == TUSB_DIR_OUT)
{
uint8_t const addr = (uint8_t) request->wIndex;
// uint16_t const flags = request->wValue;
uint16_t const len = tu_min16(request->wLength, _bufsize);
bool const stop_bit = (cmd & CMD_I2C_IO_END) ? true : false;
i2c_write(addr, _buf, len, stop_bit);
}
return true;
default: return true;
}
}
else
{
// CONTROL_STAGE_STATUS
return true;
}
}

View File

@ -0,0 +1,107 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2023 Ha Thach (tinyusb.org) for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef ADAFRUIT_USBD_I2C_H_
#define ADAFRUIT_USBD_I2C_H_
#include "Adafruit_TinyUSB.h"
#include "Wire.h"
/* commands from USB, must e.g. match command ids in kernel driver */
#define CMD_ECHO 0
#define CMD_GET_FUNC 1
#define CMD_SET_DELAY 2
#define CMD_GET_STATUS 3
#define CMD_I2C_IO 4
#define CMD_I2C_IO_BEGIN (1<<0) // flag for I2C_IO
#define CMD_I2C_IO_END (1<<1) // flag for I2C_IO
/* linux kernel flags */
#define I2C_M_TEN 0x10 /* we have a ten bit chip address */
#define I2C_M_RD 0x01
#define I2C_M_NOSTART 0x4000
#define I2C_M_REV_DIR_ADDR 0x2000
#define I2C_M_IGNORE_NAK 0x1000
#define I2C_M_NO_RD_ACK 0x0800
/* To determine what functionality is present */
#define I2C_FUNC_I2C 0x00000001
#define I2C_FUNC_10BIT_ADDR 0x00000002 /* required for I2C_M_TEN */
#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* required for I2C_M_IGNORE_NAK etc. */
#define I2C_FUNC_SMBUS_PEC 0x00000008
#define I2C_FUNC_NOSTART 0x00000010 /* required for I2C_M_NOSTART */
#define I2C_FUNC_SLAVE 0x00000020
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 or later */
#define I2C_FUNC_SMBUS_QUICK 0x00010000
#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000
#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000
#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000
#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000
#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 /* required for I2C_M_RECV_LEN */
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */
#define I2C_FUNC_SMBUS_HOST_NOTIFY 0x10000000 /* SMBus 2.0 or later */
#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE)
#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA)
#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)
#define I2C_FUNC_SMBUS_EMUL (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | \
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_PEC)
/* if I2C_M_RECV_LEN is also supported */
#define I2C_FUNC_SMBUS_EMUL_ALL (I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL)
#define I2C_STATUS_IDLE 0
#define I2C_STATUS_ACK 1
#define I2C_STATUS_NAK 2
class Adafruit_USBD_I2C : public Adafruit_USBD_Interface {
public:
Adafruit_USBD_I2C(TwoWire* wire);
uint16_t getInterfaceDescriptor(uint8_t itfnum, uint8_t* buf, uint16_t bufsize);
bool begin(uint8_t* buffer, size_t bufsize);
bool handleControlTransfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request);
private:
TwoWire* _wire;
uint8_t _state;
uint32_t _functionality;
uint8_t* _buf;
uint16_t _bufsize;
uint16_t i2c_write(uint8_t addr, uint8_t const* buf, uint16_t len, bool stop_bit);
uint16_t i2c_read(uint8_t addr, uint8_t* buf, uint16_t len, bool stop_bit);
};
#endif

View File

@ -0,0 +1,77 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include <Wire.h>
#include "Adafruit_TinyUSB.h"
#include "Adafruit_USBD_I2C.h"
/* This sketch demonstrates how to use tinyusb vendor interface to implement
* i2c-tiny-usb adapter to use with Linux
*
* Reference:
* - https://github.com/torvalds/linux/blob/master/drivers/i2c/busses/i2c-tiny-usb.c
* - https://github.com/harbaum/I2C-Tiny-USB
*
* Requirement:
* - Install i2c-tools with
* sudo apt install i2c-tools
*
* How to test example:
* - Compile and flash this sketch on your board with an i2c device, it should enumerated as
* ID 1c40:0534 EZPrototypes i2c-tiny-usb interface
*
* - Run "i2cdetect -l" to find our bus ID e.g
* i2c-8 i2c i2c-tiny-usb at bus 003 device 039 I2C adapter
*
* - Run "i2cdetect -y 8" to scan for on-board device (8 is the above bus ID)
* 0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77
- You can then interact with sensor using following commands:
i2cget i2cset i2cdump i2ctransfer or using any driver/tools that work on i2c device.
Adafruit CircuitPython library for PC
- You can use run CircuitPython library with Extended Bus to read sensor data.
- 'i2c_usb.py' is provided a sample script
*/
static uint8_t i2c_buf[800];
#define MyWire Wire
//#define MyWire Wire1
Adafruit_USBD_I2C i2c_usb(&MyWire);
void setup() {
Serial.begin(115200);
// init i2c usb with buffer and size
i2c_usb.begin(i2c_buf, sizeof(i2c_buf));
}
void loop() {
}
// callback from tinyusb
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request)
{
return i2c_usb.handleControlTransfer(rhport, stage, request);
}

View File

@ -0,0 +1,18 @@
#! /usr/bin/env python3
#
# before running, install required libraries:
# pip3 install adafruit-extended-bus
# pip3 install adafruit-circuitpython-bme280
import time
import adafruit_extended_bus
from adafruit_bme280 import basic as adafruit_bme280
# Run "i2cdetect -l" to find your bus number
i2c_usb = adafruit_extended_bus.ExtendedI2C(8)
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c_usb)
while 1:
print("\nTemperature: %0.1f C" % bme280.temperature)
print("Humidity: %0.1f %%" % bme280.humidity)
print("Pressure: %0.1f hPa" % bme280.pressure)
time.sleep(1)

View File

@ -0,0 +1,121 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This sketch demonstrates WebUSB as web serial with browser with WebUSB support (e.g Chrome).
* After enumerated successfully, Browser will pop-up notification
* with URL to landing page, click on it to test
* - Click "Connect" and select device, When connected the neopixel LED will change color to Green.
* - When received color from browser in format '#RRGGBB', device will change the color of neopixel accordingly
*
* Note:
* - The WebUSB landing page notification is currently disabled in Chrome
* on Windows due to Chromium issue 656702 (https://crbug.com/656702). You have to
* go to landing page (below) to test
*
* - On Windows 7 and prior: You need to use Zadig tool to manually bind the
* WebUSB interface with the WinUSB driver for Chrome to access. From windows 8 and 10, this
* is done automatically by firmware.
*/
#include "Adafruit_TinyUSB.h"
#include <Adafruit_NeoPixel.h>
// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1
// use on-board neopixel PIN_NEOPIXEL if existed
#ifndef PIN_NEOPIXEL
#define PIN_NEOPIXEL 8
#endif
// How many NeoPixels are attached to the Arduino?
// use on-board defined NEOPIXEL_NUM if existed
#ifndef NEOPIXEL_NUM
#define NEOPIXEL_NUM 10
#endif
// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
// example for more information on possible values.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NEOPIXEL_NUM, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
// USB WebUSB object
Adafruit_USBD_WebUSB usb_web;
// Landing Page: scheme (0: http, 1: https), url
// Page source can be found at https://github.com/hathach/tinyusb-webusb-page/tree/main/webusb-rgb
WEBUSB_URL_DEF(landingPage, 1 /*https*/, "example.tinyusb.org/webusb-rgb/index.html");
// the setup function runs once when you press reset or power the board
void setup()
{
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
// Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040
TinyUSB_Device_Init(0);
#endif
//usb_web.setStringDescriptor("TinyUSB WebUSB");
usb_web.setLandingPage(&landingPage);
usb_web.setLineStateCallback(line_state_callback);
usb_web.begin();
Serial.begin(115200);
// This initializes the NeoPixel with RED
#ifdef NEOPIXEL_POWER
pinMode(NEOPIXEL_POWER, OUTPUT);
digitalWrite(NEOPIXEL_POWER, NEOPIXEL_POWER_ON);
#endif
pixels.begin();
pixels.setBrightness(50);
pixels.fill(0xff0000);
pixels.show();
// wait until device mounted
while( !TinyUSBDevice.mounted() ) delay(1);
Serial.println("TinyUSB WebUSB RGB example");
}
// convert a hex character to number
uint8_t char2num(char c)
{
if (c >= 'a') return c - 'a' + 10;
if (c >= 'A') return c - 'A' + 10;
return c - '0';
}
void loop()
{
// Landing Page 7 characters as hex color '#RRGGBB'
if (usb_web.available() < 7) return;
uint8_t input[7];
usb_web.readBytes(input, 7);
// Print to serial for debugging
Serial.write(input, 7);
Serial.println();
uint8_t red = 16*char2num(input[1]) + char2num(input[2]);
uint8_t green = 16*char2num(input[3]) + char2num(input[4]);
uint8_t blue = 16*char2num(input[5]) + char2num(input[6]);
uint32_t color = (red << 16) | (green << 8) | blue;
pixels.fill(color);
pixels.show();
}
void line_state_callback(bool connected)
{
// connected = green, disconnected = red
pixels.fill(connected ? 0x00ff00 : 0xff0000);
pixels.show();
}

View File

@ -0,0 +1,112 @@
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This sketch demonstrates WebUSB as web serial with browser with WebUSB support (e.g Chrome).
* After enumerated successfully, Browser will pop-up notification
* with URL to landing page, click on it to test
* - Click "Connect" and select device, When connected the on-board LED will litted up.
* - Any charters received from either webusb/Serial will be echo back to webusb and Serial
*
* Note:
* - The WebUSB landing page notification is currently disabled in Chrome
* on Windows due to Chromium issue 656702 (https://crbug.com/656702). You have to
* go to landing page (below) to test
*
* - On Windows 7 and prior: You need to use Zadig tool to manually bind the
* WebUSB interface with the WinUSB driver for Chrome to access. From windows 8 and 10, this
* is done automatically by firmware.
*/
#include "Adafruit_TinyUSB.h"
// USB WebUSB object
Adafruit_USBD_WebUSB usb_web;
// Landing Page: scheme (0: http, 1: https), url
// Page source can be found at https://github.com/hathach/tinyusb-webusb-page/tree/main/webusb-serial
WEBUSB_URL_DEF(landingPage, 1 /*https*/, "example.tinyusb.org/webusb-serial/index.html");
int led_pin = LED_BUILTIN;
// the setup function runs once when you press reset or power the board
void setup()
{
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
// Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040
TinyUSB_Device_Init(0);
#endif
pinMode(led_pin, OUTPUT);
digitalWrite(led_pin, LOW);
usb_web.setLandingPage(&landingPage);
usb_web.setLineStateCallback(line_state_callback);
//usb_web.setStringDescriptor("TinyUSB WebUSB");
usb_web.begin();
Serial.begin(115200);
// wait until device mounted
while( !TinyUSBDevice.mounted() ) delay(1);
Serial.println("TinyUSB WebUSB Serial example");
}
// function to echo to both Serial and WebUSB
void echo_all(uint8_t buf[], uint32_t count)
{
if (usb_web.connected())
{
usb_web.write(buf, count);
usb_web.flush();
}
if ( Serial )
{
for(uint32_t i=0; i<count; i++)
{
Serial.write(buf[i]);
if ( buf[i] == '\r' ) Serial.write('\n');
}
Serial.flush();
}
}
void loop()
{
uint8_t buf[64];
uint32_t count;
// From Serial to both Serial & webUSB
if (Serial.available())
{
count = Serial.read(buf, 64);
echo_all(buf, count);
}
// from WebUSB to both Serial & webUSB
if (usb_web.available())
{
count = usb_web.read(buf, 64);
echo_all(buf, count);
}
}
void line_state_callback(bool connected)
{
digitalWrite(led_pin, connected);
if ( connected )
{
usb_web.println("WebUSB interface connected !!");
usb_web.flush();
}
}

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