Merge branch 'main' into anecdata-patch-1

This commit is contained in:
anecdata 2023-09-21 10:25:21 -05:00 committed by GitHub
commit 495f80c4df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
594 changed files with 41903 additions and 715 deletions

127
.github/workflows/arduino_cron.yml vendored Normal file
View 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."

View file

@ -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:

View 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

View file

View file

View 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(

Binary file not shown.

309
4x4_MIDI_Messenger/code.py Normal file
View 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())

View 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(" ")

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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

View 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)

View 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!")

View 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)

Binary file not shown.

View 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!")

View 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)

View 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())

View file

@ -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)

View file

@ -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)

View 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)

View 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!")

View 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)

View 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"))

View 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)

View 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)

View 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())

View file

@ -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)

View file

@ -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"))

View file

@ -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

View file

@ -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++;
}

View 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)

Binary file not shown.

View 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!")

View 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)

View 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)

View 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

View 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)

View 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!")

View 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")

View 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)

View 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)

View 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)

View 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)

View 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)

View 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())

View 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)

Binary file not shown.

View 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!")

View 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)

Binary file not shown.

View 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!")

View 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")

View 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)

View 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

View file

@ -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
}

View 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)

View 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)

View 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

View 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)

Binary file not shown.

View 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!")

View 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)

Binary file not shown.

View 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!")

View 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")

View 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)

View 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)

View 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)

View 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())

View file

@ -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)

View file

@ -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)

View 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);
}

View 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