Merge branch 'main' into anecdata-patch-1
This commit is contained in:
commit
495f80c4df
594 changed files with 41903 additions and 715 deletions
127
.github/workflows/arduino_cron.yml
vendored
Normal file
127
.github/workflows/arduino_cron.yml
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
name: Arduino Library CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-if-needed:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
answer: ${{ steps.is-needed.outputs.answer }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
- name: Check if run by adabot
|
||||||
|
id: check-cron
|
||||||
|
run: |
|
||||||
|
iscron=false
|
||||||
|
[[ "${{ github.event_name }}" == "push" && "${{ github.actor }}" == "adafruit-adabot" ]] && iscron=true
|
||||||
|
echo "status=$iscron" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: Check if dispatched
|
||||||
|
id: check-dispatch
|
||||||
|
run: |
|
||||||
|
isdispatch=false
|
||||||
|
[[ "${{ github.event_name }}" == "workflow_dispatch" ]] && isdispatch=true
|
||||||
|
echo "status=$isdispatch" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: Check for Arduino file updates
|
||||||
|
id: check-updated
|
||||||
|
if: ${{ steps.check-cron.outputs.status }} == false && ${{ steps.check-dispatch.outputs.status }} == false
|
||||||
|
run: |
|
||||||
|
changedfiles=$(git diff --name-only -r HEAD^1 HEAD)
|
||||||
|
ischanged=false
|
||||||
|
for changedfile in ${changedfiles[*]}; do
|
||||||
|
echo $changedfile
|
||||||
|
if [[ $changedfile == *.c ]] ||
|
||||||
|
[[ $changedfile == *.cpp ]] ||
|
||||||
|
[[ $changedfile == *.h ]] ||
|
||||||
|
[[ $changedfile == *.hpp ]] ||
|
||||||
|
[[ $changedfile == *.ino ]]; then
|
||||||
|
ischanged=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "status=$ischanged" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: Output Arduino needed
|
||||||
|
id: is-needed
|
||||||
|
run: |
|
||||||
|
isneeded=false
|
||||||
|
if [[ ${{ steps.check-cron.outputs.status }} == true ]] ||
|
||||||
|
[[ ${{ steps.check-dispatch.outputs.status }} == true ]] ||
|
||||||
|
[[ ${{ steps.check-updated.outputs.status }} == true ]]; then
|
||||||
|
isneeded=true
|
||||||
|
fi
|
||||||
|
echo "answer=$isneeded" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
arduino:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
arduino-platform: ["cpb", "cpc", "cpx_ada", "esp32", "esp8266", "feather32u4", "feather_m0_express", "feather_m4_express", "feather_rp2040", "flora", "funhouse", "gemma", "gemma_m0", "hallowing_m0", "hallowing_m4_tinyusb", "magtag", "metro_m0", "metro_m0_tinyusb", "metro_m4", "metro_m4_tinyusb", "monster_m4sk", "monster_m4sk_tinyusb", "neokeytrinkey_m0", "neotrellis_m4", "nrf52832", "nrf52840", "protrinket_5v", "proxlighttrinkey_m0", "pybadge", "pygamer", "pyportal", "qt2040_trinkey", "qtpy_m0", "qtpy_esp32s2", "rotarytrinkey_m0", "slidetrinkey_m0", "trinket_m0", "uno", "trinket_5v", "ledglasses_nrf52840" ]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: needs.check-if-needed.outputs.answer == 'true'
|
||||||
|
needs: check-if-needed
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: "3.x"
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: adafruit/ci-arduino
|
||||||
|
path: ci
|
||||||
|
|
||||||
|
- name: pre-install
|
||||||
|
run: bash ci/actions_install.sh
|
||||||
|
|
||||||
|
# manually install some libraries
|
||||||
|
- name: extra libraries
|
||||||
|
run: |
|
||||||
|
git clone --quiet https://github.com/adafruit/Cryptosuite.git /home/runner/Arduino/libraries/Cryptosuite
|
||||||
|
git clone --quiet https://github.com/adafruit/WiFiNINA.git /home/runner/Arduino/libraries/WiFiNINA
|
||||||
|
git clone --quiet https://github.com/adafruit/Adafruit_LSM303.git /home/runner/Arduino/libraries/Adafruit_LSM303
|
||||||
|
git clone --quiet https://github.com/moderndevice/CapSense.git /home/runner/Arduino/libraries/CapSense
|
||||||
|
git clone --quiet https://github.com/PaintYourDragon/ffft.git /home/runner/Arduino/libraries/ffft
|
||||||
|
git clone --quiet https://github.com/adafruit/RadioHead.git /home/runner/Arduino/libraries/RadioHead
|
||||||
|
git clone --quiet https://github.com/me-no-dev/ESPAsyncTCP /home/runner/Arduino/libraries/ESPAsyncTCP
|
||||||
|
git clone --quiet https://github.com/adafruit/Talkie /home/runner/Arduino/libraries/Talkie
|
||||||
|
git clone --quiet https://github.com/Infineon/arduino-optiga-trust-m /home/runner/Arduino/libraries/arduinoOptigaTrustM
|
||||||
|
git clone --quiet https://github.com/adafruit/HID /home/runner/Arduino/libraries/HID_Project
|
||||||
|
rm -rf /home/runner/Arduino/libraries/ArduinoHttpClient
|
||||||
|
git clone --quiet https://github.com/arduino-libraries/ArduinoHttpClient.git /home/runner/Arduino/libraries/ArduinoHttpClient
|
||||||
|
|
||||||
|
- name: test platforms
|
||||||
|
run: python3 ci/build_platform.py ${{ matrix.arduino-platform }}
|
||||||
|
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ${{ github.event.repository.name }}.${{ github.sha }}
|
||||||
|
path: |
|
||||||
|
build/*.hex
|
||||||
|
build/*.bin
|
||||||
|
build/*.uf2
|
||||||
|
|
||||||
|
- name: Zip release files
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
run: |
|
||||||
|
if [ -d build ]; then
|
||||||
|
(
|
||||||
|
echo "Built from Adafruit Learning System Guides `git describe --tags` for ${{ matrix.arduino-platform }}"
|
||||||
|
echo "Source code: https://github.com/adafruit/"
|
||||||
|
echo "Adafruit Learning System: https://learn.adafruit.com/"
|
||||||
|
) > build/README.txt
|
||||||
|
cd build && zip -9 -o ${{ matrix.arduino-platform }}.zip *.hex *.bin *.uf2 *.txt
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: build/${{ matrix.arduino-platform }}.zip
|
||||||
|
fail_on_unmatched_files: false
|
||||||
|
body: "Select the zip file corresponding to your board from the list below."
|
||||||
71
.github/workflows/githubci.yml
vendored
71
.github/workflows/githubci.yml
vendored
|
|
@ -1,4 +1,4 @@
|
||||||
name: Arduino Library CI
|
name: SPDX and Pylint
|
||||||
|
|
||||||
on: [pull_request, push, repository_dispatch]
|
on: [pull_request, push, repository_dispatch]
|
||||||
|
|
||||||
|
|
@ -15,75 +15,6 @@ jobs:
|
||||||
- name: check SPDX licensing
|
- name: check SPDX licensing
|
||||||
run: python ./SPDX.py
|
run: python ./SPDX.py
|
||||||
|
|
||||||
arduino:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
arduino-platform: ["cpb", "cpc", "cpx_ada", "esp32", "esp8266", "feather32u4", "feather_m0_express", "feather_m4_express", "feather_rp2040", "flora", "funhouse", "gemma", "gemma_m0", "hallowing_m0", "hallowing_m4_tinyusb", "magtag", "metro_m0", "metro_m0_tinyusb", "metro_m4", "metro_m4_tinyusb", "monster_m4sk", "monster_m4sk_tinyusb", "neokeytrinkey_m0", "neotrellis_m4", "nrf52832", "nrf52840", "protrinket_5v", "proxlighttrinkey_m0", "pybadge", "pygamer", "pyportal", "qt2040_trinkey", "qtpy_m0", "qtpy_esp32s2", "rotarytrinkey_m0", "slidetrinkey_m0", "trinket_m0", "uno", "trinket_5v", "ledglasses_nrf52840" ]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: "3.x"
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
repository: adafruit/ci-arduino
|
|
||||||
path: ci
|
|
||||||
|
|
||||||
- name: pre-install
|
|
||||||
run: bash ci/actions_install.sh
|
|
||||||
|
|
||||||
# manually install some libraries
|
|
||||||
- name: extra libraries
|
|
||||||
run: |
|
|
||||||
git clone --quiet https://github.com/adafruit/Cryptosuite.git /home/runner/Arduino/libraries/Cryptosuite
|
|
||||||
git clone --quiet https://github.com/adafruit/WiFiNINA.git /home/runner/Arduino/libraries/WiFiNINA
|
|
||||||
git clone --quiet https://github.com/adafruit/Adafruit_LSM303.git /home/runner/Arduino/libraries/Adafruit_LSM303
|
|
||||||
git clone --quiet https://github.com/moderndevice/CapSense.git /home/runner/Arduino/libraries/CapSense
|
|
||||||
git clone --quiet https://github.com/PaintYourDragon/ffft.git /home/runner/Arduino/libraries/ffft
|
|
||||||
git clone --quiet https://github.com/adafruit/RadioHead.git /home/runner/Arduino/libraries/RadioHead
|
|
||||||
git clone --quiet https://github.com/me-no-dev/ESPAsyncTCP /home/runner/Arduino/libraries/ESPAsyncTCP
|
|
||||||
git clone --quiet https://github.com/adafruit/Talkie /home/runner/Arduino/libraries/Talkie
|
|
||||||
git clone --quiet https://github.com/Infineon/arduino-optiga-trust-m /home/runner/Arduino/libraries/arduinoOptigaTrustM
|
|
||||||
git clone --quiet https://github.com/adafruit/HID /home/runner/Arduino/libraries/HID_Project
|
|
||||||
rm -rf /home/runner/Arduino/libraries/ArduinoHttpClient
|
|
||||||
git clone --quiet https://github.com/arduino-libraries/ArduinoHttpClient.git /home/runner/Arduino/libraries/ArduinoHttpClient
|
|
||||||
|
|
||||||
- name: test platforms
|
|
||||||
run: python3 ci/build_platform.py ${{ matrix.arduino-platform }}
|
|
||||||
|
|
||||||
|
|
||||||
- name: Upload build artifacts
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: ${{ github.event.repository.name }}.${{ github.sha }}
|
|
||||||
path: |
|
|
||||||
build/*.hex
|
|
||||||
build/*.bin
|
|
||||||
build/*.uf2
|
|
||||||
|
|
||||||
- name: Zip release files
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
run: |
|
|
||||||
if [ -d build ]; then
|
|
||||||
(
|
|
||||||
echo "Built from Adafruit Learning System Guides `git describe --tags` for ${{ matrix.arduino-platform }}"
|
|
||||||
echo "Source code: https://github.com/adafruit/"
|
|
||||||
echo "Adafruit Learning System: https://learn.adafruit.com/"
|
|
||||||
) > build/README.txt
|
|
||||||
cd build && zip -9 -o ${{ matrix.arduino-platform }}.zip *.hex *.bin *.uf2 *.txt
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Create release
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
with:
|
|
||||||
files: build/${{ matrix.arduino-platform }}.zip
|
|
||||||
fail_on_unmatched_files: false
|
|
||||||
body: "Select the zip file corresponding to your board from the list below."
|
|
||||||
|
|
||||||
pylint:
|
pylint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
622
16bit_hello_picowbell_dvi/16bit_hello_picowbell_dvi.ino
Normal file
622
16bit_hello_picowbell_dvi/16bit_hello_picowbell_dvi.ino
Normal file
|
|
@ -0,0 +1,622 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Phil B. for Adafruit Industries
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Basic full-color PicoDVI test. Provides a 16-bit color video framebuffer to
|
||||||
|
// which Adafruit_GFX calls can be made. It's based on the EYESPI_Test.ino sketch.
|
||||||
|
|
||||||
|
#include <PicoDVI.h> // Core display & graphics library
|
||||||
|
#include <Fonts/FreeSansBold18pt7b.h> // A custom font
|
||||||
|
|
||||||
|
// Here's how a 320x240 16-bit color framebuffer is declared. Double-buffering
|
||||||
|
// is not an option in 16-bit color mode, just not enough RAM; all drawing
|
||||||
|
// operations are shown as they occur. Second argument is a hardware
|
||||||
|
// configuration -- examples are written for Adafruit Feather RP2040 DVI, but
|
||||||
|
// that's easily switched out for boards like the Pimoroni Pico DV (use
|
||||||
|
// 'pimoroni_demo_hdmi_cfg') or Pico DVI Sock ('pico_sock_cfg').
|
||||||
|
DVIGFX16 display(DVI_RES_320x240p60, adafruit_dvibell_cfg);
|
||||||
|
|
||||||
|
// A 400x240 mode is possible but pushes overclocking even higher than
|
||||||
|
// 320x240 mode. SOME BOARDS MIGHT SIMPLY NOT BE COMPATIBLE WITH THIS.
|
||||||
|
// May require selecting QSPI div4 clock (Tools menu) to slow down flash
|
||||||
|
// accesses, may require further over-volting the CPU to 1.25 or 1.3 V.
|
||||||
|
//DVIGFX16 display(DVI_RES_400x240p60, adafruit_feather_dvi_cfg);
|
||||||
|
|
||||||
|
void setup() { // Runs once on startup
|
||||||
|
if (!display.begin()) { // Blink LED if insufficient RAM
|
||||||
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
|
for (;;) digitalWrite(LED_BUILTIN, (millis() / 500) & 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PAUSE 2000 // Delay (milliseconds) between examples
|
||||||
|
uint8_t rotate = 0; // Current screen orientation (0-3)
|
||||||
|
#define CORNER_RADIUS 0
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Each of these functions demonstrates a different Adafruit_GFX concept:
|
||||||
|
show_shapes();
|
||||||
|
show_charts();
|
||||||
|
show_basic_text();
|
||||||
|
show_char_map();
|
||||||
|
show_custom_text();
|
||||||
|
show_bitmap();
|
||||||
|
show_canvas();
|
||||||
|
|
||||||
|
if (++rotate > 3) rotate = 0; // Cycle through screen rotations 0-3
|
||||||
|
display.setRotation(rotate); // Takes effect on next drawing command
|
||||||
|
}
|
||||||
|
|
||||||
|
// BASIC SHAPES EXAMPLE ----------------------------------------------------
|
||||||
|
|
||||||
|
void show_shapes() {
|
||||||
|
// Draw outlined and filled shapes. This demonstrates:
|
||||||
|
// - Enclosed shapes supported by GFX (points & lines are shown later).
|
||||||
|
// - Adapting to different-sized displays, and to rounded corners.
|
||||||
|
|
||||||
|
const int16_t cx = display.width() / 2; // Center of screen =
|
||||||
|
const int16_t cy = display.height() / 2; // half of width, height
|
||||||
|
int16_t minor = min(cx, cy); // Lesser of half width or height
|
||||||
|
// Shapes will be drawn in a square region centered on the screen. But one
|
||||||
|
// particular screen -- rounded 240x280 ST7789 -- has VERY rounded corners
|
||||||
|
// that would clip a couple of shapes if drawn full size. If using that
|
||||||
|
// screen type, reduce area by a few pixels to avoid drawing in corners.
|
||||||
|
if (CORNER_RADIUS > 40) minor -= 4;
|
||||||
|
const uint8_t pad = 5; // Space between shapes is 2X this
|
||||||
|
const int16_t size = minor - pad; // Shapes are this width & height
|
||||||
|
const int16_t half = size / 2; // 1/2 of shape size
|
||||||
|
|
||||||
|
display.fillScreen(0); // Start by clearing the screen; color 0 = black
|
||||||
|
|
||||||
|
// Draw outline version of basic shapes: rectangle, triangle, circle and
|
||||||
|
// rounded rectangle in different colors. Rather than hardcoded numbers
|
||||||
|
// for position and size, some arithmetic helps adapt to screen dimensions.
|
||||||
|
display.drawRect(cx - minor, cy - minor, size, size, 0xF800);
|
||||||
|
display.drawTriangle(cx + pad, cy - pad, cx + pad + half, cy - minor,
|
||||||
|
cx + minor - 1, cy - pad, 0x07E0);
|
||||||
|
display.drawCircle(cx - pad - half, cy + pad + half, half, 0x001F);
|
||||||
|
display.drawRoundRect(cx + pad, cy + pad, size, size, size / 5, 0xFFE0);
|
||||||
|
delay(PAUSE);
|
||||||
|
|
||||||
|
// Draw same shapes, same positions, but filled this time.
|
||||||
|
display.fillRect(cx - minor, cy - minor, size, size, 0xF800);
|
||||||
|
display.fillTriangle(cx + pad, cy - pad, cx + pad + half, cy - minor,
|
||||||
|
cx + minor - 1, cy - pad, 0x07E0);
|
||||||
|
display.fillCircle(cx - pad - half, cy + pad + half, half, 0x001F);
|
||||||
|
display.fillRoundRect(cx + pad, cy + pad, size, size, size / 5, 0xFFE0);
|
||||||
|
delay(PAUSE);
|
||||||
|
} // END SHAPE EXAMPLE
|
||||||
|
|
||||||
|
// CHART EXAMPLES ----------------------------------------------------------
|
||||||
|
|
||||||
|
void show_charts() {
|
||||||
|
// Draw some graphs and charts. GFX library doesn't handle these as native
|
||||||
|
// object types, but it only takes a little code to build them from simple
|
||||||
|
// shapes. This demonstrates:
|
||||||
|
// - Drawing points and horizontal, vertical and arbitrary lines.
|
||||||
|
// - Adapting to different-sized displays.
|
||||||
|
// - Graphics being clipped off edge.
|
||||||
|
// - Use of negative values to draw shapes "backward" from an anchor point.
|
||||||
|
// - C technique for finding array size at runtime (vs hardcoding).
|
||||||
|
|
||||||
|
display.fillScreen(0); // Clear screen
|
||||||
|
|
||||||
|
const int16_t cx = display.width() / 2; // Center of screen =
|
||||||
|
const int16_t cy = display.height() / 2; // half of width, height
|
||||||
|
const int16_t minor = min(cx, cy); // Lesser of half width or height
|
||||||
|
const int16_t major = max(cx, cy); // Greater of half width or height
|
||||||
|
|
||||||
|
// Let's start with a relatively simple sine wave graph with axes.
|
||||||
|
// Draw graph axes centered on screen. drawFastHLine() and drawFastVLine()
|
||||||
|
// need fewer arguments than normal 2-point line drawing shown later.
|
||||||
|
display.drawFastHLine(0, cy, display.width(), 0x0210); // Dark blue
|
||||||
|
display.drawFastVLine(cx, 0, display.height(), 0x0210);
|
||||||
|
|
||||||
|
// Then draw some tick marks along the axes. To keep this code simple,
|
||||||
|
// these aren't to any particular scale, but a real program may want that.
|
||||||
|
// The loop here draws them from the center outward and pays no mind
|
||||||
|
// whether the screen is rectangular; any ticks that go off-screen will
|
||||||
|
// be clipped by the library.
|
||||||
|
for (uint8_t i=1; i<=10; i++) {
|
||||||
|
// The Arduino map() function scales an input value (e.g. "i") from an
|
||||||
|
// input range (0-10 here) to an output range (0 to major-1 here).
|
||||||
|
// Very handy for making graphics adjust to different screens!
|
||||||
|
int16_t n = map(i, 0, 10, 0, major - 1); // Tick offset relative to center point
|
||||||
|
display.drawFastVLine(cx - n, cy - 5, 11, 0x210);
|
||||||
|
display.drawFastVLine(cx + n, cy - 5, 11, 0x210);
|
||||||
|
display.drawFastHLine(cx - 5, cy - n, 11, 0x210);
|
||||||
|
display.drawFastHLine(cx - 5, cy + n, 11, 0x210);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then draw sine wave over this using GFX drawPixel() function.
|
||||||
|
for (int16_t x=0; x<display.width(); x++) { // Each column of screen...
|
||||||
|
// Note the inverted Y axis here (cy-value rather than cy+value)
|
||||||
|
// because GFX, like most graphics libraries, has +Y heading down,
|
||||||
|
// vs. classic Cartesian coords which have +Y heading up.
|
||||||
|
int16_t y = cy - (int16_t)(sin((x - cx) * 0.05) * (float)minor * 0.5);
|
||||||
|
display.drawPixel(x, y, 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(PAUSE);
|
||||||
|
|
||||||
|
// Next, let's draw some charts...
|
||||||
|
// NOTE: some other examples in this code take extra steps to avoid placing
|
||||||
|
// anything off in the rounded corners of certain displays. The charts do
|
||||||
|
// not. It's *possible* but would introduce a lot of complexity into code
|
||||||
|
// that's trying to show the basics. We'll leave the clipped charts here as
|
||||||
|
// a teachable moment: not all content suits all displays.
|
||||||
|
|
||||||
|
// A list of data to plot. These are Y values only; X assumed equidistant.
|
||||||
|
const uint8_t data[] = { 31, 42, 36, 58, 67, 88 }; // Percentages, 0-100
|
||||||
|
const uint8_t num_points = sizeof data / sizeof data[0]; // Length of data[] list
|
||||||
|
|
||||||
|
display.fillScreen(0); // Clear screen
|
||||||
|
display.setFont(); // Use default (built-in) font
|
||||||
|
display.setTextSize(2); // and 2X size for chart label
|
||||||
|
|
||||||
|
// Chart label is centered manually; 144 is the width in pixels of
|
||||||
|
// "Widget Sales" at 2X scale (12 chars * 6 px * 2 = 144). A later example
|
||||||
|
// shows automated centering based on string.
|
||||||
|
display.setCursor((display.width() - 144) / 2, 0);
|
||||||
|
display.print(F("Widget Sales")); // F("string") is in program memory, not RAM
|
||||||
|
// The chart-drawing code is then written to skip the top 20 rows where
|
||||||
|
// this label is located.
|
||||||
|
|
||||||
|
// First, a line chart, connecting the values point-to-point:
|
||||||
|
|
||||||
|
// Draw a grid of lines to provide scale & an interesting background.
|
||||||
|
for (uint8_t i=0; i<11; i++) {
|
||||||
|
int16_t x = map(i, 0, 10, 0, display.width() - 1); // Scale grid X to screen
|
||||||
|
display.drawFastVLine(x, 20, display.height(), 0x001F);
|
||||||
|
int16_t y = map(i, 0, 10, 20, display.height() - 1); // Scale grid Y to screen
|
||||||
|
display.drawFastHLine(0, y, display.width(), 0x001F);
|
||||||
|
}
|
||||||
|
// And then draw lines connecting data points. Load up the first point...
|
||||||
|
int16_t prev_x = 0;
|
||||||
|
int16_t prev_y = map(data[0], 0, 100, display.height() - 1, 20);
|
||||||
|
// Then connect lines to each subsequent point...
|
||||||
|
for (uint8_t i=1; i<num_points; i++) {
|
||||||
|
int16_t new_x = map(i, 0, num_points - 1, 0, display.width() - 1);
|
||||||
|
int16_t new_y = map(data[i], 0, 100, display.height() - 1, 20);
|
||||||
|
display.drawLine(prev_x, prev_y, new_x, new_y, 0x07FF);
|
||||||
|
prev_x = new_x;
|
||||||
|
prev_y = new_y;
|
||||||
|
}
|
||||||
|
// For visual interest, let's add a circle around each data point. This is
|
||||||
|
// done in a second pass so the circles are always drawn "on top" of lines.
|
||||||
|
for (uint8_t i=0; i<num_points; i++) {
|
||||||
|
int16_t x = map(i, 0, num_points - 1, 0, display.width() - 1);
|
||||||
|
int16_t y = map(data[i], 0, 100, display.height() - 1, 20);
|
||||||
|
display.drawCircle(x, y, 5, 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(PAUSE);
|
||||||
|
|
||||||
|
// Then a bar chart of the same data...
|
||||||
|
|
||||||
|
// Erase the old chart but keep the label at top.
|
||||||
|
display.fillRect(0, 20, display.width(), display.height() - 20, 0);
|
||||||
|
|
||||||
|
// Just draw the Y axis lines; bar chart doesn't really need X lines.
|
||||||
|
for (uint8_t i=0; i<11; i++) {
|
||||||
|
int16_t y = map(i, 0, 10, 20, display.height() - 1);
|
||||||
|
display.drawFastHLine(0, y, display.width(), 0x001F);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bar_width = display.width() / num_points - 4; // 2px pad to either side
|
||||||
|
for (uint8_t i=0; i<num_points; i++) {
|
||||||
|
int16_t x = map(i, 0, num_points, 0, display.width()) + 2; // Left edge of bar
|
||||||
|
int16_t height = map(data[i], 0, 100, 0, display.height() - 20);
|
||||||
|
// Some GFX functions (rects, H/V lines and similar) can accept negative
|
||||||
|
// width/height values. What this does is anchor the shape at the right or
|
||||||
|
// bottom coordinate (rather than the usual left/top) and draw back from
|
||||||
|
// there, hence the -height here (bar is anchored at bottom of screen):
|
||||||
|
display.fillRect(x, display.height() - 1, bar_width, -height, 0xFFE0);
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(PAUSE);
|
||||||
|
|
||||||
|
} // END CHART EXAMPLES
|
||||||
|
|
||||||
|
// TEXT ALIGN FUNCTIONS ----------------------------------------------------
|
||||||
|
|
||||||
|
// Adafruit_GFX only handles left-aligned text. This is normal and by design;
|
||||||
|
// it's a rare need that would further strain AVR by incurring a ton of extra
|
||||||
|
// code to properly handle, and some details would confuse. If needed, these
|
||||||
|
// functions give a fair approximation, with the "gotchas" that multi-line
|
||||||
|
// input won't work, and this operates only as a println(), not print()
|
||||||
|
// (though, unlike println(), cursor X does not reset to column 0, instead
|
||||||
|
// returning to initial column and downward by font's line spacing). If you
|
||||||
|
// can work with those constraints, it's a modest amount of code to copy
|
||||||
|
// into a project. Or, if your project only needs one or two aligned strings,
|
||||||
|
// simply use getTextBounds() for a bounding box and work from there.
|
||||||
|
// DO NOT ATTEMPT TO MAKE THIS A GFX-NATIVE FEATURE, EVERYTHING WILL BREAK.
|
||||||
|
|
||||||
|
typedef enum { // Alignment options passed to functions below
|
||||||
|
GFX_ALIGN_LEFT,
|
||||||
|
GFX_ALIGN_CENTER,
|
||||||
|
GFX_ALIGN_RIGHT
|
||||||
|
} GFXalign;
|
||||||
|
|
||||||
|
// Draw text aligned relative to current cursor position. Arguments:
|
||||||
|
// gfx : An Adafruit_GFX-derived type (e.g. display or canvas object).
|
||||||
|
// str : String to print (as a char *).
|
||||||
|
// align : One of the GFXalign values declared above.
|
||||||
|
// GFX_ALIGN_LEFT is normal left-aligned println() behavior.
|
||||||
|
// GFX_ALIGN_CENTER prints centered on cursor pos.
|
||||||
|
// GFX_ALIGN_RIGHT prints right-aligned to cursor pos.
|
||||||
|
// Cursor advances down one line a la println(). Column is unchanged.
|
||||||
|
void print_aligned(Adafruit_GFX &gfx, const char *str,
|
||||||
|
GFXalign align = GFX_ALIGN_LEFT) {
|
||||||
|
uint16_t w, h;
|
||||||
|
int16_t x, y, cursor_x, cursor_x_save;
|
||||||
|
cursor_x = cursor_x_save = gfx.getCursorX();
|
||||||
|
gfx.getTextBounds(str, 0, gfx.getCursorY(), &x, &y, &w, &h);
|
||||||
|
if (align == GFX_ALIGN_RIGHT) cursor_x -= w;
|
||||||
|
else if (align == GFX_ALIGN_CENTER) cursor_x -= w / 2;
|
||||||
|
//gfx.drawRect(cursor_x, y, w, h, 0xF800); // Debug rect
|
||||||
|
gfx.setCursor(cursor_x - x, gfx.getCursorY()); // Center/right align
|
||||||
|
gfx.println(str);
|
||||||
|
gfx.setCursor(cursor_x_save, gfx.getCursorY()); // Restore cursor X
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equivalent function for strings in flash memory (e.g. F("Foo")). Body
|
||||||
|
// appears identical to above function, but with C++ overloading it it works
|
||||||
|
// from flash instead of RAM. Any changes should be made in both places.
|
||||||
|
void print_aligned(Adafruit_GFX &gfx, const __FlashStringHelper *str,
|
||||||
|
GFXalign align = GFX_ALIGN_LEFT) {
|
||||||
|
uint16_t w, h;
|
||||||
|
int16_t x, y, cursor_x, cursor_x_save;
|
||||||
|
cursor_x = cursor_x_save = gfx.getCursorX();
|
||||||
|
gfx.getTextBounds(str, 0, gfx.getCursorY(), &x, &y, &w, &h);
|
||||||
|
if (align == GFX_ALIGN_RIGHT) cursor_x -= w;
|
||||||
|
else if (align == GFX_ALIGN_CENTER) cursor_x -= w / 2;
|
||||||
|
//gfx.drawRect(cursor_x, y, w, h, 0xF800); // Debug rect
|
||||||
|
gfx.setCursor(cursor_x - x, gfx.getCursorY()); // Center/right align
|
||||||
|
gfx.println(str);
|
||||||
|
gfx.setCursor(cursor_x_save, gfx.getCursorY()); // Restore cursor X
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equivalent function for Arduino Strings; converts to C string (char *)
|
||||||
|
// and calls corresponding print_aligned() implementation.
|
||||||
|
void print_aligned(Adafruit_GFX &gfx, const String &str,
|
||||||
|
GFXalign align = GFX_ALIGN_LEFT) {
|
||||||
|
print_aligned(gfx, const_cast<char *>(str.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEXT EXAMPLES -----------------------------------------------------------
|
||||||
|
|
||||||
|
// This section demonstrates:
|
||||||
|
// - Using the default 5x7 built-in font, including scaling in each axis.
|
||||||
|
// - How to access all characters of this font, including symbols.
|
||||||
|
// - Using a custom font, including alignment techniques that aren't a normal
|
||||||
|
// part of the GFX library (uses functions above).
|
||||||
|
|
||||||
|
void show_basic_text() {
|
||||||
|
// Show text scaling with built-in font.
|
||||||
|
display.fillScreen(0);
|
||||||
|
display.setFont(); // Use default font
|
||||||
|
display.setCursor(0, CORNER_RADIUS); // Initial cursor position
|
||||||
|
display.setTextSize(1); // Default size
|
||||||
|
display.println(F("Standard built-in font"));
|
||||||
|
display.setTextSize(2);
|
||||||
|
display.println(F("BIG TEXT"));
|
||||||
|
display.setTextSize(3);
|
||||||
|
// "BIGGER TEXT" won't fit on narrow screens, so abbreviate there.
|
||||||
|
display.println((display.width() >= 200) ? F("BIGGER TEXT") : F("BIGGER"));
|
||||||
|
display.setTextSize(2, 4);
|
||||||
|
display.println(F("TALL and"));
|
||||||
|
display.setTextSize(4, 2);
|
||||||
|
display.println(F("WIDE"));
|
||||||
|
|
||||||
|
delay(PAUSE);
|
||||||
|
} // END BASIC TEXT EXAMPLE
|
||||||
|
|
||||||
|
void show_char_map() {
|
||||||
|
// "Code Page 437" is a name given to the original IBM PC character set.
|
||||||
|
// Despite age and limited language support, still seen in small embedded
|
||||||
|
// settings as it has some useful symbols and accented characters. The
|
||||||
|
// default 5x7 pixel font of Adafruit_GFX is modeled after CP437. This
|
||||||
|
// function draws a table of all the characters & explains some issues.
|
||||||
|
|
||||||
|
// There are 256 characters in all. Draw table as 16 rows of 16 columns,
|
||||||
|
// plus hexadecimal row & column labels. How big can each cell be drawn?
|
||||||
|
const int cell_size = min(display.width(), display.height()) / 17;
|
||||||
|
if (cell_size < 8) return; // Screen is too small for table, skip example.
|
||||||
|
const int total_size = cell_size * 17; // 16 cells + 1 row or column label
|
||||||
|
|
||||||
|
// Set up for default 5x7 font at 1:1 scale. Custom fonts are NOT used
|
||||||
|
// here as most are only 128 characters to save space (the "7b" at the
|
||||||
|
// end of many GFX font names means "7 bits," i.e. 128 characters).
|
||||||
|
display.setFont();
|
||||||
|
display.setTextSize(1);
|
||||||
|
|
||||||
|
// Early Adafruit_GFX was missing one symbol, throwing off some indices!
|
||||||
|
// But fixing the library would break MANY existing sketches that relied
|
||||||
|
// on the degrees symbol and others. The default behavior is thus "broken"
|
||||||
|
// to keep older code working. New code can access the CORRECT full CP437
|
||||||
|
// table by calling this function like so:
|
||||||
|
display.cp437(true);
|
||||||
|
|
||||||
|
display.fillScreen(0);
|
||||||
|
|
||||||
|
const int16_t x = (display.width() - total_size) / 2; // Upper left corner of
|
||||||
|
int16_t y = (display.height() - total_size) / 2; // table centered on screen
|
||||||
|
if (y >= 4) { // If there's a little extra space above & below, scoot table
|
||||||
|
y += 4; // down a few pixels and show a message centered at top.
|
||||||
|
display.setCursor((display.width() - 114) / 2, 0); // 114 = pixel width
|
||||||
|
display.print(F("CP437 Character Map")); // of this message
|
||||||
|
}
|
||||||
|
|
||||||
|
const int16_t inset_x = (cell_size - 5) / 2; // To center each character within cell,
|
||||||
|
const int16_t inset_y = (cell_size - 8) / 2; // compute X & Y offset from corner.
|
||||||
|
|
||||||
|
for (uint8_t row=0; row<16; row++) { // 16 down...
|
||||||
|
// Draw row and columm headings as hexadecimal single digits. To get the
|
||||||
|
// hex value for a specific character, combine the left & top labels,
|
||||||
|
// e.g. Pi symbol is row E, column 3, thus: display.print((char)0xE3);
|
||||||
|
display.setCursor(x + (row + 1) * cell_size + inset_x, y + inset_y);
|
||||||
|
display.print(row, HEX); // This actually draws column labels
|
||||||
|
display.setCursor(x + inset_x, y + (row + 1) * cell_size + inset_y);
|
||||||
|
display.print(row, HEX); // and THIS is the row labels
|
||||||
|
for (uint8_t col=0; col<16; col++) { // 16 across...
|
||||||
|
if ((row + col) & 1) { // Fill alternating cells w/gray
|
||||||
|
display.fillRect(x + (col + 1) * cell_size, y + (row + 1) * cell_size,
|
||||||
|
cell_size, cell_size, 0x630C);
|
||||||
|
}
|
||||||
|
// drawChar() bypasses usual cursor positioning to go direct to an X/Y
|
||||||
|
// location. If foreground & background match, it's drawn transparent.
|
||||||
|
display.drawChar(x + (col + 1) * cell_size + inset_x,
|
||||||
|
y + (row + 1) * cell_size + inset_y, row * 16 + col,
|
||||||
|
0xFFFF, 0xFFFF, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(PAUSE * 2);
|
||||||
|
} // END CHAR MAP EXAMPLE
|
||||||
|
|
||||||
|
void show_custom_text() {
|
||||||
|
// Show use of custom fonts, plus how to do center or right alignment
|
||||||
|
// using some additional functions provided earlier.
|
||||||
|
|
||||||
|
display.fillScreen(0);
|
||||||
|
display.setFont(&FreeSansBold18pt7b);
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setTextWrap(false); // Allow text off edges
|
||||||
|
|
||||||
|
// Get "M height" of custom font and move initial base line there:
|
||||||
|
uint16_t w, h;
|
||||||
|
int16_t x, y;
|
||||||
|
display.getTextBounds("M", 0, 0, &x, &y, &w, &h);
|
||||||
|
// On rounded 240x280 display in tall orientation, "Custom Font" gets
|
||||||
|
// clipped by top corners. Scoot text down a few pixels in that one case.
|
||||||
|
if (CORNER_RADIUS && (display.height() == 280)) h += 20;
|
||||||
|
display.setCursor(display.width() / 2, h);
|
||||||
|
|
||||||
|
if (display.width() >= 200) {
|
||||||
|
print_aligned(display, F("Custom Font"), GFX_ALIGN_CENTER);
|
||||||
|
display.setCursor(0, display.getCursorY() + 10);
|
||||||
|
print_aligned(display, F("Align Left"), GFX_ALIGN_LEFT);
|
||||||
|
display.setCursor(display.width() / 2, display.getCursorY());
|
||||||
|
print_aligned(display, F("Centered"), GFX_ALIGN_CENTER);
|
||||||
|
// Small rounded screen, when oriented the wide way, "Right" gets
|
||||||
|
// clipped by bottom right corner. Scoot left to compensate.
|
||||||
|
int16_t x_offset = (CORNER_RADIUS && (display.height() < 200)) ? 15 : 0;
|
||||||
|
display.setCursor(display.width() - x_offset, display.getCursorY());
|
||||||
|
print_aligned(display, F("Align Right"), GFX_ALIGN_RIGHT);
|
||||||
|
} else {
|
||||||
|
// On narrow screens, use abbreviated messages
|
||||||
|
print_aligned(display, F("Font &"), GFX_ALIGN_CENTER);
|
||||||
|
print_aligned(display, F("Align"), GFX_ALIGN_CENTER);
|
||||||
|
display.setCursor(0, display.getCursorY() + 10);
|
||||||
|
print_aligned(display, F("Left"), GFX_ALIGN_LEFT);
|
||||||
|
display.setCursor(display.width() / 2, display.getCursorY());
|
||||||
|
print_aligned(display, F("Center"), GFX_ALIGN_CENTER);
|
||||||
|
display.setCursor(display.width(), display.getCursorY());
|
||||||
|
print_aligned(display, F("Right"), GFX_ALIGN_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(PAUSE);
|
||||||
|
} // END CUSTOM FONT EXAMPLE
|
||||||
|
|
||||||
|
// BITMAP EXAMPLE ----------------------------------------------------------
|
||||||
|
|
||||||
|
// This section demonstrates:
|
||||||
|
// - Embedding a small bitmap in the code (flash memory).
|
||||||
|
// - Drawing that bitmap in various colors, and transparently (only '1' bits
|
||||||
|
// are drawn; '0' bits are skipped, leaving screen contents in place).
|
||||||
|
// - Use of the color565() function to decimate 24-bit RGB to 16 bits.
|
||||||
|
|
||||||
|
#define HEX_WIDTH 16 // Bitmap width in pixels
|
||||||
|
#define HEX_HEIGHT 16 // Bitmap height in pixels
|
||||||
|
// Bitmap data. PROGMEM ensures it's in flash memory (not RAM). And while
|
||||||
|
// it would be valid to leave the brackets empty here (i.e. hex_bitmap[]),
|
||||||
|
// having dimensions with a little math makes the compiler verify the
|
||||||
|
// correct number of bytes are present in the list.
|
||||||
|
PROGMEM const uint8_t hex_bitmap[(HEX_WIDTH + 7) / 8 * HEX_HEIGHT] = {
|
||||||
|
0b00000001, 0b10000000,
|
||||||
|
0b00000111, 0b11100000,
|
||||||
|
0b00011111, 0b11111000,
|
||||||
|
0b01111111, 0b11111110,
|
||||||
|
0b01111111, 0b11111110,
|
||||||
|
0b01111111, 0b11111110,
|
||||||
|
0b01111111, 0b11111110,
|
||||||
|
0b01111111, 0b11111110,
|
||||||
|
0b01111111, 0b11111110,
|
||||||
|
0b01111111, 0b11111110,
|
||||||
|
0b01111111, 0b11111110,
|
||||||
|
0b01111111, 0b11111110,
|
||||||
|
0b01111111, 0b11111110,
|
||||||
|
0b00011111, 0b11111000,
|
||||||
|
0b00000111, 0b11100000,
|
||||||
|
0b00000001, 0b10000000,
|
||||||
|
};
|
||||||
|
#define Y_SPACING (HEX_HEIGHT - 2) // Used by code below for positioning
|
||||||
|
|
||||||
|
void show_bitmap() {
|
||||||
|
display.fillScreen(0);
|
||||||
|
|
||||||
|
// Not screen center, but UL coordinates of center hexagon bitmap
|
||||||
|
const int16_t center_x = (display.width() - HEX_WIDTH) / 2;
|
||||||
|
const int16_t center_y = (display.height() - HEX_HEIGHT) / 2;
|
||||||
|
const uint8_t steps = min((display.height() - HEX_HEIGHT) / Y_SPACING,
|
||||||
|
display.width() / HEX_WIDTH - 1) / 2;
|
||||||
|
|
||||||
|
display.drawBitmap(center_x, center_y, hex_bitmap, HEX_WIDTH, HEX_HEIGHT,
|
||||||
|
0xFFFF); // Draw center hexagon in white
|
||||||
|
|
||||||
|
// Tile the hexagon bitmap repeatedly in a range of hues. Don't mind the
|
||||||
|
// bit of repetition in the math, the optimizer easily picks this up.
|
||||||
|
// Also, if math looks odd, keep in mind "PEMDAS" operator precedence;
|
||||||
|
// multiplication and division occur before addition and subtraction.
|
||||||
|
for (uint8_t a=0; a<=steps; a++) {
|
||||||
|
for (uint8_t b=1; b<=steps; b++) {
|
||||||
|
display.drawBitmap( // Right section centered red: a = green, b = blue
|
||||||
|
center_x + (a + b) * HEX_WIDTH / 2,
|
||||||
|
center_y + (a - b) * Y_SPACING,
|
||||||
|
hex_bitmap, HEX_WIDTH, HEX_HEIGHT,
|
||||||
|
display.color565(255, 255 - 255 * a / steps, 255 - 255 * b / steps));
|
||||||
|
display.drawBitmap( // UL section centered green: a = blue, b = red
|
||||||
|
center_x - b * HEX_WIDTH + a * HEX_WIDTH / 2,
|
||||||
|
center_y - a * Y_SPACING,
|
||||||
|
hex_bitmap, HEX_WIDTH, HEX_HEIGHT,
|
||||||
|
display.color565(255 - 255 * b / steps, 255, 255 - 255 * a / steps));
|
||||||
|
display.drawBitmap( // LL section centered blue: a = red, b = green
|
||||||
|
center_x - a * HEX_WIDTH + b * HEX_WIDTH / 2,
|
||||||
|
center_y + b * Y_SPACING,
|
||||||
|
hex_bitmap, HEX_WIDTH, HEX_HEIGHT,
|
||||||
|
display.color565(255 - 255 * a / steps, 255 - 255 * b / steps, 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(PAUSE);
|
||||||
|
} // END BITMAP EXAMPLE
|
||||||
|
|
||||||
|
// CANVAS EXAMPLE ----------------------------------------------------------
|
||||||
|
|
||||||
|
// This section demonstrates:
|
||||||
|
// - How to refresh changing values onscreen without erase/redraw flicker.
|
||||||
|
// - Using an offscreen canvas. It's similar to a bitmap above, but rather
|
||||||
|
// than a fixed pattern in flash memory, it's drawable like the screen.
|
||||||
|
// - More tips on text alignment, and adapting to different screen sizes.
|
||||||
|
|
||||||
|
#define PADDING 6 // Pixels between axis label and value
|
||||||
|
|
||||||
|
void show_canvas() {
|
||||||
|
// For this example, let's suppose we want to display live readings from a
|
||||||
|
// sensor such as a three-axis accelerometer, something like:
|
||||||
|
// X: (number)
|
||||||
|
// Y: (number)
|
||||||
|
// Z: (number)
|
||||||
|
// To look extra classy, we want a custom font, and the labels for each
|
||||||
|
// axis are right-aligned so the ':' characters line up...
|
||||||
|
|
||||||
|
display.setFont(&FreeSansBold18pt7b); // Use a custom font
|
||||||
|
display.setTextSize(1); // and reset to 1:1 scale
|
||||||
|
|
||||||
|
char *label[] = { "X:", "Y:", "Z:" }; // Labels for each axis
|
||||||
|
const uint16_t color[] = { 0xF800, 0x07E0, 0x001F }; // Colors for each value
|
||||||
|
|
||||||
|
// To get the labels right-aligned, one option would be simple trial and
|
||||||
|
// error to find a column that looks good and doesn't clip anything off.
|
||||||
|
// Let's do this dynamically though, so it adapts to any font or labels!
|
||||||
|
// Start by finding the widest of the label strings:
|
||||||
|
uint16_t w, h, max_w = 0;
|
||||||
|
int16_t x, y;
|
||||||
|
for (uint8_t i=0; i<3; i++) { // For each label...
|
||||||
|
display.getTextBounds(label[i], 0, 0, &x, &y, &w, &h);
|
||||||
|
if (w > max_w) max_w = w; // Keep track of widest label
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rounded corners throwing us a curve again. If needed, scoot everything
|
||||||
|
// to the right a bit on wide displays, down a bit on tall ones.
|
||||||
|
int16_t y_offset = 0;
|
||||||
|
if (display.width() > display.height()) max_w += CORNER_RADIUS;
|
||||||
|
else y_offset = CORNER_RADIUS;
|
||||||
|
|
||||||
|
// Now we have max_w for right-aligning the labels. Before we draw them
|
||||||
|
// though...in order to perform flicker-free updates, the numbers we show
|
||||||
|
// will be rendered in either a GFXcanvas1 or GFXcanvas16 object; a 1-bit
|
||||||
|
// or 16-bit offscreen bitmap, RAM permitting. The correct size for this
|
||||||
|
// canvas could also be trial-and-errored, but again let's make this adapt
|
||||||
|
// automatically. The width of the canvas will span from max_w (plus a few
|
||||||
|
// pixels for padding) to the right edge. But the height? Looking at an
|
||||||
|
// uppercase 'M' can work in many situations, but some fonts have ascenders
|
||||||
|
// and descenders on digits, and in some locales a comma (extending below
|
||||||
|
// the baseline) is the decimal separator. Feed ALL the numeric chars into
|
||||||
|
// getTextBounds() for a cumulative height:
|
||||||
|
display.setTextWrap(false); // Keep on one line
|
||||||
|
display.getTextBounds(F("0123456789.,-"), 0, 0, &x, &y, &w, &h);
|
||||||
|
|
||||||
|
// Now declare a GFXcanvas16 object based on the computed width & height:
|
||||||
|
GFXcanvas16 canvas16(display.width() - max_w - PADDING, h);
|
||||||
|
|
||||||
|
// Small devices (e.g. ATmega328p) will almost certainly lack enough RAM
|
||||||
|
// for the canvas. Check if canvas buffer exists. If not, fall back on
|
||||||
|
// using a 1-bit (rather than 16-bit) canvas. Much more RAM friendly, but
|
||||||
|
// not as fast to draw. If a project doesn't require super interactive
|
||||||
|
// updates, consider just going straight for the more compact Canvas1.
|
||||||
|
if (canvas16.getBuffer()) {
|
||||||
|
// If here, 16-bit canvas allocated successfully! Point of interest,
|
||||||
|
// only one canvas is needed for this example, we can reuse it for all
|
||||||
|
// three numbers because the regions are the same size.
|
||||||
|
|
||||||
|
// display and canvas are independent drawable objects; must explicitly
|
||||||
|
// set the same custom font to use on the canvas now:
|
||||||
|
canvas16.setFont(&FreeSansBold18pt7b);
|
||||||
|
|
||||||
|
// Clear display and print labels. Once drawn, these remain untouched.
|
||||||
|
display.fillScreen(0);
|
||||||
|
display.setCursor(max_w, -y + y_offset); // Set baseline for first row
|
||||||
|
for (uint8_t i=0; i<3; i++) print_aligned(display, label[i], GFX_ALIGN_RIGHT);
|
||||||
|
|
||||||
|
// Last part now is to print numbers on the canvas and copy the canvas to
|
||||||
|
// the display, repeating for several seconds...
|
||||||
|
uint32_t elapsed, startTime = millis();
|
||||||
|
while ((elapsed = (millis() - startTime)) <= PAUSE * 2) {
|
||||||
|
for (uint8_t i=0; i<3; i++) { // For each label...
|
||||||
|
canvas16.fillScreen(0); // fillScreen() in this case clears canvas
|
||||||
|
canvas16.setCursor(0, -y); // Reset baseline for custom font
|
||||||
|
canvas16.setTextColor(color[i]);
|
||||||
|
// These aren't real accelerometer readings, just cool-looking numbers.
|
||||||
|
// Notice we print to the canvas, NOT the display:
|
||||||
|
canvas16.print(sin(elapsed / 200.0 + (float)i * M_PI * 2.0 / 3.0), 5);
|
||||||
|
// And HERE is the secret sauce to flicker-free updates. Canvas details
|
||||||
|
// can be passed to the drawRGBBitmap() function, which fully overwrites
|
||||||
|
// prior screen contents in that area. yAdvance is font line spacing.
|
||||||
|
display.drawRGBBitmap(max_w + PADDING, i * FreeSansBold18pt7b.yAdvance +
|
||||||
|
y_offset, canvas16.getBuffer(), canvas16.width(),
|
||||||
|
canvas16.height());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Insufficient RAM for Canvas16. Try declaring a 1-bit canvas instead...
|
||||||
|
GFXcanvas1 canvas1(display.width() - max_w - PADDING, h);
|
||||||
|
// If even this smaller object fails, can't proceed, cancel this example.
|
||||||
|
if (!canvas1.getBuffer()) return;
|
||||||
|
|
||||||
|
// Remainder here is nearly identical to the code above, simply using a
|
||||||
|
// different canvas type. It's stripped of most comments for brevity.
|
||||||
|
canvas1.setFont(&FreeSansBold18pt7b);
|
||||||
|
display.fillScreen(0);
|
||||||
|
display.setCursor(max_w, -y + y_offset);
|
||||||
|
for (uint8_t i=0; i<3; i++) print_aligned(display, label[i], GFX_ALIGN_RIGHT);
|
||||||
|
uint32_t elapsed, startTime = millis();
|
||||||
|
while ((elapsed = (millis() - startTime)) <= PAUSE * 2) {
|
||||||
|
for (uint8_t i=0; i<3; i++) {
|
||||||
|
canvas1.fillScreen(0);
|
||||||
|
canvas1.setCursor(0, -y);
|
||||||
|
canvas1.print(sin(elapsed / 200.0 + (float)i * M_PI * 2.0 / 3.0), 5);
|
||||||
|
// Here's the secret sauce to flicker-free updates with GFXcanvas1.
|
||||||
|
// Canvas details can be passed to the drawBitmap() function, and by
|
||||||
|
// specifying both a foreground AND BACKGROUND color (0), this will fully
|
||||||
|
// overwrite/erase prior screen contents in that area (vs transparent).
|
||||||
|
display.drawBitmap(max_w + PADDING, i * FreeSansBold18pt7b.yAdvance +
|
||||||
|
y_offset, canvas1.getBuffer(), canvas1.width(),
|
||||||
|
canvas1.height(), color[i], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because canvas object was declared locally to this function, it's freed
|
||||||
|
// automatically when the function returns; no explicit delete needed.
|
||||||
|
} // END CANVAS EXAMPLE
|
||||||
0
2020_shake/.feather_esp32s3.test.only
Normal file
0
2020_shake/.feather_esp32s3.test.only
Normal file
0
2020_shake/.matrixportal.test.only
Normal file
0
2020_shake/.matrixportal.test.only
Normal file
|
|
@ -27,11 +27,19 @@ bool show_new_year = true;
|
||||||
#define SHAKE_PERIOD 2000 // Period (in ms) when SHAKE_EVENTS must happen
|
#define SHAKE_PERIOD 2000 // Period (in ms) when SHAKE_EVENTS must happen
|
||||||
#define SAND_TIME 6000 // Time (in ms) to run simulation before restarting
|
#define SAND_TIME 6000 // Time (in ms) to run simulation before restarting
|
||||||
|
|
||||||
|
#if defined(_VARIANT_MATRIXPORTAL_M4_) // MatrixPortal M4
|
||||||
uint8_t rgbPins[] = {7, 8, 9, 10, 11, 12};
|
uint8_t rgbPins[] = {7, 8, 9, 10, 11, 12};
|
||||||
uint8_t addrPins[] = {17, 18, 19, 20};
|
uint8_t addrPins[] = {17, 18, 19, 20, 21};
|
||||||
uint8_t clockPin = 14;
|
uint8_t clockPin = 14;
|
||||||
uint8_t latchPin = 15;
|
uint8_t latchPin = 15;
|
||||||
uint8_t oePin = 16;
|
uint8_t oePin = 16;
|
||||||
|
#else // MatrixPortal ESP32-S3
|
||||||
|
uint8_t rgbPins[] = {42, 41, 40, 38, 39, 37};
|
||||||
|
uint8_t addrPins[] = {35, 36, 48, 45, 21};
|
||||||
|
uint8_t clockPin = 2;
|
||||||
|
uint8_t latchPin = 47;
|
||||||
|
uint8_t oePin = 14;
|
||||||
|
#endif
|
||||||
|
|
||||||
// 64x32 pixel matrix, 6-bit depth
|
// 64x32 pixel matrix, 6-bit depth
|
||||||
Adafruit_Protomatter matrix(
|
Adafruit_Protomatter matrix(
|
||||||
|
|
|
||||||
BIN
4x4_MIDI_Messenger/OCRA_small.pcf
Normal file
BIN
4x4_MIDI_Messenger/OCRA_small.pcf
Normal file
Binary file not shown.
309
4x4_MIDI_Messenger/code.py
Normal file
309
4x4_MIDI_Messenger/code.py
Normal file
|
|
@ -0,0 +1,309 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import time
|
||||||
|
import asyncio
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
from rainbowio import colorwheel
|
||||||
|
import keypad
|
||||||
|
import displayio
|
||||||
|
import busio
|
||||||
|
import adafruit_seesaw.seesaw
|
||||||
|
import adafruit_seesaw.neopixel
|
||||||
|
import adafruit_seesaw.rotaryio
|
||||||
|
import adafruit_seesaw.digitalio
|
||||||
|
from adafruit_bitmap_font import bitmap_font
|
||||||
|
from adafruit_display_text import label
|
||||||
|
import adafruit_displayio_ssd1306
|
||||||
|
import adafruit_midi
|
||||||
|
from adafruit_midi.control_change import ControlChange
|
||||||
|
import neopixel
|
||||||
|
|
||||||
|
# default MIDI channel (1-16)
|
||||||
|
midi_in_channel = 2
|
||||||
|
midi_out_channel = 2
|
||||||
|
|
||||||
|
# MIDI CC messages, values and names assigned to each encoder
|
||||||
|
cc_values = [
|
||||||
|
{'cc_val': (0, 127), 'cc_message': (14), 'cc_name': "Volume"},
|
||||||
|
{'cc_val': (0, 127), 'cc_message': (15), 'cc_name': "Repeats"},
|
||||||
|
{'cc_val': (0, 127), 'cc_message': (16), 'cc_name': "Size"},
|
||||||
|
{'cc_val': (0, 127), 'cc_message': (17), 'cc_name': "Mod"},
|
||||||
|
{'cc_val': (0, 127), 'cc_message': (18), 'cc_name': "Spread"},
|
||||||
|
{'cc_val': (0, 127), 'cc_message': (19), 'cc_name': "Scan"},
|
||||||
|
{'cc_val': (0, 127), 'cc_message': (20), 'cc_name': "Ramp"},
|
||||||
|
{'cc_val': (1, 3), 'cc_message': (21), 'cc_name': "Mod Number"},
|
||||||
|
{'cc_val': (1, 3), 'cc_message': (22), 'cc_name': "Mod Bank"},
|
||||||
|
{'cc_val': (1, 3), 'cc_message': (23), 'cc_name': "Mode"},
|
||||||
|
{'cc_val': (0, 1), 'cc_message': (102), 'cc_name': "Bypass/Engage"},
|
||||||
|
{'cc_val': (0, 127), 'cc_message': (93), 'cc_name': "Tap Tempo"},
|
||||||
|
{'cc_val': (0, 1), 'cc_message': (24), 'cc_name': "Loop (R Hold)"},
|
||||||
|
{'cc_val': (0, 1), 'cc_message': (25), 'cc_name': "Scan (L Hold)"},
|
||||||
|
{'cc_val': (0, 127), 'cc_message': (26), 'cc_name': "Clear (Both Hold)"},
|
||||||
|
{'cc_val': (0, 1), 'cc_message': (51), 'cc_name': "MIDI Clock Ignore"}
|
||||||
|
]
|
||||||
|
|
||||||
|
displayio.release_displays()
|
||||||
|
|
||||||
|
oled_reset = board.D13
|
||||||
|
|
||||||
|
i2c = board.STEMMA_I2C()
|
||||||
|
# STEMMA OLED setup
|
||||||
|
display_bus = displayio.I2CDisplay(i2c, device_address=0x3D, reset=oled_reset)
|
||||||
|
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=64)
|
||||||
|
|
||||||
|
splash = displayio.Group()
|
||||||
|
display.show(splash)
|
||||||
|
font = bitmap_font.load_font('/OCRA_small.pcf')
|
||||||
|
# main label/MIDI message name text; centered
|
||||||
|
main_area = label.Label(
|
||||||
|
font, text="4x4 MIDI Messenger", color=0xFFFFFF)
|
||||||
|
main_area.anchor_point = (0.5, 0.0)
|
||||||
|
main_area.anchored_position = (display.width / 2, 0)
|
||||||
|
# MIDI message number text
|
||||||
|
msg_area = label.Label(
|
||||||
|
font, text="CC Msg: 10", color=0xFFFFFF)
|
||||||
|
msg_area.anchor_point = (0.0, 0.5)
|
||||||
|
msg_area.anchored_position = (0, display.height / 2)
|
||||||
|
# MIDI message value text
|
||||||
|
val_area = label.Label(
|
||||||
|
font, text="CC Val: 50", color=0xFFFFFF)
|
||||||
|
val_area.anchor_point = (0.0, 1.0)
|
||||||
|
val_area.anchored_position = (0, display.height)
|
||||||
|
# MIDI message status text
|
||||||
|
status_area = label.Label(
|
||||||
|
font, text="Sent!", color=0xFFFFFF)
|
||||||
|
status_area.anchor_point = (1.0, 1.0)
|
||||||
|
status_area.anchored_position = (display.width, display.height)
|
||||||
|
|
||||||
|
splash.append(main_area)
|
||||||
|
splash.append(msg_area)
|
||||||
|
splash.append(val_area)
|
||||||
|
splash.append(status_area)
|
||||||
|
# MIDI over UART setup for MIDI FeatherWing
|
||||||
|
uart = busio.UART(board.TX, board.RX, baudrate=31250, timeout=0.001)
|
||||||
|
midi = adafruit_midi.MIDI(
|
||||||
|
midi_in=uart,
|
||||||
|
midi_out=uart,
|
||||||
|
in_channel=(midi_in_channel - 1),
|
||||||
|
out_channel=(midi_out_channel - 1),
|
||||||
|
debug=False,
|
||||||
|
)
|
||||||
|
# quad rotary encoder setup
|
||||||
|
ss0 = adafruit_seesaw.seesaw.Seesaw(i2c, 0x49)
|
||||||
|
ss1 = adafruit_seesaw.seesaw.Seesaw(i2c, 0x4A)
|
||||||
|
ss2 = adafruit_seesaw.seesaw.Seesaw(i2c, 0x4B)
|
||||||
|
ss3 = adafruit_seesaw.seesaw.Seesaw(i2c, 0x4C)
|
||||||
|
# button pins for the encoders
|
||||||
|
pins = [12, 14, 17, 9]
|
||||||
|
# interrupts for the button pins. pins are passed as a bitmask
|
||||||
|
ss0.set_GPIO_interrupts(1 << pins[0] | 1 << pins[1] | 1 << pins[2] | 1 << pins[3], True)
|
||||||
|
ss1.set_GPIO_interrupts(1 << pins[0] | 1 << pins[1] | 1 << pins[2] | 1 << pins[3], True)
|
||||||
|
ss2.set_GPIO_interrupts(1 << pins[0] | 1 << pins[1] | 1 << pins[2] | 1 << pins[3], True)
|
||||||
|
ss3.set_GPIO_interrupts(1 << pins[0] | 1 << pins[1] | 1 << pins[2] | 1 << pins[3], True)
|
||||||
|
# arrays for the encoders and switches
|
||||||
|
enc0 = []
|
||||||
|
enc1 = []
|
||||||
|
enc2 = []
|
||||||
|
enc3 = []
|
||||||
|
sw0 = []
|
||||||
|
sw1 = []
|
||||||
|
sw2 = []
|
||||||
|
sw3 = []
|
||||||
|
# creating encoders and switches, enabling interrupts for encoders
|
||||||
|
for i in range(4):
|
||||||
|
enc0.append(adafruit_seesaw.rotaryio.IncrementalEncoder(ss0, i))
|
||||||
|
enc1.append(adafruit_seesaw.rotaryio.IncrementalEncoder(ss1, i))
|
||||||
|
enc2.append(adafruit_seesaw.rotaryio.IncrementalEncoder(ss2, i))
|
||||||
|
enc3.append(adafruit_seesaw.rotaryio.IncrementalEncoder(ss3, i))
|
||||||
|
sw0.append(adafruit_seesaw.digitalio.DigitalIO(ss0, pins[i]))
|
||||||
|
sw0[i].switch_to_input(digitalio.Pull.UP)
|
||||||
|
sw1.append(adafruit_seesaw.digitalio.DigitalIO(ss1, pins[i]))
|
||||||
|
sw1[i].switch_to_input(digitalio.Pull.UP)
|
||||||
|
sw2.append(adafruit_seesaw.digitalio.DigitalIO(ss2, pins[i]))
|
||||||
|
sw2[i].switch_to_input(digitalio.Pull.UP)
|
||||||
|
sw3.append(adafruit_seesaw.digitalio.DigitalIO(ss3, pins[i]))
|
||||||
|
sw3[i].switch_to_input(digitalio.Pull.UP)
|
||||||
|
ss0.enable_encoder_interrupt(encoder=i)
|
||||||
|
ss1.enable_encoder_interrupt(encoder=i)
|
||||||
|
ss2.enable_encoder_interrupt(encoder=i)
|
||||||
|
ss3.enable_encoder_interrupt(encoder=i)
|
||||||
|
# neopixels on each PCB
|
||||||
|
pix0 = adafruit_seesaw.neopixel.NeoPixel(ss0, 18, 4, auto_write = True)
|
||||||
|
pix0.brightness = 0.5
|
||||||
|
pix1 = adafruit_seesaw.neopixel.NeoPixel(ss1, 18, 4, auto_write = True)
|
||||||
|
pix1.brightness = 0.5
|
||||||
|
pix2 = adafruit_seesaw.neopixel.NeoPixel(ss2, 18, 4, auto_write = True)
|
||||||
|
pix2.brightness = 0.5
|
||||||
|
pix3 = adafruit_seesaw.neopixel.NeoPixel(ss3, 18, 4, auto_write = True)
|
||||||
|
pix3.brightness = 0.5
|
||||||
|
# onboard Feather neopixel
|
||||||
|
pix_feather = neopixel.NeoPixel(board.NEOPIXEL, 1, auto_write = True)
|
||||||
|
pix_feather.brightness = 0.5
|
||||||
|
# encoder position arrays
|
||||||
|
last_pos0 = [60, 60, 60, 60]
|
||||||
|
last_pos1 = [60, 60, 60, 0]
|
||||||
|
last_pos2 = [0, 0, 0, 120]
|
||||||
|
last_pos3 = [0, 0, 0, 0]
|
||||||
|
pos0 = [60, 60, 60, 60]
|
||||||
|
pos1 = [60, 60, 60, 0]
|
||||||
|
pos2 = [0, 0, 0, 120]
|
||||||
|
pos3 = [0, 0, 0, 0]
|
||||||
|
# color arrays for the neopixels
|
||||||
|
c0 = [0, 16, 32, 48]
|
||||||
|
c1 = [64, 80, 96, 112]
|
||||||
|
c2 = [128, 144, 160, 176]
|
||||||
|
c3 = [192, 208, 224, 240]
|
||||||
|
# setting starting colors for neopixels
|
||||||
|
for r in range(4):
|
||||||
|
pix0[r] = colorwheel(c0[r])
|
||||||
|
pix1[r] = colorwheel(c1[r])
|
||||||
|
pix2[r] = colorwheel(c2[r])
|
||||||
|
pix3[r] = colorwheel(c3[r])
|
||||||
|
# feather neopixel color
|
||||||
|
c_feather = 0
|
||||||
|
pix_feather[0] = colorwheel(c_feather)
|
||||||
|
# array of all 16 encoder positions
|
||||||
|
encoder_posititions = [60, 60, 60, 60, 60, 60, 60, 60, 0, 0, 0, 120, 0, 0, 0, 0]
|
||||||
|
|
||||||
|
class MIDI_Messages:
|
||||||
|
# tracks sending a message and index 0-15
|
||||||
|
def __init__(self):
|
||||||
|
self.send_msg = False
|
||||||
|
self.midi_index = 0
|
||||||
|
|
||||||
|
class NeoPixel_Attributes:
|
||||||
|
# tracks color, neopixel index and seesaw
|
||||||
|
def __init__(self):
|
||||||
|
self.color = c0
|
||||||
|
self.index = 0
|
||||||
|
self.strip = pix0
|
||||||
|
self.feather_color = c_feather
|
||||||
|
|
||||||
|
async def send_midi(midi_msg):
|
||||||
|
# sends MIDI message if send_msg is True/button pressed
|
||||||
|
while True:
|
||||||
|
if midi_msg.send_msg is True:
|
||||||
|
m = midi_msg.midi_index
|
||||||
|
main_area.text = f"{cc_values[m]['cc_name']}"
|
||||||
|
msg_area.text = f"CC Msg: {cc_values[m]['cc_message']}"
|
||||||
|
val_area.text = f"CC Val: {encoder_posititions[m]}"
|
||||||
|
midi.send(ControlChange(cc_values[m]['cc_message'], encoder_posititions[m]))
|
||||||
|
status_area.text = "Sent!"
|
||||||
|
print(f"sending midi: {m}, {encoder_posititions[m]}, {cc_values[m]['cc_message']}")
|
||||||
|
time.sleep(1)
|
||||||
|
midi_msg.send_msg = False
|
||||||
|
else:
|
||||||
|
status_area.text = " "
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
|
||||||
|
async def rainbows(the_color):
|
||||||
|
# Updates colors of the neopixels to scroll through rainbow
|
||||||
|
while True:
|
||||||
|
the_color.feather_color += 8
|
||||||
|
the_color.strip[the_color.index] = colorwheel(the_color.color[the_color.index])
|
||||||
|
pix_feather[0] = colorwheel(the_color.feather_color)
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
|
||||||
|
async def monitor_interrupts(pin0, pin1, pin2, pin3, the_color, midi_msg): #pylint: disable=too-many-statements
|
||||||
|
# function to keep encoder value pinned between CC value range
|
||||||
|
def normalize(val, min_v, max_v):
|
||||||
|
return max(min(max_v, val), min_v)
|
||||||
|
# read encoder function
|
||||||
|
def read_encoder(enc_group, pos, last_pos, pix, colors, index_diff):
|
||||||
|
# check all four encoders if interrupt is detected
|
||||||
|
for p in range(4):
|
||||||
|
pos[p] = enc_group[p].position
|
||||||
|
if pos[p] != last_pos[p]:
|
||||||
|
main_index = p + index_diff
|
||||||
|
# update CC value
|
||||||
|
if pos[p] > last_pos[p]:
|
||||||
|
colors[p] += 8
|
||||||
|
encoder_posititions[main_index] = encoder_posititions[main_index] + 1
|
||||||
|
else:
|
||||||
|
colors[p] -= 8
|
||||||
|
encoder_posititions[main_index] = encoder_posititions[main_index] - 1
|
||||||
|
encoder_posititions[main_index] = normalize(encoder_posititions[main_index],
|
||||||
|
cc_values[main_index]['cc_val'][0],
|
||||||
|
cc_values[main_index]['cc_val'][1])
|
||||||
|
colors[p] = (colors[p] + 256) % 256 # wrap around to 0-256
|
||||||
|
print(main_index, encoder_posititions[main_index])
|
||||||
|
main_area.text = f"{cc_values[main_index]['cc_name']}"
|
||||||
|
msg_area.text = f"CC Msg: {cc_values[main_index]['cc_message']}"
|
||||||
|
val_area.text = f"CC Val: {encoder_posititions[main_index]}"
|
||||||
|
last_pos[p] = pos[p]
|
||||||
|
# update NeoPixel colors
|
||||||
|
the_color.color = colors
|
||||||
|
the_color.index = p
|
||||||
|
the_color.strip = pix
|
||||||
|
# function to read button press
|
||||||
|
def press_switches(sw, index):
|
||||||
|
if not sw[index].value:
|
||||||
|
# signals that a MIDI message should be sent
|
||||||
|
midi_msg.send_msg = True
|
||||||
|
midi_msg.midi_index = index
|
||||||
|
print(f"button {index} pressed")
|
||||||
|
# interrupt pins are passed as a keypad
|
||||||
|
with keypad.Keys(
|
||||||
|
(pin0, pin1, pin2, pin3,), value_when_pressed=False, pull=True
|
||||||
|
) as keys:
|
||||||
|
while True:
|
||||||
|
key_event = keys.events.get()
|
||||||
|
if key_event and key_event.pressed:
|
||||||
|
key_number = key_event.key_number
|
||||||
|
# seesaw 0
|
||||||
|
if key_number == 0:
|
||||||
|
read_encoder(enc0, pos0, last_pos0, pix0, c0, 0)
|
||||||
|
press_switches(sw0, 0)
|
||||||
|
press_switches(sw0, 1)
|
||||||
|
press_switches(sw0, 2)
|
||||||
|
press_switches(sw0, 3)
|
||||||
|
# seesaw 1
|
||||||
|
elif key_number == 1:
|
||||||
|
read_encoder(enc1, pos1, last_pos1, pix1, c1, 4)
|
||||||
|
press_switches(sw1, 0)
|
||||||
|
press_switches(sw1, 1)
|
||||||
|
press_switches(sw1, 2)
|
||||||
|
press_switches(sw1, 3)
|
||||||
|
# update index to 4-7
|
||||||
|
midi_msg.midi_index = midi_msg.midi_index + 4
|
||||||
|
# seesaw 2
|
||||||
|
elif key_number == 2:
|
||||||
|
read_encoder(enc2, pos2, last_pos2, pix2, c2, 8)
|
||||||
|
press_switches(sw2, 0)
|
||||||
|
press_switches(sw2, 1)
|
||||||
|
press_switches(sw2, 2)
|
||||||
|
press_switches(sw2, 3)
|
||||||
|
# update index 8-11
|
||||||
|
midi_msg.midi_index = midi_msg.midi_index + 8
|
||||||
|
# seesaw 3
|
||||||
|
else:
|
||||||
|
read_encoder(enc3, pos3, last_pos3, pix3, c3, 12)
|
||||||
|
press_switches(sw3, 0)
|
||||||
|
press_switches(sw3, 1)
|
||||||
|
press_switches(sw3, 2)
|
||||||
|
press_switches(sw3, 3)
|
||||||
|
# update index 12-15
|
||||||
|
midi_msg.midi_index = midi_msg.midi_index + 12
|
||||||
|
# clear interrupt flag to reset interrupt pin
|
||||||
|
ss0.get_GPIO_interrupt_flag()
|
||||||
|
ss1.get_GPIO_interrupt_flag()
|
||||||
|
ss2.get_GPIO_interrupt_flag()
|
||||||
|
ss3.get_GPIO_interrupt_flag()
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
the_color = NeoPixel_Attributes()
|
||||||
|
midi_msg = MIDI_Messages()
|
||||||
|
# interrupt listener task
|
||||||
|
interrupt_task = asyncio.create_task(monitor_interrupts(board.D5, board.D6, board.D9,
|
||||||
|
board.D10, the_color, midi_msg))
|
||||||
|
# neopixel task
|
||||||
|
pixels_task = asyncio.create_task(rainbows(the_color))
|
||||||
|
# midi task
|
||||||
|
midi_task = asyncio.create_task(send_midi(midi_msg))
|
||||||
|
|
||||||
|
await asyncio.gather(interrupt_task, pixels_task, midi_task)
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
740
ANO_Rotary_Encoder_Synth/code.py
Normal file
740
ANO_Rotary_Encoder_Synth/code.py
Normal file
|
|
@ -0,0 +1,740 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
from random import randint
|
||||||
|
import ulab.numpy as np
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
import audiomixer
|
||||||
|
import synthio
|
||||||
|
import simpleio
|
||||||
|
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
|
||||||
|
from adafruit_ht16k33 import segments
|
||||||
|
from adafruit_ht16k33.matrix import Matrix8x8x2
|
||||||
|
from adafruit_seesaw import seesaw, rotaryio, digitalio
|
||||||
|
|
||||||
|
SAMPLE_RATE = 44100
|
||||||
|
SAMPLE_SIZE = 256
|
||||||
|
VOLUME = 5000
|
||||||
|
|
||||||
|
# waveforms, envelopes and synth setup
|
||||||
|
|
||||||
|
square = np.concatenate((np.ones(SAMPLE_SIZE//2, dtype=np.int16)*VOLUME,np.ones(SAMPLE_SIZE//2,
|
||||||
|
dtype=np.int16)*-VOLUME))
|
||||||
|
sine = np.array(np.sin(np.linspace(0, 4*np.pi, SAMPLE_SIZE, endpoint=False)) * VOLUME,
|
||||||
|
dtype=np.int16)
|
||||||
|
saw = np.linspace(VOLUME, -VOLUME, num=SAMPLE_SIZE, dtype=np.int16)
|
||||||
|
noise = np.array([randint(-VOLUME, VOLUME) for i in range(SAMPLE_SIZE)], dtype=np.int16)
|
||||||
|
|
||||||
|
lfo = synthio.LFO(rate = .5, waveform = sine)
|
||||||
|
|
||||||
|
amp_env0 = synthio.Envelope(attack_time=0.1, decay_time = 0.1, release_time=0.1,
|
||||||
|
attack_level=1, sustain_level=0.05)
|
||||||
|
amp_env1 = synthio.Envelope(attack_time=0.05, decay_time = 0.1, release_time=0.1,
|
||||||
|
attack_level=1, sustain_level=0.05)
|
||||||
|
|
||||||
|
# synth plays the notes
|
||||||
|
synth = synthio.Synthesizer(sample_rate=SAMPLE_RATE)
|
||||||
|
|
||||||
|
# these are the notes
|
||||||
|
synth0 = synthio.Note(frequency = 0.0, envelope=amp_env0, waveform=square, ring_frequency = 0,
|
||||||
|
ring_bend = lfo, ring_waveform = sine)
|
||||||
|
synth1 = synthio.Note(frequency = 0.0, envelope=amp_env1, waveform=sine, ring_frequency = 0,
|
||||||
|
ring_bend = lfo, ring_waveform = sine)
|
||||||
|
synth2 = synthio.Note(frequency = 0.0, envelope=amp_env0, waveform=square, ring_frequency = 0,
|
||||||
|
ring_bend = lfo, ring_waveform = sine)
|
||||||
|
synth3 = synthio.Note(frequency = 0.0, envelope=amp_env1, waveform=sine, ring_frequency = 0,
|
||||||
|
ring_bend = lfo, ring_waveform = sine)
|
||||||
|
|
||||||
|
synths = [synth0, synth1, synth2, synth3]
|
||||||
|
wave_names = ["SQUR", "SINE", "SAW ", "NOIZ"]
|
||||||
|
waveforms = [square, sine, saw, noise]
|
||||||
|
synth0_wave = 0
|
||||||
|
synth1_wave = 1
|
||||||
|
synth2_wave = 0
|
||||||
|
synth3_wave = 1
|
||||||
|
|
||||||
|
# i2s amp setup
|
||||||
|
audio = audiobusio.I2SOut(bit_clock=board.D10, word_select=board.D11, data=board.D9)
|
||||||
|
mixer = audiomixer.Mixer(voice_count=4, sample_rate=SAMPLE_RATE, channel_count=1,
|
||||||
|
bits_per_sample=16, samples_signed=True, buffer_size=2048 )
|
||||||
|
audio.play(mixer)
|
||||||
|
vol_val = 2
|
||||||
|
mixer.voice[0].play(synth)
|
||||||
|
mixer.voice[0].level = 0.3
|
||||||
|
|
||||||
|
# these are the triads, all major
|
||||||
|
c_tones = [130.81, 164.81, 196.00]
|
||||||
|
g_tones = [196.00, 246.94, 293.66]
|
||||||
|
d_tones = [146.83, 185.00, 220.00]
|
||||||
|
a_tones = [220.00, 277.18, 329.63]
|
||||||
|
e_tones = [164.81, 207.65, 246.94]
|
||||||
|
b_tones = [246.94, 311.13, 369.99]
|
||||||
|
fsharp_tones = [185.00, 233.08, 277.18]
|
||||||
|
csharp_tones = [138.59, 174.61, 207.65]
|
||||||
|
aflat_tones = [207.65, 261.63, 311.13]
|
||||||
|
eflat_tones = [155.56, 196.00, 233.08]
|
||||||
|
bflat_tones = [233.08, 293.66, 349.23]
|
||||||
|
f_tones = [174.61, 220.00, 261.63]
|
||||||
|
|
||||||
|
# names for the alphanumeric displays
|
||||||
|
chord_names = ["Cmaj", "Gmaj", "Dmaj", "Amaj", "Emaj", "Bmaj",
|
||||||
|
"F#ma", "C#ma", "Abma", "Ebma", "Bbma", "Fmaj"]
|
||||||
|
chords = [c_tones, g_tones, d_tones, a_tones, e_tones, b_tones, fsharp_tones, csharp_tones,
|
||||||
|
aflat_tones, eflat_tones, bflat_tones, f_tones]
|
||||||
|
|
||||||
|
# i2c setup
|
||||||
|
i2c = board.I2C()
|
||||||
|
# the encoders
|
||||||
|
seesaw0 = seesaw.Seesaw(i2c, addr=0x49)
|
||||||
|
seesaw1 = seesaw.Seesaw(i2c, addr=0x4A)
|
||||||
|
seesaw2 = seesaw.Seesaw(i2c, addr=0x4B)
|
||||||
|
seesaw3 = seesaw.Seesaw(i2c, addr=0x4C)
|
||||||
|
menu_seesaw = seesaw.Seesaw(i2c, addr=0x4D)
|
||||||
|
# the alphanumeric displays
|
||||||
|
display0 = segments.Seg14x4(i2c, address=0x70)
|
||||||
|
display1 = segments.Seg14x4(i2c, address=0x71)
|
||||||
|
display2 = segments.Seg14x4(i2c, address=0x72)
|
||||||
|
display3 = segments.Seg14x4(i2c, address=0x73)
|
||||||
|
menu_display = segments.Seg14x4(i2c, address=0x74)
|
||||||
|
# the matrix
|
||||||
|
matrix0 = Matrix8x8x2(i2c, address=0x75)
|
||||||
|
|
||||||
|
seesaws = [seesaw0, seesaw1, seesaw2, seesaw3, menu_seesaw]
|
||||||
|
buttons0 = []
|
||||||
|
buttons1 = []
|
||||||
|
buttons2 = []
|
||||||
|
buttons3 = []
|
||||||
|
menu_buttons = []
|
||||||
|
button0_states = []
|
||||||
|
button1_states = []
|
||||||
|
button2_states = []
|
||||||
|
button3_states = []
|
||||||
|
menu_states = []
|
||||||
|
button0_names = ["Select", "Up", "Left", "Down", "Right"]
|
||||||
|
|
||||||
|
# setup the buttons on all of the encoders
|
||||||
|
for i in range(1, 6):
|
||||||
|
seesaw0.pin_mode(i, seesaw0.INPUT_PULLUP)
|
||||||
|
seesaw1.pin_mode(i, seesaw1.INPUT_PULLUP)
|
||||||
|
seesaw2.pin_mode(i, seesaw2.INPUT_PULLUP)
|
||||||
|
seesaw3.pin_mode(i, seesaw3.INPUT_PULLUP)
|
||||||
|
menu_seesaw.pin_mode(i, menu_seesaw.INPUT_PULLUP)
|
||||||
|
buttons0.append(digitalio.DigitalIO(seesaw0, i))
|
||||||
|
buttons1.append(digitalio.DigitalIO(seesaw1, i))
|
||||||
|
buttons2.append(digitalio.DigitalIO(seesaw2, i))
|
||||||
|
buttons3.append(digitalio.DigitalIO(seesaw3, i))
|
||||||
|
menu_buttons.append(digitalio.DigitalIO(menu_seesaw, i))
|
||||||
|
button0_states.append(False)
|
||||||
|
button1_states.append(False)
|
||||||
|
button2_states.append(False)
|
||||||
|
button3_states.append(False)
|
||||||
|
menu_states.append(False)
|
||||||
|
|
||||||
|
# make all of the encoders
|
||||||
|
encoder0 = rotaryio.IncrementalEncoder(seesaw0)
|
||||||
|
last_position0 = 0
|
||||||
|
encoder1 = rotaryio.IncrementalEncoder(seesaw1)
|
||||||
|
last_position1 = 0
|
||||||
|
encoder2 = rotaryio.IncrementalEncoder(seesaw2)
|
||||||
|
last_position2 = 0
|
||||||
|
encoder3 = rotaryio.IncrementalEncoder(seesaw3)
|
||||||
|
last_position3 = 0
|
||||||
|
menu_enc = rotaryio.IncrementalEncoder(menu_seesaw)
|
||||||
|
last_menuPosition = 0
|
||||||
|
|
||||||
|
# Python Implementation of Björklund's Algorithm by Brian House
|
||||||
|
# MIT License 2011
|
||||||
|
# https://github.com/brianhouse/bjorklund
|
||||||
|
|
||||||
|
def bjorklund(steps, pulses):
|
||||||
|
steps = int(steps)
|
||||||
|
pulses = int(pulses)
|
||||||
|
if pulses > steps:
|
||||||
|
raise ValueError
|
||||||
|
pattern = []
|
||||||
|
counts = []
|
||||||
|
remainders = []
|
||||||
|
divisor = steps - pulses
|
||||||
|
remainders.append(pulses)
|
||||||
|
level = 0
|
||||||
|
while True:
|
||||||
|
counts.append(divisor // remainders[level])
|
||||||
|
remainders.append(divisor % remainders[level])
|
||||||
|
divisor = remainders[level]
|
||||||
|
level = level + 1
|
||||||
|
if remainders[level] <= 1:
|
||||||
|
break
|
||||||
|
counts.append(divisor)
|
||||||
|
|
||||||
|
def build(level):
|
||||||
|
if level == -1:
|
||||||
|
pattern.append(0)
|
||||||
|
elif level == -2:
|
||||||
|
pattern.append(1)
|
||||||
|
else:
|
||||||
|
for _ in range(0, counts[level]):
|
||||||
|
build(level - 1)
|
||||||
|
if remainders[level] != 0:
|
||||||
|
build(level - 2)
|
||||||
|
|
||||||
|
build(level)
|
||||||
|
p = pattern.index(1)
|
||||||
|
pattern = pattern[p:] + pattern[0:p]
|
||||||
|
return pattern
|
||||||
|
|
||||||
|
# using ticks for time tracking
|
||||||
|
clock = ticks_ms()
|
||||||
|
|
||||||
|
# default BPM
|
||||||
|
bpm = 120
|
||||||
|
|
||||||
|
# beat divison
|
||||||
|
beat_div = [15, 30, 60, 120, 240]
|
||||||
|
beat_index = 2
|
||||||
|
beat_names = ["1/16", "1/8 ", "1/4 ", "1/2 ", "HOLE"]
|
||||||
|
delay = int((beat_div[beat_index] / bpm) * 1000)
|
||||||
|
|
||||||
|
# variables for euclidean
|
||||||
|
c0 = 0
|
||||||
|
c1 = 0
|
||||||
|
c2 = 0
|
||||||
|
c3 = 0
|
||||||
|
r0 = 0
|
||||||
|
r1 = 0
|
||||||
|
r2 = 0
|
||||||
|
r3 = 0
|
||||||
|
last_r0 = 0
|
||||||
|
last_r1 = 0
|
||||||
|
last_r2 = 0
|
||||||
|
last_r3 = 0
|
||||||
|
|
||||||
|
euclid0_steps = 8
|
||||||
|
euclid0_pulses = 4
|
||||||
|
euclid1_steps = 8
|
||||||
|
euclid1_pulses = 4
|
||||||
|
euclid2_steps = 8
|
||||||
|
euclid2_pulses = 4
|
||||||
|
euclid3_steps = 8
|
||||||
|
euclid3_pulses = 4
|
||||||
|
|
||||||
|
rhythm0 = bjorklund(euclid0_steps, euclid0_pulses)
|
||||||
|
rhythm1 = bjorklund(euclid1_steps, euclid1_pulses)
|
||||||
|
rhythm2 = bjorklund(euclid2_steps, euclid2_pulses)
|
||||||
|
rhythm3 = bjorklund(euclid3_steps, euclid3_pulses)
|
||||||
|
|
||||||
|
# read buttons to update Euclidean rhythms
|
||||||
|
# pylint: disable=too-many-branches
|
||||||
|
def read_buttons(button_array, button_states, euc, e_step, e_pulse, the_step):
|
||||||
|
for b in range(5):
|
||||||
|
if not button_array[b].value and button_states[b] is False:
|
||||||
|
button_states[b] = True
|
||||||
|
if button0_names[b] == "Select":
|
||||||
|
e_step = 8
|
||||||
|
e_pulse = 4
|
||||||
|
if the_step >= e_step:
|
||||||
|
the_step = 0
|
||||||
|
elif button0_names[b] == "Up":
|
||||||
|
if e_step > 16:
|
||||||
|
e_step = 16
|
||||||
|
else:
|
||||||
|
e_step += 1
|
||||||
|
elif button0_names[b] == "Down":
|
||||||
|
if e_step < 1:
|
||||||
|
e_step = 1
|
||||||
|
else:
|
||||||
|
e_step -= 1
|
||||||
|
if the_step >= e_step:
|
||||||
|
the_step = 0
|
||||||
|
elif button0_names[b] == "Left":
|
||||||
|
e_pulse -= 1
|
||||||
|
e_pulse = max(e_pulse, 1)
|
||||||
|
else:
|
||||||
|
e_pulse += 1
|
||||||
|
e_pulse = min(e_pulse, e_step)
|
||||||
|
euc = bjorklund(e_step, e_pulse)
|
||||||
|
if button_array[b].value and button_states[b] is True:
|
||||||
|
button_states[b] = False
|
||||||
|
if button0_names[b] in ("Select", "Up", "Down"):
|
||||||
|
matrix0.fill(matrix0.LED_OFF)
|
||||||
|
draw_steps(euclid0_steps, 0)
|
||||||
|
draw_steps(euclid1_steps, 2)
|
||||||
|
draw_steps(euclid2_steps, 4)
|
||||||
|
draw_steps(euclid3_steps, 6)
|
||||||
|
return euc, e_step, e_pulse, the_step
|
||||||
|
|
||||||
|
# play euclidean rhythms and update matrix
|
||||||
|
def play_euclidean(this_synth, n, the_rhythm, rhythm_count, last_count, c, matrix_slot):
|
||||||
|
if last_count <= 7:
|
||||||
|
matrix0[matrix_slot, last_count] = matrix0.LED_GREEN
|
||||||
|
else:
|
||||||
|
c -= 1
|
||||||
|
matrix0[matrix_slot + 1, (last_count - last_count) + c] = matrix0.LED_GREEN
|
||||||
|
c += 1
|
||||||
|
|
||||||
|
if the_rhythm[rhythm_count] == 1:
|
||||||
|
this_synth.frequency = n[randint(0, 2)]
|
||||||
|
synth.press(this_synth)
|
||||||
|
if rhythm_count <= 7:
|
||||||
|
matrix0[matrix_slot, rhythm_count] = matrix0.LED_RED
|
||||||
|
else:
|
||||||
|
matrix0[matrix_slot + 1, (rhythm_count - rhythm_count) + c] = matrix0.LED_RED
|
||||||
|
c += 1
|
||||||
|
else:
|
||||||
|
synth.release(this_synth)
|
||||||
|
if rhythm_count > 7:
|
||||||
|
c += 1
|
||||||
|
last_count = rhythm_count
|
||||||
|
|
||||||
|
rhythm_count += 1
|
||||||
|
if rhythm_count >= len(the_rhythm):
|
||||||
|
rhythm_count = 0
|
||||||
|
if rhythm_count == 1:
|
||||||
|
c = 0
|
||||||
|
return rhythm_count, last_count, c
|
||||||
|
|
||||||
|
# initial matrix draw
|
||||||
|
def draw_steps(euc_steps, col):
|
||||||
|
dif = 0
|
||||||
|
for m in range(euc_steps):
|
||||||
|
if m <= 7:
|
||||||
|
matrix0[col, m] = matrix0.LED_GREEN
|
||||||
|
else:
|
||||||
|
matrix0[col + 1, (m - m) + dif] = matrix0.LED_GREEN
|
||||||
|
dif += 1
|
||||||
|
draw_steps(euclid0_steps, 0)
|
||||||
|
draw_steps(euclid1_steps, 2)
|
||||||
|
draw_steps(euclid2_steps, 4)
|
||||||
|
draw_steps(euclid3_steps, 6)
|
||||||
|
|
||||||
|
# clocks for playing euclidean and reading menu encoder
|
||||||
|
enc_clock = ticks_ms()
|
||||||
|
menu_clock = ticks_ms()
|
||||||
|
|
||||||
|
# the modes menu
|
||||||
|
modes = ["PLAY", "EUC ", "BPM ", "BEAT", "ADSR", "WAVE", "RING", "LFO ", "VOL "]
|
||||||
|
mode_index = 0
|
||||||
|
mode = modes[mode_index]
|
||||||
|
menu_display.print(f" {mode}")
|
||||||
|
|
||||||
|
# default chords
|
||||||
|
chord0_sel = 0
|
||||||
|
chord1_sel = 1
|
||||||
|
chord2_sel = 0
|
||||||
|
chord3_sel = 1
|
||||||
|
|
||||||
|
display0.print(chord_names[chord0_sel])
|
||||||
|
display1.print(chord_names[chord1_sel])
|
||||||
|
display2.print(chord_names[chord2_sel])
|
||||||
|
display3.print(chord_names[chord3_sel])
|
||||||
|
|
||||||
|
# arrays of individual buttons
|
||||||
|
|
||||||
|
select_buttons = [buttons0[0], buttons1[0], buttons2[0], buttons3[0]]
|
||||||
|
left_buttons = [buttons0[2], buttons1[2], buttons2[2], buttons3[2]]
|
||||||
|
right_buttons = [buttons0[4], buttons1[4], buttons2[4], buttons3[4]]
|
||||||
|
select_states = [button0_states[0], button1_states[0], button2_states[0], button3_states[0]]
|
||||||
|
left_states = [button0_states[2], button1_states[2], button2_states[2], button3_states[2]]
|
||||||
|
right_states = [button0_states[4], button1_states[4], button2_states[4], button3_states[4]]
|
||||||
|
select_index = 0
|
||||||
|
left_index = 0
|
||||||
|
right_index = 0
|
||||||
|
|
||||||
|
# adsr mode
|
||||||
|
adsr_names = ["A", "D", "S", "R"]
|
||||||
|
|
||||||
|
synth_adsr_indexes = [0, 0, 0, 0]
|
||||||
|
|
||||||
|
adsr_properties = [0, 1, 4, 2]
|
||||||
|
|
||||||
|
adsr0_values = [amp_env0.attack_time, amp_env0.decay_time,
|
||||||
|
amp_env0.sustain_level, amp_env0.release_time]
|
||||||
|
adsr1_values = [amp_env1.attack_time, amp_env1.decay_time,
|
||||||
|
amp_env1.sustain_level, amp_env1.release_time]
|
||||||
|
adsr2_values = [amp_env0.attack_time, amp_env0.decay_time,
|
||||||
|
amp_env0.sustain_level, amp_env0.release_time]
|
||||||
|
adsr3_values = [amp_env1.attack_time, amp_env1.decay_time,
|
||||||
|
amp_env1.sustain_level, amp_env1.release_time]
|
||||||
|
|
||||||
|
all_adsr_values = [adsr0_values, adsr1_values, adsr2_values, adsr3_values]
|
||||||
|
|
||||||
|
adsr0_val = int(simpleio.map_range(amp_env0.attack_time, 0.0, 1.0, 0, 19))
|
||||||
|
|
||||||
|
adsr1_val = int(simpleio.map_range(amp_env0.decay_time, 0.0, 1.0, 0, 19))
|
||||||
|
|
||||||
|
adsr2_val = int(simpleio.map_range(amp_env0.sustain_level, 0.0, 1.0, 0, 19))
|
||||||
|
|
||||||
|
adsr3_val = int(simpleio.map_range(amp_env0.release_time, 0.0, 1.0, 0, 19))
|
||||||
|
|
||||||
|
clock_stretch = False
|
||||||
|
|
||||||
|
ring0_val = 0
|
||||||
|
ring1_val = 0
|
||||||
|
ring2_val = 0
|
||||||
|
ring3_val = 0
|
||||||
|
|
||||||
|
lfo_val = 0
|
||||||
|
|
||||||
|
# used to play/pause
|
||||||
|
play_states = [True, True, True, True]
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# rotary encoder reading
|
||||||
|
if ticks_diff(ticks_ms(), enc_clock) >= 100:
|
||||||
|
position0 = encoder0.position
|
||||||
|
position1 = encoder1.position
|
||||||
|
position2 = encoder2.position
|
||||||
|
position3 = encoder3.position
|
||||||
|
menuPosition = menu_enc.position
|
||||||
|
# menu changes mode
|
||||||
|
if menuPosition != last_menuPosition:
|
||||||
|
if menuPosition > last_menuPosition:
|
||||||
|
mode_index = (mode_index + 1) % len(modes)
|
||||||
|
else:
|
||||||
|
mode_index = (mode_index - 1) % len(modes)
|
||||||
|
if mode in ("EUC ", "ADSR"):
|
||||||
|
clock_stretch = True
|
||||||
|
if mode in ("PLAY", "BPM ", "BEAT", "WAVE") and clock_stretch:
|
||||||
|
clock = ticks_ms()
|
||||||
|
clock_stretch = False
|
||||||
|
mode = modes[mode_index]
|
||||||
|
menu_display.print(f" {mode}")
|
||||||
|
last_menuPosition = menuPosition
|
||||||
|
# encoder functionality depends on mode
|
||||||
|
# encoder 0 has most functionality
|
||||||
|
if position0 != last_position0:
|
||||||
|
if position0 > last_position0:
|
||||||
|
if mode == "PLAY":
|
||||||
|
chord0_sel = (chord0_sel + 1) % len(chords)
|
||||||
|
display0.print(chord_names[chord0_sel])
|
||||||
|
elif mode == "BEAT":
|
||||||
|
beat_index = (beat_index + 1) % 5
|
||||||
|
delay = int((beat_div[beat_index] / bpm) * 1000)
|
||||||
|
display0.print(f" {beat_names[beat_index]}")
|
||||||
|
elif mode == "BPM ":
|
||||||
|
bpm += 1
|
||||||
|
delay = int((beat_div[beat_index] / bpm) * 1000)
|
||||||
|
display0.print(f" {bpm}")
|
||||||
|
elif mode == "ADSR":
|
||||||
|
adsr0_val = (adsr0_val + 1) % 20
|
||||||
|
mapped_val = simpleio.map_range(adsr0_val, 0, 19, 0.0, 1.0)
|
||||||
|
all_adsr_values[0][synth_adsr_indexes[0]] = mapped_val
|
||||||
|
the_env = synthio.Envelope(attack_time=all_adsr_values[0][0],
|
||||||
|
decay_time = all_adsr_values[0][1],
|
||||||
|
release_time=all_adsr_values[0][3],
|
||||||
|
attack_level=1, sustain_level=all_adsr_values[0][2])
|
||||||
|
synth0.envelope = the_env
|
||||||
|
elif mode == "WAVE":
|
||||||
|
synth0_wave = (synth0_wave + 1) % len(wave_names)
|
||||||
|
synth0.waveform = waveforms[synth0_wave]
|
||||||
|
elif mode == "RING":
|
||||||
|
ring0_val = (ring0_val + 1) % 25
|
||||||
|
mapped_val = simpleio.map_range(ring0_val, 0, 24, 0.0, 220.0)
|
||||||
|
synth0.ring_frequency = mapped_val
|
||||||
|
elif mode == "LFO ":
|
||||||
|
lfo_val = (lfo_val + 1) % 10
|
||||||
|
mapped_val = simpleio.map_range(lfo_val, 0, 9, 0.0, 5.0)
|
||||||
|
lfo.rate = mapped_val
|
||||||
|
elif mode == "VOL ":
|
||||||
|
vol_val = (vol_val + 1) % 10
|
||||||
|
mapped_val = simpleio.map_range(vol_val, 0, 9, 0.0, 1.0)
|
||||||
|
mixer.voice[0].level = mapped_val
|
||||||
|
else:
|
||||||
|
if mode == "PLAY":
|
||||||
|
chord0_sel = (chord0_sel - 1) % len(chords)
|
||||||
|
display0.print(chord_names[chord0_sel])
|
||||||
|
elif mode == "BEAT":
|
||||||
|
beat_index = (beat_index - 1) % 5
|
||||||
|
delay = int((beat_div[beat_index] / bpm) * 1000)
|
||||||
|
display0.print(f" {beat_names[beat_index]}")
|
||||||
|
elif mode == "BPM ":
|
||||||
|
bpm -= 1
|
||||||
|
display0.print(f" {bpm}")
|
||||||
|
elif mode == "ADSR":
|
||||||
|
adsr0_val = (adsr0_val - 1) % 20
|
||||||
|
mapped_val = simpleio.map_range(adsr0_val, 0, 19, 0.0, 1.0)
|
||||||
|
all_adsr_values[0][synth_adsr_indexes[0]] = mapped_val
|
||||||
|
the_env = synthio.Envelope(attack_time=all_adsr_values[0][0],
|
||||||
|
decay_time = all_adsr_values[0][1],
|
||||||
|
release_time=all_adsr_values[0][3],
|
||||||
|
attack_level=1, sustain_level=all_adsr_values[0][2])
|
||||||
|
synth0.envelope = the_env
|
||||||
|
elif mode == "WAVE":
|
||||||
|
synth0_wave = (synth0_wave - 1) % len(wave_names)
|
||||||
|
synth0.waveform = waveforms[synth0_wave]
|
||||||
|
elif mode == "RING":
|
||||||
|
ring0_val = (ring0_val - 1) % 25
|
||||||
|
mapped_val = simpleio.map_range(ring0_val, 0, 24, 0.0, 220.0)
|
||||||
|
synth0.ring_frequency = mapped_val
|
||||||
|
elif mode == "LFO ":
|
||||||
|
lfo_val = (lfo_val - 1) % 10
|
||||||
|
mapped_val = simpleio.map_range(lfo_val, 0, 9, 0.0, 5.0)
|
||||||
|
lfo.rate = mapped_val
|
||||||
|
elif mode == "VOL ":
|
||||||
|
vol_val = (vol_val - 1) % 10
|
||||||
|
mapped_val = simpleio.map_range(vol_val, 0, 9, 0.0, 1.0)
|
||||||
|
mixer.voice[0].level = mapped_val
|
||||||
|
last_position0 = position0
|
||||||
|
if position1 != last_position1:
|
||||||
|
if position1 > last_position1:
|
||||||
|
if mode == "PLAY":
|
||||||
|
chord1_sel = (chord1_sel + 1) % len(chords)
|
||||||
|
display1.print(chord_names[chord1_sel])
|
||||||
|
elif mode == "ADSR":
|
||||||
|
adsr1_val = (adsr1_val + 1) % 20
|
||||||
|
mapped_val = simpleio.map_range(adsr1_val, 0, 19, 0.0, 1.0)
|
||||||
|
all_adsr_values[1][synth_adsr_indexes[1]] = mapped_val
|
||||||
|
the_env = synthio.Envelope(attack_time=all_adsr_values[1][0],
|
||||||
|
decay_time = all_adsr_values[1][1],
|
||||||
|
release_time=all_adsr_values[1][3],
|
||||||
|
attack_level=1, sustain_level=all_adsr_values[1][2])
|
||||||
|
synth1.envelope = the_env
|
||||||
|
elif mode == "WAVE":
|
||||||
|
synth1_wave = (synth1_wave + 1) % len(wave_names)
|
||||||
|
synth1.waveform = waveforms[synth1_wave]
|
||||||
|
elif mode == "RING":
|
||||||
|
ring1_val = (ring1_val + 1) % 25
|
||||||
|
mapped_val = simpleio.map_range(ring1_val, 0, 24, 0.0, 220.0)
|
||||||
|
synth1.ring_frequency = mapped_val
|
||||||
|
else:
|
||||||
|
if mode == "PLAY":
|
||||||
|
chord1_sel = (chord1_sel - 1) % len(chords)
|
||||||
|
display1.print(chord_names[chord1_sel])
|
||||||
|
elif mode == "ADSR":
|
||||||
|
adsr1_val = (adsr1_val - 1) % 20
|
||||||
|
mapped_val = simpleio.map_range(adsr1_val, 0, 19, 0.0, 1.0)
|
||||||
|
all_adsr_values[1][synth_adsr_indexes[1]] = mapped_val
|
||||||
|
the_env = synthio.Envelope(attack_time=all_adsr_values[1][0],
|
||||||
|
decay_time = all_adsr_values[1][1],
|
||||||
|
release_time=all_adsr_values[1][3],
|
||||||
|
attack_level=1, sustain_level=all_adsr_values[1][2])
|
||||||
|
synth1.envelope = the_env
|
||||||
|
elif mode == "WAVE":
|
||||||
|
synth1_wave = (synth1_wave - 1) % len(wave_names)
|
||||||
|
synth1.waveform = waveforms[synth1_wave]
|
||||||
|
elif mode == "RING":
|
||||||
|
ring1_val = (ring1_val - 1) % 25
|
||||||
|
mapped_val = simpleio.map_range(ring1_val, 0, 24, 0.0, 220.0)
|
||||||
|
synth1.ring_frequency = mapped_val
|
||||||
|
last_position1 = position1
|
||||||
|
if position2 != last_position2:
|
||||||
|
if position2 > last_position2:
|
||||||
|
if mode == "PLAY":
|
||||||
|
chord2_sel = (chord2_sel + 1) % len(chords)
|
||||||
|
elif mode == "ADSR":
|
||||||
|
adsr2_val = (adsr2_val + 1) % 20
|
||||||
|
mapped_val = simpleio.map_range(adsr2_val, 0, 19, 0.0, 1.0)
|
||||||
|
all_adsr_values[2][synth_adsr_indexes[2]] = mapped_val
|
||||||
|
the_env = synthio.Envelope(attack_time=all_adsr_values[2][0],
|
||||||
|
decay_time = all_adsr_values[2][1],
|
||||||
|
release_time=all_adsr_values[2][3],
|
||||||
|
attack_level=1, sustain_level=all_adsr_values[2][2])
|
||||||
|
synth2.envelope = the_env
|
||||||
|
elif mode == "WAVE":
|
||||||
|
synth2_wave = (synth2_wave + 1) % len(wave_names)
|
||||||
|
synth2.waveform = waveforms[synth2_wave]
|
||||||
|
elif mode == "RING":
|
||||||
|
ring2_val = (ring2_val + 1) % 25
|
||||||
|
mapped_val = simpleio.map_range(ring2_val, 0, 24, 0.0, 220.0)
|
||||||
|
synth2.ring_frequency = mapped_val
|
||||||
|
else:
|
||||||
|
if mode == "PLAY":
|
||||||
|
chord2_sel = (chord2_sel - 1) % len(chords)
|
||||||
|
display2.print(chord_names[chord2_sel])
|
||||||
|
elif mode == "ADSR":
|
||||||
|
adsr2_val = (adsr2_val - 1) % 20
|
||||||
|
mapped_val = simpleio.map_range(adsr2_val, 0, 19, 0.0, 1.0)
|
||||||
|
all_adsr_values[2][synth_adsr_indexes[2]] = mapped_val
|
||||||
|
the_env = synthio.Envelope(attack_time=all_adsr_values[2][0],
|
||||||
|
decay_time = all_adsr_values[2][1],
|
||||||
|
release_time=all_adsr_values[2][3],
|
||||||
|
attack_level=1, sustain_level=all_adsr_values[2][2])
|
||||||
|
synth2.envelope = the_env
|
||||||
|
elif mode == "WAVE":
|
||||||
|
synth2_wave = (synth2_wave - 1) % len(wave_names)
|
||||||
|
synth2.waveform = waveforms[synth2_wave]
|
||||||
|
elif mode == "RING":
|
||||||
|
ring2_val = (ring2_val - 1) % 25
|
||||||
|
mapped_val = simpleio.map_range(ring2_val, 0, 24, 0.0, 220.0)
|
||||||
|
synth2.ring_frequency = mapped_val
|
||||||
|
last_position2 = position2
|
||||||
|
if position3 != last_position3:
|
||||||
|
if position3 > last_position3:
|
||||||
|
if mode == "PLAY":
|
||||||
|
chord3_sel = (chord3_sel + 1) % len(chords)
|
||||||
|
display3.print(chord_names[chord3_sel])
|
||||||
|
elif mode == "ADSR":
|
||||||
|
adsr3_val = (adsr3_val + 1) % 20
|
||||||
|
mapped_val = simpleio.map_range(adsr3_val, 0, 19, 0.0, 1.0)
|
||||||
|
all_adsr_values[3][synth_adsr_indexes[3]] = mapped_val
|
||||||
|
the_env = synthio.Envelope(attack_time=all_adsr_values[3][0],
|
||||||
|
decay_time = all_adsr_values[3][1],
|
||||||
|
release_time=all_adsr_values[3][3],
|
||||||
|
attack_level=1, sustain_level=all_adsr_values[3][2])
|
||||||
|
synth3.envelope = the_env
|
||||||
|
elif mode == "WAVE":
|
||||||
|
synth3_wave = (synth3_wave + 1) % len(wave_names)
|
||||||
|
synth3.waveform = waveforms[synth3_wave]
|
||||||
|
elif mode == "RING":
|
||||||
|
ring3_val = (ring3_val + 1) % 25
|
||||||
|
mapped_val = simpleio.map_range(ring3_val, 0, 24, 0.0, 220.0)
|
||||||
|
synth3.ring_frequency = mapped_val
|
||||||
|
else:
|
||||||
|
if mode == "PLAY":
|
||||||
|
chord3_sel = (chord3_sel - 1) % len(chords)
|
||||||
|
display3.print(chord_names[chord3_sel])
|
||||||
|
elif mode == "ADSR":
|
||||||
|
adsr3_val = (adsr3_val - 1) % 20
|
||||||
|
mapped_val = simpleio.map_range(adsr3_val, 0, 19, 0.0, 1.0)
|
||||||
|
all_adsr_values[3][synth_adsr_indexes[3]] = mapped_val
|
||||||
|
the_env = synthio.Envelope(attack_time=all_adsr_values[3][0],
|
||||||
|
decay_time = all_adsr_values[3][1],
|
||||||
|
release_time=all_adsr_values[3][3],
|
||||||
|
attack_level=1, sustain_level=all_adsr_values[3][2])
|
||||||
|
synth3.envelope = the_env
|
||||||
|
elif mode == "WAVE":
|
||||||
|
synth3_wave = (synth3_wave - 1) % len(wave_names)
|
||||||
|
synth3.waveform = waveforms[synth3_wave]
|
||||||
|
elif mode == "RING":
|
||||||
|
ring3_val = (ring3_val - 1) % 25
|
||||||
|
mapped_val = simpleio.map_range(ring3_val, 0, 24, 0.0, 220.0)
|
||||||
|
synth3.ring_frequency = mapped_val
|
||||||
|
last_position3 = position3
|
||||||
|
enc_clock = ticks_add(enc_clock, 100)
|
||||||
|
|
||||||
|
# synth plays based on ticks timing
|
||||||
|
if ticks_diff(ticks_ms(), clock) >= delay:
|
||||||
|
if play_states[0] is True:
|
||||||
|
r0, last_r0, c0 = play_euclidean(synth0, chords[chord0_sel],
|
||||||
|
rhythm0, r0, last_r0, c0, 0)
|
||||||
|
if play_states[1] is True:
|
||||||
|
r1, last_r1, c1 = play_euclidean(synth1, chords[chord1_sel],
|
||||||
|
rhythm1, r1, last_r1, c1, 2)
|
||||||
|
if play_states[2] is True:
|
||||||
|
r2, last_r2, c2 = play_euclidean(synth2, chords[chord2_sel],
|
||||||
|
rhythm2, r2, last_r2, c2, 4)
|
||||||
|
if play_states[3] is True:
|
||||||
|
r3, last_r3, c3 = play_euclidean(synth3, chords[chord3_sel],
|
||||||
|
rhythm3, r3, last_r3, c3, 6)
|
||||||
|
clock = ticks_add(clock, delay)
|
||||||
|
# in PLAY select button controls play/pause
|
||||||
|
if mode == "PLAY":
|
||||||
|
for i in range(4):
|
||||||
|
if not select_buttons[i].value and select_states[i] is False:
|
||||||
|
select_states[i] = True
|
||||||
|
if play_states[i] is True:
|
||||||
|
synth.release(synths[i])
|
||||||
|
play_states[i] = False
|
||||||
|
else:
|
||||||
|
play_states[i] = True
|
||||||
|
if select_buttons[i].value and select_states[i] is True:
|
||||||
|
select_states[i] = False
|
||||||
|
display0.print(chord_names[chord0_sel])
|
||||||
|
display1.print(chord_names[chord1_sel])
|
||||||
|
display2.print(chord_names[chord2_sel])
|
||||||
|
display3.print(chord_names[chord3_sel])
|
||||||
|
# EUC menu select resets cycle count
|
||||||
|
elif mode == "EUC ":
|
||||||
|
if not menu_buttons[0].value and menu_states[0] is False:
|
||||||
|
r0 = 0
|
||||||
|
r1 = 0
|
||||||
|
r2 = 0
|
||||||
|
r3 = 0
|
||||||
|
menu_states[0] = True
|
||||||
|
if menu_buttons[0].value and menu_states[0] is True:
|
||||||
|
menu_states[0] = False
|
||||||
|
rhythm0, euclid0_steps, euclid0_pulses, r0 = read_buttons(buttons0, button0_states,
|
||||||
|
rhythm0, euclid0_steps,
|
||||||
|
euclid0_pulses, r0)
|
||||||
|
rhythm1, euclid1_steps, euclid1_pulses, r1 = read_buttons(buttons1, button1_states,
|
||||||
|
rhythm1, euclid1_steps,
|
||||||
|
euclid1_pulses, r1)
|
||||||
|
rhythm2, euclid2_steps, euclid2_pulses, r2 = read_buttons(buttons2, button2_states,
|
||||||
|
rhythm2, euclid2_steps,
|
||||||
|
euclid2_pulses, r2)
|
||||||
|
rhythm3, euclid3_steps, euclid3_pulses, r3 = read_buttons(buttons3, button3_states,
|
||||||
|
rhythm3, euclid3_steps,
|
||||||
|
euclid3_pulses, r3)
|
||||||
|
display0.print(f" {euclid0_pulses}")
|
||||||
|
display1.print(f" {euclid1_pulses}")
|
||||||
|
display2.print(f" {euclid2_pulses}")
|
||||||
|
display3.print(f" {euclid3_pulses}")
|
||||||
|
# BPM is adjusted
|
||||||
|
elif mode == "BPM ":
|
||||||
|
if not select_buttons[0].value and select_states[0] is False:
|
||||||
|
bpm = 120
|
||||||
|
select_states[0] = True
|
||||||
|
if select_buttons[0].value and select_states[0] is True:
|
||||||
|
select_states[0] = False
|
||||||
|
display0.print(f" {bpm}")
|
||||||
|
display1.print(" ")
|
||||||
|
display2.print(" ")
|
||||||
|
display3.print(" ")
|
||||||
|
# beat division is changed
|
||||||
|
elif mode == "BEAT":
|
||||||
|
if not select_buttons[0].value and select_states[0] is False:
|
||||||
|
beat_names[beat_index] = 2
|
||||||
|
select_states[0] = True
|
||||||
|
if select_buttons[0].value and select_states[0] is True:
|
||||||
|
select_states[0] = False
|
||||||
|
display0.print(f" {beat_names[beat_index]}")
|
||||||
|
display1.print(" ")
|
||||||
|
display2.print(" ")
|
||||||
|
display3.print(" ")
|
||||||
|
# adsr for each voice
|
||||||
|
elif mode == "ADSR":
|
||||||
|
for i in range(4):
|
||||||
|
if not left_buttons[i].value and left_states[i] is False:
|
||||||
|
synth_adsr_indexes[i] = (synth_adsr_indexes[i] - 1) % 4
|
||||||
|
left_states[i] = True
|
||||||
|
the_synth = synths[i]
|
||||||
|
if left_buttons[i].value and left_states[i] is True:
|
||||||
|
left_states[i] = False
|
||||||
|
if not right_buttons[i].value and right_states[i] is False:
|
||||||
|
synth_adsr_indexes[i] = (synth_adsr_indexes[i] + 1) % 4
|
||||||
|
right_states[i] = True
|
||||||
|
if right_buttons[i].value and right_states[i] is True:
|
||||||
|
right_states[i] = False
|
||||||
|
if not select_buttons[i].value and select_states[i] is False:
|
||||||
|
the_synth = synths[i]
|
||||||
|
all_adsr_values[i][0] = 0.1
|
||||||
|
all_adsr_values[i][1] = 0.1
|
||||||
|
all_adsr_values[i][3] = 0.1
|
||||||
|
all_adsr_values[i][2] = 0.05
|
||||||
|
the_env = synthio.Envelope(attack_time=all_adsr_values[i][0],
|
||||||
|
decay_time = all_adsr_values[i][1],
|
||||||
|
release_time=all_adsr_values[i][3],
|
||||||
|
attack_level=1, sustain_level=all_adsr_values[i][2])
|
||||||
|
the_synth.envelope = the_env
|
||||||
|
select_states[i] = True
|
||||||
|
if select_buttons[i].value and select_states[i] is True:
|
||||||
|
select_states[i] = False
|
||||||
|
# pylint: disable=line-too-long
|
||||||
|
display0.print(f"{adsr_names[synth_adsr_indexes[0]]}{synth0.envelope[adsr_properties[synth_adsr_indexes[0]]]:.2f}")
|
||||||
|
display1.print(f"{adsr_names[synth_adsr_indexes[1]]}{synth1.envelope[adsr_properties[synth_adsr_indexes[1]]]:.2f}")
|
||||||
|
display2.print(f"{adsr_names[synth_adsr_indexes[2]]}{synth2.envelope[adsr_properties[synth_adsr_indexes[2]]]:.2f}")
|
||||||
|
display3.print(f"{adsr_names[synth_adsr_indexes[3]]}{synth3.envelope[adsr_properties[synth_adsr_indexes[3]]]:.2f}")
|
||||||
|
# change waveform
|
||||||
|
elif mode == "WAVE":
|
||||||
|
display0.print(f" {wave_names[synth0_wave]}")
|
||||||
|
display1.print(f" {wave_names[synth1_wave]}")
|
||||||
|
display2.print(f" {wave_names[synth2_wave]}")
|
||||||
|
display3.print(f" {wave_names[synth3_wave]}")
|
||||||
|
# adjust ring modulation
|
||||||
|
elif mode == "RING":
|
||||||
|
display0.print(f" {synth0.ring_frequency:.1f}")
|
||||||
|
display1.print(f" {synth1.ring_frequency:.1f}")
|
||||||
|
display2.print(f" {synth2.ring_frequency:.1f}")
|
||||||
|
display3.print(f" {synth3.ring_frequency:.1f}")
|
||||||
|
# adjust lfo rate used for ring modulation
|
||||||
|
elif mode == "LFO ":
|
||||||
|
display0.print("RATE")
|
||||||
|
display1.print(f" {lfo.rate:.1f}")
|
||||||
|
display2.print(" ")
|
||||||
|
display3.print(" ")
|
||||||
|
# overall volume 0.0 - 1.0
|
||||||
|
elif mode == "VOL ":
|
||||||
|
display0.print(f" {mixer.voice[0].level:.1f}")
|
||||||
|
display1.print(" ")
|
||||||
|
display2.print(" ")
|
||||||
|
display3.print(" ")
|
||||||
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/adabot.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/adabot.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/circuits.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/circuits.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/database.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/database.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/didibreaksomething.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/didibreaksomething.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/doyoureadme.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/doyoureadme.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/electrons.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/electrons.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/excellent.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/excellent.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/hearsome.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/hearsome.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/hello.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/hello.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/interesting.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/interesting.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/lemonade.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/lemonade.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/meetyou.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/meetyou.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/moodmusic.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/moodmusic.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/operational.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/operational.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/powerful.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/powerful.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/uhoh.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/uhoh.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/whhaatt.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/whhaatt.wav
Normal file
Binary file not shown.
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/whowork.wav
Normal file
BIN
Adabot_RP2040_Prop-Maker_Feather/WAVs/whowork.wav
Normal file
Binary file not shown.
149
Adabot_RP2040_Prop-Maker_Feather/code.py
Normal file
149
Adabot_RP2040_Prop-Maker_Feather/code.py
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import board
|
||||||
|
import audiocore
|
||||||
|
import audiobusio
|
||||||
|
import audiomixer
|
||||||
|
import pwmio
|
||||||
|
import neopixel
|
||||||
|
import adafruit_lis3dh
|
||||||
|
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
|
||||||
|
from digitalio import DigitalInOut, Direction, Pull
|
||||||
|
from adafruit_motor import servo
|
||||||
|
from adafruit_led_animation.animation.comet import Comet
|
||||||
|
from adafruit_led_animation.animation.pulse import Pulse
|
||||||
|
from adafruit_led_animation.animation.sparkle import Sparkle
|
||||||
|
from adafruit_led_animation.color import RED, BLUE, BLACK
|
||||||
|
|
||||||
|
# enable external power pin
|
||||||
|
# provides power to the external components
|
||||||
|
external_power = DigitalInOut(board.EXTERNAL_POWER)
|
||||||
|
external_power.direction = Direction.OUTPUT
|
||||||
|
external_power.value = True
|
||||||
|
|
||||||
|
i2c = board.I2C()
|
||||||
|
int1 = DigitalInOut(board.ACCELEROMETER_INTERRUPT)
|
||||||
|
lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c, int1=int1)
|
||||||
|
lis3dh.range = adafruit_lis3dh.RANGE_2_G
|
||||||
|
|
||||||
|
switch = DigitalInOut(board.EXTERNAL_BUTTON)
|
||||||
|
switch.direction = Direction.INPUT
|
||||||
|
switch.pull = Pull.UP
|
||||||
|
switch_state = False
|
||||||
|
|
||||||
|
wavs = []
|
||||||
|
for filename in os.listdir('/WAVs'):
|
||||||
|
if filename.lower().endswith('.wav') and not filename.startswith('.'):
|
||||||
|
wavs.append("/WAVs/"+filename)
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
|
||||||
|
mixer = audiomixer.Mixer(voice_count=1, sample_rate=22050, channel_count=1,
|
||||||
|
bits_per_sample=16, samples_signed=True, buffer_size=32768)
|
||||||
|
|
||||||
|
mixer.voice[0].level = 1
|
||||||
|
track_number = 0
|
||||||
|
wav_filename = wavs[track_number]
|
||||||
|
wav_file = open(wav_filename, "rb")
|
||||||
|
wave = audiocore.WaveFile(wav_file)
|
||||||
|
audio.play(mixer)
|
||||||
|
mixer.voice[0].play(wave)
|
||||||
|
|
||||||
|
def open_audio(num):
|
||||||
|
n = wavs[num]
|
||||||
|
f = open(n, "rb")
|
||||||
|
w = audiocore.WaveFile(f)
|
||||||
|
return w
|
||||||
|
|
||||||
|
PIXEL_PIN = board.EXTERNAL_NEOPIXELS
|
||||||
|
SERVO_PIN = board.EXTERNAL_SERVO
|
||||||
|
NUM_PIXELS = 8
|
||||||
|
ORDER = neopixel.GRB
|
||||||
|
BRIGHTNESS = 0.3
|
||||||
|
|
||||||
|
PWM = pwmio.PWMOut(SERVO_PIN, duty_cycle=2 ** 15, frequency=50)
|
||||||
|
SERVO = servo.Servo(PWM)
|
||||||
|
|
||||||
|
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)
|
||||||
|
pixel.brightness = 1
|
||||||
|
|
||||||
|
PIXELS = neopixel.NeoPixel(PIXEL_PIN, NUM_PIXELS, auto_write=False,
|
||||||
|
pixel_order=ORDER)
|
||||||
|
LARSON = Comet(PIXELS, bounce=True, speed=0.07,
|
||||||
|
tail_length=NUM_PIXELS//2,
|
||||||
|
color=(BLUE[0] * BRIGHTNESS,
|
||||||
|
BLUE[1] * BRIGHTNESS,
|
||||||
|
BLUE[2] * BRIGHTNESS))
|
||||||
|
pulse = Pulse(PIXELS, speed=0.05,
|
||||||
|
color=(BLUE[0] * BRIGHTNESS,
|
||||||
|
BLUE[1] * BRIGHTNESS,
|
||||||
|
BLUE[2] * BRIGHTNESS), period=3)
|
||||||
|
sparkle = Sparkle(PIXELS, speed=0.2,
|
||||||
|
color=(RED[0] * BRIGHTNESS,
|
||||||
|
RED[1] * BRIGHTNESS,
|
||||||
|
RED[2] * BRIGHTNESS), num_sparkles=10)
|
||||||
|
|
||||||
|
SERVO.angle = POSITION = NEXT_POSITION = 90
|
||||||
|
MOVING = False
|
||||||
|
START_TIME = ticks_ms()
|
||||||
|
DURATION = 1000
|
||||||
|
|
||||||
|
adabot_talk = False
|
||||||
|
|
||||||
|
clock = ticks_ms()
|
||||||
|
prop_time = 1000
|
||||||
|
adabot_nap = False
|
||||||
|
|
||||||
|
mixer.voice[0].play(wave)
|
||||||
|
while mixer.playing:
|
||||||
|
LARSON.animate()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if ticks_diff(ticks_ms(), clock) >= prop_time:
|
||||||
|
x, y, z = [
|
||||||
|
value / adafruit_lis3dh.STANDARD_GRAVITY for value in lis3dh.acceleration
|
||||||
|
]
|
||||||
|
if z > 0.9:
|
||||||
|
adabot_nap = True
|
||||||
|
SERVO.angle = POSITION = NEXT_POSITION = 90
|
||||||
|
else:
|
||||||
|
adabot_nap = False
|
||||||
|
if not adabot_nap:
|
||||||
|
MOVING = not MOVING
|
||||||
|
if MOVING:
|
||||||
|
POSITION = NEXT_POSITION
|
||||||
|
while abs(POSITION - NEXT_POSITION) < 10:
|
||||||
|
NEXT_POSITION = random.uniform(0, 180)
|
||||||
|
DURATION = 0.2 + 0.6 * abs(POSITION - NEXT_POSITION) / 180
|
||||||
|
else:
|
||||||
|
SERVO.angle = NEXT_POSITION
|
||||||
|
DURATION = random.uniform(0.5, 2.5)
|
||||||
|
clock = ticks_add(clock, prop_time)
|
||||||
|
if MOVING:
|
||||||
|
FRACTION = 0.0 / DURATION
|
||||||
|
FRACTION = (3 * FRACTION ** 2) - (2 * FRACTION ** 3)
|
||||||
|
SERVO.angle = POSITION + (NEXT_POSITION - POSITION) * FRACTION
|
||||||
|
if adabot_talk:
|
||||||
|
wave = open_audio(random.randint(1, 17))
|
||||||
|
mixer.voice[0].play(wave)
|
||||||
|
while mixer.playing:
|
||||||
|
sparkle.animate()
|
||||||
|
if not mixer.playing:
|
||||||
|
adabot_talk = False
|
||||||
|
PIXELS.fill(BLACK)
|
||||||
|
PIXELS.show()
|
||||||
|
elif adabot_nap:
|
||||||
|
pulse.animate()
|
||||||
|
else:
|
||||||
|
LARSON.animate()
|
||||||
|
|
||||||
|
if not switch.value and switch_state is False:
|
||||||
|
PIXELS.fill(BLACK)
|
||||||
|
PIXELS.show()
|
||||||
|
adabot_talk = True
|
||||||
|
switch_state = True
|
||||||
|
if switch.value and switch_state is True:
|
||||||
|
switch_state = False
|
||||||
29
Adafruit_Feather_ESP32-S2_Reverse_TFT/i2s_tone/code.py
Normal file
29
Adafruit_Feather_ESP32-S2_Reverse_TFT/i2s_tone/code.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython I2S Tone playback example.
|
||||||
|
Plays a tone for one second on, one
|
||||||
|
second off, in a loop.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import array
|
||||||
|
import math
|
||||||
|
import audiocore
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.A0, board.A1, board.A2)
|
||||||
|
|
||||||
|
tone_volume = 0.1 # Increase this to increase the volume of the tone.
|
||||||
|
frequency = 440 # Set this to the Hz of the tone you want to generate.
|
||||||
|
length = 8000 // frequency
|
||||||
|
sine_wave = array.array("h", [0] * length)
|
||||||
|
for i in range(length):
|
||||||
|
sine_wave[i] = int((math.sin(math.pi * 2 * i / length)) * tone_volume * (2 ** 15 - 1))
|
||||||
|
sine_wave_sample = audiocore.RawSample(sine_wave)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
audio.play(sine_wave_sample, loop=True)
|
||||||
|
time.sleep(1)
|
||||||
|
audio.stop()
|
||||||
|
time.sleep(1)
|
||||||
BIN
Adafruit_Feather_ESP32-S2_Reverse_TFT/i2s_wav/StreetChicken.wav
Executable file
BIN
Adafruit_Feather_ESP32-S2_Reverse_TFT/i2s_wav/StreetChicken.wav
Executable file
Binary file not shown.
21
Adafruit_Feather_ESP32-S2_Reverse_TFT/i2s_wav/code.py
Normal file
21
Adafruit_Feather_ESP32-S2_Reverse_TFT/i2s_wav/code.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython I2S WAV file playback.
|
||||||
|
Plays a WAV file once.
|
||||||
|
"""
|
||||||
|
import audiocore
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.A0, board.A1, board.A2)
|
||||||
|
|
||||||
|
with open("StreetChicken.wav", "rb") as wave_file:
|
||||||
|
wav = audiocore.WaveFile(wave_file)
|
||||||
|
|
||||||
|
print("Playing wav file!")
|
||||||
|
audio.play(wav)
|
||||||
|
while audio.playing:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("Done!")
|
||||||
28
Adafruit_Feather_RP2040_DVI/I2S/Tone/code.py
Normal file
28
Adafruit_Feather_RP2040_DVI/I2S/Tone/code.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython I2S Tone playback example.
|
||||||
|
Plays a tone for one second on, one second off, in a loop.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import array
|
||||||
|
import math
|
||||||
|
import audiocore
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.A0, board.A1, board.A2)
|
||||||
|
|
||||||
|
tone_volume = 0.1 # Increase this to increase the volume of the tone.
|
||||||
|
frequency = 440 # Set this to the Hz of the tone you want to generate.
|
||||||
|
length = 8000 // frequency
|
||||||
|
sine_wave = array.array("h", [0] * length)
|
||||||
|
for i in range(length):
|
||||||
|
sine_wave[i] = int((math.sin(math.pi * 2 * i / length)) * tone_volume * (2 ** 15 - 1))
|
||||||
|
sine_wave_sample = audiocore.RawSample(sine_wave)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
audio.play(sine_wave_sample, loop=True)
|
||||||
|
time.sleep(1)
|
||||||
|
audio.stop()
|
||||||
|
time.sleep(1)
|
||||||
BIN
Adafruit_Feather_RP2040_DVI/I2S/WAV/StreetChicken.wav
Normal file
BIN
Adafruit_Feather_RP2040_DVI/I2S/WAV/StreetChicken.wav
Normal file
Binary file not shown.
21
Adafruit_Feather_RP2040_DVI/I2S/WAV/code.py
Normal file
21
Adafruit_Feather_RP2040_DVI/I2S/WAV/code.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython I2S WAV file playback.
|
||||||
|
Plays a WAV file once.
|
||||||
|
"""
|
||||||
|
import audiocore
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.A0, board.A1, board.A2)
|
||||||
|
|
||||||
|
with open("StreetChicken.wav", "rb") as wave_file:
|
||||||
|
wav = audiocore.WaveFile(wave_file)
|
||||||
|
|
||||||
|
print("Playing wav file!")
|
||||||
|
audio.play(wav)
|
||||||
|
while audio.playing:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("Done!")
|
||||||
14
Adafruit_Feather_RP2040_DVI/Storage/code.py
Normal file
14
Adafruit_Feather_RP2040_DVI/Storage/code.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Essentials Storage CP Filesystem boot.py file
|
||||||
|
"""
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
import storage
|
||||||
|
|
||||||
|
button = digitalio.DigitalInOut(board.BUTTON)
|
||||||
|
button.switch_to_input(pull=digitalio.Pull.UP)
|
||||||
|
|
||||||
|
# If the button is connected to ground, the filesystem is writable by CircuitPython
|
||||||
|
storage.remount("/", readonly=button.value)
|
||||||
80
Adafruit_Feather_RP2040_DVI/asyncio/code.py
Normal file
80
Adafruit_Feather_RP2040_DVI/asyncio/code.py
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2022 Dan Halbert for Adafruit Industries
|
||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython asyncio example for two NeoPixel rings and one button.
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import board
|
||||||
|
import neopixel
|
||||||
|
import keypad
|
||||||
|
from rainbowio import colorwheel
|
||||||
|
|
||||||
|
button_pin = board.BUTTON # The pin the button is connected to.
|
||||||
|
num_pixels = 16 # The number of NeoPixels on a single ring.
|
||||||
|
brightness = 0.2 # The LED brightness.
|
||||||
|
|
||||||
|
# Set up NeoPixel rings.
|
||||||
|
ring_one = neopixel.NeoPixel(board.A1, num_pixels, brightness=brightness, auto_write=False)
|
||||||
|
ring_two = neopixel.NeoPixel(board.A2, num_pixels, brightness=brightness, auto_write=False)
|
||||||
|
|
||||||
|
|
||||||
|
class AnimationControls:
|
||||||
|
"""The controls to allow you to vary the rainbow and blink animations."""
|
||||||
|
def __init__(self):
|
||||||
|
self.reverse = False
|
||||||
|
self.wait = 0.0
|
||||||
|
self.delay = 0.5
|
||||||
|
|
||||||
|
|
||||||
|
async def rainbow_cycle(controls):
|
||||||
|
"""Rainbow cycle animation on ring one."""
|
||||||
|
while True:
|
||||||
|
for j in range(255, -1, -1) if controls.reverse else range(0, 256, 1):
|
||||||
|
for i in range(num_pixels):
|
||||||
|
rc_index = (i * 256 // num_pixels) + j
|
||||||
|
ring_one[i] = colorwheel(rc_index & 255)
|
||||||
|
ring_one.show()
|
||||||
|
await asyncio.sleep(controls.wait)
|
||||||
|
|
||||||
|
|
||||||
|
async def blink(controls):
|
||||||
|
"""Blink animation on ring two."""
|
||||||
|
while True:
|
||||||
|
ring_two.fill((0, 0, 255))
|
||||||
|
ring_two.show()
|
||||||
|
await asyncio.sleep(controls.delay)
|
||||||
|
ring_two.fill((0, 0, 0))
|
||||||
|
ring_two.show()
|
||||||
|
await asyncio.sleep(controls.delay)
|
||||||
|
await asyncio.sleep(controls.wait)
|
||||||
|
|
||||||
|
|
||||||
|
async def monitor_button(button, controls):
|
||||||
|
"""Monitor button that reverses rainbow direction and changes blink speed.
|
||||||
|
Assume button is active low.
|
||||||
|
"""
|
||||||
|
with keypad.Keys((button,), value_when_pressed=False, pull=True) as key:
|
||||||
|
while True:
|
||||||
|
key_event = key.events.get()
|
||||||
|
if key_event:
|
||||||
|
if key_event.pressed:
|
||||||
|
controls.reverse = True
|
||||||
|
controls.delay = 0.1
|
||||||
|
elif key_event.released:
|
||||||
|
controls.reverse = False
|
||||||
|
controls.delay = 0.5
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
animation_controls = AnimationControls()
|
||||||
|
button_task = asyncio.create_task(monitor_button(button_pin, animation_controls))
|
||||||
|
animation_task = asyncio.create_task(rainbow_cycle(animation_controls))
|
||||||
|
blink_task = asyncio.create_task(blink(animation_controls))
|
||||||
|
|
||||||
|
# This will run forever, because no tasks ever finish.
|
||||||
|
await asyncio.gather(button_task, animation_task, blink_task)
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Capacitive Touch Pin Example - Print to the serial console when one pin is touched.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
import touchio
|
||||||
|
|
||||||
|
touch = touchio.TouchIn(board.A3)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if touch.value:
|
||||||
|
print("Pin touched!")
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Capacitive Two Touch Pin Example - Print to the serial console when a pin is touched.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
import touchio
|
||||||
|
|
||||||
|
touch_one = touchio.TouchIn(board.A3)
|
||||||
|
touch_two = touchio.TouchIn(board.D24)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if touch_one.value:
|
||||||
|
print("Pin one touched!")
|
||||||
|
if touch_two.value:
|
||||||
|
print("Pin two touched!")
|
||||||
|
time.sleep(0.1)
|
||||||
29
Adafruit_Feather_RP2040_RFM69/I2S/Tone/code.py
Normal file
29
Adafruit_Feather_RP2040_RFM69/I2S/Tone/code.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython I2S Tone playback example.
|
||||||
|
Plays a tone for one second on, one
|
||||||
|
second off, in a loop.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import array
|
||||||
|
import math
|
||||||
|
import audiocore
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.A0, board.A1, board.A2)
|
||||||
|
|
||||||
|
tone_volume = 0.1 # Increase this to increase the volume of the tone.
|
||||||
|
frequency = 440 # Set this to the Hz of the tone you want to generate.
|
||||||
|
length = 8000 // frequency
|
||||||
|
sine_wave = array.array("h", [0] * length)
|
||||||
|
for i in range(length):
|
||||||
|
sine_wave[i] = int((math.sin(math.pi * 2 * i / length)) * tone_volume * (2 ** 15 - 1))
|
||||||
|
sine_wave_sample = audiocore.RawSample(sine_wave)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
audio.play(sine_wave_sample, loop=True)
|
||||||
|
time.sleep(1)
|
||||||
|
audio.stop()
|
||||||
|
time.sleep(1)
|
||||||
BIN
Adafruit_Feather_RP2040_RFM69/I2S/WAV/StreetChicken.wav
Normal file
BIN
Adafruit_Feather_RP2040_RFM69/I2S/WAV/StreetChicken.wav
Normal file
Binary file not shown.
21
Adafruit_Feather_RP2040_RFM69/I2S/WAV/code.py
Normal file
21
Adafruit_Feather_RP2040_RFM69/I2S/WAV/code.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython I2S WAV file playback.
|
||||||
|
Plays a WAV file once.
|
||||||
|
"""
|
||||||
|
import audiocore
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.A0, board.A1, board.A2)
|
||||||
|
|
||||||
|
with open("StreetChicken.wav", "rb") as wave_file:
|
||||||
|
wav = audiocore.WaveFile(wave_file)
|
||||||
|
|
||||||
|
print("Playing wav file!")
|
||||||
|
audio.play(wav)
|
||||||
|
while audio.playing:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("Done!")
|
||||||
56
Adafruit_Feather_RP2040_RFM69/Send_and_Receive_Demo/Receive_Demo/code.py
Executable file
56
Adafruit_Feather_RP2040_RFM69/Send_and_Receive_Demo/Receive_Demo/code.py
Executable file
|
|
@ -0,0 +1,56 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
CircuitPython Feather RP2040 RFM69 Packet Receive Demo
|
||||||
|
|
||||||
|
This demo waits for a "button" packet. When the first packet is received, the NeoPixel LED
|
||||||
|
lights up red. The next packet changes it to green. The next packet changes it to blue.
|
||||||
|
Subsequent button packets cycle through the same colors in the same order.
|
||||||
|
|
||||||
|
This example is meant to be paired with the Packet Send Demo code running
|
||||||
|
on a second Feather RP2040 RFM69 board.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
import neopixel
|
||||||
|
import adafruit_rfm69
|
||||||
|
|
||||||
|
# Set up NeoPixel.
|
||||||
|
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)
|
||||||
|
pixel.brightness = 0.5
|
||||||
|
|
||||||
|
# Define the possible NeoPixel colors. You can add as many colors to this list as you like!
|
||||||
|
# Simply follow the format shown below. Make sure you include the comma after the color tuple!
|
||||||
|
color_values = [
|
||||||
|
(255, 0, 0),
|
||||||
|
(0, 255, 0),
|
||||||
|
(0, 0, 255),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Define radio frequency in MHz. Must match your
|
||||||
|
# module. Can be a value like 915.0, 433.0, etc.
|
||||||
|
RADIO_FREQ_MHZ = 915.0
|
||||||
|
|
||||||
|
# Define Chip Select and Reset pins for the radio module.
|
||||||
|
CS = digitalio.DigitalInOut(board.RFM_CS)
|
||||||
|
RESET = digitalio.DigitalInOut(board.RFM_RST)
|
||||||
|
|
||||||
|
# Initialise RFM69 radio
|
||||||
|
rfm69 = adafruit_rfm69.RFM69(board.SPI(), CS, RESET, RADIO_FREQ_MHZ)
|
||||||
|
|
||||||
|
color_index = 0
|
||||||
|
# Wait to receive packets.
|
||||||
|
print("Waiting for packets...")
|
||||||
|
while True:
|
||||||
|
# Look for a new packet - wait up to 5 seconds:
|
||||||
|
packet = rfm69.receive(timeout=5.0)
|
||||||
|
# If no packet was received during the timeout then None is returned.
|
||||||
|
if packet is not None:
|
||||||
|
print("Received a packet!")
|
||||||
|
# If the received packet is b'button'...
|
||||||
|
if packet == b'button':
|
||||||
|
# ...cycle the NeoPixel LED color through the color_values list.
|
||||||
|
pixel.fill(color_values[color_index])
|
||||||
|
color_index = (color_index + 1) % len(color_values)
|
||||||
36
Adafruit_Feather_RP2040_RFM69/Send_and_Receive_Demo/Send_Demo/code.py
Executable file
36
Adafruit_Feather_RP2040_RFM69/Send_and_Receive_Demo/Send_Demo/code.py
Executable file
|
|
@ -0,0 +1,36 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
CircuitPython Feather RP2040 RFM69 Packet Send Demo
|
||||||
|
|
||||||
|
This demo sends a "button" packet when the Boot button is pressed.
|
||||||
|
|
||||||
|
This example is meant to be paired with the Packet Receive Demo code running
|
||||||
|
on a second Feather RP2040 RFM69 board.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
import keypad
|
||||||
|
import adafruit_rfm69
|
||||||
|
|
||||||
|
# Set up button using keypad module.
|
||||||
|
button = keypad.Keys((board.BUTTON,), value_when_pressed=False)
|
||||||
|
|
||||||
|
# Define radio frequency in MHz. Must match your
|
||||||
|
# module. Can be a value like 915.0, 433.0, etc.
|
||||||
|
RADIO_FREQ_MHZ = 915.0
|
||||||
|
|
||||||
|
# Define Chip Select and Reset pins for the radio module.
|
||||||
|
CS = digitalio.DigitalInOut(board.RFM_CS)
|
||||||
|
RESET = digitalio.DigitalInOut(board.RFM_RST)
|
||||||
|
|
||||||
|
# Initialise RFM69 radio
|
||||||
|
rfm69 = adafruit_rfm69.RFM69(board.SPI(), CS, RESET, RADIO_FREQ_MHZ)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
button_press = button.events.get()
|
||||||
|
if button_press:
|
||||||
|
if button_press.pressed:
|
||||||
|
rfm69.send(bytes("button", "UTF-8"))
|
||||||
14
Adafruit_Feather_RP2040_RFM69/Storage/boot.py
Normal file
14
Adafruit_Feather_RP2040_RFM69/Storage/boot.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Essentials Storage CP Filesystem boot.py file
|
||||||
|
"""
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
import storage
|
||||||
|
|
||||||
|
button = digitalio.DigitalInOut(board.BUTTON)
|
||||||
|
button.switch_to_input(pull=digitalio.Pull.UP)
|
||||||
|
|
||||||
|
# If the OBJECT_NAME is connected to ground, the filesystem is writable by CircuitPython
|
||||||
|
storage.remount("/", readonly=button.value)
|
||||||
37
Adafruit_Feather_RP2040_RFM69/Storage/code.py
Normal file
37
Adafruit_Feather_RP2040_RFM69/Storage/code.py
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Essentials Storage CP Filesystem code.py file
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
import microcontroller
|
||||||
|
|
||||||
|
led = digitalio.DigitalInOut(board.LED)
|
||||||
|
led.switch_to_output()
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open("/temperature.txt", "a") as temp_log:
|
||||||
|
while True:
|
||||||
|
# The microcontroller temperature in Celsius. Include the
|
||||||
|
# math to do the C to F conversion here, if desired.
|
||||||
|
temperature = microcontroller.cpu.temperature
|
||||||
|
|
||||||
|
# Write the temperature to the temperature.txt file every 10 seconds.
|
||||||
|
temp_log.write('{0:.2f}\n'.format(temperature))
|
||||||
|
temp_log.flush()
|
||||||
|
|
||||||
|
# Blink the LED on every write...
|
||||||
|
led.value = True
|
||||||
|
time.sleep(1) # ...for one second.
|
||||||
|
led.value = False # Then turn it off...
|
||||||
|
time.sleep(9) # ...for the other 9 seconds.
|
||||||
|
|
||||||
|
except OSError as e: # When the filesystem is NOT writable by CircuitPython...
|
||||||
|
delay = 0.5 # ...blink the LED every half second.
|
||||||
|
if e.args[0] == 28: # If the file system is full...
|
||||||
|
delay = 0.15 # ...blink the LED every 0.15 seconds!
|
||||||
|
while True:
|
||||||
|
led.value = not led.value
|
||||||
|
time.sleep(delay)
|
||||||
80
Adafruit_Feather_RP2040_RFM69/asyncio/code.py
Normal file
80
Adafruit_Feather_RP2040_RFM69/asyncio/code.py
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2022 Dan Halbert for Adafruit Industries
|
||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython asyncio example for two NeoPixel rings and one button.
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import board
|
||||||
|
import neopixel
|
||||||
|
import keypad
|
||||||
|
from rainbowio import colorwheel
|
||||||
|
|
||||||
|
button_pin = board.BUTTON # The pin the button is connected to.
|
||||||
|
num_pixels = 16 # The number of NeoPixels on a single ring.
|
||||||
|
brightness = 0.2 # The LED brightness.
|
||||||
|
|
||||||
|
# Set up NeoPixel rings.
|
||||||
|
ring_one = neopixel.NeoPixel(board.A1, num_pixels, brightness=brightness, auto_write=False)
|
||||||
|
ring_two = neopixel.NeoPixel(board.A2, num_pixels, brightness=brightness, auto_write=False)
|
||||||
|
|
||||||
|
|
||||||
|
class AnimationControls:
|
||||||
|
"""The controls to allow you to vary the rainbow and blink animations."""
|
||||||
|
def __init__(self):
|
||||||
|
self.reverse = False
|
||||||
|
self.wait = 0.0
|
||||||
|
self.delay = 0.5
|
||||||
|
|
||||||
|
|
||||||
|
async def rainbow_cycle(controls):
|
||||||
|
"""Rainbow cycle animation on ring one."""
|
||||||
|
while True:
|
||||||
|
for j in range(255, -1, -1) if controls.reverse else range(0, 256, 1):
|
||||||
|
for i in range(num_pixels):
|
||||||
|
rc_index = (i * 256 // num_pixels) + j
|
||||||
|
ring_one[i] = colorwheel(rc_index & 255)
|
||||||
|
ring_one.show()
|
||||||
|
await asyncio.sleep(controls.wait)
|
||||||
|
|
||||||
|
|
||||||
|
async def blink(controls):
|
||||||
|
"""Blink animation on ring two."""
|
||||||
|
while True:
|
||||||
|
ring_two.fill((0, 0, 255))
|
||||||
|
ring_two.show()
|
||||||
|
await asyncio.sleep(controls.delay)
|
||||||
|
ring_two.fill((0, 0, 0))
|
||||||
|
ring_two.show()
|
||||||
|
await asyncio.sleep(controls.delay)
|
||||||
|
await asyncio.sleep(controls.wait)
|
||||||
|
|
||||||
|
|
||||||
|
async def monitor_button(button, controls):
|
||||||
|
"""Monitor button that reverses rainbow direction and changes blink speed.
|
||||||
|
Assume button is active low.
|
||||||
|
"""
|
||||||
|
with keypad.Keys((button,), value_when_pressed=False, pull=True) as key:
|
||||||
|
while True:
|
||||||
|
key_event = key.events.get()
|
||||||
|
if key_event:
|
||||||
|
if key_event.pressed:
|
||||||
|
controls.reverse = True
|
||||||
|
controls.delay = 0.1
|
||||||
|
elif key_event.released:
|
||||||
|
controls.reverse = False
|
||||||
|
controls.delay = 0.5
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
animation_controls = AnimationControls()
|
||||||
|
button_task = asyncio.create_task(monitor_button(button_pin, animation_controls))
|
||||||
|
animation_task = asyncio.create_task(rainbow_cycle(animation_controls))
|
||||||
|
blink_task = asyncio.create_task(blink(animation_controls))
|
||||||
|
|
||||||
|
# This will run forever, because no tasks ever finish.
|
||||||
|
await asyncio.gather(button_task, animation_task, blink_task)
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
CircuitPython Feather RP2040 RFM95 Packet Receive Demo
|
||||||
|
|
||||||
|
This demo waits for a "button" packet. When the first packet is received, the NeoPixel LED
|
||||||
|
lights up red. The next packet changes it to green. The next packet changes it to blue.
|
||||||
|
Subsequent button packets cycle through the same colors in the same order.
|
||||||
|
|
||||||
|
This example is meant to be paired with the Packet Send Demo code running
|
||||||
|
on a second Feather RP2040 RFM95 board.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
import neopixel
|
||||||
|
import adafruit_rfm9x
|
||||||
|
|
||||||
|
# Set up NeoPixel.
|
||||||
|
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)
|
||||||
|
pixel.brightness = 0.5
|
||||||
|
|
||||||
|
# Define the possible NeoPixel colors. You can add as many colors to this list as you like!
|
||||||
|
# Simply follow the format shown below. Make sure you include the comma after the color tuple!
|
||||||
|
color_values = [
|
||||||
|
(255, 0, 0),
|
||||||
|
(0, 255, 0),
|
||||||
|
(0, 0, 255),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Define radio frequency in MHz. Must match your
|
||||||
|
# module. Can be a value like 915.0, 433.0, etc.
|
||||||
|
RADIO_FREQ_MHZ = 915.0
|
||||||
|
|
||||||
|
# Define Chip Select and Reset pins for the radio module.
|
||||||
|
CS = digitalio.DigitalInOut(board.RFM_CS)
|
||||||
|
RESET = digitalio.DigitalInOut(board.RFM_RST)
|
||||||
|
|
||||||
|
# Initialise RFM95 radio
|
||||||
|
rfm95 = adafruit_rfm9x.RFM9x(board.SPI(), CS, RESET, RADIO_FREQ_MHZ)
|
||||||
|
|
||||||
|
color_index = 0
|
||||||
|
# Wait to receive packets.
|
||||||
|
print("Waiting for packets...")
|
||||||
|
while True:
|
||||||
|
# Look for a new packet - wait up to 5 seconds:
|
||||||
|
packet = rfm95.receive(timeout=5.0)
|
||||||
|
# If no packet was received during the timeout then None is returned.
|
||||||
|
if packet is not None:
|
||||||
|
print("Received a packet!")
|
||||||
|
# If the received packet is b'button'...
|
||||||
|
if packet == b'button':
|
||||||
|
# ...cycle the NeoPixel LED color through the color_values list.
|
||||||
|
pixel.fill(color_values[color_index])
|
||||||
|
color_index = (color_index + 1) % len(color_values)
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
CircuitPython Feather RP2040 RFM95 Packet Send Demo
|
||||||
|
|
||||||
|
This demo sends a "button" packet when the Boot button is pressed.
|
||||||
|
|
||||||
|
This example is meant to be paired with the Packet Receive Demo code running
|
||||||
|
on a second Feather RP2040 RFM95 board.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
import keypad
|
||||||
|
import adafruit_rfm9x
|
||||||
|
|
||||||
|
# Set up button using keypad module.
|
||||||
|
button = keypad.Keys((board.BUTTON,), value_when_pressed=False)
|
||||||
|
|
||||||
|
# Define radio frequency in MHz. Must match your
|
||||||
|
# module. Can be a value like 915.0, 433.0, etc.
|
||||||
|
RADIO_FREQ_MHZ = 915.0
|
||||||
|
|
||||||
|
# Define Chip Select and Reset pins for the radio module.
|
||||||
|
CS = digitalio.DigitalInOut(board.RFM_CS)
|
||||||
|
RESET = digitalio.DigitalInOut(board.RFM_RST)
|
||||||
|
|
||||||
|
# Initialise RFM95 radio
|
||||||
|
rfm95 = adafruit_rfm9x.RFM9x(board.SPI(), CS, RESET, RADIO_FREQ_MHZ)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
button_press = button.events.get()
|
||||||
|
if button_press:
|
||||||
|
if button_press.pressed:
|
||||||
|
rfm95.send(bytes("button", "UTF-8"))
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Ladyada for Adafruit Industries
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
/*
|
||||||
|
This example plays a 'raw' PCM file from memory to I2S
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <I2S.h>
|
||||||
|
|
||||||
|
#include "startup.h" // audio file in flash
|
||||||
|
|
||||||
|
// Create the I2S port using a PIO state machine
|
||||||
|
I2S i2s(OUTPUT);
|
||||||
|
|
||||||
|
// GPIO pin numbers
|
||||||
|
#define pBCLK A2 // QT Py BFF default BITCLOCK
|
||||||
|
#define pWS A1 // QT Py BFF default LRCLOCK
|
||||||
|
#define pDOUT A0 // QT Py BFF default DATA
|
||||||
|
|
||||||
|
#define USERBUTTON 21 // QT Py RP2040 built in button
|
||||||
|
|
||||||
|
// variable shared between cores
|
||||||
|
volatile bool playaudio = false;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
//while (!Serial) delay(10);
|
||||||
|
Serial.println("I2S playback demo");
|
||||||
|
|
||||||
|
pinMode(USERBUTTON, INPUT_PULLUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// on button press tell the other core to play audio clip!
|
||||||
|
if (!digitalRead(USERBUTTON)) {
|
||||||
|
playaudio = true;
|
||||||
|
} else {
|
||||||
|
playaudio = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup1() {
|
||||||
|
i2s.setBCLK(pBCLK);
|
||||||
|
i2s.setDATA(pDOUT);
|
||||||
|
i2s.setBitsPerSample(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop1() {
|
||||||
|
// the main loop will tell us when it wants us to play!
|
||||||
|
if (playaudio) {
|
||||||
|
play_i2s(startupAudioData, sizeof(startupAudioData), startupSampleRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void play_i2s(const uint8_t *data, uint32_t len, uint32_t rate) {
|
||||||
|
// start I2S at the sample rate with 16-bits per sample
|
||||||
|
if (!i2s.begin(rate)) {
|
||||||
|
Serial.println("Failed to initialize I2S!");
|
||||||
|
delay(500);
|
||||||
|
i2s.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint32_t i=0; i<len; i++) {
|
||||||
|
uint16_t sample = (uint16_t)data[i] << 6; // our data is 10 bit but we want 16 bit so we add some gain
|
||||||
|
// write the same sample twice, once for left and once for the right channel
|
||||||
|
i2s.write(sample);
|
||||||
|
i2s.write(sample);
|
||||||
|
}
|
||||||
|
i2s.end();
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,74 @@
|
||||||
|
// SPDX-FileCopyrightText: 2016 Sandeep Mistry
|
||||||
|
// SPDX-FileCopyrightText: 2022 Earle F. Philhower, III
|
||||||
|
// SPDX-FileCopyrightText: 2023 Ladyada for Adafruit Industries
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
/*
|
||||||
|
This example generates a square wave based tone at a specified frequency
|
||||||
|
and sample rate. Then outputs the data using the I2S interface to a
|
||||||
|
MAX08357 I2S Amp Breakout board.
|
||||||
|
|
||||||
|
created 17 November 2016
|
||||||
|
by Sandeep Mistry
|
||||||
|
modified for RP2040 by Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||||
|
|
||||||
|
|
||||||
|
bool setBCLK(pin_size_t pin);
|
||||||
|
- This assigns two adjacent pins - the pin after this one (one greater)
|
||||||
|
is the WS (word select) signal, which toggles before the sample for
|
||||||
|
each channel is sent
|
||||||
|
|
||||||
|
bool setDATA(pin_size_t pin);
|
||||||
|
- Sets the DOUT pin, can be any valid GPIO pin
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <I2S.h>
|
||||||
|
|
||||||
|
// Create the I2S port using a PIO state machine
|
||||||
|
I2S i2s(OUTPUT);
|
||||||
|
|
||||||
|
// GPIO pin numbers
|
||||||
|
#define pBCLK A2 // QT Py BFF default BITCLOCK
|
||||||
|
#define pWS A1 // QT Py BFF default LRCLOCK
|
||||||
|
#define pDOUT A0 // QT Py BFF default DATA
|
||||||
|
|
||||||
|
const int frequency = 440; // frequency of square wave in Hz
|
||||||
|
const int amplitude = 500; // amplitude of square wave
|
||||||
|
const int sampleRate = 16000; // 16 KHz is a good quality
|
||||||
|
|
||||||
|
const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave
|
||||||
|
|
||||||
|
int16_t sample = amplitude; // current sample value
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial) delay(10);
|
||||||
|
Serial.println("I2S simple tone");
|
||||||
|
|
||||||
|
i2s.setBCLK(pBCLK);
|
||||||
|
i2s.setDATA(pDOUT);
|
||||||
|
i2s.setBitsPerSample(16);
|
||||||
|
|
||||||
|
// start I2S at the sample rate with 16-bits per sample
|
||||||
|
if (!i2s.begin(sampleRate)) {
|
||||||
|
Serial.println("Failed to initialize I2S!");
|
||||||
|
while (1); // do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (count % halfWavelength == 0) {
|
||||||
|
// invert the sample every half wavelength count multiple to generate square wave
|
||||||
|
sample = -1 * sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the same sample twice, once for left and once for the right channel
|
||||||
|
i2s.write(sample);
|
||||||
|
i2s.write(sample);
|
||||||
|
|
||||||
|
// increment the counter for the next sample
|
||||||
|
count++;
|
||||||
|
}
|
||||||
28
Adafruit_I2S_BFF/CircuitPython/Tone/code.py
Normal file
28
Adafruit_I2S_BFF/CircuitPython/Tone/code.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython I2S Tone playback example.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import array
|
||||||
|
import math
|
||||||
|
import audiocore
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
|
||||||
|
TONE_VOLUME = 0.1 # Increase this to increase the volume of the tone.
|
||||||
|
FREQUENCY = 440 # Set this to the Hz of the tone you want to generate.
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.A2, board.A1, board.A0)
|
||||||
|
|
||||||
|
length = 8000 // FREQUENCY
|
||||||
|
sine_wave = array.array("h", [0] * length)
|
||||||
|
for i in range(length):
|
||||||
|
sine_wave[i] = int((math.sin(math.pi * 2 * i / length)) * TONE_VOLUME * (2 ** 15 - 1))
|
||||||
|
sine_wave_sample = audiocore.RawSample(sine_wave)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
audio.play(sine_wave_sample, loop=True)
|
||||||
|
time.sleep(1)
|
||||||
|
audio.stop()
|
||||||
|
time.sleep(1)
|
||||||
BIN
Adafruit_I2S_BFF/CircuitPython/WAV/booploop.wav
Normal file
BIN
Adafruit_I2S_BFF/CircuitPython/WAV/booploop.wav
Normal file
Binary file not shown.
22
Adafruit_I2S_BFF/CircuitPython/WAV/code.py
Normal file
22
Adafruit_I2S_BFF/CircuitPython/WAV/code.py
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython I2S WAV file playback.
|
||||||
|
"""
|
||||||
|
import audiocore
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
|
||||||
|
LOOP = False # Update to True loop WAV playback. False plays once.
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.A2, board.A1, board.A0)
|
||||||
|
|
||||||
|
with open("chikken.wav", "rb") as wave_file:
|
||||||
|
wav = audiocore.WaveFile(wave_file)
|
||||||
|
|
||||||
|
print("Playing wav file!")
|
||||||
|
audio.play(wav, loop=LOOP)
|
||||||
|
while audio.playing:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("Done!")
|
||||||
15
Adafruit_Metro_ESP32-S3/Capacitive_Touch/One_Pin/code.py
Normal file
15
Adafruit_Metro_ESP32-S3/Capacitive_Touch/One_Pin/code.py
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Capacitive Touch Pin Example - Print to the serial console when one pin is touched.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
import touchio
|
||||||
|
|
||||||
|
touch = touchio.TouchIn(board.A0)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if touch.value:
|
||||||
|
print("Pin touched!")
|
||||||
|
time.sleep(0.1)
|
||||||
18
Adafruit_Metro_ESP32-S3/Capacitive_Touch/Two_Pins/code.py
Normal file
18
Adafruit_Metro_ESP32-S3/Capacitive_Touch/Two_Pins/code.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Capacitive Two Touch Pin Example - Print to the serial console when a pin is touched.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
import touchio
|
||||||
|
|
||||||
|
touch_one = touchio.TouchIn(board.A0)
|
||||||
|
touch_two = touchio.TouchIn(board.A5)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if touch_one.value:
|
||||||
|
print("Pin one touched!")
|
||||||
|
if touch_two.value:
|
||||||
|
print("Pin two touched!")
|
||||||
|
time.sleep(0.1)
|
||||||
19
Adafruit_Metro_ESP32-S3/Digital_Input/code.py
Normal file
19
Adafruit_Metro_ESP32-S3/Digital_Input/code.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Digital Input Example - Blinking an LED using a button switch.
|
||||||
|
"""
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
|
||||||
|
led = digitalio.DigitalInOut(board.LED)
|
||||||
|
led.direction = digitalio.Direction.OUTPUT
|
||||||
|
|
||||||
|
button = digitalio.DigitalInOut(board.A0)
|
||||||
|
button.switch_to_input(pull=digitalio.Pull.UP)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if not button.value:
|
||||||
|
led.value = True
|
||||||
|
else:
|
||||||
|
led.value = False
|
||||||
29
Adafruit_Metro_ESP32-S3/I2S/Tone_Playback/code.py
Normal file
29
Adafruit_Metro_ESP32-S3/I2S/Tone_Playback/code.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython I2S Tone playback example.
|
||||||
|
Plays a tone for one second on, one
|
||||||
|
second off, in a loop.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import array
|
||||||
|
import math
|
||||||
|
import audiocore
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.A0, board.A1, board.A2)
|
||||||
|
|
||||||
|
tone_volume = 0.1 # Increase this to increase the volume of the tone.
|
||||||
|
frequency = 440 # Set this to the Hz of the tone you want to generate.
|
||||||
|
length = 8000 // frequency
|
||||||
|
sine_wave = array.array("h", [0] * length)
|
||||||
|
for i in range(length):
|
||||||
|
sine_wave[i] = int((math.sin(math.pi * 2 * i / length)) * tone_volume * (2 ** 15 - 1))
|
||||||
|
sine_wave_sample = audiocore.RawSample(sine_wave)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
audio.play(sine_wave_sample, loop=True)
|
||||||
|
time.sleep(1)
|
||||||
|
audio.stop()
|
||||||
|
time.sleep(1)
|
||||||
BIN
Adafruit_Metro_ESP32-S3/I2S/WAV_Playback/StreetChicken.wav
Normal file
BIN
Adafruit_Metro_ESP32-S3/I2S/WAV_Playback/StreetChicken.wav
Normal file
Binary file not shown.
21
Adafruit_Metro_ESP32-S3/I2S/WAV_Playback/code.py
Normal file
21
Adafruit_Metro_ESP32-S3/I2S/WAV_Playback/code.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython I2S WAV file playback.
|
||||||
|
Plays a WAV file once.
|
||||||
|
"""
|
||||||
|
import audiocore
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.A0, board.A1, board.A2)
|
||||||
|
|
||||||
|
with open("StreetChicken.wav", "rb") as wave_file:
|
||||||
|
wav = audiocore.WaveFile(wave_file)
|
||||||
|
|
||||||
|
print("Playing wav file!")
|
||||||
|
audio.play(wav)
|
||||||
|
while audio.playing:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("Done!")
|
||||||
52
Adafruit_Metro_ESP32-S3/SDCard/SD_Read/code.py
Normal file
52
Adafruit_Metro_ESP32-S3/SDCard/SD_Read/code.py
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""CircuitPython Essentials SD Card Read Demo"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import digitalio
|
||||||
|
import board
|
||||||
|
import storage
|
||||||
|
import adafruit_sdcard
|
||||||
|
|
||||||
|
# The SD_CS pin is the chip select line.
|
||||||
|
SD_CS = board.SD_CS
|
||||||
|
|
||||||
|
# Connect to the card and mount the filesystem.
|
||||||
|
cs = digitalio.DigitalInOut(SD_CS)
|
||||||
|
sdcard = adafruit_sdcard.SDCard(board.SPI(), cs)
|
||||||
|
vfs = storage.VfsFat(sdcard)
|
||||||
|
storage.mount(vfs, "/sd")
|
||||||
|
|
||||||
|
# Use the filesystem as normal! Our files are under /sd
|
||||||
|
|
||||||
|
# This helper function will print the contents of the SD
|
||||||
|
def print_directory(path, tabs=0):
|
||||||
|
for file in os.listdir(path):
|
||||||
|
stats = os.stat(path + "/" + file)
|
||||||
|
filesize = stats[6]
|
||||||
|
isdir = stats[0] & 0x4000
|
||||||
|
|
||||||
|
if filesize < 1000:
|
||||||
|
sizestr = str(filesize) + " bytes"
|
||||||
|
elif filesize < 1000000:
|
||||||
|
sizestr = "%0.1f KB" % (filesize / 1000)
|
||||||
|
else:
|
||||||
|
sizestr = "%0.1f MB" % (filesize / 1000000)
|
||||||
|
|
||||||
|
prettyprintname = ""
|
||||||
|
for _ in range(tabs):
|
||||||
|
prettyprintname += " "
|
||||||
|
prettyprintname += file
|
||||||
|
if isdir:
|
||||||
|
prettyprintname += "/"
|
||||||
|
print("{0:<40} Size: {1:>10}".format(prettyprintname, sizestr))
|
||||||
|
|
||||||
|
# recursively print directory contents
|
||||||
|
if isdir:
|
||||||
|
print_directory(path + "/" + file, tabs + 1)
|
||||||
|
|
||||||
|
|
||||||
|
print("Files on filesystem:")
|
||||||
|
print("====================")
|
||||||
|
print_directory("/sd")
|
||||||
36
Adafruit_Metro_ESP32-S3/SDCard/SD_Write/code.py
Normal file
36
Adafruit_Metro_ESP32-S3/SDCard/SD_Write/code.py
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
CircuitPython Essentials SD Card Write Demo
|
||||||
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
|
import adafruit_sdcard
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
import microcontroller
|
||||||
|
import storage
|
||||||
|
|
||||||
|
# The SD_CS pin is the chip select line.
|
||||||
|
SD_CS = board.SD_CS
|
||||||
|
|
||||||
|
# Connect to the card and mount the filesystem.
|
||||||
|
cs = digitalio.DigitalInOut(SD_CS)
|
||||||
|
sdcard = adafruit_sdcard.SDCard(board.SPI(), cs)
|
||||||
|
vfs = storage.VfsFat(sdcard)
|
||||||
|
storage.mount(vfs, "/sd")
|
||||||
|
|
||||||
|
# Use the filesystem as normal! Our files are under /sd
|
||||||
|
|
||||||
|
print("Logging temperature to filesystem")
|
||||||
|
# append to the file!
|
||||||
|
while True:
|
||||||
|
# open file for append
|
||||||
|
with open("/sd/temperature.txt", "a") as f:
|
||||||
|
t = microcontroller.cpu.temperature
|
||||||
|
print("Temperature = %0.1f" % t)
|
||||||
|
f.write("%0.1f\n" % t)
|
||||||
|
# file is saved
|
||||||
|
time.sleep(1)
|
||||||
14
Adafruit_Metro_ESP32-S3/Storage/boot.py
Normal file
14
Adafruit_Metro_ESP32-S3/Storage/boot.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Essentials Storage CP Filesystem boot.py file
|
||||||
|
"""
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
import storage
|
||||||
|
|
||||||
|
pin = digitalio.DigitalInOut(board.A0)
|
||||||
|
pin.switch_to_input(pull=digitalio.Pull.UP)
|
||||||
|
|
||||||
|
# If the pin is connected to ground, the filesystem is writable by CircuitPython
|
||||||
|
storage.remount("/", readonly=pin.value)
|
||||||
39
Adafruit_Metro_ESP32-S3/Storage/code.py
Normal file
39
Adafruit_Metro_ESP32-S3/Storage/code.py
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Essentials Storage CP Filesystem code.py file
|
||||||
|
|
||||||
|
For use with boards with a built-in red LED.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
import microcontroller
|
||||||
|
|
||||||
|
led = digitalio.DigitalInOut(board.LED)
|
||||||
|
led.switch_to_output()
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open("/temperature.txt", "a") as temp_log:
|
||||||
|
while True:
|
||||||
|
# The microcontroller temperature in Celsius. Include the
|
||||||
|
# math to do the C to F conversion here, if desired.
|
||||||
|
temperature = microcontroller.cpu.temperature
|
||||||
|
|
||||||
|
# Write the temperature to the temperature.txt file every 10 seconds.
|
||||||
|
temp_log.write('{0:.2f}\n'.format(temperature))
|
||||||
|
temp_log.flush()
|
||||||
|
|
||||||
|
# Blink the LED on every write...
|
||||||
|
led.value = True
|
||||||
|
time.sleep(1) # ...for one second.
|
||||||
|
led.value = False # Then turn it off...
|
||||||
|
time.sleep(9) # ...for the other 9 seconds.
|
||||||
|
|
||||||
|
except OSError as e: # When the filesystem is NOT writable by CircuitPython...
|
||||||
|
delay = 0.5 # ...blink the LED every half second.
|
||||||
|
if e.args[0] == 28: # If the file system is full...
|
||||||
|
delay = 0.15 # ...blink the LED every 0.15 seconds!
|
||||||
|
while True:
|
||||||
|
led.value = not led.value
|
||||||
|
time.sleep(delay)
|
||||||
12
Adafruit_Metro_ESP32-S3/analog_in/analog_value/code.py
Normal file
12
Adafruit_Metro_ESP32-S3/analog_in/analog_value/code.py
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""CircuitPython analog pin value example"""
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
import analogio
|
||||||
|
|
||||||
|
analog_pin = analogio.AnalogIn(board.D3)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
print(analog_pin.value)
|
||||||
|
time.sleep(0.1)
|
||||||
17
Adafruit_Metro_ESP32-S3/analog_in/get_voltage/code.py
Normal file
17
Adafruit_Metro_ESP32-S3/analog_in/get_voltage/code.py
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""CircuitPython Analog In Voltage Example for ESP32-S2"""
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
import analogio
|
||||||
|
|
||||||
|
analog_pin = analogio.AnalogIn(board.D3)
|
||||||
|
|
||||||
|
|
||||||
|
def get_voltage(pin):
|
||||||
|
return (pin.value * 2.57) / 51000
|
||||||
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
print(get_voltage(analog_pin))
|
||||||
|
time.sleep(0.1)
|
||||||
80
Adafruit_Metro_ESP32-S3/asyncio/code.py
Normal file
80
Adafruit_Metro_ESP32-S3/asyncio/code.py
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2022 Dan Halbert for Adafruit Industries
|
||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2022 Kattni Rembor for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython asyncio example for two NeoPixel rings and one button.
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import board
|
||||||
|
import neopixel
|
||||||
|
import keypad
|
||||||
|
from rainbowio import colorwheel
|
||||||
|
|
||||||
|
button_pin = board.A0 # The pin the button is connected to.
|
||||||
|
num_pixels = 16 # The number of NeoPixels on a single ring.
|
||||||
|
brightness = 0.2 # The LED brightness.
|
||||||
|
|
||||||
|
# Set up NeoPixel rings.
|
||||||
|
ring_one = neopixel.NeoPixel(board.A1, num_pixels, brightness=brightness, auto_write=False)
|
||||||
|
ring_two = neopixel.NeoPixel(board.A2, num_pixels, brightness=brightness, auto_write=False)
|
||||||
|
|
||||||
|
|
||||||
|
class AnimationControls:
|
||||||
|
"""The controls to allow you to vary the rainbow and blink animations."""
|
||||||
|
def __init__(self):
|
||||||
|
self.reverse = False
|
||||||
|
self.wait = 0.0
|
||||||
|
self.delay = 0.5
|
||||||
|
|
||||||
|
|
||||||
|
async def rainbow_cycle(controls):
|
||||||
|
"""Rainbow cycle animation on ring one."""
|
||||||
|
while True:
|
||||||
|
for j in range(255, -1, -1) if controls.reverse else range(0, 256, 1):
|
||||||
|
for i in range(num_pixels):
|
||||||
|
rc_index = (i * 256 // num_pixels) + j
|
||||||
|
ring_one[i] = colorwheel(rc_index & 255)
|
||||||
|
ring_one.show()
|
||||||
|
await asyncio.sleep(controls.wait)
|
||||||
|
|
||||||
|
|
||||||
|
async def blink(controls):
|
||||||
|
"""Blink animation on ring two."""
|
||||||
|
while True:
|
||||||
|
ring_two.fill((0, 0, 255))
|
||||||
|
ring_two.show()
|
||||||
|
await asyncio.sleep(controls.delay)
|
||||||
|
ring_two.fill((0, 0, 0))
|
||||||
|
ring_two.show()
|
||||||
|
await asyncio.sleep(controls.delay)
|
||||||
|
await asyncio.sleep(controls.wait)
|
||||||
|
|
||||||
|
|
||||||
|
async def monitor_button(button, controls):
|
||||||
|
"""Monitor button that reverses rainbow direction and changes blink speed.
|
||||||
|
Assume button is active low.
|
||||||
|
"""
|
||||||
|
with keypad.Keys((button,), value_when_pressed=False, pull=True) as key:
|
||||||
|
while True:
|
||||||
|
key_event = key.events.get()
|
||||||
|
if key_event:
|
||||||
|
if key_event.pressed:
|
||||||
|
controls.reverse = True
|
||||||
|
controls.delay = 0.1
|
||||||
|
elif key_event.released:
|
||||||
|
controls.reverse = False
|
||||||
|
controls.delay = 0.5
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
animation_controls = AnimationControls()
|
||||||
|
button_task = asyncio.create_task(monitor_button(button_pin, animation_controls))
|
||||||
|
animation_task = asyncio.create_task(rainbow_cycle(animation_controls))
|
||||||
|
blink_task = asyncio.create_task(blink(animation_controls))
|
||||||
|
|
||||||
|
# This will run forever, because no tasks ever finish.
|
||||||
|
await asyncio.gather(button_task, animation_task, blink_task)
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
29
Adafruit_Metro_M7_SD/I2S/Tone/code.py
Normal file
29
Adafruit_Metro_M7_SD/I2S/Tone/code.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython I2S Tone playback example.
|
||||||
|
Plays a tone for one second on, one
|
||||||
|
second off, in a loop.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import array
|
||||||
|
import math
|
||||||
|
import audiocore
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.D10, board.D9, board.D12)
|
||||||
|
|
||||||
|
tone_volume = 0.1 # Increase this to increase the volume of the tone.
|
||||||
|
frequency = 440 # Set this to the Hz of the tone you want to generate.
|
||||||
|
length = 8000 // frequency
|
||||||
|
sine_wave = array.array("h", [0] * length)
|
||||||
|
for i in range(length):
|
||||||
|
sine_wave[i] = int((math.sin(math.pi * 2 * i / length)) * tone_volume * (2 ** 15 - 1))
|
||||||
|
sine_wave_sample = audiocore.RawSample(sine_wave)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
audio.play(sine_wave_sample, loop=True)
|
||||||
|
time.sleep(1)
|
||||||
|
audio.stop()
|
||||||
|
time.sleep(1)
|
||||||
BIN
Adafruit_Metro_M7_SD/I2S/WAV/StreetChicken.wav
Normal file
BIN
Adafruit_Metro_M7_SD/I2S/WAV/StreetChicken.wav
Normal file
Binary file not shown.
21
Adafruit_Metro_M7_SD/I2S/WAV/code.py
Normal file
21
Adafruit_Metro_M7_SD/I2S/WAV/code.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython I2S WAV file playback.
|
||||||
|
Plays a WAV file once.
|
||||||
|
"""
|
||||||
|
import audiocore
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.D10, board.D9, board.D12)
|
||||||
|
|
||||||
|
with open("StreetChicken.wav", "rb") as wave_file:
|
||||||
|
wav = audiocore.WaveFile(wave_file)
|
||||||
|
|
||||||
|
print("Playing wav file!")
|
||||||
|
audio.play(wav)
|
||||||
|
while audio.playing:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("Done!")
|
||||||
32
Adafruit_Metro_M7_SD/PWM_Audio/Tone/code.py
Normal file
32
Adafruit_Metro_M7_SD/PWM_Audio/Tone/code.py
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
CircuitPython PWM Audio Out tone example
|
||||||
|
Plays a tone for one second on, one
|
||||||
|
second off, in a loop.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import array
|
||||||
|
import math
|
||||||
|
import board
|
||||||
|
from audiocore import RawSample
|
||||||
|
from audiopwmio import PWMAudioOut as AudioOut
|
||||||
|
|
||||||
|
audio = AudioOut(board.A1)
|
||||||
|
|
||||||
|
tone_volume = 0.1 # Increase this to increase the volume of the tone.
|
||||||
|
frequency = 440 # Set this to the Hz of the tone you want to generate.
|
||||||
|
length = 8000 // frequency
|
||||||
|
sine_wave = array.array("H", [0] * length)
|
||||||
|
for i in range(length):
|
||||||
|
sine_wave[i] = int((1 + math.sin(math.pi * 2 * i / length)) * tone_volume * (2 ** 15 - 1))
|
||||||
|
|
||||||
|
sine_wave_sample = RawSample(sine_wave)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
audio.play(sine_wave_sample, loop=True)
|
||||||
|
time.sleep(1)
|
||||||
|
audio.stop()
|
||||||
|
time.sleep(1)
|
||||||
BIN
Adafruit_Metro_M7_SD/PWM_Audio/WAV/StreetChicken.wav
Normal file
BIN
Adafruit_Metro_M7_SD/PWM_Audio/WAV/StreetChicken.wav
Normal file
Binary file not shown.
22
Adafruit_Metro_M7_SD/PWM_Audio/WAV/code.py
Normal file
22
Adafruit_Metro_M7_SD/PWM_Audio/WAV/code.py
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
CircuitPython PWM Audio Out WAV example
|
||||||
|
Play a WAV file once.
|
||||||
|
"""
|
||||||
|
import board
|
||||||
|
from audiocore import WaveFile
|
||||||
|
from audiopwmio import PWMAudioOut as AudioOut
|
||||||
|
|
||||||
|
audio = AudioOut(board.A1)
|
||||||
|
|
||||||
|
with open("StreetChicken.wav", "rb") as wave_file:
|
||||||
|
wave = WaveFile(wave_file)
|
||||||
|
print("Playing wav file!")
|
||||||
|
audio.play(wave)
|
||||||
|
while audio.playing:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("Done!")
|
||||||
54
Adafruit_Metro_M7_SD/SD_Card/SD_Read/code.py
Normal file
54
Adafruit_Metro_M7_SD/SD_Card/SD_Read/code.py
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
CircuitPython Essentials SD Card Read Demo
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import digitalio
|
||||||
|
import board
|
||||||
|
import storage
|
||||||
|
import adafruit_sdcard
|
||||||
|
|
||||||
|
# The SD_CS pin is the chip select line.
|
||||||
|
SD_CS = board.SD_CS
|
||||||
|
|
||||||
|
# Connect to the card and mount the filesystem.
|
||||||
|
cs = digitalio.DigitalInOut(SD_CS)
|
||||||
|
sdcard = adafruit_sdcard.SDCard(board.SPI(), cs)
|
||||||
|
vfs = storage.VfsFat(sdcard)
|
||||||
|
storage.mount(vfs, "/sd")
|
||||||
|
|
||||||
|
# Use the filesystem as normal! Our files are under /sd
|
||||||
|
|
||||||
|
# This helper function will print the contents of the SD
|
||||||
|
def print_directory(path, tabs=0):
|
||||||
|
for file in os.listdir(path):
|
||||||
|
stats = os.stat(path + "/" + file)
|
||||||
|
filesize = stats[6]
|
||||||
|
isdir = stats[0] & 0x4000
|
||||||
|
|
||||||
|
if filesize < 1000:
|
||||||
|
sizestr = str(filesize) + " bytes"
|
||||||
|
elif filesize < 1000000:
|
||||||
|
sizestr = "%0.1f KB" % (filesize / 1000)
|
||||||
|
else:
|
||||||
|
sizestr = "%0.1f MB" % (filesize / 1000000)
|
||||||
|
|
||||||
|
prettyprintname = ""
|
||||||
|
for _ in range(tabs):
|
||||||
|
prettyprintname += " "
|
||||||
|
prettyprintname += file
|
||||||
|
if isdir:
|
||||||
|
prettyprintname += "/"
|
||||||
|
print("{0:<40} Size: {1:>10}".format(prettyprintname, sizestr))
|
||||||
|
|
||||||
|
# recursively print directory contents
|
||||||
|
if isdir:
|
||||||
|
print_directory(path + "/" + file, tabs + 1)
|
||||||
|
|
||||||
|
|
||||||
|
print("Files on filesystem:")
|
||||||
|
print("====================")
|
||||||
|
print_directory("/sd")
|
||||||
36
Adafruit_Metro_M7_SD/SD_Card/SD_Write/code.py
Normal file
36
Adafruit_Metro_M7_SD/SD_Card/SD_Write/code.py
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
CircuitPython Essentials SD Card Write Demo
|
||||||
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
|
import adafruit_sdcard
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
import microcontroller
|
||||||
|
import storage
|
||||||
|
|
||||||
|
# The SD_CS pin is the chip select line.
|
||||||
|
SD_CS = board.SD_CS
|
||||||
|
|
||||||
|
# Connect to the card and mount the filesystem.
|
||||||
|
cs = digitalio.DigitalInOut(SD_CS)
|
||||||
|
sdcard = adafruit_sdcard.SDCard(board.SPI(), cs)
|
||||||
|
vfs = storage.VfsFat(sdcard)
|
||||||
|
storage.mount(vfs, "/sd")
|
||||||
|
|
||||||
|
# Use the filesystem as normal! Our files are under /sd
|
||||||
|
|
||||||
|
print("Logging temperature to filesystem")
|
||||||
|
# append to the file!
|
||||||
|
while True:
|
||||||
|
# open file for append
|
||||||
|
with open("/sd/temperature.txt", "a") as f:
|
||||||
|
t = microcontroller.cpu.temperature
|
||||||
|
print("Temperature = %0.1f" % t)
|
||||||
|
f.write("%0.1f\n" % t)
|
||||||
|
# file is saved
|
||||||
|
time.sleep(1)
|
||||||
19
Adafruit_Metro_M7_SD/digital_input/code.py
Normal file
19
Adafruit_Metro_M7_SD/digital_input/code.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Digital Input Example - Blinking an LED using a button switch.
|
||||||
|
"""
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
|
||||||
|
led = digitalio.DigitalInOut(board.LED)
|
||||||
|
led.direction = digitalio.Direction.OUTPUT
|
||||||
|
|
||||||
|
button = digitalio.DigitalInOut(board.A0)
|
||||||
|
button.switch_to_input(pull=digitalio.Pull.UP)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if not button.value:
|
||||||
|
led.value = True
|
||||||
|
else:
|
||||||
|
led.value = False
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
/*
|
||||||
|
SD card read/write
|
||||||
|
|
||||||
|
This example shows how to read and write data to and from an SD card file
|
||||||
|
The circuit:
|
||||||
|
* SD card attached to SPI0 bus as follows:
|
||||||
|
** MOSI - pin 19
|
||||||
|
** MISO - pin 20
|
||||||
|
** CLK - pin 18
|
||||||
|
|
||||||
|
created Nov 2010
|
||||||
|
by David A. Mellis
|
||||||
|
modified 9 Apr 2012
|
||||||
|
by Tom Igoe
|
||||||
|
modified 14 Feb 2023
|
||||||
|
by Liz Clark
|
||||||
|
|
||||||
|
This example code is in the public domain.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "SdFat.h"
|
||||||
|
SdFat sd;
|
||||||
|
|
||||||
|
#define SD_FAT_TYPE 1
|
||||||
|
|
||||||
|
// default CS pin is 23 for Metro RP2040
|
||||||
|
#define SD_CS_PIN 23
|
||||||
|
|
||||||
|
File32 myFile;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// Open serial communications and wait for port to open:
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial) {
|
||||||
|
; // wait for serial port to connect. Needed for native USB port only
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Serial.print("Initializing SD card...");
|
||||||
|
|
||||||
|
if (!sd.begin(SD_CS_PIN)) {
|
||||||
|
Serial.println("initialization failed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Serial.println("initialization done.");
|
||||||
|
|
||||||
|
// open the file. note that only one file can be open at a time,
|
||||||
|
// so you have to close this one before opening another.
|
||||||
|
myFile.open("test.txt", FILE_WRITE);
|
||||||
|
|
||||||
|
// if the file opened okay, write to it:
|
||||||
|
if (myFile) {
|
||||||
|
Serial.print("Writing to test.txt...");
|
||||||
|
myFile.println("testing 1, 2, 3.");
|
||||||
|
myFile.println("hello metro rp2040!");
|
||||||
|
// close the file:
|
||||||
|
myFile.close();
|
||||||
|
Serial.println("done.");
|
||||||
|
} else {
|
||||||
|
// if the file didn't open, print an error:
|
||||||
|
Serial.println("error opening test.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-open the file for reading:
|
||||||
|
myFile.open("test.txt");
|
||||||
|
if (myFile) {
|
||||||
|
Serial.println("test.txt:");
|
||||||
|
|
||||||
|
// read from the file until there's nothing else in it:
|
||||||
|
while (myFile.available()) {
|
||||||
|
Serial.write(myFile.read());
|
||||||
|
}
|
||||||
|
// close the file:
|
||||||
|
myFile.close();
|
||||||
|
} else {
|
||||||
|
// if the file didn't open, print an error:
|
||||||
|
Serial.println("error opening test.txt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// nothing happens after setup
|
||||||
|
}
|
||||||
15
Adafruit_Metro_RP2040/Capacitive_Touch/One_Pin/code.py
Normal file
15
Adafruit_Metro_RP2040/Capacitive_Touch/One_Pin/code.py
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Capacitive Touch Pin Example - Print to the serial console when one pin is touched.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
import touchio
|
||||||
|
|
||||||
|
touch = touchio.TouchIn(board.A1)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if touch.value:
|
||||||
|
print("Pin touched!")
|
||||||
|
time.sleep(0.1)
|
||||||
18
Adafruit_Metro_RP2040/Capacitive_Touch/Two_Pins/code.py
Normal file
18
Adafruit_Metro_RP2040/Capacitive_Touch/Two_Pins/code.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Capacitive Two Touch Pin Example - Print to the serial console when a pin is touched.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
import touchio
|
||||||
|
|
||||||
|
touch_one = touchio.TouchIn(board.A1)
|
||||||
|
touch_two = touchio.TouchIn(board.A2)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if touch_one.value:
|
||||||
|
print("Pin one touched!")
|
||||||
|
if touch_two.value:
|
||||||
|
print("Pin two touched!")
|
||||||
|
time.sleep(0.1)
|
||||||
19
Adafruit_Metro_RP2040/Digital_Input/code.py
Normal file
19
Adafruit_Metro_RP2040/Digital_Input/code.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Digital Input Example - Blinking an LED using a button switch.
|
||||||
|
"""
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
|
||||||
|
led = digitalio.DigitalInOut(board.LED)
|
||||||
|
led.direction = digitalio.Direction.OUTPUT
|
||||||
|
|
||||||
|
button = digitalio.DigitalInOut(board.A1)
|
||||||
|
button.switch_to_input(pull=digitalio.Pull.UP)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if not button.value:
|
||||||
|
led.value = True
|
||||||
|
else:
|
||||||
|
led.value = False
|
||||||
29
Adafruit_Metro_RP2040/I2S/Tone/code.py
Normal file
29
Adafruit_Metro_RP2040/I2S/Tone/code.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython I2S Tone playback example.
|
||||||
|
Plays a tone for one second on, one
|
||||||
|
second off, in a loop.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import array
|
||||||
|
import math
|
||||||
|
import audiocore
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.A0, board.A1, board.A2)
|
||||||
|
|
||||||
|
tone_volume = 0.1 # Increase this to increase the volume of the tone.
|
||||||
|
frequency = 440 # Set this to the Hz of the tone you want to generate.
|
||||||
|
length = 8000 // frequency
|
||||||
|
sine_wave = array.array("h", [0] * length)
|
||||||
|
for i in range(length):
|
||||||
|
sine_wave[i] = int((math.sin(math.pi * 2 * i / length)) * tone_volume * (2 ** 15 - 1))
|
||||||
|
sine_wave_sample = audiocore.RawSample(sine_wave)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
audio.play(sine_wave_sample, loop=True)
|
||||||
|
time.sleep(1)
|
||||||
|
audio.stop()
|
||||||
|
time.sleep(1)
|
||||||
BIN
Adafruit_Metro_RP2040/I2S/WAV/StreetChicken.wav
Executable file
BIN
Adafruit_Metro_RP2040/I2S/WAV/StreetChicken.wav
Executable file
Binary file not shown.
21
Adafruit_Metro_RP2040/I2S/WAV/code.py
Normal file
21
Adafruit_Metro_RP2040/I2S/WAV/code.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython I2S WAV file playback.
|
||||||
|
Plays a WAV file once.
|
||||||
|
"""
|
||||||
|
import audiocore
|
||||||
|
import board
|
||||||
|
import audiobusio
|
||||||
|
|
||||||
|
audio = audiobusio.I2SOut(board.A0, board.A1, board.A2)
|
||||||
|
|
||||||
|
with open("StreetChicken.wav", "rb") as wave_file:
|
||||||
|
wav = audiocore.WaveFile(wave_file)
|
||||||
|
|
||||||
|
print("Playing wav file!")
|
||||||
|
audio.play(wav)
|
||||||
|
while audio.playing:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("Done!")
|
||||||
32
Adafruit_Metro_RP2040/PWM_Audio/Tone/code.py
Normal file
32
Adafruit_Metro_RP2040/PWM_Audio/Tone/code.py
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
CircuitPython PWM Audio Out tone example
|
||||||
|
Plays a tone for one second on, one
|
||||||
|
second off, in a loop.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import array
|
||||||
|
import math
|
||||||
|
import board
|
||||||
|
from audiocore import RawSample
|
||||||
|
from audiopwmio import PWMAudioOut as AudioOut
|
||||||
|
|
||||||
|
audio = AudioOut(board.A0)
|
||||||
|
|
||||||
|
tone_volume = 0.1 # Increase this to increase the volume of the tone.
|
||||||
|
frequency = 440 # Set this to the Hz of the tone you want to generate.
|
||||||
|
length = 8000 // frequency
|
||||||
|
sine_wave = array.array("H", [0] * length)
|
||||||
|
for i in range(length):
|
||||||
|
sine_wave[i] = int((1 + math.sin(math.pi * 2 * i / length)) * tone_volume * (2 ** 15 - 1))
|
||||||
|
|
||||||
|
sine_wave_sample = RawSample(sine_wave)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
audio.play(sine_wave_sample, loop=True)
|
||||||
|
time.sleep(1)
|
||||||
|
audio.stop()
|
||||||
|
time.sleep(1)
|
||||||
BIN
Adafruit_Metro_RP2040/PWM_Audio/WAV/StreetChicken.wav
Executable file
BIN
Adafruit_Metro_RP2040/PWM_Audio/WAV/StreetChicken.wav
Executable file
Binary file not shown.
22
Adafruit_Metro_RP2040/PWM_Audio/WAV/code.py
Normal file
22
Adafruit_Metro_RP2040/PWM_Audio/WAV/code.py
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
CircuitPython PWM Audio Out WAV example
|
||||||
|
Play a WAV file once.
|
||||||
|
"""
|
||||||
|
import board
|
||||||
|
from audiocore import WaveFile
|
||||||
|
from audiopwmio import PWMAudioOut as AudioOut
|
||||||
|
|
||||||
|
audio = AudioOut(board.A0)
|
||||||
|
|
||||||
|
with open("StreetChicken.wav", "rb") as wave_file:
|
||||||
|
wave = WaveFile(wave_file)
|
||||||
|
print("Playing wav file!")
|
||||||
|
audio.play(wave)
|
||||||
|
while audio.playing:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("Done!")
|
||||||
54
Adafruit_Metro_RP2040/SD_Read/code.py
Normal file
54
Adafruit_Metro_RP2040/SD_Read/code.py
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
CircuitPython Essentials SD Card Read Demo
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import digitalio
|
||||||
|
import board
|
||||||
|
import storage
|
||||||
|
import adafruit_sdcard
|
||||||
|
|
||||||
|
# The SD_CS pin is the chip select line.
|
||||||
|
SD_CS = board.SD_CS
|
||||||
|
|
||||||
|
# Connect to the card and mount the filesystem.
|
||||||
|
cs = digitalio.DigitalInOut(SD_CS)
|
||||||
|
sdcard = adafruit_sdcard.SDCard(board.SPI(), cs)
|
||||||
|
vfs = storage.VfsFat(sdcard)
|
||||||
|
storage.mount(vfs, "/sd")
|
||||||
|
|
||||||
|
# Use the filesystem as normal! Our files are under /sd
|
||||||
|
|
||||||
|
# This helper function will print the contents of the SD
|
||||||
|
def print_directory(path, tabs=0):
|
||||||
|
for file in os.listdir(path):
|
||||||
|
stats = os.stat(path + "/" + file)
|
||||||
|
filesize = stats[6]
|
||||||
|
isdir = stats[0] & 0x4000
|
||||||
|
|
||||||
|
if filesize < 1000:
|
||||||
|
sizestr = str(filesize) + " bytes"
|
||||||
|
elif filesize < 1000000:
|
||||||
|
sizestr = "%0.1f KB" % (filesize / 1000)
|
||||||
|
else:
|
||||||
|
sizestr = "%0.1f MB" % (filesize / 1000000)
|
||||||
|
|
||||||
|
prettyprintname = ""
|
||||||
|
for _ in range(tabs):
|
||||||
|
prettyprintname += " "
|
||||||
|
prettyprintname += file
|
||||||
|
if isdir:
|
||||||
|
prettyprintname += "/"
|
||||||
|
print("{0:<40} Size: {1:>10}".format(prettyprintname, sizestr))
|
||||||
|
|
||||||
|
# recursively print directory contents
|
||||||
|
if isdir:
|
||||||
|
print_directory(path + "/" + file, tabs + 1)
|
||||||
|
|
||||||
|
|
||||||
|
print("Files on filesystem:")
|
||||||
|
print("====================")
|
||||||
|
print_directory("/sd")
|
||||||
35
Adafruit_Metro_RP2040/SD_Write/code.py
Normal file
35
Adafruit_Metro_RP2040/SD_Write/code.py
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
CircuitPython Essentials SD Card Write Demo
|
||||||
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
|
import adafruit_sdcard
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
import microcontroller
|
||||||
|
import storage
|
||||||
|
|
||||||
|
# The SD_CS pin is the chip select line.
|
||||||
|
SD_CS = board.SD_CS
|
||||||
|
|
||||||
|
# Connect to the card and mount the filesystem.
|
||||||
|
cs = digitalio.DigitalInOut(SD_CS)
|
||||||
|
sdcard = adafruit_sdcard.SDCard(board.SPI(), cs)
|
||||||
|
vfs = storage.VfsFat(sdcard)
|
||||||
|
storage.mount(vfs, "/sd")
|
||||||
|
|
||||||
|
# Use the filesystem as normal! Our files are under /sd
|
||||||
|
|
||||||
|
print("Logging temperature to filesystem")
|
||||||
|
# append to the file!
|
||||||
|
while True:
|
||||||
|
# open file for append
|
||||||
|
with open("/sd/temperature.txt", "a") as f:
|
||||||
|
t = microcontroller.cpu.temperature
|
||||||
|
print("Temperature = %0.1f" % t)
|
||||||
|
f.write("%0.1f\n" % t)
|
||||||
|
# file is saved
|
||||||
|
time.sleep(1)
|
||||||
14
Adafruit_Metro_RP2040/Storage/boot.py
Normal file
14
Adafruit_Metro_RP2040/Storage/boot.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Essentials Storage CP Filesystem boot.py file
|
||||||
|
"""
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
import storage
|
||||||
|
|
||||||
|
pin = digitalio.DigitalInOut(board.A0)
|
||||||
|
pin.switch_to_input(pull=digitalio.Pull.UP)
|
||||||
|
|
||||||
|
# If the pin is connected to ground, the filesystem is writable by CircuitPython
|
||||||
|
storage.remount("/", readonly=pin.value)
|
||||||
39
Adafruit_Metro_RP2040/Storage/code.py
Normal file
39
Adafruit_Metro_RP2040/Storage/code.py
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython Essentials Storage CP Filesystem code.py file
|
||||||
|
|
||||||
|
For use with boards with a built-in red LED.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
import digitalio
|
||||||
|
import microcontroller
|
||||||
|
|
||||||
|
led = digitalio.DigitalInOut(board.LED)
|
||||||
|
led.switch_to_output()
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open("/temperature.txt", "a") as temp_log:
|
||||||
|
while True:
|
||||||
|
# The microcontroller temperature in Celsius. Include the
|
||||||
|
# math to do the C to F conversion here, if desired.
|
||||||
|
temperature = microcontroller.cpu.temperature
|
||||||
|
|
||||||
|
# Write the temperature to the temperature.txt file every 10 seconds.
|
||||||
|
temp_log.write('{0:.2f}\n'.format(temperature))
|
||||||
|
temp_log.flush()
|
||||||
|
|
||||||
|
# Blink the LED on every write...
|
||||||
|
led.value = True
|
||||||
|
time.sleep(1) # ...for one second.
|
||||||
|
led.value = False # Then turn it off...
|
||||||
|
time.sleep(9) # ...for the other 9 seconds.
|
||||||
|
|
||||||
|
except OSError as e: # When the filesystem is NOT writable by CircuitPython...
|
||||||
|
delay = 0.5 # ...blink the LED every half second.
|
||||||
|
if e.args[0] == 28: # If the file system is full...
|
||||||
|
delay = 0.15 # ...blink the LED every 0.15 seconds!
|
||||||
|
while True:
|
||||||
|
led.value = not led.value
|
||||||
|
time.sleep(delay)
|
||||||
80
Adafruit_Metro_RP2040/asyncio/code.py
Normal file
80
Adafruit_Metro_RP2040/asyncio/code.py
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2022 Dan Halbert for Adafruit Industries
|
||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2022 Kattni Rembor for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
CircuitPython asyncio example for two NeoPixel rings and one button.
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import board
|
||||||
|
import neopixel
|
||||||
|
import keypad
|
||||||
|
from rainbowio import colorwheel
|
||||||
|
|
||||||
|
button_pin = board.A0 # The pin the button is connected to.
|
||||||
|
num_pixels = 16 # The number of NeoPixels on a single ring.
|
||||||
|
brightness = 0.2 # The LED brightness.
|
||||||
|
|
||||||
|
# Set up NeoPixel rings.
|
||||||
|
ring_one = neopixel.NeoPixel(board.A1, num_pixels, brightness=brightness, auto_write=False)
|
||||||
|
ring_two = neopixel.NeoPixel(board.A2, num_pixels, brightness=brightness, auto_write=False)
|
||||||
|
|
||||||
|
|
||||||
|
class AnimationControls:
|
||||||
|
"""The controls to allow you to vary the rainbow and blink animations."""
|
||||||
|
def __init__(self):
|
||||||
|
self.reverse = False
|
||||||
|
self.wait = 0.0
|
||||||
|
self.delay = 0.5
|
||||||
|
|
||||||
|
|
||||||
|
async def rainbow_cycle(controls):
|
||||||
|
"""Rainbow cycle animation on ring one."""
|
||||||
|
while True:
|
||||||
|
for j in range(255, -1, -1) if controls.reverse else range(0, 256, 1):
|
||||||
|
for i in range(num_pixels):
|
||||||
|
rc_index = (i * 256 // num_pixels) + j
|
||||||
|
ring_one[i] = colorwheel(rc_index & 255)
|
||||||
|
ring_one.show()
|
||||||
|
await asyncio.sleep(controls.wait)
|
||||||
|
|
||||||
|
|
||||||
|
async def blink(controls):
|
||||||
|
"""Blink animation on ring two."""
|
||||||
|
while True:
|
||||||
|
ring_two.fill((0, 0, 255))
|
||||||
|
ring_two.show()
|
||||||
|
await asyncio.sleep(controls.delay)
|
||||||
|
ring_two.fill((0, 0, 0))
|
||||||
|
ring_two.show()
|
||||||
|
await asyncio.sleep(controls.delay)
|
||||||
|
await asyncio.sleep(controls.wait)
|
||||||
|
|
||||||
|
|
||||||
|
async def monitor_button(button, controls):
|
||||||
|
"""Monitor button that reverses rainbow direction and changes blink speed.
|
||||||
|
Assume button is active low.
|
||||||
|
"""
|
||||||
|
with keypad.Keys((button,), value_when_pressed=False, pull=True) as key:
|
||||||
|
while True:
|
||||||
|
key_event = key.events.get()
|
||||||
|
if key_event:
|
||||||
|
if key_event.pressed:
|
||||||
|
controls.reverse = True
|
||||||
|
controls.delay = 0.1
|
||||||
|
elif key_event.released:
|
||||||
|
controls.reverse = False
|
||||||
|
controls.delay = 0.5
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
animation_controls = AnimationControls()
|
||||||
|
button_task = asyncio.create_task(monitor_button(button_pin, animation_controls))
|
||||||
|
animation_task = asyncio.create_task(rainbow_cycle(animation_controls))
|
||||||
|
blink_task = asyncio.create_task(blink(animation_controls))
|
||||||
|
|
||||||
|
# This will run forever, because no tasks ever finish.
|
||||||
|
await asyncio.gather(button_task, animation_task, blink_task)
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
|
|
@ -1,17 +1,30 @@
|
||||||
# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries
|
# SPDX-FileCopyrightText: 2019, 2023 Kattni Rembor for Adafruit Industries
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
"""Simple rainbow swirl example for 3W LED"""
|
"""Simple rainbow swirl example for 3W LED"""
|
||||||
|
import time
|
||||||
import pwmio
|
import pwmio
|
||||||
import board
|
import board
|
||||||
from rainbowio import colorwheel
|
|
||||||
import digitalio
|
import digitalio
|
||||||
|
|
||||||
enable = digitalio.DigitalInOut(board.D10)
|
enable = digitalio.DigitalInOut(board.D10)
|
||||||
enable.direction = digitalio.Direction.OUTPUT
|
enable.direction = digitalio.Direction.OUTPUT
|
||||||
enable.value = True
|
enable.value = True
|
||||||
|
|
||||||
|
|
||||||
|
def colorwheel(pos):
|
||||||
|
if pos < 0 or pos > 255:
|
||||||
|
return 0, 0, 0
|
||||||
|
if pos < 85:
|
||||||
|
return int(255 - pos * 3), int(pos * 3), 0
|
||||||
|
if pos < 170:
|
||||||
|
pos -= 85
|
||||||
|
return 0, int(255 - pos * 3), int(pos * 3)
|
||||||
|
pos -= 170
|
||||||
|
return int(pos * 3), 0, int(255 - pos * 3)
|
||||||
|
|
||||||
|
|
||||||
red = pwmio.PWMOut(board.D11, duty_cycle=0, frequency=20000)
|
red = pwmio.PWMOut(board.D11, duty_cycle=0, frequency=20000)
|
||||||
green = pwmio.PWMOut(board.D12, duty_cycle=0, frequency=20000)
|
green = pwmio.PWMOut(board.D12, duty_cycle=0, frequency=20000)
|
||||||
blue = pwmio.PWMOut(board.D13, duty_cycle=0, frequency=20000)
|
blue = pwmio.PWMOut(board.D13, duty_cycle=0, frequency=20000)
|
||||||
|
|
@ -22,3 +35,4 @@ while True:
|
||||||
red.duty_cycle = int(r * 65536 / 256)
|
red.duty_cycle = int(r * 65536 / 256)
|
||||||
green.duty_cycle = int(g * 65536 / 256)
|
green.duty_cycle = int(g * 65536 / 256)
|
||||||
blue.duty_cycle = int(b * 65536 / 256)
|
blue.duty_cycle = int(b * 65536 / 256)
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries
|
# SPDX-FileCopyrightText: 2019, 2023 Kattni Rembor for Adafruit Industries
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
|
@ -6,16 +6,23 @@
|
||||||
# This example only works on Feathers that have analog audio out!
|
# This example only works on Feathers that have analog audio out!
|
||||||
import digitalio
|
import digitalio
|
||||||
import board
|
import board
|
||||||
import audioio
|
|
||||||
import audiocore
|
import audiocore
|
||||||
|
|
||||||
|
try:
|
||||||
|
from audioio import AudioOut
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
from audiopwmio import PWMAudioOut as AudioOut
|
||||||
|
except ImportError:
|
||||||
|
pass # not always supported by every board!
|
||||||
|
|
||||||
WAV_FILE_NAME = "StreetChicken.wav" # Change to the name of your wav file!
|
WAV_FILE_NAME = "StreetChicken.wav" # Change to the name of your wav file!
|
||||||
|
|
||||||
enable = digitalio.DigitalInOut(board.D10)
|
enable = digitalio.DigitalInOut(board.D10)
|
||||||
enable.direction = digitalio.Direction.OUTPUT
|
enable.direction = digitalio.Direction.OUTPUT
|
||||||
enable.value = True
|
enable.value = True
|
||||||
|
|
||||||
with audioio.AudioOut(board.A0) as audio: # Speaker connector
|
with AudioOut(board.A0) as audio: # Speaker connector
|
||||||
wave_file = open(WAV_FILE_NAME, "rb")
|
wave_file = open(WAV_FILE_NAME, "rb")
|
||||||
wave = audiocore.WaveFile(wave_file)
|
wave = audiocore.WaveFile(wave_file)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
// SPDX-FileCopyrightText: Earle F. Philhower, III
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
/*
|
||||||
|
This example plays a tune through a mono amplifier using a simple sine wave.
|
||||||
|
|
||||||
|
Released to the public domain by Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||||
|
|
||||||
|
Adapted from stereo original example 2023 by Kattni Rembor
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <PWMAudio.h>
|
||||||
|
|
||||||
|
PWMAudio pwm(0, true); // GP0 = left, GP1 = right
|
||||||
|
|
||||||
|
const int freq = 48000; // Output frequency for PWM
|
||||||
|
|
||||||
|
int16_t mono = 0;
|
||||||
|
|
||||||
|
const int notes[] = { 784, 880, 698, 349, 523 };
|
||||||
|
const int dly[] = { 400, 500, 700, 500, 1000 };
|
||||||
|
const int noteCnt = sizeof(notes) / sizeof(notes[0]);
|
||||||
|
|
||||||
|
int freqMono = 1;
|
||||||
|
|
||||||
|
double sineTable[128]; // Precompute sine wave in 128 steps
|
||||||
|
|
||||||
|
unsigned int cnt = 0;
|
||||||
|
void cb() {
|
||||||
|
while (pwm.availableForWrite()) {
|
||||||
|
double now = ((double)cnt) / (double)freq;
|
||||||
|
int freqScale = freqMono << 7; // Prescale by 128 to avoid FP math later on
|
||||||
|
pwm.write((int16_t)(mono * sineTable[(int)(now * freqScale) & 127]));
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// Set up sine table for waveform generation
|
||||||
|
for (int i = 0; i < 128; i++) {
|
||||||
|
sineTable[i] = sin(i * 2.0 * 3.14159 / 128.0);
|
||||||
|
}
|
||||||
|
pwm.setBuffers(4, 32); // Give larger buffers since we're are 48khz sample rate
|
||||||
|
pwm.onTransmit(cb);
|
||||||
|
pwm.begin(freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
delay(1000);
|
||||||
|
mono = 0;
|
||||||
|
Serial.println("loop");
|
||||||
|
for (int i = 0; i < noteCnt; i++) {
|
||||||
|
freqMono = notes[i];
|
||||||
|
mono = 5000;
|
||||||
|
delay(dly[i]);
|
||||||
|
}
|
||||||
|
mono = 0;
|
||||||
|
delay(3000);
|
||||||
|
}
|
||||||
43
Adafruit_STEMMA_Audio_Amp/CircuitPython/code.py
Normal file
43
Adafruit_STEMMA_Audio_Amp/CircuitPython/code.py
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
CircuitPython PWM Audio Short Tone Tune Demo
|
||||||
|
|
||||||
|
Plays a five-note tune on a loop.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import array
|
||||||
|
import math
|
||||||
|
import board
|
||||||
|
from audiocore import RawSample
|
||||||
|
from audiopwmio import PWMAudioOut as AudioOut
|
||||||
|
|
||||||
|
# Increase this to increase the volume of the tone.
|
||||||
|
tone_volume = 0.1
|
||||||
|
# The tones are provided as a frequency in Hz. You can change the current tones or
|
||||||
|
# add your own to make a new tune. Follow the format with commas between values.
|
||||||
|
tone_frequency = [784, 880, 698, 349, 523]
|
||||||
|
|
||||||
|
audio = AudioOut(board.A0)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Play each tone in succession.
|
||||||
|
for frequency in tone_frequency:
|
||||||
|
# Compute the sine wave for the current frequency.
|
||||||
|
length = 8000 // frequency
|
||||||
|
sine_wave = array.array("H", [0] * length)
|
||||||
|
for index in range(length):
|
||||||
|
sine_wave[index] = int((1 + math.sin(math.pi * 2 * index / length))
|
||||||
|
* tone_volume * (2 ** 15 - 1))
|
||||||
|
|
||||||
|
sine_wave_sample = RawSample(sine_wave)
|
||||||
|
|
||||||
|
# Play the current frequency.
|
||||||
|
audio.play(sine_wave_sample, loop=True)
|
||||||
|
time.sleep(0.5)
|
||||||
|
audio.stop()
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# All done playing all tones; start over from the beginning.
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue