Compare commits
No commits in common. "main" and "anecdata-patch-1" have entirely different histories.
main
...
anecdata-p
3464 changed files with 9812 additions and 553693 deletions
134
.github/workflows/arduino_cron.yml
vendored
134
.github/workflows/arduino_cron.yml
vendored
|
|
@ -1,134 +0,0 @@
|
|||
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@v4
|
||||
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 ]] ||
|
||||
[[ $changedfile == *.yml ]]; 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_esp32c6", "feather_m0_express", "feather_m4_express", "feather_rp2040", "feather_rp2040_adalogger", "feather_rp2350", "flora", "fruit_jam_tinyusb", "funhouse", "gemma", "gemma_m0", "hallowing_m0", "hallowing_m4_tinyusb", "ledglasses_nrf52840", "magtag", "metro_m0", "metro_m0_tinyusb", "metro_m4", "metro_m4_tinyusb", "monster_m4sk", "monster_m4sk_tinyusb", "metro_rp2350", "neokeytrinkey_m0", "neotrellis_m4", "nrf52832", "nrf52840", "pixeltrinkey_m0", "protrinket_5v", "proxlighttrinkey_m0", "pybadge", "pycamera_s3", "pygamer", "pyportal", "qualia_s3_rgb666", "qt2040_trinkey", "qtpy_m0", "qtpy_esp32s2", "rotarytrinkey_m0", "sht4xtrinkey_m0", "slidetrinkey_m0", "trinket_5v", "trinket_m0", "uno"]
|
||||
runs-on: ubuntu-latest
|
||||
if: needs.check-if-needed.outputs.answer == 'true'
|
||||
needs: check-if-needed
|
||||
steps:
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
# Checkout the learn repo itself
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Checkout the CI scripts
|
||||
- uses: actions/checkout@v4
|
||||
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
|
||||
git clone --quiet https://github.com/pschatzmann/ESP32-A2DP /home/runner/Arduino/libraries/ESP32-A2DP
|
||||
git clone --quiet https://github.com/pschatzmann/arduino-audio-tools /home/runner/Arduino/libraries/arduino-audio-tools
|
||||
|
||||
- name: test platforms
|
||||
run: python3 ci/build_platform.py ${{ matrix.arduino-platform }}
|
||||
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
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."
|
||||
79
.github/workflows/githubci.yml
vendored
79
.github/workflows/githubci.yml
vendored
|
|
@ -1,4 +1,4 @@
|
|||
name: SPDX and Pylint
|
||||
name: Arduino Library CI
|
||||
|
||||
on: [pull_request, push, repository_dispatch]
|
||||
|
||||
|
|
@ -6,20 +6,89 @@ jobs:
|
|||
spdx:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Checkout Current Repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: check SPDX licensing
|
||||
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:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Versions
|
||||
|
|
@ -29,7 +98,7 @@ jobs:
|
|||
run: |
|
||||
pip install --force-reinstall pylint==2.7.1
|
||||
- name: Checkout Current Repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: lint
|
||||
run: ./pylint_check.sh
|
||||
|
|
|
|||
4
.github/workflows/images.yml
vendored
4
.github/workflows/images.yml
vendored
|
|
@ -21,10 +21,10 @@ jobs:
|
|||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python 3.x
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,622 +0,0 @@
|
|||
// 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
|
||||
|
|
@ -27,19 +27,11 @@ bool show_new_year = true;
|
|||
#define SHAKE_PERIOD 2000 // Period (in ms) when SHAKE_EVENTS must happen
|
||||
#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 addrPins[] = {17, 18, 19, 20, 21};
|
||||
uint8_t addrPins[] = {17, 18, 19, 20};
|
||||
uint8_t clockPin = 14;
|
||||
uint8_t latchPin = 15;
|
||||
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
|
||||
Adafruit_Protomatter matrix(
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,310 +0,0 @@
|
|||
# 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 i2cdisplaybus
|
||||
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 = i2cdisplaybus.I2CDisplayBus(i2c, device_address=0x3D, reset=oled_reset)
|
||||
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=64)
|
||||
|
||||
splash = displayio.Group()
|
||||
display.root_group = 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())
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <Adafruit_MCP23X17.h>
|
||||
|
||||
#define NOID_1 0 // MCP23XXX pin LED is attached to
|
||||
#define NOID_2 4 // MCP23XXX pin LED is attached to
|
||||
|
||||
Adafruit_MCP23X17 mcp;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
while (!Serial);
|
||||
Serial.println("8 Channel Solenoid Driver Demo");
|
||||
if (!mcp.begin_I2C()) {
|
||||
Serial.println("Couldn't find MCP23017..");
|
||||
while (1);
|
||||
}
|
||||
mcp.pinMode(NOID_1, OUTPUT);
|
||||
mcp.pinMode(NOID_2, OUTPUT);
|
||||
|
||||
Serial.println("Found MCP23017, looping...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.println("Solenoid 1!");
|
||||
mcp.digitalWrite(NOID_1, HIGH);
|
||||
delay(500);
|
||||
mcp.digitalWrite(NOID_1, LOW);
|
||||
delay(500);
|
||||
Serial.println("Solenoid 2!");
|
||||
mcp.digitalWrite(NOID_2, HIGH);
|
||||
delay(500);
|
||||
mcp.digitalWrite(NOID_2, LOW);
|
||||
delay(500);
|
||||
Serial.println("Together!");
|
||||
mcp.digitalWrite(NOID_1, HIGH);
|
||||
mcp.digitalWrite(NOID_2, HIGH);
|
||||
delay(1000);
|
||||
mcp.digitalWrite(NOID_1, LOW);
|
||||
mcp.digitalWrite(NOID_2, LOW);
|
||||
delay(2000);
|
||||
Serial.println("Repeat!");
|
||||
Serial.println();
|
||||
delay(500);
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
import board
|
||||
from adafruit_mcp230xx.mcp23017 import MCP23017
|
||||
|
||||
i2c = board.STEMMA_I2C()
|
||||
|
||||
mcp = MCP23017(i2c)
|
||||
|
||||
noid_1 = mcp.get_pin(0)
|
||||
noid_2 = mcp.get_pin(4)
|
||||
noid_1.switch_to_output(value=False)
|
||||
noid_2.switch_to_output(value=False)
|
||||
|
||||
while True:
|
||||
noid_1.value = True
|
||||
print(f"Solenoid 1: {noid_1.value}, Solenoid 2: {noid_2.value}")
|
||||
time.sleep(0.2)
|
||||
noid_1.value = False
|
||||
print(f"Solenoid 1: {noid_1.value}, Solenoid 2: {noid_2.value}")
|
||||
time.sleep(0.2)
|
||||
noid_2.value = True
|
||||
print(f"Solenoid 1: {noid_1.value}, Solenoid 2: {noid_2.value}")
|
||||
time.sleep(0.2)
|
||||
noid_2.value = False
|
||||
print(f"Solenoid 1: {noid_1.value}, Solenoid 2: {noid_2.value}")
|
||||
time.sleep(1)
|
||||
noid_1.value = True
|
||||
noid_2.value = True
|
||||
print(f"Solenoid 1: {noid_1.value}, Solenoid 2: {noid_2.value}")
|
||||
time.sleep(1)
|
||||
noid_1.value = False
|
||||
noid_2.value = False
|
||||
print(f"Solenoid 1: {noid_1.value}, Solenoid 2: {noid_2.value}")
|
||||
time.sleep(2)
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
const int DIR = 5;
|
||||
const int STEP = 6;
|
||||
const int microMode = 16; // microstep mode, default is 1/16 so 16; ex: 1/4 would be 4
|
||||
// full rotation * microstep divider
|
||||
const int steps = 200 * microMode;
|
||||
|
||||
void setup()
|
||||
{
|
||||
// setup step and dir pins as outputs
|
||||
pinMode(STEP, OUTPUT);
|
||||
pinMode(DIR, OUTPUT);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// change direction every loop
|
||||
digitalWrite(DIR, !digitalRead(DIR));
|
||||
// toggle STEP to move
|
||||
for(int x = 0; x < steps; x++)
|
||||
{
|
||||
digitalWrite(STEP, HIGH);
|
||||
delay(2);
|
||||
digitalWrite(STEP, LOW);
|
||||
delay(2);
|
||||
}
|
||||
delay(1000); // 1 second delay
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
import board
|
||||
from digitalio import DigitalInOut, Direction
|
||||
|
||||
# direction and step pins as outputs
|
||||
DIR = DigitalInOut(board.D5)
|
||||
DIR.direction = Direction.OUTPUT
|
||||
STEP = DigitalInOut(board.D6)
|
||||
STEP.direction = Direction.OUTPUT
|
||||
|
||||
# microstep mode, default is 1/16 so 16
|
||||
# another ex: 1/4 microstep would be 4
|
||||
microMode = 16
|
||||
# full rotation multiplied by the microstep divider
|
||||
steps = 200 * microMode
|
||||
|
||||
while True:
|
||||
# change direction every loop
|
||||
DIR.value = not DIR.value
|
||||
# toggle STEP pin to move the motor
|
||||
for i in range(steps):
|
||||
STEP.value = True
|
||||
time.sleep(0.001)
|
||||
STEP.value = False
|
||||
time.sleep(0.001)
|
||||
print("rotated! now reverse")
|
||||
# 1 second delay before starting again
|
||||
time.sleep(1)
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <Adafruit_ADG72x.h>
|
||||
|
||||
Adafruit_ADG72x adg72x;
|
||||
|
||||
bool isADG728 = false; // which chip are we connected to?
|
||||
|
||||
int analogIn = A0;
|
||||
int analogValue = 0;
|
||||
unsigned long switchTimer = 1000; // 1000 ms = 1 second for channel switch
|
||||
unsigned long readTimer = 10; // 10 ms for analog read
|
||||
unsigned long lastSwitchTime = 0; // Last time the channels were switched
|
||||
unsigned long lastReadTime = 0; // Last time the analog was read
|
||||
uint8_t currentChannel = 0; // Current channel being selected
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Wait for serial port to open
|
||||
while (!Serial) {
|
||||
delay(1);
|
||||
}
|
||||
|
||||
// Try with the ADG728 default address first...
|
||||
if (adg72x.begin(ADG728_DEFAULT_ADDR, &Wire)) {
|
||||
Serial.println("ADG728 found!");
|
||||
isADG728 = true;
|
||||
}
|
||||
// Maybe they have an ADG729?
|
||||
else if (adg72x.begin(ADG729_DEFAULT_ADDR, &Wire)) {
|
||||
Serial.println("ADG729 found!");
|
||||
isADG728 = false;
|
||||
}
|
||||
else {
|
||||
Serial.println("No ADG device found? Check wiring!");
|
||||
while (1); // Stop here if no device was found
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
unsigned long currentTime = millis();
|
||||
|
||||
// read and print analog value every 10ms
|
||||
if ((currentTime - lastReadTime) >= readTimer) {
|
||||
analogValue = analogRead(analogIn);
|
||||
Serial.println(analogValue);
|
||||
lastReadTime = currentTime;
|
||||
}
|
||||
|
||||
// switch channels every 1 second
|
||||
if ((currentTime - lastSwitchTime) >= switchTimer) {
|
||||
uint8_t bits = 1 << currentChannel; // Shift a '1' from LSB to MSB
|
||||
if (!adg72x.selectChannels(bits)) {
|
||||
Serial.println("Failed to set channels...");
|
||||
}
|
||||
/*Serial.print((currentChannel % 4) + 1);
|
||||
if (currentChannel < 4) Serial.println("A");
|
||||
else Serial.println("B");*/
|
||||
currentChannel = (currentChannel + 1) % 8; // Move to the next channel, wrap around at 8
|
||||
lastSwitchTime = currentTime;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <Adafruit_ADG72x.h>
|
||||
|
||||
Adafruit_ADG72x adg72x;
|
||||
|
||||
int analogInA0 = A0;
|
||||
int analogInA1 = A1;
|
||||
int analogValueDA = 0;
|
||||
int analogValueDB = 0;
|
||||
unsigned long switchTimer = 1000; // 1000 ms = 1 second for channel switch
|
||||
unsigned long readTimer = 10; // 10 ms for analog read
|
||||
unsigned long lastSwitchTime = 0; // Last time the channels were switched
|
||||
unsigned long lastReadTime = 0; // Last time the analog was read
|
||||
uint8_t currentChannel = 0; // Current channel being selected
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Wait for serial port to open
|
||||
while (!Serial) {
|
||||
delay(1);
|
||||
}
|
||||
|
||||
// Try with the ADG728 default address first...
|
||||
if (adg72x.begin(ADG728_DEFAULT_ADDR, &Wire)) {
|
||||
//Serial.println("ADG728 found!");
|
||||
}
|
||||
// Maybe they have an ADG729?
|
||||
else if (adg72x.begin(ADG729_DEFAULT_ADDR, &Wire)) {
|
||||
//Serial.println("ADG729 found!");
|
||||
}
|
||||
else {
|
||||
Serial.println("No ADG72x device found? Check wiring!");
|
||||
while (1); // Stop here if no device was found
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
unsigned long currentTime = millis();
|
||||
|
||||
// read and print analog value every 10ms
|
||||
if ((currentTime - lastReadTime) >= readTimer) {
|
||||
analogValueDA = analogRead(analogInA0);
|
||||
analogValueDB = analogRead(analogInA1);
|
||||
Serial.print(analogValueDA);
|
||||
Serial.print(",");
|
||||
Serial.println(analogValueDB);
|
||||
lastReadTime = currentTime;
|
||||
}
|
||||
|
||||
// switch channels every 1 second
|
||||
if ((currentTime - lastSwitchTime) >= switchTimer) {
|
||||
uint8_t bits = 1 << currentChannel; // Shift a '1' from LSB to MSB
|
||||
if (!adg72x.selectChannels(bits)) {
|
||||
Serial.println("Failed to set channels...");
|
||||
}
|
||||
/*Serial.print((currentChannel % 4) + 1);
|
||||
if (currentChannel < 4) Serial.println("A");
|
||||
else Serial.println("B");*/
|
||||
currentChannel = (currentChannel + 1) % 8; // Move to the next channel, wrap around at 8
|
||||
lastSwitchTime = currentTime;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2024 Liz Clark for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
import board
|
||||
import adafruit_adg72x
|
||||
from analogio import AnalogIn
|
||||
|
||||
analog_in = AnalogIn(board.A0)
|
||||
|
||||
i2c = board.I2C()
|
||||
switch = adafruit_adg72x.ADG72x(i2c)
|
||||
|
||||
c = 0
|
||||
switch_time = 2
|
||||
channels = [0, 4]
|
||||
clock = time.monotonic()
|
||||
while True:
|
||||
if (time.monotonic() - clock) > switch_time:
|
||||
print(f"Selecting channel {channels[c] + 1}")
|
||||
switch.channel = channels[c]
|
||||
c = (c + 1) % 2
|
||||
clock = time.monotonic()
|
||||
print((analog_in.value,))
|
||||
time.sleep(0.1)
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2024 Liz Clark for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
import board
|
||||
import adafruit_adg72x
|
||||
from analogio import AnalogIn
|
||||
|
||||
analog_in_DA = AnalogIn(board.A0)
|
||||
analog_in_DB = AnalogIn(board.A1)
|
||||
|
||||
i2c = board.I2C()
|
||||
switch = adafruit_adg72x.ADG72x(i2c, 0x44)
|
||||
|
||||
c = 0
|
||||
switch_time = 3
|
||||
clock = time.monotonic()
|
||||
|
||||
while True:
|
||||
if (time.monotonic() - clock) > switch_time:
|
||||
if c < 4:
|
||||
channels = "A"
|
||||
else:
|
||||
channels = "B"
|
||||
print(f"Selecting channel {(c % 4) + 1}{channels}")
|
||||
switch.channel = c
|
||||
c = (c + 1) % 8
|
||||
clock = time.monotonic()
|
||||
print((analog_in_DA.value, analog_in_DB.value,))
|
||||
time.sleep(0.1)
|
||||
|
|
@ -11,7 +11,6 @@ import adafruit_ahtx0
|
|||
|
||||
# OLED
|
||||
import displayio
|
||||
import i2cdisplaybus
|
||||
import terminalio
|
||||
from adafruit_display_text import label
|
||||
import adafruit_displayio_ssd1306
|
||||
|
|
@ -26,12 +25,12 @@ aht20 = adafruit_ahtx0.AHTx0(i2c)
|
|||
|
||||
|
||||
#OLED
|
||||
display_bus = i2cdisplaybus.I2CDisplayBus(i2c, device_address=0x3C)
|
||||
display_bus = displayio.I2CDisplay(i2c, device_address=0x3C)
|
||||
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=32)
|
||||
|
||||
# Make the display context
|
||||
splash = displayio.Group()
|
||||
display.root_group = splash
|
||||
display.show(splash)
|
||||
|
||||
text = "hello world"
|
||||
text_area = label.Label(terminalio.FONT, color=0xFFFF00, x=15, y=0)
|
||||
|
|
|
|||
|
|
@ -1,740 +0,0 @@
|
|||
# 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(" ")
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2025 Liz Clark for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
"""AS5600 Encoder"""
|
||||
import usb_hid
|
||||
import board
|
||||
from adafruit_hid.consumer_control import ConsumerControl
|
||||
from adafruit_hid.consumer_control_code import ConsumerControlCode
|
||||
import adafruit_as5600
|
||||
|
||||
i2c = board.STEMMA_I2C()
|
||||
sensor = adafruit_as5600.AS5600(i2c)
|
||||
enc_inc = ConsumerControlCode.VOLUME_INCREMENT
|
||||
enc_dec = ConsumerControlCode.VOLUME_DECREMENT
|
||||
cc = ConsumerControl(usb_hid.devices)
|
||||
|
||||
last_val = sensor.angle
|
||||
|
||||
THRESHOLD = sensor.max_angle // 2 # default max_angle is 4095
|
||||
# you can change the max_angle. ex: sensor.max_angle = 1000
|
||||
|
||||
MIN_CHANGE = 25 # minimum change to register as movement
|
||||
# increase to make less sensitive, decrease to make more sensitive
|
||||
|
||||
while True:
|
||||
enc_val = sensor.angle
|
||||
if abs(enc_val - last_val) >= MIN_CHANGE or abs(enc_val - last_val) > THRESHOLD:
|
||||
# Calculate the difference
|
||||
diff = enc_val - last_val
|
||||
# Check for wraparound
|
||||
if diff > THRESHOLD:
|
||||
# Wrapped from ~4095 to ~0 (actually turning backwards)
|
||||
cc.send(enc_dec)
|
||||
elif diff < -THRESHOLD:
|
||||
# Wrapped from ~0 to ~4095 (actually turning forwards)
|
||||
cc.send(enc_inc)
|
||||
elif diff > 0:
|
||||
# Normal forward rotation
|
||||
cc.send(enc_inc)
|
||||
else:
|
||||
# Normal backward rotation (diff < 0)
|
||||
cc.send(enc_dec)
|
||||
last_val = enc_val
|
||||
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.
Binary file not shown.
|
|
@ -1,149 +0,0 @@
|
|||
# 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
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2025 Carter Nelson for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Basic ON/OFF control of DC motor via DRV8833
|
||||
|
||||
#define AIN1 5
|
||||
#define AIN2 6
|
||||
#define SLP 7
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Adafruit DRV8833 DC Motor Example - ON/OFF");
|
||||
|
||||
// configure pins
|
||||
pinMode(AIN1, OUTPUT);
|
||||
pinMode(AIN2, OUTPUT);
|
||||
pinMode(SLP, OUTPUT);
|
||||
|
||||
// enable DRV8833
|
||||
digitalWrite(SLP, HIGH);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
//
|
||||
// FORWARD
|
||||
//
|
||||
Serial.println("Forward");
|
||||
digitalWrite(AIN1, HIGH);
|
||||
digitalWrite(AIN2, LOW);
|
||||
delay(1000);
|
||||
|
||||
//
|
||||
// REVERSE
|
||||
//
|
||||
Serial.println("Reverse");
|
||||
digitalWrite(AIN1, LOW);
|
||||
digitalWrite(AIN2, HIGH);
|
||||
delay(1000);
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2025 Carter Nelson for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// PWM speed control of DC motor via DRV8833
|
||||
|
||||
#define AIN1 5
|
||||
#define AIN2 6
|
||||
#define SLP 7
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Adafruit DRV8833 DC Motor Example - PWM");
|
||||
|
||||
// configure pins
|
||||
pinMode(AIN1, OUTPUT);
|
||||
pinMode(AIN2, OUTPUT);
|
||||
pinMode(SLP, OUTPUT);
|
||||
|
||||
// enable DRV8833
|
||||
digitalWrite(SLP, HIGH);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
//
|
||||
// FORWARD
|
||||
//
|
||||
Serial.println("Forward");
|
||||
digitalWrite(AIN2, LOW);
|
||||
// ramp speed up
|
||||
Serial.println(" ramping up");
|
||||
for (int duty_cycle=0; duty_cycle<256; duty_cycle++) {
|
||||
analogWrite(AIN1, duty_cycle);
|
||||
delay(10);
|
||||
}
|
||||
// ramp speed down
|
||||
Serial.println(" ramping down");
|
||||
for (int duty_cycle=255; duty_cycle>=0; duty_cycle--) {
|
||||
analogWrite(AIN1, duty_cycle);
|
||||
delay(10);
|
||||
}
|
||||
|
||||
//
|
||||
// REVERSE
|
||||
//
|
||||
Serial.println("Reverse");
|
||||
digitalWrite(AIN1, LOW);
|
||||
// ramp speed up
|
||||
Serial.println(" ramping up");
|
||||
for (int duty_cycle=0; duty_cycle<256; duty_cycle++) {
|
||||
analogWrite(AIN2, duty_cycle);
|
||||
delay(10);
|
||||
}
|
||||
// ramp speed down
|
||||
Serial.println(" ramping down");
|
||||
for (int duty_cycle=255; duty_cycle>=0; duty_cycle--) {
|
||||
analogWrite(AIN2, duty_cycle);
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2025 lady ada for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <Stepper.h>
|
||||
|
||||
// change this to the number of steps on your motor
|
||||
#define STEPS 200
|
||||
|
||||
// create an instance of the stepper class, specifying
|
||||
// the number of steps of the motor and the pins it's
|
||||
// attached to
|
||||
Stepper stepper(STEPS, 4, 5, 6, 7);
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(9600);
|
||||
Serial.println("Stepper test!");
|
||||
// set the speed of the motor to 30 RPMs
|
||||
stepper.setSpeed(60);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
Serial.println("Forward");
|
||||
stepper.step(STEPS);
|
||||
Serial.println("Backward");
|
||||
stepper.step(-STEPS);
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2025 Carter Nelson for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
import board
|
||||
import digitalio
|
||||
|
||||
# Configure pins
|
||||
AIN1 = digitalio.DigitalInOut(board.D5)
|
||||
AIN2 = digitalio.DigitalInOut(board.D6)
|
||||
SLP = digitalio.DigitalInOut(board.D7)
|
||||
|
||||
AIN1.switch_to_output()
|
||||
AIN2.switch_to_output()
|
||||
SLP.switch_to_output()
|
||||
|
||||
# Enable DRV8833
|
||||
SLP.value = True
|
||||
|
||||
# Loop forever
|
||||
while True:
|
||||
#
|
||||
# FORWARD
|
||||
#
|
||||
print("Forward")
|
||||
AIN1.value = True
|
||||
AIN2.value = False
|
||||
time.sleep(1)
|
||||
|
||||
#
|
||||
# REVERSE
|
||||
#
|
||||
print("Reverse")
|
||||
AIN1.value = False
|
||||
AIN2.value = True
|
||||
time.sleep(1)
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2025 Carter Nelson for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
import board
|
||||
import digitalio
|
||||
import pwmio
|
||||
|
||||
# Configure pins
|
||||
AIN1 = pwmio.PWMOut(board.D5, frequency=2000)
|
||||
AIN2 = pwmio.PWMOut(board.D6, frequency=2000)
|
||||
SLP = digitalio.DigitalInOut(board.D7)
|
||||
|
||||
SLP.switch_to_output()
|
||||
|
||||
# Enable DRV8833
|
||||
SLP.value = True
|
||||
|
||||
# Loop forever
|
||||
while True:
|
||||
#
|
||||
# FORWARD
|
||||
#
|
||||
print("Forward")
|
||||
AIN2.duty_cycle = 0
|
||||
print(" ramping up")
|
||||
for duty_cycle in range(0, 65536, 100):
|
||||
AIN1.duty_cycle = duty_cycle
|
||||
time.sleep(0.01)
|
||||
print(" ramping down")
|
||||
for duty_cycle in range(65535, -1, -100):
|
||||
AIN1.duty_cycle = duty_cycle
|
||||
time.sleep(0.01)
|
||||
|
||||
#
|
||||
# REVERSE
|
||||
#
|
||||
print("Reverse")
|
||||
AIN1.duty_cycle = 0
|
||||
print(" ramping up")
|
||||
for duty_cycle in range(0, 65536, 100):
|
||||
AIN2.duty_cycle = duty_cycle
|
||||
time.sleep(0.01)
|
||||
print(" ramping down")
|
||||
for duty_cycle in range(65535, -1, -100):
|
||||
AIN2.duty_cycle = duty_cycle
|
||||
time.sleep(0.01)
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
//#define ADAFRUIT_FEATHER_ESP32_V2
|
||||
|
||||
// then these pins will be defined for us
|
||||
#if defined(ADAFRUIT_FEATHER_ESP32_V2) or defined(ARDUINO_ADAFRUIT_ITSYBITSY_ESP32)
|
||||
#if defined(ADAFRUIT_FEATHER_ESP32_V2)
|
||||
#define PIN_NEOPIXEL 0
|
||||
#define NEOPIXEL_I2C_POWER 2
|
||||
#endif
|
||||
|
|
@ -93,7 +93,7 @@ void disableInternalPower() {
|
|||
#endif
|
||||
|
||||
#if defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2)
|
||||
// turn off the I2C power by setting pin to rest state (off)
|
||||
// turn on the I2C power by setting pin to rest state (off)
|
||||
pinMode(PIN_I2C_POWER, INPUT);
|
||||
pinMode(NEOPIXEL_POWER, OUTPUT);
|
||||
digitalWrite(NEOPIXEL_POWER, LOW);
|
||||
|
|
|
|||
|
|
@ -109,32 +109,16 @@ void setup() {
|
|||
digitalWrite(ESP32_RESETN, HIGH);
|
||||
pixel.setPixelColor(0, 20, 20, 0); pixel.show();
|
||||
delay(100);
|
||||
|
||||
#if defined(LED_BUILTIN)
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop() {
|
||||
while (Serial.available()) {
|
||||
#if defined(ARDUINO_ARCH_RP2040) // Neopixel is blocking and this annoys esptool
|
||||
#if defined(LED_BUILTIN)
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
#endif
|
||||
#else
|
||||
pixel.setPixelColor(0, 10, 0, 0); pixel.show();
|
||||
#endif
|
||||
SerialESP32.write(Serial.read());
|
||||
}
|
||||
|
||||
while (SerialESP32.available()) {
|
||||
#if defined(ARDUINO_ARCH_RP2040) // Neopixel is blocking and this annoys esptool
|
||||
#if defined(LED_BUILTIN)
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
#endif
|
||||
#else
|
||||
pixel.setPixelColor(0, 0, 0, 10); pixel.show();
|
||||
#endif
|
||||
Serial.write(SerialESP32.read());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,105 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2025 Limor Fried for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include "config.h"
|
||||
#include <Adafruit_BME280.h>
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
|
||||
Adafruit_BME280 bme; // I2C
|
||||
|
||||
AdafruitIO_Feed *temperature = io.feed("temperature");
|
||||
AdafruitIO_Feed *humidity = io.feed("humidity");
|
||||
AdafruitIO_Feed *pressure = io.feed("pressure");
|
||||
float temp, humid, pres;
|
||||
|
||||
Adafruit_NeoPixel pixel(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
|
||||
// wait for serial monitor to open
|
||||
//while(! Serial);
|
||||
|
||||
// turn on neopixel
|
||||
pinMode(NEOPIXEL_POWER, OUTPUT);
|
||||
digitalWrite(NEOPIXEL_POWER, HIGH);
|
||||
pixel.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
|
||||
pixel.setBrightness(10); // not so bright
|
||||
|
||||
pixel.setPixelColor(0, 0xFF0000); // red
|
||||
pixel.show();
|
||||
|
||||
if (! bme.begin()) {
|
||||
Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
|
||||
deepSleep();
|
||||
}
|
||||
Serial.println("Found BME280");
|
||||
float temp = bme.readTemperature();
|
||||
float pres = bme.readPressure() / 100.0F;
|
||||
float hum = bme.readHumidity();
|
||||
// shhh time to close your eyes
|
||||
bme.setSampling(Adafruit_BME280::MODE_SLEEP,
|
||||
Adafruit_BME280::SAMPLING_X16, Adafruit_BME280::SAMPLING_X16, Adafruit_BME280::SAMPLING_X16,
|
||||
Adafruit_BME280::FILTER_OFF,
|
||||
Adafruit_BME280::STANDBY_MS_1000);
|
||||
|
||||
Serial.print("Connecting to Adafruit IO");
|
||||
|
||||
pixel.setPixelColor(0, 0xFFFF00); // yellow
|
||||
pixel.show();
|
||||
|
||||
// connect to io.adafruit.com
|
||||
io.connect();
|
||||
|
||||
// wait for a connection
|
||||
while(io.status() < AIO_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
// we are connected
|
||||
pixel.setPixelColor(0, 0x00FF00); // green
|
||||
pixel.show();
|
||||
Serial.println();
|
||||
Serial.println(io.statusText());
|
||||
|
||||
io.run();
|
||||
|
||||
temp = temp * 9.0 / 5.0 + 32;
|
||||
Serial.print("Temperature = ");
|
||||
Serial.print(temp);
|
||||
Serial.println(" *F");
|
||||
temperature->save(temp);
|
||||
|
||||
Serial.print("Pressure = ");
|
||||
Serial.print(pres);
|
||||
Serial.println(" hPa");
|
||||
pressure->save(pres);
|
||||
|
||||
Serial.print("Humidity = ");
|
||||
Serial.print(hum);
|
||||
Serial.println(" %");
|
||||
humidity->save(hum);
|
||||
|
||||
Serial.println();
|
||||
|
||||
deepSleep();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// we never get here!
|
||||
}
|
||||
|
||||
|
||||
void deepSleep() {
|
||||
pinMode(NEOPIXEL_POWER, OUTPUT);
|
||||
digitalWrite(NEOPIXEL_POWER, LOW); // off
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
|
||||
esp_sleep_enable_timer_wakeup(300000000); // 5 minutes
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2025 Limor Fried for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
#define IO_USERNAME "your-aio-username"
|
||||
#define IO_KEY "your-aio-token"
|
||||
#define WIFI_SSID "your-wifi-ssid"
|
||||
#define WIFI_PASS "your-wifi-pass"
|
||||
|
||||
#include "AdafruitIO_WiFi.h"
|
||||
AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS);
|
||||
|
|
@ -3,8 +3,6 @@
|
|||
"""
|
||||
CircuitPython Adafruit IO Example for BME280 and LC709203 Sensors
|
||||
"""
|
||||
|
||||
from os import getenv
|
||||
import time
|
||||
import ssl
|
||||
import alarm
|
||||
|
|
@ -16,21 +14,11 @@ import adafruit_requests
|
|||
from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError
|
||||
from adafruit_lc709203f import LC709203F, PackSize
|
||||
from adafruit_bme280 import basic as adafruit_bme280
|
||||
|
||||
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
|
||||
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
|
||||
ssid = getenv("CIRCUITPY_WIFI_SSID")
|
||||
password = getenv("CIRCUITPY_WIFI_PASSWORD")
|
||||
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
|
||||
aio_key = getenv("ADAFRUIT_AIO_KEY")
|
||||
|
||||
if None in [ssid, password, aio_username, aio_key]:
|
||||
raise RuntimeError(
|
||||
"WiFi and Adafruit IO settings are kept in settings.toml, "
|
||||
"please add them there. The settings file must contain "
|
||||
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
|
||||
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
|
||||
)
|
||||
try:
|
||||
from secrets import secrets
|
||||
except ImportError:
|
||||
print("WiFi and Adafruit IO credentials are kept in secrets.py, please add them there!")
|
||||
raise
|
||||
|
||||
# Duration of sleep in seconds. Default is 600 seconds (10 minutes).
|
||||
# Feather will sleep for this duration between sensor readings / sending data to AdafruitIO
|
||||
|
|
@ -90,9 +78,9 @@ def send_io_data(feed, value):
|
|||
# Wi-Fi connections can have issues! This ensures the code will continue to run.
|
||||
try:
|
||||
# Connect to Wi-Fi
|
||||
wifi.radio.connect(ssid, password)
|
||||
print(f"Connected to {ssid}!")
|
||||
print(f"IP: {wifi.radio.ipv4_address}")
|
||||
wifi.radio.connect(secrets["ssid"], secrets["password"])
|
||||
print("Connected to {}!".format(secrets["ssid"]))
|
||||
print("IP:", wifi.radio.ipv4_address)
|
||||
|
||||
pool = socketpool.SocketPool(wifi.radio)
|
||||
requests = adafruit_requests.Session(pool, ssl.create_default_context())
|
||||
|
|
@ -102,6 +90,12 @@ except Exception as e: # pylint: disable=broad-except
|
|||
print(e)
|
||||
go_to_sleep(60)
|
||||
|
||||
# Set your Adafruit IO Username and Key in secrets.py
|
||||
# (visit io.adafruit.com if you need to create an account,
|
||||
# or if you need your Adafruit IO key.)
|
||||
aio_username = secrets["aio_username"]
|
||||
aio_key = secrets["aio_key"]
|
||||
|
||||
# Initialize an Adafruit IO HTTP API object
|
||||
io = IO_HTTP(aio_username, aio_key, requests)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ CircuitPython Simple Example for BME280 and LC709203 Sensors
|
|||
"""
|
||||
import time
|
||||
import board
|
||||
from adafruit_lc709203f import LC709203F, PackSize
|
||||
from adafruit_bme280 import basic as adafruit_bme280
|
||||
from adafruit_lc709203f import LC709203F, PackSize
|
||||
|
||||
# Create sensor objects, using the board's default I2C bus.
|
||||
i2c = board.I2C() # uses board.SCL and board.SDA
|
||||
|
|
|
|||
|
|
@ -1,110 +0,0 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
"""
|
||||
CircuitPython example for deep sleep and BME280 sensor sending data
|
||||
to Adafruit IO.
|
||||
"""
|
||||
from os import getenv
|
||||
import time
|
||||
import alarm
|
||||
import board
|
||||
import digitalio
|
||||
import neopixel
|
||||
import wifi
|
||||
|
||||
from adafruit_bme280 import advanced as adafruit_bme280
|
||||
import adafruit_connection_manager
|
||||
import adafruit_requests
|
||||
from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError
|
||||
|
||||
|
||||
# enable power to NeoPixels.
|
||||
np_power = digitalio.DigitalInOut(board.NEOPIXEL_POWER)
|
||||
np_power.switch_to_output(value=True)
|
||||
|
||||
# standard LED
|
||||
builtin_led = digitalio.DigitalInOut(board.LED)
|
||||
builtin_led.switch_to_output(value=True)
|
||||
|
||||
# neopixel to use for status
|
||||
status_pixel = neopixel.NeoPixel(
|
||||
board.NEOPIXEL, 1, brightness=0.1, pixel_order=neopixel.GRB, auto_write=True
|
||||
)
|
||||
status_pixel[0] = 0xFF0000
|
||||
|
||||
# Create sensor object, using the board's default I2C bus.
|
||||
i2c = board.I2C() # uses board.SCL and board.SDA
|
||||
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c)
|
||||
print("Found BME280")
|
||||
# change this to match the location's pressure (hPa) at sea level
|
||||
bme280.sea_level_pressure = 1013.25
|
||||
|
||||
# temperature converted to F
|
||||
temperature = bme280.temperature * 9 / 5 + 32
|
||||
humidity = bme280.relative_humidity
|
||||
pressure = bme280.pressure
|
||||
print("\nTemperature: %0.1f F" % temperature)
|
||||
print("Humidity: %0.1f %%" % humidity)
|
||||
print("Pressure: %0.1f hPa" % pressure)
|
||||
|
||||
bme280.mode = adafruit_bme280.MODE_SLEEP
|
||||
bme280.overscan_temperature = adafruit_bme280.OVERSCAN_X16
|
||||
bme280.overscan_humidity = adafruit_bme280.OVERSCAN_X16
|
||||
bme280.overscan_pressure = adafruit_bme280.OVERSCAN_X16
|
||||
bme280.iir_filter = adafruit_bme280.IIR_FILTER_DISABLE
|
||||
bme280.standby_period = adafruit_bme280.STANDBY_TC_1000
|
||||
|
||||
# set status pixel to yellow
|
||||
status_pixel[0] = 0xFFFF00
|
||||
|
||||
print("Connecting to AdafruitIO")
|
||||
|
||||
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
|
||||
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
|
||||
ssid = getenv("WIFI_SSID")
|
||||
password = getenv("WIFI_PASSWORD")
|
||||
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
|
||||
aio_key = getenv("ADAFRUIT_AIO_KEY")
|
||||
|
||||
print("Connecting to %s" % ssid)
|
||||
wifi.radio.connect(ssid, password)
|
||||
print("Connected to %s!" % ssid)
|
||||
|
||||
# setup socket pool and requests session
|
||||
pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio)
|
||||
ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio)
|
||||
requests = adafruit_requests.Session(pool, ssl_context)
|
||||
|
||||
# Initialize an Adafruit IO HTTP API object
|
||||
io = IO_HTTP(aio_username, aio_key, requests)
|
||||
|
||||
# set status pixel to green
|
||||
status_pixel[0] = 0x00FF00
|
||||
|
||||
try:
|
||||
# Get the feeds from Adafruit IO
|
||||
temperature_feed = io.get_feed("temperature")
|
||||
humidity_feed = io.get_feed("humidity")
|
||||
pressure_feed = io.get_feed("pressure")
|
||||
|
||||
# send data to the feeds
|
||||
io.send_data(temperature_feed["key"], temperature)
|
||||
io.send_data(humidity_feed["key"], humidity)
|
||||
io.send_data(pressure_feed["key"], pressure)
|
||||
|
||||
except AdafruitIO_RequestError as e:
|
||||
print(e)
|
||||
print(
|
||||
"You must create feeds on AdafruitIO for: temperature, humidity, and pressure"
|
||||
)
|
||||
|
||||
# turn off the neopixel and builtin LED
|
||||
np_power.value = False
|
||||
builtin_led.value = False
|
||||
|
||||
# Create an alarm that will trigger 5 minutes from now.
|
||||
time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + (5 * 60))
|
||||
# Exit the program, and then deep sleep until the alarm wakes us.
|
||||
alarm.exit_and_deep_sleep_until_alarms(time_alarm)
|
||||
# Does not return, so we never get here.
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
This test will initialize the display using displayio and draw a solid green
|
||||
background, a smaller purple rectangle, and some yellow text.
|
||||
"""
|
||||
import board
|
||||
import terminalio
|
||||
import displayio
|
||||
from adafruit_display_text import label
|
||||
|
||||
# First set some parameters used for shapes and text
|
||||
BORDER = 20
|
||||
FONTSCALE = 2
|
||||
BACKGROUND_COLOR = 0x00FF00 # Bright Green
|
||||
FOREGROUND_COLOR = 0xAA0088 # Purple
|
||||
TEXT_COLOR = 0xFFFF00
|
||||
|
||||
display = board.DISPLAY
|
||||
|
||||
# Make the display context
|
||||
splash = displayio.Group()
|
||||
display.root_group = splash
|
||||
|
||||
color_bitmap = displayio.Bitmap(display.width, display.height, 1)
|
||||
color_palette = displayio.Palette(1)
|
||||
color_palette[0] = BACKGROUND_COLOR
|
||||
|
||||
bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
|
||||
splash.append(bg_sprite)
|
||||
|
||||
# Draw a smaller inner rectangle
|
||||
inner_bitmap = displayio.Bitmap(
|
||||
display.width - BORDER * 2, display.height - BORDER * 2, 1
|
||||
)
|
||||
inner_palette = displayio.Palette(1)
|
||||
inner_palette[0] = FOREGROUND_COLOR
|
||||
inner_sprite = displayio.TileGrid(
|
||||
inner_bitmap, pixel_shader=inner_palette, x=BORDER, y=BORDER
|
||||
)
|
||||
splash.append(inner_sprite)
|
||||
|
||||
# Draw a label
|
||||
text = "Hello World!"
|
||||
text_area = label.Label(terminalio.FONT, text=text, color=TEXT_COLOR)
|
||||
text_width = text_area.bounding_box[2] * FONTSCALE
|
||||
text_group = displayio.Group(
|
||||
scale=FONTSCALE,
|
||||
x=display.width // 2 - text_width // 2,
|
||||
y=display.height // 2,
|
||||
)
|
||||
text_group.append(text_area) # Subgroup for text scaling
|
||||
splash.append(text_group)
|
||||
|
||||
while True:
|
||||
pass
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
# 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.
|
|
@ -1,21 +0,0 @@
|
|||
# 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!")
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
# 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.
|
|
@ -1,21 +0,0 @@
|
|||
# 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!")
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
# 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)
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
# 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())
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
# 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)
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# 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)
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
# 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.
|
|
@ -1,21 +0,0 @@
|
|||
# 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!")
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
# 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)
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
# 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"))
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
# 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)
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
# 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)
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
# 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())
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
# 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)
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
# 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"))
|
||||
|
|
@ -8,26 +8,20 @@ import array
|
|||
import math
|
||||
import board
|
||||
import audiobusio
|
||||
from adafruit_apds9960.apds9960 import APDS9960
|
||||
from adafruit_bmp280 import Adafruit_BMP280_I2C
|
||||
from adafruit_lis3mdl import LIS3MDL
|
||||
from adafruit_sht31d import SHT31D
|
||||
import adafruit_apds9960.apds9960
|
||||
import adafruit_bmp280
|
||||
import adafruit_lis3mdl
|
||||
import adafruit_lsm6ds.lsm6ds33
|
||||
import adafruit_sht31d
|
||||
|
||||
i2c = board.I2C() # uses board.SCL and board.SDA
|
||||
# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller
|
||||
|
||||
# check for LSM6DS33 or LSM6DS3TR-C
|
||||
try:
|
||||
from adafruit_lsm6ds.lsm6ds33 import LSM6DS33 as LSM6DS
|
||||
lsm6ds = LSM6DS(i2c)
|
||||
except RuntimeError:
|
||||
from adafruit_lsm6ds.lsm6ds3 import LSM6DS3 as LSM6DS
|
||||
lsm6ds = LSM6DS(i2c)
|
||||
|
||||
apds9960 = APDS9960(i2c)
|
||||
bmp280 = Adafruit_BMP280_I2C(i2c)
|
||||
lis3mdl = LIS3MDL(i2c)
|
||||
sht31d = SHT31D(i2c)
|
||||
apds9960 = adafruit_apds9960.apds9960.APDS9960(i2c)
|
||||
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c)
|
||||
lis3mdl = adafruit_lis3mdl.LIS3MDL(i2c)
|
||||
lsm6ds33 = adafruit_lsm6ds.lsm6ds33.LSM6DS33(i2c)
|
||||
sht31d = adafruit_sht31d.SHT31D(i2c)
|
||||
microphone = audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA,
|
||||
sample_rate=16000, bit_depth=16)
|
||||
|
||||
|
|
@ -48,17 +42,14 @@ while True:
|
|||
|
||||
print("\nFeather Sense Sensor Demo")
|
||||
print("---------------------------------------------")
|
||||
print(f"Proximity: {apds9960.proximity}")
|
||||
print(f"Red: {apds9960.color_data[0]}, Green: {apds9960.color_data[1]}, " +
|
||||
f"Blue: {apds9960.color_data[2]}, Clear: {apds9960.color_data[3]}")
|
||||
print(f"Temperature: {bmp280.temperature:.1f} C")
|
||||
print(f"Barometric pressure: {bmp280.pressure}")
|
||||
print(f"Altitude: {bmp280.altitude:.1f} m")
|
||||
print(f"Magnetic: {lis3mdl.magnetic[0]:.3f} {lis3mdl.magnetic[1]:.3f} " +
|
||||
f"{lis3mdl.magnetic[2]:.3f} uTesla")
|
||||
print(f"Acceleration: {lsm6ds.acceleration[0]:.2f} " +
|
||||
f"{lsm6ds.acceleration[1]:.2f} {lsm6ds.acceleration[2]:.2f} m/s^2")
|
||||
print(f"Gyro: {lsm6ds.gyro[0]:.2f} {lsm6ds.gyro[1]:.2f} {lsm6ds.gyro[2]:.2f} dps")
|
||||
print(f"Humidity: {sht31d.relative_humidity:.1f} %")
|
||||
print(f"Sound level: {normalized_rms(samples)}")
|
||||
print("Proximity:", apds9960.proximity)
|
||||
print("Red: {}, Green: {}, Blue: {}, Clear: {}".format(*apds9960.color_data))
|
||||
print("Temperature: {:.1f} C".format(bmp280.temperature))
|
||||
print("Barometric pressure:", bmp280.pressure)
|
||||
print("Altitude: {:.1f} m".format(bmp280.altitude))
|
||||
print("Magnetic: {:.3f} {:.3f} {:.3f} uTesla".format(*lis3mdl.magnetic))
|
||||
print("Acceleration: {:.2f} {:.2f} {:.2f} m/s^2".format(*lsm6ds33.acceleration))
|
||||
print("Gyro: {:.2f} {:.2f} {:.2f} dps".format(*lsm6ds33.gyro))
|
||||
print("Humidity: {:.1f} %".format(sht31d.relative_humidity))
|
||||
print("Sound level:", normalized_rms(samples))
|
||||
time.sleep(0.3)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
#include <Adafruit_BMP280.h>
|
||||
#include <Adafruit_LIS3MDL.h>
|
||||
#include <Adafruit_LSM6DS33.h>
|
||||
#include <Adafruit_LSM6DS3TRC.h>
|
||||
#include <Adafruit_SHT31.h>
|
||||
#include <Adafruit_Sensor.h>
|
||||
#include <PDM.h>
|
||||
|
|
@ -14,8 +13,7 @@
|
|||
Adafruit_APDS9960 apds9960; // proximity, light, color, gesture
|
||||
Adafruit_BMP280 bmp280; // temperautre, barometric pressure
|
||||
Adafruit_LIS3MDL lis3mdl; // magnetometer
|
||||
Adafruit_LSM6DS3TRC lsm6ds3trc; // accelerometer, gyroscope
|
||||
Adafruit_LSM6DS33 lsm6ds33;
|
||||
Adafruit_LSM6DS33 lsm6ds33; // accelerometer, gyroscope
|
||||
Adafruit_SHT31 sht30; // humidity
|
||||
|
||||
uint8_t proximity;
|
||||
|
|
@ -26,15 +24,11 @@ float accel_x, accel_y, accel_z;
|
|||
float gyro_x, gyro_y, gyro_z;
|
||||
float humidity;
|
||||
int32_t mic;
|
||||
long int accel_array[6];
|
||||
long int check_array[6]={0.00, 0.00, 0.00, 0.00, 0.00, 0.00};
|
||||
|
||||
extern PDMClass PDM;
|
||||
short sampleBuffer[256]; // buffer to read samples into, each sample is 16-bits
|
||||
volatile int samplesRead; // number of samples read
|
||||
|
||||
bool new_rev = true;
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
// while (!Serial) delay(10);
|
||||
|
|
@ -47,28 +41,6 @@ void setup(void) {
|
|||
bmp280.begin();
|
||||
lis3mdl.begin_I2C();
|
||||
lsm6ds33.begin_I2C();
|
||||
// check for readings from LSM6DS33
|
||||
sensors_event_t accel;
|
||||
sensors_event_t gyro;
|
||||
sensors_event_t temp;
|
||||
lsm6ds33.getEvent(&accel, &gyro, &temp);
|
||||
accel_array[0] = accel.acceleration.x;
|
||||
accel_array[1] = accel.acceleration.y;
|
||||
accel_array[2] = accel.acceleration.z;
|
||||
accel_array[3] = gyro.gyro.x;
|
||||
accel_array[4] = gyro.gyro.y;
|
||||
accel_array[5] = gyro.gyro.z;
|
||||
// if all readings are empty, then new rev
|
||||
for (int i =0; i < 5; i++) {
|
||||
if (accel_array[i] != check_array[i]) {
|
||||
new_rev = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// and we need to instantiate the LSM6DS3TRC
|
||||
if (new_rev) {
|
||||
lsm6ds3trc.begin_I2C();
|
||||
}
|
||||
sht30.begin();
|
||||
PDM.onReceive(onPDMdata);
|
||||
PDM.begin(1, 16000);
|
||||
|
|
@ -93,12 +65,7 @@ void loop(void) {
|
|||
sensors_event_t accel;
|
||||
sensors_event_t gyro;
|
||||
sensors_event_t temp;
|
||||
if (new_rev) {
|
||||
lsm6ds3trc.getEvent(&accel, &gyro, &temp);
|
||||
}
|
||||
else {
|
||||
lsm6ds33.getEvent(&accel, &gyro, &temp);
|
||||
}
|
||||
lsm6ds33.getEvent(&accel, &gyro, &temp);
|
||||
accel_x = accel.acceleration.x;
|
||||
accel_y = accel.acceleration.y;
|
||||
accel_z = accel.acceleration.z;
|
||||
|
|
@ -110,7 +77,7 @@ void loop(void) {
|
|||
|
||||
samplesRead = 0;
|
||||
mic = getPDMwave(4000);
|
||||
|
||||
|
||||
Serial.println("\nFeather Sense Sensor Demo");
|
||||
Serial.println("---------------------------------------------");
|
||||
Serial.print("Proximity: ");
|
||||
|
|
@ -190,4 +157,4 @@ void onPDMdata() {
|
|||
|
||||
// 16-bit, 2 bytes per sample
|
||||
samplesRead = bytesAvailable / 2;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
"""
|
||||
CircuitPython Adafruit IO Example for LC709203 Sensor
|
||||
"""
|
||||
|
||||
from os import getenv
|
||||
import time
|
||||
import ssl
|
||||
import alarm
|
||||
|
|
@ -15,21 +13,11 @@ import socketpool
|
|||
import adafruit_requests
|
||||
from adafruit_io.adafruit_io import IO_HTTP
|
||||
from adafruit_lc709203f import LC709203F, PackSize
|
||||
|
||||
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
|
||||
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
|
||||
ssid = getenv("CIRCUITPY_WIFI_SSID")
|
||||
password = getenv("CIRCUITPY_WIFI_PASSWORD")
|
||||
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
|
||||
aio_key = getenv("ADAFRUIT_AIO_KEY")
|
||||
|
||||
if None in [ssid, password, aio_username, aio_key]:
|
||||
raise RuntimeError(
|
||||
"WiFi and Adafruit IO settings are kept in settings.toml, "
|
||||
"please add them there. The settings file must contain "
|
||||
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
|
||||
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
|
||||
)
|
||||
try:
|
||||
from secrets import secrets
|
||||
except ImportError:
|
||||
print("WiFi and Adafruit IO credentials are kept in secrets.py, please add them there!")
|
||||
raise
|
||||
|
||||
# Duration of sleep in seconds. Default is 600 seconds (10 minutes).
|
||||
# Feather will sleep for this duration between sensor readings / sending data to AdafruitIO
|
||||
|
|
@ -70,9 +58,9 @@ def send_io_data(feed, value):
|
|||
# Wi-Fi connections can have issues! This ensures the code will continue to run.
|
||||
try:
|
||||
# Connect to Wi-Fi
|
||||
wifi.radio.connect(ssid, password)
|
||||
print(f"Connected to {ssid}!")
|
||||
print(f"IP: {wifi.radio.ipv4_address}")
|
||||
wifi.radio.connect(secrets["ssid"], secrets["password"])
|
||||
print("Connected to {}!".format(secrets["ssid"]))
|
||||
print("IP:", wifi.radio.ipv4_address)
|
||||
|
||||
pool = socketpool.SocketPool(wifi.radio)
|
||||
requests = adafruit_requests.Session(pool, ssl.create_default_context())
|
||||
|
|
@ -82,6 +70,12 @@ except Exception as e: # pylint: disable=broad-except
|
|||
print(e)
|
||||
go_to_sleep(60)
|
||||
|
||||
# Set your Adafruit IO Username and Key in secrets.py
|
||||
# (visit io.adafruit.com if you need to create an account,
|
||||
# or if you need your Adafruit IO key.)
|
||||
aio_username = secrets["aio_username"]
|
||||
aio_key = secrets["aio_key"]
|
||||
|
||||
# Initialize an Adafruit IO HTTP API object
|
||||
io = IO_HTTP(aio_username, aio_key, requests)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
"""
|
||||
CircuitPython GitHub Stars viewer
|
||||
"""
|
||||
|
||||
from os import getenv
|
||||
import time
|
||||
import ssl
|
||||
import wifi
|
||||
|
|
@ -15,17 +13,12 @@ from adafruit_display_text import bitmap_label
|
|||
from adafruit_bitmap_font import bitmap_font
|
||||
import adafruit_requests
|
||||
|
||||
# Get WiFi details, ensure these are setup in settings.toml
|
||||
ssid = getenv("CIRCUITPY_WIFI_SSID")
|
||||
password = getenv("CIRCUITPY_WIFI_PASSWORD")
|
||||
|
||||
if None in [ssid, password]:
|
||||
raise RuntimeError(
|
||||
"WiFi settings are kept in settings.toml, "
|
||||
"please add them there. The settings file must contain "
|
||||
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
|
||||
"at a minimum."
|
||||
)
|
||||
# Get WiFi details secrets.py file
|
||||
try:
|
||||
from secrets import secrets
|
||||
except ImportError:
|
||||
print("WiFi secrets are kept in secrets.py, please add them there!")
|
||||
raise
|
||||
|
||||
display = board.DISPLAY
|
||||
|
||||
|
|
@ -42,13 +35,13 @@ text_area = bitmap_label.Label(font, text="----", color=0xFFFFFF)
|
|||
text_area.x = 135
|
||||
text_area.y = 90
|
||||
group.append(text_area)
|
||||
display.root_group = group
|
||||
display.show(group)
|
||||
|
||||
# Connect to WiFi
|
||||
print(f"Connecting to {ssid}")
|
||||
wifi.radio.connect(ssid, password)
|
||||
print(f"Connected to {ssid}!")
|
||||
print(f"My IP address is {wifi.radio.ipv4_address}")
|
||||
print("Connecting to %s"%secrets["ssid"])
|
||||
wifi.radio.connect(secrets["ssid"], secrets["password"])
|
||||
print("Connected to %s!"%secrets["ssid"])
|
||||
print("My IP address is", wifi.radio.ipv4_address)
|
||||
|
||||
pool = socketpool.SocketPool(wifi.radio)
|
||||
requests = adafruit_requests.Session(pool, ssl.create_default_context())
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ scale = 3
|
|||
text_area = bitmap_label.Label(terminalio.FONT, text=text, scale=scale)
|
||||
text_area.x = 10
|
||||
text_area.y = 10
|
||||
board.DISPLAY.root_group = text_area
|
||||
board.DISPLAY.show(text_area)
|
||||
|
||||
while True:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ blinka_img = OnDiskBitmap("images/adafruit_blinka.bmp")
|
|||
tile_grid = TileGrid(bitmap=blinka_img, pixel_shader=blinka_img.pixel_shader)
|
||||
main_group.append(tile_grid)
|
||||
|
||||
board.DISPLAY.root_group = main_group
|
||||
board.DISPLAY.show(main_group)
|
||||
|
||||
tile_grid.x = board.DISPLAY.width // 2 - blinka_img.width // 2
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ sensor = LC709203F(i2c)
|
|||
|
||||
print("IC version:", hex(sensor.ic_version))
|
||||
|
||||
board.DISPLAY.root_group = main_group
|
||||
board.DISPLAY.show(main_group)
|
||||
|
||||
while True:
|
||||
text_area.text = "Battery:\n{:.1f} Volts \n{}%".format(sensor.cell_voltage, sensor.cell_percent)
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
// 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
|
|
@ -1,74 +0,0 @@
|
|||
// 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++;
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
# 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.
|
|
@ -1,22 +0,0 @@
|
|||
# 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("booploop.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!")
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from os import getenv
|
||||
import time
|
||||
import board
|
||||
import busio
|
||||
|
|
@ -23,23 +22,15 @@ USE_CELSIUS = False
|
|||
# Interval the sensor publishes to Adafruit IO, in minutes
|
||||
PUBLISH_INTERVAL = 10
|
||||
|
||||
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
|
||||
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
|
||||
ssid = getenv("CIRCUITPY_WIFI_SSID")
|
||||
password = getenv("CIRCUITPY_WIFI_PASSWORD")
|
||||
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
|
||||
aio_key = getenv("ADAFRUIT_AIO_KEY")
|
||||
|
||||
if None in [ssid, password, aio_username, aio_key]:
|
||||
raise RuntimeError(
|
||||
"WiFi and Adafruit IO settings are kept in settings.toml, "
|
||||
"please add them there. The settings file must contain "
|
||||
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
|
||||
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
|
||||
)
|
||||
|
||||
### WiFi ###
|
||||
|
||||
# Get wifi details and more from a secrets.py file
|
||||
try:
|
||||
from secrets import secrets
|
||||
except ImportError:
|
||||
print("WiFi secrets are kept in secrets.py, please add them there!")
|
||||
raise
|
||||
|
||||
# AirLift FeatherWing
|
||||
esp32_cs = DigitalInOut(board.D13)
|
||||
esp32_reset = DigitalInOut(board.D12)
|
||||
|
|
@ -47,8 +38,8 @@ esp32_ready = DigitalInOut(board.D11)
|
|||
|
||||
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
|
||||
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
|
||||
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
|
||||
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
|
||||
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
|
||||
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
|
||||
|
||||
# Connect to a PM2.5 sensor over UART
|
||||
reset_pin = None
|
||||
|
|
@ -151,7 +142,7 @@ def read_bme(is_celsius=False):
|
|||
|
||||
|
||||
# Create an instance of the Adafruit IO HTTP client
|
||||
io = IO_HTTP(aio_username, aio_key, wifi)
|
||||
io = IO_HTTP(secrets["aio_user"], secrets["aio_key"], wifi)
|
||||
|
||||
# Describes feeds used to hold Adafruit IO data
|
||||
feed_aqi = io.get_feed("air-quality-sensor.aqi")
|
||||
|
|
@ -159,11 +150,11 @@ feed_aqi_category = io.get_feed("air-quality-sensor.category")
|
|||
feed_humidity = io.get_feed("air-quality-sensor.humidity")
|
||||
feed_temperature = io.get_feed("air-quality-sensor.temperature")
|
||||
|
||||
# Set up location metadata from settings.toml file
|
||||
# Set up location metadata from secrets.py file
|
||||
location_metadata = {
|
||||
"lat": getenv("latitude"),
|
||||
"lon": getenv("longitude"),
|
||||
"ele": getenv("elevation"),
|
||||
"lat": secrets["latitude"],
|
||||
"lon": secrets["longitude"],
|
||||
"ele": secrets["elevation"],
|
||||
}
|
||||
|
||||
elapsed_minutes = 0
|
||||
|
|
|
|||
|
|
@ -2,32 +2,26 @@
|
|||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import os
|
||||
import time
|
||||
import board
|
||||
import busio
|
||||
from digitalio import DigitalInOut
|
||||
import neopixel
|
||||
import adafruit_connection_manager
|
||||
from adafruit_esp32spi import adafruit_esp32spi
|
||||
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
|
||||
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
|
||||
|
||||
import adafruit_minimqtt.adafruit_minimqtt as MQTT
|
||||
|
||||
# Get WiFi details, ensure these are setup in settings.toml
|
||||
ssid = os.getenv("CIRCUITPY_WIFI_SSID")
|
||||
password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
|
||||
|
||||
if None in [ssid, password]:
|
||||
raise RuntimeError(
|
||||
"WiFi settings are kept in settings.toml, "
|
||||
"please add them there. The settings file must contain "
|
||||
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
|
||||
"at a minimum."
|
||||
)
|
||||
|
||||
### WiFi ###
|
||||
|
||||
# Get wifi details and more from a secrets.py file
|
||||
try:
|
||||
from secrets import secrets
|
||||
except ImportError:
|
||||
print("WiFi secrets are kept in secrets.py, please add them there!")
|
||||
raise
|
||||
|
||||
# If you are using a board with pre-defined ESP32 Pins:
|
||||
esp32_cs = DigitalInOut(board.ESP_CS)
|
||||
esp32_ready = DigitalInOut(board.ESP_BUSY)
|
||||
|
|
@ -41,19 +35,19 @@ esp32_reset = DigitalInOut(board.ESP_RESET)
|
|||
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
|
||||
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
|
||||
"""Use below for Most Boards"""
|
||||
status_pixel = neopixel.NeoPixel(
|
||||
status_light = neopixel.NeoPixel(
|
||||
board.NEOPIXEL, 1, brightness=0.2
|
||||
) # Uncomment for Most Boards
|
||||
"""Uncomment below for ItsyBitsy M4"""
|
||||
# status_pixel = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
|
||||
# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
|
||||
# Uncomment below for an externally defined RGB LED
|
||||
# import adafruit_rgbled
|
||||
# from adafruit_esp32spi import PWMOut
|
||||
# RED_LED = PWMOut.PWMOut(esp, 26)
|
||||
# GREEN_LED = PWMOut.PWMOut(esp, 27)
|
||||
# BLUE_LED = PWMOut.PWMOut(esp, 25)
|
||||
# status_pixel = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
|
||||
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
|
||||
# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
|
||||
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
|
||||
|
||||
# Set up a pin for controlling the relay
|
||||
power_pin = DigitalInOut(board.D3)
|
||||
|
|
@ -61,7 +55,7 @@ power_pin.switch_to_output()
|
|||
|
||||
### Feeds ###
|
||||
# Set up a feed named Relay for subscribing to the relay feed on Adafruit IO
|
||||
feed_relay = os.getenv("AIO_USERNAME") + "/feeds/relay"
|
||||
feed_relay = secrets["aio_username"] + "/feeds/relay"
|
||||
|
||||
### Code ###
|
||||
|
||||
|
|
@ -107,16 +101,13 @@ print("Connecting to WiFi...")
|
|||
wifi.connect()
|
||||
print("Connected!")
|
||||
|
||||
pool = adafruit_connection_manager.get_radio_socketpool(esp)
|
||||
ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp)
|
||||
MQTT.set_socket(socket, esp)
|
||||
|
||||
# Set up a MiniMQTT Client
|
||||
client = MQTT.MQTT(
|
||||
broker="io.adafruit.com",
|
||||
username=os.getenv("AIO_USERNAME"),
|
||||
password=os.getenv("AIO_KEY"),
|
||||
socket_pool=pool,
|
||||
ssl_context=ssl_context,
|
||||
username=secrets["aio_username"],
|
||||
password=secrets["aio_key"],
|
||||
)
|
||||
|
||||
# Setup the callback methods above
|
||||
|
|
|
|||
|
|
@ -2,34 +2,18 @@
|
|||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from os import getenv
|
||||
import time
|
||||
import board
|
||||
import busio
|
||||
from digitalio import DigitalInOut
|
||||
import neopixel
|
||||
import adafruit_bh1750
|
||||
import adafruit_connection_manager
|
||||
from adafruit_esp32spi import adafruit_esp32spi
|
||||
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
|
||||
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
|
||||
|
||||
import adafruit_minimqtt.adafruit_minimqtt as MQTT
|
||||
|
||||
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
|
||||
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
|
||||
ssid = getenv("CIRCUITPY_WIFI_SSID")
|
||||
password = getenv("CIRCUITPY_WIFI_PASSWORD")
|
||||
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
|
||||
aio_key = getenv("ADAFRUIT_AIO_KEY")
|
||||
|
||||
if None in [ssid, password, aio_username, aio_key]:
|
||||
raise RuntimeError(
|
||||
"WiFi and Adafruit IO settings are kept in settings.toml, "
|
||||
"please add them there. The settings file must contain "
|
||||
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
|
||||
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
|
||||
)
|
||||
|
||||
### Sensor Calibration ###
|
||||
# Appliance power LED's light level, in Lux
|
||||
APPLIANCE_ON_LUX = 30.0
|
||||
|
|
@ -38,6 +22,13 @@ SENSOR_READ_TIME = 10.0
|
|||
|
||||
### WiFi ###
|
||||
|
||||
# Get wifi details and more from a secrets.py file
|
||||
try:
|
||||
from secrets import secrets
|
||||
except ImportError:
|
||||
print("WiFi secrets are kept in secrets.py, please add them there!")
|
||||
raise
|
||||
|
||||
# If you are using a board with pre-defined ESP32 Pins:
|
||||
esp32_cs = DigitalInOut(board.ESP_CS)
|
||||
esp32_ready = DigitalInOut(board.ESP_BUSY)
|
||||
|
|
@ -51,19 +42,19 @@ esp32_reset = DigitalInOut(board.ESP_RESET)
|
|||
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
|
||||
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
|
||||
"""Use below for Most Boards"""
|
||||
status_pixel = neopixel.NeoPixel(
|
||||
status_light = neopixel.NeoPixel(
|
||||
board.NEOPIXEL, 1, brightness=0.2
|
||||
) # Uncomment for Most Boards
|
||||
"""Uncomment below for ItsyBitsy M4"""
|
||||
# status_pixel = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
|
||||
# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
|
||||
# Uncomment below for an externally defined RGB LED
|
||||
# import adafruit_rgbled
|
||||
# from adafruit_esp32spi import PWMOut
|
||||
# RED_LED = PWMOut.PWMOut(esp, 26)
|
||||
# GREEN_LED = PWMOut.PWMOut(esp, 27)
|
||||
# BLUE_LED = PWMOut.PWMOut(esp, 25)
|
||||
# status_pixel = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
|
||||
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
|
||||
# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
|
||||
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
|
||||
|
||||
# Set up a pin for controlling the relay
|
||||
power_pin = DigitalInOut(board.D3)
|
||||
|
|
@ -76,10 +67,10 @@ sensor = adafruit_bh1750.BH1750(i2c)
|
|||
|
||||
### Feeds ###
|
||||
# Set up a feed named Relay for subscribing to the relay feed on Adafruit IO
|
||||
feed_relay = f"{aio_username}/feeds/relay"
|
||||
feed_relay = secrets["aio_username"] + "/feeds/relay"
|
||||
|
||||
# Set up a feed named status for subscribing to the status feed on Adafruit IO
|
||||
feed_status = f"{aio_username}/feeds/status"
|
||||
feed_status = secrets["aio_username"] + "/feeds/status"
|
||||
|
||||
### Code ###
|
||||
|
||||
|
|
@ -125,16 +116,13 @@ print("Connecting to WiFi...")
|
|||
wifi.connect()
|
||||
print("Connected!")
|
||||
|
||||
pool = adafruit_connection_manager.get_radio_socketpool(esp)
|
||||
ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp)
|
||||
MQTT.set_socket(socket, esp)
|
||||
|
||||
# Set up a MiniMQTT Client
|
||||
client = MQTT.MQTT(
|
||||
broker="io.adafruit.com",
|
||||
username=aio_username,
|
||||
password=aio_key,
|
||||
socket_pool=pool,
|
||||
ssl_context=ssl_context,
|
||||
username=secrets["aio_username"],
|
||||
password=secrets["aio_key"],
|
||||
)
|
||||
|
||||
# Setup the callback methods above
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
{
|
||||
"exportVersion": "1.0.0",
|
||||
"exportedBy": "tyeth_demo",
|
||||
"exportedAt": "2025-05-02T17:08:03.857Z",
|
||||
"exportedFromDevice": {
|
||||
"board": "rpi-pico-w",
|
||||
"firmwareVersion": "1.0.0-beta.100"
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"name": "Reed Switch",
|
||||
"pinName": "D13",
|
||||
"type": "reed_switch",
|
||||
"mode": "DIGITAL",
|
||||
"direction": "INPUT",
|
||||
"period": 0,
|
||||
"pull": "UP",
|
||||
"isPin": true,
|
||||
"visualization": {
|
||||
"offLabel": "Open",
|
||||
"offIcon": "fa6:solid:door-open",
|
||||
"onLabel": "Closed",
|
||||
"onIcon": "fa6:regular:door-closed"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -2,35 +2,26 @@
|
|||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from os import getenv
|
||||
import time
|
||||
import board
|
||||
import busio
|
||||
from digitalio import DigitalInOut
|
||||
import adafruit_connection_manager
|
||||
from adafruit_esp32spi import adafruit_esp32spi
|
||||
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
|
||||
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
|
||||
import neopixel
|
||||
import adafruit_minimqtt.adafruit_minimqtt as MQTT
|
||||
from adafruit_io.adafruit_io import IO_MQTT
|
||||
|
||||
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
|
||||
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
|
||||
ssid = getenv("CIRCUITPY_WIFI_SSID")
|
||||
password = getenv("CIRCUITPY_WIFI_PASSWORD")
|
||||
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
|
||||
aio_key = getenv("ADAFRUIT_AIO_KEY")
|
||||
|
||||
if None in [ssid, password, aio_username, aio_key]:
|
||||
raise RuntimeError(
|
||||
"WiFi and Adafruit IO settings are kept in settings.toml, "
|
||||
"please add them there. The settings file must contain "
|
||||
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
|
||||
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
|
||||
)
|
||||
|
||||
### WiFi ###
|
||||
|
||||
# Get wifi details and more from a secrets.py file
|
||||
try:
|
||||
from secrets import secrets
|
||||
except ImportError:
|
||||
print("WiFi secrets are kept in secrets.py, please add them there!")
|
||||
raise
|
||||
|
||||
# If you are using a board with pre-defined ESP32 Pins:
|
||||
esp32_cs = DigitalInOut(board.ESP_CS)
|
||||
esp32_ready = DigitalInOut(board.ESP_BUSY)
|
||||
|
|
@ -44,19 +35,19 @@ esp32_reset = DigitalInOut(board.ESP_RESET)
|
|||
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
|
||||
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
|
||||
"""Use below for Most Boards"""
|
||||
status_pixel = neopixel.NeoPixel(
|
||||
status_light = neopixel.NeoPixel(
|
||||
board.NEOPIXEL, 1, brightness=0.2
|
||||
) # Uncomment for Most Boards
|
||||
"""Uncomment below for ItsyBitsy M4"""
|
||||
# status_pixel = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
|
||||
# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
|
||||
# Uncomment below for an externally defined RGB LED
|
||||
# import adafruit_rgbled
|
||||
# from adafruit_esp32spi import PWMOut
|
||||
# RED_LED = PWMOut.PWMOut(esp, 26)
|
||||
# GREEN_LED = PWMOut.PWMOut(esp, 27)
|
||||
# BLUE_LED = PWMOut.PWMOut(esp, 25)
|
||||
# status_pixel = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
|
||||
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
|
||||
# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
|
||||
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
|
||||
|
||||
# Set up a pin for controlling the relay
|
||||
power_pin = DigitalInOut(board.D3)
|
||||
|
|
@ -98,11 +89,11 @@ def on_message(client, feed_id, payload):
|
|||
|
||||
def on_relay_msg(client, topic, message):
|
||||
# Method called whenever user/feeds/relay has a new value
|
||||
if message == "1":
|
||||
print("Received 1 - turning outlet ON")
|
||||
if message == "morning":
|
||||
print("Morning - turning outlet ON")
|
||||
power_pin.value = True
|
||||
elif message == "0":
|
||||
print("Received 0 - turning outlet OFF")
|
||||
elif message == "night":
|
||||
print("Night - turning outlet OFF")
|
||||
power_pin.value = False
|
||||
else:
|
||||
print("Unexpected value received on relay feed.")
|
||||
|
|
@ -113,16 +104,14 @@ print("Connecting to WiFi...")
|
|||
wifi.connect()
|
||||
print("Connected!")
|
||||
|
||||
pool = adafruit_connection_manager.get_radio_socketpool(esp)
|
||||
ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp)
|
||||
# Initialize MQTT interface with the esp interface
|
||||
MQTT.set_socket(socket, esp)
|
||||
|
||||
# Initialize a new MQTT Client object
|
||||
mqtt_client = MQTT.MQTT(
|
||||
broker="io.adafruit.com",
|
||||
username=aio_username,
|
||||
password=aio_key,
|
||||
socket_pool=pool,
|
||||
ssl_context=ssl_context,
|
||||
username=secrets["aio_username"],
|
||||
password=secrets["aio_key"],
|
||||
)
|
||||
|
||||
# Initialize an Adafruit IO MQTT Client
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ while True:
|
|||
time.sleep(30)
|
||||
|
||||
# Stepper 1
|
||||
if not st1.is_alive() and int(stepper_start.value):
|
||||
if not st1.isAlive() and int(stepper_start.value):
|
||||
stepper_1_steps = aio.receive(feed_step_1_steps.key)
|
||||
stepper_1_steps = int(stepper_1_steps.value)
|
||||
if stepper_1_steps > 0: # stepper slider is set
|
||||
|
|
@ -125,7 +125,7 @@ while True:
|
|||
st1.start()
|
||||
|
||||
# Stepper 2
|
||||
if not st2.is_alive() and int(stepper_start.value):
|
||||
if not st2.isAlive() and int(stepper_start.value):
|
||||
stepper_2_steps = aio.receive(feed_step_2_steps.key)
|
||||
stepper_2_steps = int(stepper_2_steps.value)
|
||||
if stepper_2_steps > 0: # stepper slider is set
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
{
|
||||
"exportVersion": "1.0.0",
|
||||
"exportedBy": "tyeth",
|
||||
"exportedAt": "2025-06-10T18:13:03.071Z",
|
||||
"exportedFromDevice": {
|
||||
"board": "rpi-pico-w",
|
||||
"firmwareVersion": "1.0.0-beta.100"
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"name": "💦 Wee Button",
|
||||
"pinName": "D18",
|
||||
"type": "push_button",
|
||||
"mode": "DIGITAL",
|
||||
"direction": "INPUT",
|
||||
"period": 0,
|
||||
"pull": "UP",
|
||||
"isPin": true
|
||||
},
|
||||
{
|
||||
"name": "💦 Wee LED",
|
||||
"pinName": "D2",
|
||||
"type": "led",
|
||||
"mode": "DIGITAL",
|
||||
"direction": "OUTPUT",
|
||||
"isPin": true
|
||||
},
|
||||
{
|
||||
"name": "💩 Poo Button",
|
||||
"pinName": "D19",
|
||||
"type": "push_button",
|
||||
"mode": "DIGITAL",
|
||||
"direction": "INPUT",
|
||||
"period": 0,
|
||||
"pull": "UP",
|
||||
"isPin": true
|
||||
},
|
||||
{
|
||||
"name": "💩 Poo LED",
|
||||
"pinName": "D3",
|
||||
"type": "led",
|
||||
"mode": "DIGITAL",
|
||||
"direction": "OUTPUT",
|
||||
"isPin": true
|
||||
},
|
||||
{
|
||||
"name": "❌ Didn't Go Button",
|
||||
"pinName": "D20",
|
||||
"type": "push_button",
|
||||
"mode": "DIGITAL",
|
||||
"direction": "INPUT",
|
||||
"period": 0,
|
||||
"pull": "UP",
|
||||
"isPin": true
|
||||
},
|
||||
{
|
||||
"name": "❌ Didn't Go LED",
|
||||
"pinName": "D4",
|
||||
"type": "led",
|
||||
"mode": "DIGITAL",
|
||||
"direction": "OUTPUT",
|
||||
"isPin": true
|
||||
},
|
||||
{
|
||||
"name": "🔔 Tell Adult Button",
|
||||
"pinName": "D21",
|
||||
"type": "push_button",
|
||||
"mode": "DIGITAL",
|
||||
"direction": "INPUT",
|
||||
"period": 0,
|
||||
"pull": "UP",
|
||||
"isPin": true
|
||||
},
|
||||
{
|
||||
"name": "🔔 Tell Adult LED",
|
||||
"pinName": "D5",
|
||||
"type": "led",
|
||||
"mode": "DIGITAL",
|
||||
"direction": "OUTPUT",
|
||||
"isPin": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
<b>🌟 Daily Potty Training Report 🌟</b>
|
||||
Hi there! Here's how our little superstar did:
|
||||
---
|
||||
<b>🚽 Successful Wees: {{ vars.wee_progress }}</b>
|
||||
{% for i in (1..vars.wee_progress) %}💧{% endfor %}
|
||||
{% if vars.wee_progress <= 5 -%}
|
||||
Great start! Every success counts, and they're building good habits one wee at a time! 🌱
|
||||
{% elsif vars.wee_progress <= 15 -%}
|
||||
Fantastic progress! They're really getting the hang of this - keep up the amazing work! 🎯
|
||||
{% else -%}
|
||||
SUPERSTAR ALERT! 🌟 Absolutely crushing it with those wee successes! They're a potty champion! 🏆
|
||||
{% endif %}---
|
||||
|
||||
<b>💩 Successful Poos: {{ vars.poo_progress }}</b>
|
||||
{% for i in (1..vars.poo_progress) %}🟤{% endfor %}
|
||||
{% if vars.poo_progress == 0 -%}
|
||||
Poos can be tricky, but they're being so brave! Every try is a step forward! 💪
|
||||
{% elsif vars.poo_progress < 2 -%}
|
||||
Look at them go! They're becoming a real poo pro - that's awesome progress! 🎉
|
||||
{% else -%}
|
||||
POO CHAMPION! 🏅 They've mastered one of the trickiest parts - so proud! 🎊
|
||||
{% endif %}---
|
||||
|
||||
<b>🤝 Told an Adult: {{ vars.informed_progress }}</b>
|
||||
{% for i in (1..vars.informed_progress) %}🗣️{% endfor %}
|
||||
{% if vars.informed_progress <= 5 -%}
|
||||
Communication is key! Keep practicing saying when they need to go - They're doing great! 📢
|
||||
{% elsif vars.informed_progress <= 15 -%}
|
||||
Wonderful communication skills! They're really good at letting us know - that's so helpful! 👏
|
||||
{% else -%}
|
||||
COMMUNICATION SUPERSTAR! 🌟 They're amazing at telling adults - that's such a big kid skill! 🎯
|
||||
{% endif %}---
|
||||
|
||||
<b>👻 Nothing Happened: {{ vars.nothing_progress }}</b>
|
||||
{% for i in (1..vars.nothing_progress) %}⭕{% endfor %}
|
||||
{% if vars.nothing_progress <= 3 -%}
|
||||
That's okay! Trying is what matters, and their body will let them know when it's ready! 🌈
|
||||
{% else -%}
|
||||
So patient and persistent! Even when nothing happens, they keep trying - that's real determination! 💫
|
||||
{% endif %}---
|
||||
|
||||
<b>📊 Daily Summary:</b>
|
||||
{% capture total_tries -%}{{ vars.wee_progress | plus: vars.poo_progress | plus: vars.nothing_progress }}{% endcapture -%}
|
||||
{% capture successes -%}{{ vars.wee_progress | plus: vars.poo_progress }}{% endcapture -%}
|
||||
{% capture success_rate -%}{% if total_tries != "0" -%}{{ successes | times: 100 | divided_by: total_tries }}{% else -%}100{% endif -%}{% endcapture -%}
|
||||
{% capture bar_filled -%}{{ success_rate | divided_by: 10 }}{% endcapture -%}
|
||||
{% capture bar_empty -%}{{ 10 | minus: bar_filled }}{% endcapture -%}
|
||||
Total potty visits: {{ total_tries }}
|
||||
Success rate: {{ success_rate }}% [{%- for i in (1..bar_filled) -%}█{%- endfor -%}{%- for i in (1..bar_empty) -%}░{%- endfor -%}]
|
||||
{%- assign total_events = vars.wee_progress | plus: vars.poo_progress | plus: vars.nothing_progress | plus: vars.informed_progress -%}
|
||||
({{ total_events }} events today )
|
||||
{% if total_events <= 3 %}
|
||||
💝 <b>Today:</b> They're doing such a great job learning! Every day gets a little easier...
|
||||
{% elsif total_events <= 5 %}
|
||||
🌟 <b>Today:</b> Ayee! They're really getting the hang of this potty training thing! Keep it up!
|
||||
{% elsif total_events <= 8 %}
|
||||
🌟 <b>Today:</b> WOW! Look at all that practice! They're becoming such a potty expert.
|
||||
{% else %}
|
||||
🏆 <b>Today:</b> INCREDIBLE DAY! They're absolutely rocking this potty training journey! 🎊🎉
|
||||
{%- endif %}
|
||||
|
||||
Keep being awesome!
|
||||
With love and high-fives! 🙌
|
||||
---
|
||||
{%- assign event_mod = total_events | modulo: 3 %}
|
||||
{% if event_mod == 0 -%}
|
||||
<i>P.S. Remember: Every expert was once a beginner - they're doing brilliantly! 🌟</i>
|
||||
{% elsif event_mod == 1 -%}
|
||||
<i>P.S. Fun fact: Even superheroes had to learn to use the potty! 🦸</i>
|
||||
{% else -%}
|
||||
<i>P.S. Remember: accidents are just practice in disguise! They're doing amazingly! 💕</i>
|
||||
{% endif %}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
# 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)
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# 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)
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
# 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
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
# 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.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue