kopia lustrzana https://github.com/pimoroni/pimoroni-pico
Porównaj commity
89 Commity
Autor | SHA1 | Data |
---|---|---|
![]() |
f18f1ba259 | |
![]() |
f495fb8b70 | |
![]() |
7a2ebe5c0d | |
![]() |
48f2446f6c | |
![]() |
6eef96d9c9 | |
![]() |
9771274e90 | |
![]() |
7c05770a44 | |
![]() |
3440ab232c | |
![]() |
7d0bb04d6d | |
![]() |
392890e99b | |
![]() |
7b7d9fe383 | |
![]() |
ccd00f6289 | |
![]() |
10fed7edaf | |
![]() |
517f8ae1cd | |
![]() |
5b6903ce72 | |
![]() |
84cabe077d | |
![]() |
64e26dc6f4 | |
![]() |
47e3aed88f | |
![]() |
bf6fa4bc45 | |
![]() |
0b133c750e | |
![]() |
5691573f89 | |
![]() |
b41bb4abf7 | |
![]() |
8b928d8891 | |
![]() |
56e6d66c9b | |
![]() |
ffa12c2165 | |
![]() |
b9f3990995 | |
![]() |
cca4f3f7f1 | |
![]() |
981a38b989 | |
![]() |
25786def35 | |
![]() |
a387e45098 | |
![]() |
452d700ba1 | |
![]() |
9d7d651565 | |
![]() |
b3a0c4fb9d | |
![]() |
f962c3cd3c | |
![]() |
b5a040f6cc | |
![]() |
d790d3d344 | |
![]() |
ba7b8388bf | |
![]() |
829f688285 | |
![]() |
8fb17a3c26 | |
![]() |
4db7cc61ab | |
![]() |
c510a3c875 | |
![]() |
425f132096 | |
![]() |
76683acb0d | |
![]() |
7c287192de | |
![]() |
f15c657b44 | |
![]() |
f3b53b6b5f | |
![]() |
37c4d22527 | |
![]() |
616b1cc8d6 | |
![]() |
45a9925072 | |
![]() |
3998b0c8bf | |
![]() |
32c10482d9 | |
![]() |
6ed743b32c | |
![]() |
17d59f4729 | |
![]() |
4c44b77193 | |
![]() |
5510c82564 | |
![]() |
3a10b29f54 | |
![]() |
8cf276b992 | |
![]() |
8bb17087d9 | |
![]() |
6fcbaf5616 | |
![]() |
6803f00b31 | |
![]() |
6e71a62c65 | |
![]() |
f1ea35fbbf | |
![]() |
c066325ca0 | |
![]() |
fd4eb165f8 | |
![]() |
8fc8a8ee06 | |
![]() |
3bfb548686 | |
![]() |
9edcdcc126 | |
![]() |
e8e550b18b | |
![]() |
cdb7b4bf2c | |
![]() |
4fc3095433 | |
![]() |
9c5b529754 | |
![]() |
a87d5581aa | |
![]() |
44d7875f7e | |
![]() |
a90c31fb3b | |
![]() |
458b0ac209 | |
![]() |
a537672dd4 | |
![]() |
d34e692f51 | |
![]() |
27b913124c | |
![]() |
c7b788cd1d | |
![]() |
c386b3e9cf | |
![]() |
b499296867 | |
![]() |
193adaca72 | |
![]() |
a7a2e2bee0 | |
![]() |
b0babcfe9f | |
![]() |
19fa8864cf | |
![]() |
e34b2420c6 | |
![]() |
964cf5eedf | |
![]() |
eab1595352 | |
![]() |
5dd76ed31b |
|
@ -25,7 +25,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Compiler Cache
|
- name: Compiler Cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: /home/runner/.ccache
|
path: /home/runner/.ccache
|
||||||
key: ccache-cmake-${{github.ref}}-${{matrix.board}}-${{github.sha}}
|
key: ccache-cmake-${{github.ref}}-${{matrix.board}}-${{github.sha}}
|
||||||
|
@ -34,13 +34,13 @@ jobs:
|
||||||
ccache-cmake-${{github.ref}}
|
ccache-cmake-${{github.ref}}
|
||||||
ccache-cmake
|
ccache-cmake
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
# Check out the Pico SDK
|
# Check out the Pico SDK
|
||||||
- name: Checkout Pico SDK
|
- name: Checkout Pico SDK
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: raspberrypi/pico-sdk
|
repository: raspberrypi/pico-sdk
|
||||||
path: pico-sdk
|
path: pico-sdk
|
||||||
|
@ -48,7 +48,7 @@ jobs:
|
||||||
|
|
||||||
# Check out the Pico Extras
|
# Check out the Pico Extras
|
||||||
- name: Checkout Pico Extras
|
- name: Checkout Pico Extras
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: raspberrypi/pico-extras
|
repository: raspberrypi/pico-extras
|
||||||
path: pico-extras
|
path: pico-extras
|
||||||
|
|
|
@ -7,20 +7,23 @@ on:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
MICROPYTHON_VERSION: v1.22.2
|
MICROPYTHON_VERSION: v1.23.0
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: ${{ matrix.name }} (${{ matrix.board }})
|
name: ${{ matrix.name }} (${{ matrix.board }})
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
continue-on-error: true
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- name: pico
|
- name: pico
|
||||||
board: RPI_PICO
|
board: RPI_PICO
|
||||||
|
- name: pico_usb
|
||||||
|
board: RPI_PICO_USB
|
||||||
- name: picow
|
- name: picow
|
||||||
board: RPI_PICO_W
|
board: RPI_PICO_W
|
||||||
- name: tiny2040
|
- name: tiny2040_8mb
|
||||||
board: PIMORONI_TINY2040
|
board: PIMORONI_TINY2040
|
||||||
- name: picolipo_4mb
|
- name: picolipo_4mb
|
||||||
board: PIMORONI_PICOLIPO_4MB
|
board: PIMORONI_PICOLIPO_4MB
|
||||||
|
@ -41,7 +44,7 @@ jobs:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# MicroPython version will be contained in github.event.release.tag_name for releases
|
# MicroPython version will be contained in github.event.release.tag_name for releases
|
||||||
RELEASE_FILE: pimoroni-${{ matrix.name }}-${{ github.event.release.tag_name || github.sha }}-micropython
|
RELEASE_FILE: ${{ matrix.name }}-${{ github.event.release.tag_name || github.sha }}-pimoroni-micropython
|
||||||
PIMORONI_PICO_DIR: "${{ github.workspace }}/pimoroni-pico-${{ github.sha }}"
|
PIMORONI_PICO_DIR: "${{ github.workspace }}/pimoroni-pico-${{ github.sha }}"
|
||||||
MICROPY_BOARD_DIR: "${{ github.workspace }}/pimoroni-pico-${{ github.sha }}/micropython/board/${{ matrix.BOARD }}"
|
MICROPY_BOARD_DIR: "${{ github.workspace }}/pimoroni-pico-${{ github.sha }}/micropython/board/${{ matrix.BOARD }}"
|
||||||
USER_C_MODULES: "${{ github.workspace }}/pimoroni-pico-${{ github.sha }}/micropython/modules/micropython-${{ matrix.name }}.cmake"
|
USER_C_MODULES: "${{ github.workspace }}/pimoroni-pico-${{ github.sha }}/micropython/modules/micropython-${{ matrix.name }}.cmake"
|
||||||
|
@ -52,7 +55,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Compiler Cache
|
- name: Compiler Cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: /home/runner/.ccache
|
path: /home/runner/.ccache
|
||||||
key: ccache-micropython-${{ matrix.name }}-${{ github.ref }}-${{ github.sha }}
|
key: ccache-micropython-${{ matrix.name }}-${{ github.ref }}-${{ github.sha }}
|
||||||
|
@ -80,6 +83,13 @@ jobs:
|
||||||
source $BUILD_TOOLS
|
source $BUILD_TOOLS
|
||||||
micropython_clone
|
micropython_clone
|
||||||
|
|
||||||
|
- name: "Py_Decl: Checkout py_decl"
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: gadgetoid/py_decl
|
||||||
|
ref: v0.0.1
|
||||||
|
path: py_decl
|
||||||
|
|
||||||
- name: Build MPY Cross
|
- name: Build MPY Cross
|
||||||
run: |
|
run: |
|
||||||
source $BUILD_TOOLS
|
source $BUILD_TOOLS
|
||||||
|
@ -110,8 +120,13 @@ jobs:
|
||||||
source $BUILD_TOOLS
|
source $BUILD_TOOLS
|
||||||
cmake_build
|
cmake_build
|
||||||
|
|
||||||
|
- name: "Py_Decl: Verify UF2"
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
python3 py_decl/py_decl.py --to-json --verify build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2
|
||||||
|
|
||||||
- name: Store .uf2 as artifact
|
- name: Store .uf2 as artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.RELEASE_FILE }}.uf2
|
name: ${{ env.RELEASE_FILE }}.uf2
|
||||||
path: build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2
|
path: build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2
|
||||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
||||||
name: Python Linting
|
name: Python Linting
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Python Deps
|
- name: Install Python Deps
|
||||||
run: python3 -m pip install flake8
|
run: python3 -m pip install flake8
|
||||||
|
|
|
@ -44,6 +44,10 @@ You can find MicroPython examples for supported sensors, packs and bases in the
|
||||||
|
|
||||||
* [MicroPython Examples](micropython/examples)
|
* [MicroPython Examples](micropython/examples)
|
||||||
|
|
||||||
|
You can also install MicroPython stubs into Visual Studio Code to give you auto-complete, see:
|
||||||
|
|
||||||
|
* [MicroPython Stubs](https://github.com/pimoroni/pimoroni-pico-stubs)
|
||||||
|
|
||||||
# C/C++
|
# C/C++
|
||||||
|
|
||||||
Advanced users that want to unleash the full power of Pico can use our C++ libraries. If you know what you're doing and want to build your own Pimoroni Pico project then start with the [Pimoroni Pico SDK Boilerplate](https://github.com/pimoroni/pico-boilerplate).
|
Advanced users that want to unleash the full power of Pico can use our C++ libraries. If you know what you're doing and want to build your own Pimoroni Pico project then start with the [Pimoroni Pico SDK Boilerplate](https://github.com/pimoroni/pico-boilerplate).
|
||||||
|
|
|
@ -14,8 +14,10 @@ function log_warning {
|
||||||
|
|
||||||
function micropython_clone {
|
function micropython_clone {
|
||||||
log_inform "Using MicroPython $MICROPYTHON_VERSION"
|
log_inform "Using MicroPython $MICROPYTHON_VERSION"
|
||||||
git clone https://github.com/micropython/micropython --depth=1 --branch=$MICROPYTHON_VERSION
|
git clone https://github.com/micropython/micropython
|
||||||
cd micropython
|
cd micropython
|
||||||
|
git checkout $MICROPYTHON_VERSION
|
||||||
|
git cherry-pick -n 932f76c6ba64c5a3e68de3324556d9979f09303b
|
||||||
git submodule update --init lib/pico-sdk
|
git submodule update --init lib/pico-sdk
|
||||||
git submodule update --init lib/cyw43-driver
|
git submodule update --init lib/cyw43-driver
|
||||||
git submodule update --init lib/lwip
|
git submodule update --init lib/lwip
|
||||||
|
@ -72,4 +74,4 @@ function cmake_build {
|
||||||
ccache --show-stats || true
|
ccache --show-stats || true
|
||||||
cd build-$BOARD_NAME
|
cd build-$BOARD_NAME
|
||||||
cp firmware.uf2 $RELEASE_FILE.uf2
|
cp firmware.uf2 $RELEASE_FILE.uf2
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,15 @@ namespace pimoroni {
|
||||||
i2c_write_blocking(i2c, address, buffer, 2, false);
|
i2c_write_blocking(i2c, address, buffer, 2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void I2C::reg_write_uint16(uint8_t address, uint8_t reg, uint16_t value) {
|
||||||
|
uint8_t buffer[3] = {
|
||||||
|
reg,
|
||||||
|
(uint8_t)((value & 0xff00) >> 8),
|
||||||
|
(uint8_t)(value & 0x00ff)
|
||||||
|
};
|
||||||
|
i2c_write_blocking(i2c, address, buffer, 3, false);
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t I2C::reg_read_uint8(uint8_t address, uint8_t reg) {
|
uint8_t I2C::reg_read_uint8(uint8_t address, uint8_t reg) {
|
||||||
uint8_t value;
|
uint8_t value;
|
||||||
i2c_write_blocking(i2c, address, ®, 1, false);
|
i2c_write_blocking(i2c, address, ®, 1, false);
|
||||||
|
|
|
@ -63,6 +63,7 @@ namespace pimoroni {
|
||||||
i2c_inst_t* pin_to_inst(uint pin);
|
i2c_inst_t* pin_to_inst(uint pin);
|
||||||
|
|
||||||
void reg_write_uint8(uint8_t address, uint8_t reg, uint8_t value);
|
void reg_write_uint8(uint8_t address, uint8_t reg, uint8_t value);
|
||||||
|
void reg_write_uint16(uint8_t address, uint8_t reg, uint16_t value);
|
||||||
uint8_t reg_read_uint8(uint8_t address, uint8_t reg);
|
uint8_t reg_read_uint8(uint8_t address, uint8_t reg);
|
||||||
uint16_t reg_read_uint16(uint8_t address, uint8_t reg);
|
uint16_t reg_read_uint16(uint8_t address, uint8_t reg);
|
||||||
int16_t reg_read_int16(uint8_t address, uint8_t reg);
|
int16_t reg_read_int16(uint8_t address, uint8_t reg);
|
||||||
|
|
|
@ -16,6 +16,7 @@ add_subdirectory(is31fl3731)
|
||||||
add_subdirectory(fatfs)
|
add_subdirectory(fatfs)
|
||||||
add_subdirectory(sdcard)
|
add_subdirectory(sdcard)
|
||||||
add_subdirectory(as7262)
|
add_subdirectory(as7262)
|
||||||
|
add_subdirectory(as7343)
|
||||||
add_subdirectory(bh1745)
|
add_subdirectory(bh1745)
|
||||||
add_subdirectory(bme68x)
|
add_subdirectory(bme68x)
|
||||||
add_subdirectory(bmp280)
|
add_subdirectory(bmp280)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
include(as7343.cmake)
|
|
@ -0,0 +1,10 @@
|
||||||
|
set(DRIVER_NAME as7343)
|
||||||
|
add_library(${DRIVER_NAME} INTERFACE)
|
||||||
|
|
||||||
|
target_sources(${DRIVER_NAME} INTERFACE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp)
|
||||||
|
|
||||||
|
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
|
||||||
|
# Pull in pico libraries that we need
|
||||||
|
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_i2c pimoroni_i2c)
|
|
@ -0,0 +1,160 @@
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <math.h>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "as7343.hpp"
|
||||||
|
|
||||||
|
namespace pimoroni {
|
||||||
|
|
||||||
|
bool AS7343::init() {
|
||||||
|
if(interrupt != PIN_UNUSED) {
|
||||||
|
gpio_set_function(interrupt, GPIO_FUNC_SIO);
|
||||||
|
gpio_set_dir(interrupt, GPIO_IN);
|
||||||
|
gpio_pull_up(interrupt);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t aux_id, revision_id, hardware_id;
|
||||||
|
get_version(aux_id, revision_id, hardware_id);
|
||||||
|
|
||||||
|
if(hardware_id != HARDWARE_ID) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset();
|
||||||
|
|
||||||
|
bank_select(0);
|
||||||
|
|
||||||
|
// Enable all 7 channels in output FIFO
|
||||||
|
i2c->set_bits(address, reg::FIFO_MAP, 0, FIFO_MAP_CH5 | FIFO_MAP_CH4 | FIFO_MAP_CH3 | FIFO_MAP_CH2 | FIFO_MAP_CH1 | FIFO_MAP_CH0 | FIFO_MAP_ASTATUS);
|
||||||
|
|
||||||
|
// Set the PON bit
|
||||||
|
i2c->reg_write_uint8(address, reg::ENABLE, ENABLE_WEN | ENABLE_SMUXEN | ENABLE_SP_EN | ENABLE_PON);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AS7343::reset() {
|
||||||
|
i2c->reg_write_uint8(address, reg::CONTROL, CONTROL_SW_RESET);
|
||||||
|
sleep_ms(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_inst_t* AS7343::get_i2c() const {
|
||||||
|
return i2c->get_i2c();
|
||||||
|
}
|
||||||
|
|
||||||
|
int AS7343::get_sda() const {
|
||||||
|
return i2c->get_sda();
|
||||||
|
}
|
||||||
|
|
||||||
|
int AS7343::get_scl() const {
|
||||||
|
return i2c->get_scl();
|
||||||
|
}
|
||||||
|
|
||||||
|
int AS7343::get_int() const {
|
||||||
|
return interrupt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AS7343::bank_select(uint8_t bank) {
|
||||||
|
if(bank == 1) {
|
||||||
|
i2c->set_bits(address, reg::CFG0, 0, CFG0_BANK);
|
||||||
|
} else {
|
||||||
|
i2c->clear_bits(address, reg::CFG0, 0, CFG0_BANK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AS7343::get_version(uint8_t &auxid, uint8_t &revid, uint8_t &hwid) {
|
||||||
|
bank_select(1);
|
||||||
|
auxid = i2c->reg_read_uint8(address, reg::AUXID) & 0b00001111;
|
||||||
|
revid = i2c->reg_read_uint8(address, reg::REVID) & 0b00000111;
|
||||||
|
hwid = i2c->reg_read_uint8(address, reg::ID);
|
||||||
|
bank_select(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AS7343::set_gain(float gain) {
|
||||||
|
if(gain == 0.5f) {
|
||||||
|
i2c->reg_write_uint8(address, reg::CFG1, 0);
|
||||||
|
} else {
|
||||||
|
uint16_t ugain = (uint16_t)gain;
|
||||||
|
uint16_t bitlength = 0;
|
||||||
|
while(ugain > 0) {
|
||||||
|
bitlength++;
|
||||||
|
ugain >>= 1;
|
||||||
|
}
|
||||||
|
i2c->reg_write_uint8(address, reg::CFG1, bitlength & 0x1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AS7343::set_channels(channel_count channel_count) {
|
||||||
|
this->read_cycles = uint8_t(channel_count) / 6;
|
||||||
|
this->ch_count = (uint8_t)channel_count;
|
||||||
|
uint8_t temp = i2c->reg_read_uint8(address, reg::CFG20) & ~CFG20_18_CH;
|
||||||
|
switch(channel_count) {
|
||||||
|
case channel_count::SIX_CHANNEL:
|
||||||
|
temp |= CFG20_6_CH;
|
||||||
|
break;
|
||||||
|
case channel_count::TWELVE_CHANNEL:
|
||||||
|
temp |= CFG20_12_CH;
|
||||||
|
break;
|
||||||
|
case channel_count::EIGHTEEN_CHANNEL:
|
||||||
|
temp |= CFG20_18_CH;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i2c->reg_write_uint8(address, reg::CFG20, temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AS7343::set_illumination_current(float current) {
|
||||||
|
current -= 4;
|
||||||
|
current /= 2.0f;
|
||||||
|
uint8_t temp = i2c->reg_read_uint8(address, reg::LED) & 0b10000000;
|
||||||
|
temp |= (uint8_t)current;
|
||||||
|
i2c->reg_write_uint8(address, reg::LED, temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AS7343::set_illumination_led(bool state) {
|
||||||
|
uint8_t temp = i2c->reg_read_uint8(address, reg::LED) & 0b01111111;
|
||||||
|
temp |= state ? 0x80 : 0x00;
|
||||||
|
i2c->reg_write_uint8(address, reg::LED, temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AS7343::set_measurement_time(float measurement_time_ms) {
|
||||||
|
constexpr float resolution = 2.78f;
|
||||||
|
i2c->reg_write_uint8(address, reg::WTIME, (uint8_t)((measurement_time_ms - resolution) / resolution));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AS7343::set_integration_time(float integration_time_us, uint8_t repeat) {
|
||||||
|
constexpr float resolution = 2.78f;
|
||||||
|
constexpr float max_astep = (65534 + 1) * resolution;
|
||||||
|
|
||||||
|
if (integration_time_us <= max_astep) {
|
||||||
|
i2c->reg_write_uint8(address, reg::ATIME, repeat - 1);
|
||||||
|
i2c->reg_write_uint16(address, reg::ASTEP, (uint16_t)((integration_time_us - resolution) / resolution) & 0xfffe);
|
||||||
|
} else {
|
||||||
|
// Time out of range...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AS7343::read_fifo(uint16_t *buf) {
|
||||||
|
uint16_t expected_results = read_cycles * 7;
|
||||||
|
uint16_t result_slot = 0;
|
||||||
|
|
||||||
|
while (i2c->reg_read_uint8(address, reg::FIFO_LVL) < expected_results) {
|
||||||
|
sleep_ms(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (i2c->reg_read_uint8(address, reg::FIFO_LVL) > 0 && expected_results > 0) {
|
||||||
|
buf[result_slot] = i2c->reg_read_uint16(address, reg::FDATA);
|
||||||
|
expected_results--;
|
||||||
|
result_slot++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AS7343::reading AS7343::read() {
|
||||||
|
reading result;
|
||||||
|
read_fifo((uint16_t *)&result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "hardware/i2c.h"
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
#include "common/pimoroni_common.hpp"
|
||||||
|
#include "common/pimoroni_i2c.hpp"
|
||||||
|
#include "as7343_regs.hpp"
|
||||||
|
|
||||||
|
namespace pimoroni {
|
||||||
|
|
||||||
|
class AS7343 {
|
||||||
|
//--------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
//--------------------------------------------------
|
||||||
|
public:
|
||||||
|
static const uint8_t DEFAULT_I2C_ADDRESS = 0x39;
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// Enums
|
||||||
|
//--------------------------------------------------
|
||||||
|
public:
|
||||||
|
enum class channel_count : uint8_t {
|
||||||
|
SIX_CHANNEL = 6,
|
||||||
|
TWELVE_CHANNEL = 12,
|
||||||
|
EIGHTEEN_CHANNEL = 18
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
enum class COMPENSATION_GAIN : float {
|
||||||
|
F1 = 1.84f,
|
||||||
|
F2 = 6.03f,
|
||||||
|
FZ = 4.88f,
|
||||||
|
F3 = 13.74f,
|
||||||
|
F4 = 3.37f,
|
||||||
|
FY = 2.82f,
|
||||||
|
F5 = 6.72f,
|
||||||
|
FXL = 2.22f,
|
||||||
|
F6 = 3.17f,
|
||||||
|
F7 = 1.95f,
|
||||||
|
F8 = 12.25f,
|
||||||
|
NIR = 1.00f,
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// Substructures
|
||||||
|
//--------------------------------------------------
|
||||||
|
public:
|
||||||
|
struct reading {
|
||||||
|
// Cycle 1
|
||||||
|
uint16_t FZ = 0; // 428-480 nm
|
||||||
|
uint16_t FY = 0; // 534-593 nm
|
||||||
|
uint16_t FXL = 0; // 593-628 nm
|
||||||
|
uint16_t NIR = 0; // 849-903 nm
|
||||||
|
|
||||||
|
uint16_t c1_vis_tl = 0; // Visible top-left
|
||||||
|
uint16_t c1_vis_br = 0; // Visible bottom-right
|
||||||
|
uint16_t c1_astatus = 0; // (c1_astatus >> 8) & 0b10001111
|
||||||
|
// 0b10000000 = saturated
|
||||||
|
// 0b00001111 = gain lowest nibble
|
||||||
|
|
||||||
|
// Cycle 2
|
||||||
|
uint16_t F2 = 0; // 408-448 nm
|
||||||
|
uint16_t F3 = 0; // 448-500 mn
|
||||||
|
uint16_t F4 = 0; // 500-534 nm
|
||||||
|
uint16_t F6 = 0; // 618-665 nm
|
||||||
|
|
||||||
|
uint16_t c2_vis_tl = 0;
|
||||||
|
uint16_t c2_vis_br = 0;
|
||||||
|
uint16_t c2_astatus = 0;
|
||||||
|
|
||||||
|
// Cycle 3
|
||||||
|
uint16_t F1 = 0; // 396-408 nm
|
||||||
|
uint16_t F5 = 0; // 531-594 nm
|
||||||
|
uint16_t F7 = 0; // 685-715 nm
|
||||||
|
uint16_t F8 = 0; // 715-766 nm
|
||||||
|
|
||||||
|
uint16_t c3_vis_tl = 0;
|
||||||
|
uint16_t c3_vis_br = 0;
|
||||||
|
uint16_t c3_astatus = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// Variables
|
||||||
|
//--------------------------------------------------
|
||||||
|
private:
|
||||||
|
I2C *i2c;
|
||||||
|
|
||||||
|
// interface pins with our standard defaults where appropriate
|
||||||
|
int8_t address = DEFAULT_I2C_ADDRESS;
|
||||||
|
uint interrupt = PIN_UNUSED;
|
||||||
|
|
||||||
|
uint8_t read_cycles = 1;
|
||||||
|
uint8_t ch_count = (uint8_t)channel_count::SIX_CHANNEL;
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// Constructors/Destructor
|
||||||
|
//--------------------------------------------------
|
||||||
|
public:
|
||||||
|
AS7343(uint interrupt = PIN_UNUSED) : AS7343(new I2C(), interrupt) {};
|
||||||
|
|
||||||
|
AS7343(I2C *i2c, uint interrupt = PIN_UNUSED) : i2c(i2c), interrupt(interrupt) {}
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// Methods
|
||||||
|
//--------------------------------------------------
|
||||||
|
public:
|
||||||
|
bool init();
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
// For print access in micropython
|
||||||
|
i2c_inst_t* get_i2c() const;
|
||||||
|
int get_sda() const;
|
||||||
|
int get_scl() const;
|
||||||
|
int get_int() const;
|
||||||
|
|
||||||
|
void get_version(uint8_t &auxid, uint8_t &revid, uint8_t &hwid);
|
||||||
|
|
||||||
|
reading read();
|
||||||
|
|
||||||
|
void read_fifo(uint16_t *buf);
|
||||||
|
|
||||||
|
void set_gain(float gain);
|
||||||
|
void set_illumination_current(float current);
|
||||||
|
void set_illumination_led(bool state);
|
||||||
|
void set_measurement_time(float measurement_time_ms);
|
||||||
|
void set_integration_time(float integration_time_us, uint8_t repeat=1);
|
||||||
|
|
||||||
|
void set_channels(channel_count channel_count);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void bank_select(uint8_t bank=0);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace pimoroni {
|
||||||
|
/***** Device registers and masks here *****/
|
||||||
|
|
||||||
|
enum reg {
|
||||||
|
// Bank 1
|
||||||
|
AUXID = 0x58, // AUXID = 0b00001111
|
||||||
|
REVID = 0x59, // REVID = 0b00000111
|
||||||
|
ID = 0x5A, // ID = 0b11111111
|
||||||
|
CFG12 = 0x66, // SP_TH_CH = 0b00000111
|
||||||
|
|
||||||
|
// Bank 0
|
||||||
|
ENABLE = 0x80, // FDEN = 0b01000000
|
||||||
|
// SMUXEN = 0b00010000
|
||||||
|
// WEN = 0b00001000
|
||||||
|
// SP_EN = 0b00000010
|
||||||
|
// PON = 0b00000001
|
||||||
|
ATIME = 0x81, // ATIME = 0b11111111
|
||||||
|
// Integration Time Step Size
|
||||||
|
// 0 = 2.87us
|
||||||
|
// n 2.87us x (n + 1)
|
||||||
|
ASTEP = 0xD4, // STEP = 0xFFFF
|
||||||
|
// Spectral Measurement Wait Time
|
||||||
|
// 0 = 1 cycle = 2.78ms
|
||||||
|
// n = 2.78ms x (n + 1)
|
||||||
|
WTIME = 0x83, // WTIME = 0b11111111
|
||||||
|
SP_TH = 0x84, // SP_TH_L = 0xFFFF0000 Spectral Low Threshold
|
||||||
|
// SP_TH_H = 0x0000FFFF Spectral High Threshold
|
||||||
|
STATUS = 0x93, // ASAT = 0b10000000 Spectral Saturation (if ASIEN set)
|
||||||
|
// AINT = 0b00001000 Spectral Channel Interrupt (if SP_IEN set)
|
||||||
|
// FINT = 0b00000100 FIFO Buffer Interrupt
|
||||||
|
// SINT = 0b00000001 System Interrupt
|
||||||
|
ASTATUS = 0x94,// ASAT = 0b10000000 Saturation Status
|
||||||
|
// AGAIN = 0b00000111 Gain Status
|
||||||
|
DATA = 0x95, // 36 bytes, fields 0 to 17 (2 bytes eac)
|
||||||
|
STATUS2 = 0x90,// AVALID = 0b01000000 Spectral Data Valid
|
||||||
|
// ASAT_DIG = 0b00010000 Digital Saturation
|
||||||
|
// ASAT_ANA = 0b00001000 Analog Saturation
|
||||||
|
// FDSAT_ANA= 0b00000010 Flicker Analog Saturation
|
||||||
|
// FDSAT_DIG= 0b00000001 Flicker Digital Saturation
|
||||||
|
STATUS3 = 0x91,// INT_SP_H = 0b00100000 Spectral Above High Threshold
|
||||||
|
// INT_SP_L = 0b00010000 Spectral Below Low Threshold
|
||||||
|
STATUS5 = 0xBB,// SINT_FD = 0b00001000 Flicker Detect Interrupt (if SIEN_FD set)
|
||||||
|
// SINT_SMUX= 0b00000100 SMUS Operation Interrupt (SMUX exec finished)
|
||||||
|
STATUS4 = 0xBC,// FIFO_OV = 0b10000000 FIFO Buffer Overflow
|
||||||
|
// OVTEMP = 0b00100000 Over Temperature
|
||||||
|
// FD_TRIG = 0b00010000 Flicker Detect Trigger Error
|
||||||
|
// SD_TRIG = 0b00000100 Spectral Trigger Error
|
||||||
|
// SAI_ACT = 0b00000010 Sleep After Interrupt Active
|
||||||
|
// INT_BUSY = 0b00000001 Initialization busy (1 for ~300us after power on)
|
||||||
|
CFG0 = 0xBF, // LOW_POWER= 0b00100000
|
||||||
|
// REG_BANK = 0b00010000 0 - Register 0x80 and above
|
||||||
|
// 1 - Register 0x20 to 0x7f
|
||||||
|
// WLONG = 0b00000100 Increase WTIME by factor of 16
|
||||||
|
// Spectral Engines Gain Setting
|
||||||
|
// 0 = 0.5x, # 1 = 1x, 2 = 2x, 12 = 2048x
|
||||||
|
// GAINx = 1 << (n - 1)
|
||||||
|
CFG1 = 0xC6,
|
||||||
|
CFG3 = 0xC7, // SAI = 0b00100000 Sleep after interrupt
|
||||||
|
CFG6 = 0xF5, // SMUS_CMD = 0b00011000 0 - ROM_init
|
||||||
|
// 1 - Read_SMUX
|
||||||
|
// 2 - Write_SMUX
|
||||||
|
CFG8 = 0xC9, // FIFO_TH = 0b11000000 0b00, 0b01, 0b10, 0b11
|
||||||
|
CFG9 = 0xCA, // SIEN_FD = 0b01000000 System Interrupt Flicker Detection
|
||||||
|
// SIEN_SMUX= 0b00010000 System Interrupt SMUX Operation
|
||||||
|
CFG10 = 0x65, // FD_PERS = 0b00000111 Flicker Detect Persistence
|
||||||
|
// Number of results that must differ before status change
|
||||||
|
PERS = 0xCF, // APERS = 0b00001111
|
||||||
|
GPIO = 0x6B, // GPIO_INV = 0b00001000 Invert GPIO output
|
||||||
|
// GPIO_IN_EN=0b00000100 Enable GPIO input
|
||||||
|
// GPIO_OUT = 0b00000010 GPIO Output
|
||||||
|
// GPIO_IN = 0b00000001 GPIO Input
|
||||||
|
CFG20 = 0xD6, // FD_FIFO_8b=0b10000000 Enable 8bit FIFO mode for Flicker Detect (FD_TIME < 256)
|
||||||
|
// auto_SMUX= 0b01100000 Auto channel read-out
|
||||||
|
LED = 0xCD, // LED_ACT = 0b10000000 External LED (LDR) Control
|
||||||
|
// LED_DRIVE= 0b01111111 External LED drive strength (N - 4) >> 1
|
||||||
|
// Flicker Detection AGC Gain Max
|
||||||
|
// Max = 2^N (0 = 0.5x)
|
||||||
|
AGC_GAIN_MAX = 0xD7, // ADC_FD_GAIN_MAX = 0b11110000
|
||||||
|
AZ_CONFIG = 0xDE, // AT_NTH_ITERATION = 0b11111111 Auto-zero Frequency
|
||||||
|
// 0 - Never (Not Recommended)
|
||||||
|
// n - Every n integration cycles
|
||||||
|
// 255 - only before first measurement cycle
|
||||||
|
FD_TIME_1 = 0xE0, // FD_TIME=0b11111111 Flicker Detection Integration Time (Do not change if FDEN = 1 & PON = 1)
|
||||||
|
FD_TIME_2 = 0xE2, // FD_GAIN=0b11111000 Flicker Detection Gain (0 = 0.5x, 1 = 1x, 2 = 2x, 12 = 2048x)
|
||||||
|
// FD_TIME=0b00000111 Flicker Detection Time (Do not change if FDEN = 1 & PON = 1)
|
||||||
|
FD_CFG0 = 0xDF, // FIFO_WRITE_FD = 0b10000000 Write flicker raw data to FIFO (1 byte per sample)
|
||||||
|
FD_STATUS = 0xE3, // FD_VALID=0b00100000
|
||||||
|
// FD_SAT = 0b00010000
|
||||||
|
// FD_120HZ_VALID = 0b00001000
|
||||||
|
// FD_100HZ_VALID = 0b00000100
|
||||||
|
// FD_120HZ = 0b00000010
|
||||||
|
// FD_100HZ = 0b00000001
|
||||||
|
INTERNAB = 0xF9, // ASIEN = 0b10000000 Saturation Interrupt Enable
|
||||||
|
// SP_IEN = 0b00001000 Spectral Interrupt Enable
|
||||||
|
// FIEN = 0b00000100 FIFO Buffer Interrupt Enable
|
||||||
|
// SIEN = 0b00000001 System Interrupt Enable
|
||||||
|
CONTROL = 0xFA, // SW_RESET = 0b00001000 Software Reset
|
||||||
|
// SP_MAN_AZ= 0b00000100 Spectral Manual Auto-zero
|
||||||
|
// FIFO_CLR = 0b00000010 FIFO Buffer Clear
|
||||||
|
// CLEAR_SAI_ACT = 0b00000001 Clear sleep-after-interrupt
|
||||||
|
FIFO_MAP = 0xFC, // CH5 = 0b01000000
|
||||||
|
// CH4 = 0b00100000
|
||||||
|
// CH3 = 0b00010000
|
||||||
|
// CH2 = 0b00001000
|
||||||
|
// CH1 = 0b00000100
|
||||||
|
// CH0 = 0b00000010
|
||||||
|
// ASTATUS = 0b00000001
|
||||||
|
FIFO_LVL = 0xFD,
|
||||||
|
FDATA = 0xFE // 0xFFFF
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr uint8_t HARDWARE_ID = 0b10000001;
|
||||||
|
|
||||||
|
constexpr uint8_t CFG0_LOW_POWER = 0b00100000;
|
||||||
|
constexpr uint8_t CFG0_BANK = 0b00010000;
|
||||||
|
constexpr uint8_t CFG0_WLONG = 0b00000100;
|
||||||
|
|
||||||
|
constexpr uint8_t CFG20_6_CH = 0b00000000;
|
||||||
|
constexpr uint8_t CFG20_12_CH = 0b01000000;
|
||||||
|
constexpr uint8_t CFG20_18_CH = 0b01100000;
|
||||||
|
|
||||||
|
constexpr uint8_t ENABLE_FDEN = 0b01000000;
|
||||||
|
constexpr uint8_t ENABLE_SMUXEN = 0b00010000;
|
||||||
|
constexpr uint8_t ENABLE_WEN = 0b00001000;
|
||||||
|
constexpr uint8_t ENABLE_SP_EN = 0b00000010;
|
||||||
|
constexpr uint8_t ENABLE_PON = 0b00000001;
|
||||||
|
|
||||||
|
constexpr uint8_t FD_CFG0_FIFO_WRITE = 0b10000000;
|
||||||
|
|
||||||
|
constexpr uint8_t FD_VALID = 0b00100000;
|
||||||
|
constexpr uint8_t FD_SAT = 0b00010000;
|
||||||
|
constexpr uint8_t FD_120HZ_VALID = 0b00001000;
|
||||||
|
constexpr uint8_t FD_100HZ_VALID = 0b00000100;
|
||||||
|
constexpr uint8_t FD_120HZ = 0b00000010;
|
||||||
|
constexpr uint8_t FD_100HZ = 0b00000001;
|
||||||
|
|
||||||
|
constexpr uint8_t INTERNAB_ASIEN = 0b10000000;
|
||||||
|
constexpr uint8_t INTERNAB_SP_IEN = 0b00001000;
|
||||||
|
constexpr uint8_t INTERNAB_FIEN = 0b00000100;
|
||||||
|
constexpr uint8_t INTERNAB_SIEN = 0b00000001;
|
||||||
|
|
||||||
|
constexpr uint8_t CONTROL_SW_RESET = 0b00001000;
|
||||||
|
constexpr uint8_t CONTROL_SP_MAN_AZ = 0b00000100;
|
||||||
|
constexpr uint8_t CONTROL_FIFO_CLR = 0b00000010;
|
||||||
|
constexpr uint8_t CONTROL_CLEAR_SAI_ACT = 0b00000001;
|
||||||
|
|
||||||
|
constexpr uint8_t FIFO_MAP_CH5 = 0b01000000;
|
||||||
|
constexpr uint8_t FIFO_MAP_CH4 = 0b00100000;
|
||||||
|
constexpr uint8_t FIFO_MAP_CH3 = 0b00010000;
|
||||||
|
constexpr uint8_t FIFO_MAP_CH2 = 0b00001000;
|
||||||
|
constexpr uint8_t FIFO_MAP_CH1 = 0b00000100;
|
||||||
|
constexpr uint8_t FIFO_MAP_CH0 = 0b00000010;
|
||||||
|
constexpr uint8_t FIFO_MAP_ASTATUS = 0b00000001;
|
||||||
|
}
|
|
@ -47,8 +47,9 @@ namespace pimoroni {
|
||||||
return !(sr.read() & 128);
|
return !(sr.read() & 128);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inky73::busy_wait() {
|
void Inky73::busy_wait(uint timeout_ms) {
|
||||||
while(is_busy()) {
|
absolute_time_t timeout = make_timeout_time_ms(timeout_ms);
|
||||||
|
while(is_busy() && !time_reached(timeout)) {
|
||||||
tight_loop_contents();
|
tight_loop_contents();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ namespace pimoroni {
|
||||||
// Methods
|
// Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
public:
|
public:
|
||||||
void busy_wait();
|
void busy_wait(uint timeout_ms=45000);
|
||||||
void reset();
|
void reset();
|
||||||
void power_off();
|
void power_off();
|
||||||
|
|
||||||
|
|
|
@ -133,8 +133,6 @@ namespace pimoroni {
|
||||||
|
|
||||||
void ST7789::configure_display(Rotation rotate) {
|
void ST7789::configure_display(Rotation rotate) {
|
||||||
|
|
||||||
bool rotate180 = rotate == ROTATE_180 || rotate == ROTATE_90;
|
|
||||||
|
|
||||||
if(rotate == ROTATE_90 || rotate == ROTATE_270) {
|
if(rotate == ROTATE_90 || rotate == ROTATE_270) {
|
||||||
std::swap(width, height);
|
std::swap(width, height);
|
||||||
}
|
}
|
||||||
|
@ -185,20 +183,30 @@ namespace pimoroni {
|
||||||
// Pico Display
|
// Pico Display
|
||||||
if(width == 240 && height == 135) {
|
if(width == 240 && height == 135) {
|
||||||
caset[0] = 40; // 240 cols
|
caset[0] = 40; // 240 cols
|
||||||
caset[1] = 279;
|
caset[1] = 40 + width - 1;
|
||||||
raset[0] = 53; // 135 rows
|
raset[0] = 52; // 135 rows
|
||||||
raset[1] = 187;
|
raset[1] = 52 + height - 1;
|
||||||
madctl = rotate180 ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
|
if (rotate == ROTATE_0) {
|
||||||
|
raset[0] += 1;
|
||||||
|
raset[1] += 1;
|
||||||
|
}
|
||||||
|
madctl = rotate == ROTATE_180 ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
|
||||||
madctl |= MADCTL::SWAP_XY | MADCTL::SCAN_ORDER;
|
madctl |= MADCTL::SWAP_XY | MADCTL::SCAN_ORDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pico Display at 90 degree rotation
|
// Pico Display at 90 degree rotation
|
||||||
if(width == 135 && height == 240) {
|
if(width == 135 && height == 240) {
|
||||||
caset[0] = 52; // 135 cols
|
caset[0] = 52; // 135 cols
|
||||||
caset[1] = 186;
|
caset[1] = 52 + width - 1;
|
||||||
raset[0] = 40; // 240 rows
|
raset[0] = 40; // 240 rows
|
||||||
raset[1] = 279;
|
raset[1] = 40 + height - 1;
|
||||||
madctl = rotate180 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
|
madctl = 0;
|
||||||
|
if (rotate == ROTATE_90) {
|
||||||
|
caset[0] += 1;
|
||||||
|
caset[1] += 1;
|
||||||
|
madctl = MADCTL::COL_ORDER | MADCTL::ROW_ORDER;
|
||||||
|
}
|
||||||
|
madctl = rotate == ROTATE_90 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pico Display 2.0
|
// Pico Display 2.0
|
||||||
|
@ -207,7 +215,7 @@ namespace pimoroni {
|
||||||
caset[1] = 319;
|
caset[1] = 319;
|
||||||
raset[0] = 0;
|
raset[0] = 0;
|
||||||
raset[1] = 239;
|
raset[1] = 239;
|
||||||
madctl = rotate180 ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
|
madctl = (rotate == ROTATE_180 || rotate == ROTATE_90) ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
|
||||||
madctl |= MADCTL::SWAP_XY | MADCTL::SCAN_ORDER;
|
madctl |= MADCTL::SWAP_XY | MADCTL::SCAN_ORDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +225,7 @@ namespace pimoroni {
|
||||||
caset[1] = 239;
|
caset[1] = 239;
|
||||||
raset[0] = 0;
|
raset[0] = 0;
|
||||||
raset[1] = 319;
|
raset[1] = 319;
|
||||||
madctl = rotate180 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
|
madctl = (rotate == ROTATE_180 || rotate == ROTATE_90) ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Byte swap the 16bit rows/cols values
|
// Byte swap the 16bit rows/cols values
|
||||||
|
|
|
@ -19,6 +19,7 @@ add_subdirectory(breakout_bme688)
|
||||||
add_subdirectory(breakout_bmp280)
|
add_subdirectory(breakout_bmp280)
|
||||||
add_subdirectory(breakout_bme280)
|
add_subdirectory(breakout_bme280)
|
||||||
add_subdirectory(breakout_as7262)
|
add_subdirectory(breakout_as7262)
|
||||||
|
add_subdirectory(as7343)
|
||||||
add_subdirectory(breakout_bh1745)
|
add_subdirectory(breakout_bh1745)
|
||||||
add_subdirectory(breakout_icp10125)
|
add_subdirectory(breakout_icp10125)
|
||||||
add_subdirectory(breakout_scd41)
|
add_subdirectory(breakout_scd41)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/as7343_demo.cmake")
|
|
@ -0,0 +1,16 @@
|
||||||
|
set(OUTPUT_NAME as7343_demo)
|
||||||
|
|
||||||
|
add_executable(
|
||||||
|
${OUTPUT_NAME}
|
||||||
|
as7343_demo.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# enable usb output, disable uart output
|
||||||
|
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
|
||||||
|
pico_enable_stdio_uart(${OUTPUT_NAME} 1)
|
||||||
|
|
||||||
|
# Pull in pico libraries that we need
|
||||||
|
target_link_libraries(${OUTPUT_NAME} pico_stdlib as7343)
|
||||||
|
|
||||||
|
# create map/bin/hex file etc.
|
||||||
|
pico_add_extra_outputs(${OUTPUT_NAME})
|
|
@ -0,0 +1,69 @@
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "common/pimoroni_common.hpp"
|
||||||
|
|
||||||
|
#include "as7343.hpp"
|
||||||
|
|
||||||
|
using namespace pimoroni;
|
||||||
|
|
||||||
|
I2C i2c(6, 7);
|
||||||
|
AS7343 as7343(&i2c);
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
stdio_init_all();
|
||||||
|
|
||||||
|
printf("AS7343 Demo\n");
|
||||||
|
as7343.init();
|
||||||
|
printf("Init done...\n");
|
||||||
|
|
||||||
|
uint8_t aux_id;
|
||||||
|
uint8_t revision_id;
|
||||||
|
uint8_t hardware_id;
|
||||||
|
|
||||||
|
as7343.get_version(aux_id, revision_id, hardware_id);
|
||||||
|
|
||||||
|
printf("Aux: %d, Rev: %d, HW: %d\n", aux_id, revision_id, hardware_id);
|
||||||
|
|
||||||
|
printf("set_channels\n");
|
||||||
|
as7343.set_channels(AS7343::channel_count::EIGHTEEN_CHANNEL);
|
||||||
|
|
||||||
|
printf("set_gain\n");
|
||||||
|
as7343.set_gain(1024);
|
||||||
|
|
||||||
|
printf("set_measurement_time\n");
|
||||||
|
as7343.set_measurement_time(500);
|
||||||
|
|
||||||
|
printf("set_integration_time\n");
|
||||||
|
as7343.set_integration_time(27800);
|
||||||
|
|
||||||
|
printf("set_illumination_current\n");
|
||||||
|
as7343.set_illumination_current(4);
|
||||||
|
|
||||||
|
printf("set_illumination_led\n");
|
||||||
|
as7343.set_illumination_led(true);
|
||||||
|
|
||||||
|
printf("start...\n");
|
||||||
|
while(true) {
|
||||||
|
|
||||||
|
AS7343::reading reading = as7343.read();
|
||||||
|
printf("FZ: %d FY: %d FXL: %d NIR: %d F2 %d F3: %d F4: %d F5: %d F1: %d F5: %d F7: %d F8: %d \n",
|
||||||
|
reading.FZ,
|
||||||
|
reading.FY,
|
||||||
|
reading.FXL,
|
||||||
|
reading.NIR,
|
||||||
|
|
||||||
|
reading.F2,
|
||||||
|
reading.F3,
|
||||||
|
reading.F4,
|
||||||
|
reading.F6,
|
||||||
|
|
||||||
|
reading.F1,
|
||||||
|
reading.F5,
|
||||||
|
reading.F7,
|
||||||
|
reading.F8
|
||||||
|
);
|
||||||
|
|
||||||
|
sleep_ms(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ int main() {
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
BME280::bme280_reading result = bme280.read_forced();
|
BME280::bme280_reading result = bme280.read_forced();
|
||||||
printf("%s %0.2lf deg C, %0.2lf hPa, %0.2lf%%\n", result.status == BME280_OK ? "OK" : "ER", result.temperature, result.pressure, result.humidity);
|
printf("%s %0.2lf deg C, %0.2lf hPa, %0.2lf%%\n", result.status ? "OK" : "ER", result.temperature, result.pressure, result.humidity);
|
||||||
sleep_ms(1000);
|
sleep_ms(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ int main() {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
graphics.set_pen(1);
|
graphics.set_pen(15);
|
||||||
graphics.clear();
|
graphics.clear();
|
||||||
|
|
||||||
float s = (sin(i / 10.0f) * 1.0f) + 1.5f;
|
float s = (sin(i / 10.0f) * 1.0f) + 1.5f;
|
||||||
|
|
|
@ -45,7 +45,7 @@ int main() {
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
BME280::bme280_reading result = bme.read_forced();
|
BME280::bme280_reading result = bme.read_forced();
|
||||||
printf("%s %0.2lf deg C, %0.2lf hPa, %0.2lf%%\n", result.status == BME280_OK ? "OK" : "ER", result.temperature, result.pressure, result.humidity);
|
printf("%s %0.2lf deg C, %0.2lf hPa, %0.2lf%%\n", result.status ? "OK" : "ER", result.temperature, result.pressure, result.humidity);
|
||||||
|
|
||||||
// calculates a colour
|
// calculates a colour
|
||||||
float hue = HUE_START + ((float)(result.temperature - MIN) * (float)(HUE_END - HUE_START) / (float)(MAX - MIN));
|
float hue = HUE_START + ((float)(result.temperature - MIN) * (float)(HUE_END - HUE_START) / (float)(MAX - MIN));
|
||||||
|
|
|
@ -32,11 +32,11 @@ ST7789 st7789(
|
||||||
|
|
||||||
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
|
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
|
||||||
|
|
||||||
Button button_a(Tufty2040::A);
|
Button button_a(Tufty2040::A, Polarity::ACTIVE_HIGH);
|
||||||
Button button_b(Tufty2040::B);
|
Button button_b(Tufty2040::B, Polarity::ACTIVE_HIGH);
|
||||||
Button button_c(Tufty2040::C);
|
Button button_c(Tufty2040::C, Polarity::ACTIVE_HIGH);
|
||||||
Button button_up(Tufty2040::UP);
|
Button button_up(Tufty2040::UP, Polarity::ACTIVE_HIGH);
|
||||||
Button button_down(Tufty2040::DOWN);
|
Button button_down(Tufty2040::DOWN, Polarity::ACTIVE_HIGH);
|
||||||
|
|
||||||
uint32_t time() {
|
uint32_t time() {
|
||||||
absolute_time_t t = get_absolute_time();
|
absolute_time_t t = get_absolute_time();
|
||||||
|
|
|
@ -20,9 +20,9 @@ namespace pimoroni {
|
||||||
static const uint8_t LED_B = 2;
|
static const uint8_t LED_B = 2;
|
||||||
|
|
||||||
static const uint8_t MICS_VREF = 14;
|
static const uint8_t MICS_VREF = 14;
|
||||||
static const uint8_t MICS_RED = 12;
|
static const uint8_t MICS_RED = 13;
|
||||||
static const uint8_t MICS_NH3 = 11;
|
static const uint8_t MICS_NH3 = 11;
|
||||||
static const uint8_t MICS_OX = 13;
|
static const uint8_t MICS_OX = 12;
|
||||||
static const uint8_t MICS_HEATER_EN = 1;
|
static const uint8_t MICS_HEATER_EN = 1;
|
||||||
|
|
||||||
static const bool INVERT_OUTPUT = true; //true for common cathode, false for common anode
|
static const bool INVERT_OUTPUT = true; //true for common cathode, false for common anode
|
||||||
|
|
|
@ -494,11 +494,14 @@ namespace pimoroni {
|
||||||
void CosmicUnicorn::set_brightness(float value) {
|
void CosmicUnicorn::set_brightness(float value) {
|
||||||
value = value < 0.0f ? 0.0f : value;
|
value = value < 0.0f ? 0.0f : value;
|
||||||
value = value > 1.0f ? 1.0f : value;
|
value = value > 1.0f ? 1.0f : value;
|
||||||
|
// Max brightness is - in fact - 256 since it's applied with:
|
||||||
|
// result = (channel * brightness) >> 8
|
||||||
|
// eg: (255 * 256) >> 8 == 255
|
||||||
this->brightness = floor(value * 256.0f);
|
this->brightness = floor(value * 256.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
float CosmicUnicorn::get_brightness() {
|
float CosmicUnicorn::get_brightness() {
|
||||||
return this->brightness / 255.0f;
|
return this->brightness / 256.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CosmicUnicorn::adjust_brightness(float delta) {
|
void CosmicUnicorn::adjust_brightness(float delta) {
|
||||||
|
|
|
@ -488,11 +488,14 @@ namespace pimoroni {
|
||||||
void GalacticUnicorn::set_brightness(float value) {
|
void GalacticUnicorn::set_brightness(float value) {
|
||||||
value = value < 0.0f ? 0.0f : value;
|
value = value < 0.0f ? 0.0f : value;
|
||||||
value = value > 1.0f ? 1.0f : value;
|
value = value > 1.0f ? 1.0f : value;
|
||||||
|
// Max brightness is - in fact - 256 since it's applied with:
|
||||||
|
// result = (channel * brightness) >> 8
|
||||||
|
// eg: (255 * 256) >> 8 == 255
|
||||||
this->brightness = floor(value * 256.0f);
|
this->brightness = floor(value * 256.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
float GalacticUnicorn::get_brightness() {
|
float GalacticUnicorn::get_brightness() {
|
||||||
return this->brightness / 255.0f;
|
return this->brightness / 256.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GalacticUnicorn::adjust_brightness(float delta) {
|
void GalacticUnicorn::adjust_brightness(float delta) {
|
||||||
|
|
|
@ -485,11 +485,14 @@ namespace pimoroni {
|
||||||
void StellarUnicorn::set_brightness(float value) {
|
void StellarUnicorn::set_brightness(float value) {
|
||||||
value = value < 0.0f ? 0.0f : value;
|
value = value < 0.0f ? 0.0f : value;
|
||||||
value = value > 1.0f ? 1.0f : value;
|
value = value > 1.0f ? 1.0f : value;
|
||||||
|
// Max brightness is - in fact - 256 since it's applied with:
|
||||||
|
// result = (channel * brightness) >> 8
|
||||||
|
// eg: (255 * 256) >> 8 == 255
|
||||||
this->brightness = floor(value * 256.0f);
|
this->brightness = floor(value * 256.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
float StellarUnicorn::get_brightness() {
|
float StellarUnicorn::get_brightness() {
|
||||||
return this->brightness / 255.0f;
|
return this->brightness / 256.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StellarUnicorn::adjust_brightness(float delta) {
|
void StellarUnicorn::adjust_brightness(float delta) {
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"deploy": [
|
||||||
|
"../deploy.md"
|
||||||
|
],
|
||||||
|
"docs": "",
|
||||||
|
"features": [
|
||||||
|
"Breadboard friendly",
|
||||||
|
"Castellated Pads",
|
||||||
|
"Micro USB"
|
||||||
|
],
|
||||||
|
"id": "rp2-pico",
|
||||||
|
"images": [
|
||||||
|
"rp2-pico.jpg"
|
||||||
|
],
|
||||||
|
"mcu": "rp2040",
|
||||||
|
"product": "Pico",
|
||||||
|
"thumbnail": "",
|
||||||
|
"url": "https://www.raspberrypi.com/products/raspberry-pi-pico/",
|
||||||
|
"vendor": "Raspberry Pi"
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
include("$(PORT_DIR)/boards/manifest.py")
|
||||||
|
|
||||||
|
include("../manifest_pico.py")
|
||||||
|
|
||||||
|
require("usb-device")
|
||||||
|
require("usb-device-hid")
|
||||||
|
require("usb-device-cdc")
|
||||||
|
require("usb-device-keyboard")
|
||||||
|
require("usb-device-mouse")
|
||||||
|
require("usb-device-midi")
|
|
@ -0,0 +1,7 @@
|
||||||
|
# cmake file for Raspberry Pi Pico
|
||||||
|
set(PICO_BOARD "pico")
|
||||||
|
|
||||||
|
# Board specific version of the frozen manifest
|
||||||
|
set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py)
|
||||||
|
|
||||||
|
set(MICROPY_C_HEAP_SIZE 4096)
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Board and hardware specific configuration
|
||||||
|
#define MICROPY_HW_BOARD_NAME "Raspberry Pi Pico"
|
||||||
|
#define MICROPY_HW_FLASH_STORAGE_BYTES (1024 * 1024)
|
||||||
|
#define MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE 1
|
|
@ -0,0 +1,28 @@
|
||||||
|
GP0,GPIO0
|
||||||
|
GP1,GPIO1
|
||||||
|
GP2,GPIO2
|
||||||
|
GP3,GPIO3
|
||||||
|
GP4,GPIO4
|
||||||
|
GP5,GPIO5
|
||||||
|
GP6,GPIO6
|
||||||
|
GP7,GPIO7
|
||||||
|
GP8,GPIO8
|
||||||
|
GP9,GPIO9
|
||||||
|
GP10,GPIO10
|
||||||
|
GP11,GPIO11
|
||||||
|
GP12,GPIO12
|
||||||
|
GP13,GPIO13
|
||||||
|
GP14,GPIO14
|
||||||
|
GP15,GPIO15
|
||||||
|
GP16,GPIO16
|
||||||
|
GP17,GPIO17
|
||||||
|
GP18,GPIO18
|
||||||
|
GP19,GPIO19
|
||||||
|
GP20,GPIO20
|
||||||
|
GP21,GPIO21
|
||||||
|
GP22,GPIO22
|
||||||
|
GP25,GPIO25
|
||||||
|
GP26,GPIO26
|
||||||
|
GP27,GPIO27
|
||||||
|
GP28,GPIO28
|
||||||
|
LED,GPIO25
|
|
|
@ -2,4 +2,4 @@ freeze("../modules_py", "gfx_pack.py")
|
||||||
freeze("../modules_py", "interstate75.py")
|
freeze("../modules_py", "interstate75.py")
|
||||||
|
|
||||||
freeze("../modules_py", "pimoroni.py")
|
freeze("../modules_py", "pimoroni.py")
|
||||||
freeze("../modules_py", "boot.py")
|
freeze("../modules_py", "boot.py")
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_P4
|
||||||
|
from breakout_as7343 import BreakoutAS7343
|
||||||
|
from pimoroni_i2c import PimoroniI2C
|
||||||
|
|
||||||
|
|
||||||
|
display = PicoGraphics(DISPLAY_TUFTY_2040, pen_type=PEN_P4, rotate=90)
|
||||||
|
|
||||||
|
WIDTH, HEIGHT = display.get_bounds()
|
||||||
|
|
||||||
|
BLACK = display.create_pen(0, 0, 0)
|
||||||
|
|
||||||
|
FZ = display.create_pen(0, 0, 128)
|
||||||
|
FY = display.create_pen(0, 255, 0)
|
||||||
|
FXL = display.create_pen(255, 128, 0)
|
||||||
|
NIR = display.create_pen(128, 0, 0)
|
||||||
|
F2 = display.create_pen(128, 0, 128)
|
||||||
|
F3 = display.create_pen(0, 0, 255)
|
||||||
|
F4 = display.create_pen(0, 128, 255)
|
||||||
|
F6 = display.create_pen(128, 64, 0)
|
||||||
|
|
||||||
|
F1 = display.create_pen(196, 0, 196)
|
||||||
|
F5 = display.create_pen(64, 255, 64)
|
||||||
|
F7 = display.create_pen(255, 0, 0)
|
||||||
|
F8 = display.create_pen(196, 0, 0)
|
||||||
|
|
||||||
|
WHITE = display.create_pen(255, 255, 255)
|
||||||
|
|
||||||
|
i2c = PimoroniI2C(sda=4, scl=5)
|
||||||
|
as7343 = BreakoutAS7343(i2c)
|
||||||
|
|
||||||
|
as7343.set_channels(18)
|
||||||
|
as7343.set_gain(1024)
|
||||||
|
as7343.set_measurement_time(33) # Roughly 30fps at 16ms/measurement
|
||||||
|
as7343.set_integration_time(27800)
|
||||||
|
|
||||||
|
as7343.set_illumination_current(4)
|
||||||
|
as7343.set_illumination_led(True)
|
||||||
|
|
||||||
|
|
||||||
|
BAR_WIDTH = 18
|
||||||
|
BAR_SPACING = 4
|
||||||
|
MARGIN = display.measure_text("NIR") + 2
|
||||||
|
BAR_HEIGHT = WIDTH - MARGIN
|
||||||
|
|
||||||
|
OFFSET_LEFT = int((HEIGHT - ((BAR_WIDTH + BAR_SPACING) * 12)) / 2)
|
||||||
|
|
||||||
|
# Satrting max values for auto-ranging
|
||||||
|
# From figure 8 of the datasheet
|
||||||
|
MAX_VALUES = [
|
||||||
|
2711,
|
||||||
|
4684,
|
||||||
|
5970,
|
||||||
|
13226,
|
||||||
|
2371,
|
||||||
|
962,
|
||||||
|
3926,
|
||||||
|
4170,
|
||||||
|
7760,
|
||||||
|
1967,
|
||||||
|
6774,
|
||||||
|
1166
|
||||||
|
]
|
||||||
|
|
||||||
|
LABELS = [
|
||||||
|
"FZ",
|
||||||
|
"FY",
|
||||||
|
"FXL",
|
||||||
|
"NIR",
|
||||||
|
"F2",
|
||||||
|
"F3",
|
||||||
|
"F4",
|
||||||
|
"F6",
|
||||||
|
"F1",
|
||||||
|
"F5",
|
||||||
|
"F7",
|
||||||
|
"F8"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
display.set_pen(0)
|
||||||
|
display.clear()
|
||||||
|
readings = as7343.read()
|
||||||
|
for i, reading in enumerate(readings):
|
||||||
|
MAX_VALUES[i] = max(reading, MAX_VALUES[i])
|
||||||
|
scaled = int(reading / MAX_VALUES[i] * BAR_HEIGHT)
|
||||||
|
|
||||||
|
y = i * (BAR_WIDTH + BAR_SPACING)
|
||||||
|
y += OFFSET_LEFT
|
||||||
|
|
||||||
|
display.set_pen(i + 1)
|
||||||
|
display.rectangle(MARGIN, y, scaled, BAR_WIDTH)
|
||||||
|
|
||||||
|
display.set_pen(WHITE)
|
||||||
|
display.text(LABELS[i], 0, y + 1)
|
||||||
|
|
||||||
|
display.update()
|
|
@ -0,0 +1,30 @@
|
||||||
|
import time
|
||||||
|
from machine import Pin
|
||||||
|
from pimoroni_i2c import PimoroniI2C
|
||||||
|
from breakout_ltr559 import BreakoutLTR559
|
||||||
|
|
||||||
|
PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5}
|
||||||
|
PINS_PICO_EXPLORER = {"sda": 20, "scl": 21}
|
||||||
|
PIN_INTERRUPT = 22 # 3 for Breakout Garden
|
||||||
|
|
||||||
|
i2c = PimoroniI2C(**PINS_PICO_EXPLORER)
|
||||||
|
ltr = BreakoutLTR559(i2c, interrupt=PIN_INTERRUPT)
|
||||||
|
interrupt = Pin(PIN_INTERRUPT, Pin.IN, Pin.PULL_DOWN)
|
||||||
|
|
||||||
|
ltr.light_threshold(0, 10) # COUNTS, NOT LUX!!!
|
||||||
|
ltr.proximity_threshold(0, 10)
|
||||||
|
|
||||||
|
|
||||||
|
def read(pin):
|
||||||
|
reading = ltr.get_reading()
|
||||||
|
if reading is not None:
|
||||||
|
print("T: ", time.ticks_ms(), " Lux: ", reading[BreakoutLTR559.LUX], " Prox: ", reading[BreakoutLTR559.PROXIMITY])
|
||||||
|
|
||||||
|
|
||||||
|
interrupt.irq(trigger=Pin.IRQ_RISING, handler=read)
|
||||||
|
|
||||||
|
part_id = ltr.part_id()
|
||||||
|
print("Found LTR559. Part ID: 0x", '{:02x}'.format(part_id), sep="")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
pass
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,276 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Christopher Parrott for Pimoroni Ltd
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import os
|
||||||
|
import math
|
||||||
|
import struct
|
||||||
|
from machine import I2S, Pin
|
||||||
|
|
||||||
|
"""
|
||||||
|
A class for playing Wav files out of an I2S audio amp. It can also play pure tones.
|
||||||
|
This code is based heavily on the work of Mike Teachman, at:
|
||||||
|
https://github.com/miketeachman/micropython-i2s-examples/blob/master/examples/wavplayer.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class WavPlayer:
|
||||||
|
# Internal states
|
||||||
|
PLAY = 0
|
||||||
|
PAUSE = 1
|
||||||
|
FLUSH = 2
|
||||||
|
STOP = 3
|
||||||
|
NONE = 4
|
||||||
|
|
||||||
|
MODE_WAV = 0
|
||||||
|
MODE_TONE = 1
|
||||||
|
|
||||||
|
# Default buffer length
|
||||||
|
SILENCE_BUFFER_LENGTH = 1000
|
||||||
|
WAV_BUFFER_LENGTH = 10000
|
||||||
|
INTERNAL_BUFFER_LENGTH = 20000
|
||||||
|
|
||||||
|
TONE_SAMPLE_RATE = 44_100
|
||||||
|
TONE_BITS_PER_SAMPLE = 16
|
||||||
|
TONE_FULL_WAVES = 2
|
||||||
|
|
||||||
|
def __init__(self, id, sck_pin, ws_pin, sd_pin, amp_enable=None, ibuf_len=INTERNAL_BUFFER_LENGTH, root="/"):
|
||||||
|
self.__id = id
|
||||||
|
self.__sck_pin = sck_pin
|
||||||
|
self.__ws_pin = ws_pin
|
||||||
|
self.__sd_pin = sd_pin
|
||||||
|
self.__ibuf_len = ibuf_len
|
||||||
|
self.__enable = None
|
||||||
|
|
||||||
|
if amp_enable is not None:
|
||||||
|
self.__enable = Pin(amp_enable, Pin.OUT)
|
||||||
|
|
||||||
|
# Set the directory to search for files in
|
||||||
|
self.set_root(root)
|
||||||
|
|
||||||
|
self.__state = WavPlayer.NONE
|
||||||
|
self.__mode = WavPlayer.MODE_WAV
|
||||||
|
self.__wav_file = None
|
||||||
|
self.__loop_wav = False
|
||||||
|
self.__first_sample_offset = None
|
||||||
|
self.__flush_count = 0
|
||||||
|
self.__audio_out = None
|
||||||
|
|
||||||
|
# Allocate a small array of blank audio samples used for silence
|
||||||
|
self.__silence_samples = bytearray(self.SILENCE_BUFFER_LENGTH)
|
||||||
|
|
||||||
|
# Allocate a larger array for WAV audio samples, using a memoryview for more efficient access
|
||||||
|
self.__wav_samples_mv = memoryview(bytearray(self.WAV_BUFFER_LENGTH))
|
||||||
|
|
||||||
|
# Reserve a variable for audio samples used for tones
|
||||||
|
self.__tone_samples = None
|
||||||
|
self.__queued_samples = None
|
||||||
|
|
||||||
|
def set_root(self, root):
|
||||||
|
self.__root = root.rstrip("/") + "/"
|
||||||
|
|
||||||
|
def play_wav(self, wav_file, loop=False):
|
||||||
|
if os.listdir(self.__root).count(wav_file) == 0:
|
||||||
|
raise ValueError(f"'{wav_file}' not found")
|
||||||
|
|
||||||
|
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
|
||||||
|
|
||||||
|
self.__wav_file = open(self.__root + wav_file, "rb") # Open the chosen WAV file in read-only, binary mode
|
||||||
|
self.__loop_wav = loop # Record if the user wants the file to loop
|
||||||
|
|
||||||
|
# Parse the WAV file, returning the necessary parameters to initialise I2S communication
|
||||||
|
format, sample_rate, bits_per_sample, self.__first_sample_offset, self.sample_size = WavPlayer.__parse_wav(self.__wav_file)
|
||||||
|
|
||||||
|
# Keep a track of total bytes read from WAV File
|
||||||
|
self.total_bytes_read = 0
|
||||||
|
|
||||||
|
self.__wav_file.seek(self.__first_sample_offset) # Advance to first byte of sample data
|
||||||
|
|
||||||
|
self.__start_i2s(bits=bits_per_sample,
|
||||||
|
format=format,
|
||||||
|
rate=sample_rate,
|
||||||
|
state=WavPlayer.PLAY,
|
||||||
|
mode=WavPlayer.MODE_WAV)
|
||||||
|
|
||||||
|
def play_tone(self, frequency, amplitude):
|
||||||
|
if frequency < 20.0 or frequency > 20_000:
|
||||||
|
raise ValueError("frequency out of range. Expected between 20Hz and 20KHz")
|
||||||
|
|
||||||
|
if amplitude < 0.0 or amplitude > 1.0:
|
||||||
|
raise ValueError("amplitude out of range. Expected 0.0 to 1.0")
|
||||||
|
|
||||||
|
# Create a buffer containing the pure tone samples
|
||||||
|
samples_per_cycle = self.TONE_SAMPLE_RATE // frequency
|
||||||
|
sample_size_in_bytes = self.TONE_BITS_PER_SAMPLE // 8
|
||||||
|
samples = bytearray(self.TONE_FULL_WAVES * samples_per_cycle * sample_size_in_bytes)
|
||||||
|
range = pow(2, self.TONE_BITS_PER_SAMPLE) // 2
|
||||||
|
|
||||||
|
format = "<h" if self.TONE_BITS_PER_SAMPLE == 16 else "<l"
|
||||||
|
|
||||||
|
# Populate the buffer with multiple cycles to avoid it completing too quickly and causing drop outs
|
||||||
|
for i in range(samples_per_cycle * self.TONE_FULL_WAVES):
|
||||||
|
sample = int((range - 1) * (math.sin(2 * math.pi * i / samples_per_cycle)) * amplitude)
|
||||||
|
struct.pack_into(format, samples, i * sample_size_in_bytes, sample)
|
||||||
|
|
||||||
|
# Are we not already playing tones?
|
||||||
|
if not (self.__mode == WavPlayer.MODE_TONE and (self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE)):
|
||||||
|
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
|
||||||
|
self.__tone_samples = samples
|
||||||
|
self.__start_i2s(bits=self.TONE_BITS_PER_SAMPLE,
|
||||||
|
format=I2S.MONO,
|
||||||
|
rate=self.TONE_SAMPLE_RATE,
|
||||||
|
state=WavPlayer.PLAY,
|
||||||
|
mode=WavPlayer.MODE_TONE)
|
||||||
|
else:
|
||||||
|
self.__queued_samples = samples
|
||||||
|
self.__state = WavPlayer.PLAY
|
||||||
|
|
||||||
|
def pause(self):
|
||||||
|
if self.__state == WavPlayer.PLAY:
|
||||||
|
self.__state = WavPlayer.PAUSE # Enter the pause state on the next callback
|
||||||
|
|
||||||
|
def resume(self):
|
||||||
|
if self.__state == WavPlayer.PAUSE:
|
||||||
|
self.__state = WavPlayer.PLAY # Enter the play state on the next callback
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
if self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE:
|
||||||
|
if self.__mode == WavPlayer.MODE_WAV:
|
||||||
|
# Enter the flush state on the next callback and close the file
|
||||||
|
# It is done in this order to prevent the callback entering the play
|
||||||
|
# state after we close the file but before we change the state)
|
||||||
|
self.__state = WavPlayer.FLUSH
|
||||||
|
self.__wav_file.close()
|
||||||
|
else:
|
||||||
|
self.__state = WavPlayer.STOP
|
||||||
|
|
||||||
|
def is_playing(self):
|
||||||
|
return self.__state != WavPlayer.NONE and self.__state != WavPlayer.STOP
|
||||||
|
|
||||||
|
def is_paused(self):
|
||||||
|
return self.__state == WavPlayer.PAUSE
|
||||||
|
|
||||||
|
def __start_i2s(self, bits=16, format=I2S.MONO, rate=44_100, state=STOP, mode=MODE_WAV):
|
||||||
|
import gc
|
||||||
|
gc.collect()
|
||||||
|
self.__audio_out = I2S(
|
||||||
|
self.__id,
|
||||||
|
sck=self.__sck_pin,
|
||||||
|
ws=self.__ws_pin,
|
||||||
|
sd=self.__sd_pin,
|
||||||
|
mode=I2S.TX,
|
||||||
|
bits=bits,
|
||||||
|
format=format,
|
||||||
|
rate=rate,
|
||||||
|
ibuf=self.__ibuf_len,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.__state = state
|
||||||
|
self.__mode = mode
|
||||||
|
self.__flush_count = self.__ibuf_len // self.SILENCE_BUFFER_LENGTH + 1
|
||||||
|
self.__audio_out.irq(self.__i2s_callback)
|
||||||
|
self.__audio_out.write(self.__silence_samples)
|
||||||
|
|
||||||
|
if self.__enable is not None:
|
||||||
|
self.__enable.on()
|
||||||
|
|
||||||
|
def __stop_i2s(self):
|
||||||
|
self.stop() # Stop any active playback
|
||||||
|
while self.is_playing(): # and wait for it to complete
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self.__enable is not None:
|
||||||
|
self.__enable.off()
|
||||||
|
|
||||||
|
if self.__audio_out is not None:
|
||||||
|
self.__audio_out.deinit() # Deinit any active I2S comms
|
||||||
|
|
||||||
|
self.__state == WavPlayer.NONE # Return to the none state
|
||||||
|
|
||||||
|
def __i2s_callback(self, arg):
|
||||||
|
# PLAY
|
||||||
|
if self.__state == WavPlayer.PLAY:
|
||||||
|
if self.__mode == WavPlayer.MODE_WAV:
|
||||||
|
num_read = self.__wav_file.readinto(self.__wav_samples_mv) # Read the next section of the WAV file
|
||||||
|
self.total_bytes_read += num_read
|
||||||
|
# Have we reached the end of the file?
|
||||||
|
if num_read == 0:
|
||||||
|
# Do we want to loop the WAV playback?
|
||||||
|
if self.__loop_wav:
|
||||||
|
_ = self.__wav_file.seek(self.__first_sample_offset) # Play again, so advance to first byte of sample data
|
||||||
|
else:
|
||||||
|
self.__wav_file.close() # Stop playing, so close the file
|
||||||
|
self.__state = WavPlayer.FLUSH # and enter the flush state on the next callback
|
||||||
|
|
||||||
|
self.__audio_out.write(self.__silence_samples) # In both cases play silence to end this callback
|
||||||
|
else:
|
||||||
|
if num_read > 0 and num_read < self.WAV_BUFFER_LENGTH:
|
||||||
|
num_read = num_read - (self.total_bytes_read - self.sample_size)
|
||||||
|
self.__audio_out.write(self.__wav_samples_mv[: num_read]) # We are within the file, so write out the next audio samples
|
||||||
|
else:
|
||||||
|
if self.__queued_samples is not None:
|
||||||
|
self.__tone_samples = self.__queued_samples
|
||||||
|
self.__queued_samples = None
|
||||||
|
self.__audio_out.write(self.__tone_samples)
|
||||||
|
|
||||||
|
# PAUSE or STOP
|
||||||
|
elif self.__state == WavPlayer.PAUSE or self.__state == WavPlayer.STOP:
|
||||||
|
self.__audio_out.write(self.__silence_samples) # Play silence
|
||||||
|
|
||||||
|
# FLUSH
|
||||||
|
elif self.__state == WavPlayer.FLUSH:
|
||||||
|
# Flush is used to allow the residual audio samples in the internal buffer to be written
|
||||||
|
# to the I2S peripheral. This step avoids part of the sound file from being cut off
|
||||||
|
if self.__flush_count > 0:
|
||||||
|
self.__flush_count -= 1
|
||||||
|
else:
|
||||||
|
self.__state = WavPlayer.STOP # Enter the stop state on the next callback
|
||||||
|
self.__audio_out.write(self.__silence_samples) # Play silence
|
||||||
|
|
||||||
|
# NONE
|
||||||
|
elif self.__state == WavPlayer.NONE:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __parse_wav(wav_file):
|
||||||
|
chunk_ID = wav_file.read(4)
|
||||||
|
if chunk_ID != b"RIFF":
|
||||||
|
raise ValueError("WAV chunk ID invalid")
|
||||||
|
_ = wav_file.read(4) # chunk_size
|
||||||
|
format = wav_file.read(4)
|
||||||
|
if format != b"WAVE":
|
||||||
|
raise ValueError("WAV format invalid")
|
||||||
|
sub_chunk1_ID = wav_file.read(4)
|
||||||
|
if sub_chunk1_ID != b"fmt ":
|
||||||
|
raise ValueError("WAV sub chunk 1 ID invalid")
|
||||||
|
_ = wav_file.read(4) # sub_chunk1_size
|
||||||
|
_ = struct.unpack("<H", wav_file.read(2))[0] # audio_format
|
||||||
|
num_channels = struct.unpack("<H", wav_file.read(2))[0]
|
||||||
|
|
||||||
|
if num_channels == 1:
|
||||||
|
format = I2S.MONO
|
||||||
|
else:
|
||||||
|
format = I2S.STEREO
|
||||||
|
|
||||||
|
sample_rate = struct.unpack("<I", wav_file.read(4))[0]
|
||||||
|
# if sample_rate != 44_100 and sample_rate != 48_000:
|
||||||
|
# raise ValueError(f"WAV sample rate of {sample_rate} invalid. Only 44.1KHz or 48KHz audio are supported")
|
||||||
|
|
||||||
|
_ = struct.unpack("<I", wav_file.read(4))[0] # byte_rate
|
||||||
|
_ = struct.unpack("<H", wav_file.read(2))[0] # block_align
|
||||||
|
bits_per_sample = struct.unpack("<H", wav_file.read(2))[0]
|
||||||
|
|
||||||
|
# usually the sub chunk2 ID ("data") comes next, but
|
||||||
|
# some online MP3->WAV converters add
|
||||||
|
# binary data before "data". So, read a fairly large
|
||||||
|
# block of bytes and search for "data".
|
||||||
|
|
||||||
|
binary_block = wav_file.read(200)
|
||||||
|
offset = binary_block.find(b"data")
|
||||||
|
if offset == -1:
|
||||||
|
raise ValueError("WAV sub chunk 2 ID not found")
|
||||||
|
|
||||||
|
wav_file.seek(40)
|
||||||
|
sub_chunk2_size = struct.unpack("<I", wav_file.read(4))[0]
|
||||||
|
|
||||||
|
return (format, sample_rate, bits_per_sample, 44 + offset, sub_chunk2_size)
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,91 @@
|
||||||
|
from machine import Timer
|
||||||
|
from audio import WavPlayer
|
||||||
|
from cosmic import CosmicUnicorn
|
||||||
|
from picographics import PicoGraphics, DISPLAY_COSMIC_UNICORN as DISPLAY
|
||||||
|
import time
|
||||||
|
|
||||||
|
cu = CosmicUnicorn()
|
||||||
|
graphics = PicoGraphics(DISPLAY)
|
||||||
|
|
||||||
|
graphics.set_font("bitmap6")
|
||||||
|
WHITE = graphics.create_pen(255, 255, 255)
|
||||||
|
BLUE = graphics.create_pen(0, 0, 255)
|
||||||
|
CLEAR = graphics.create_pen(0, 0, 0)
|
||||||
|
RED = graphics.create_pen(255, 0, 0)
|
||||||
|
GREEN = graphics.create_pen(0, 255, 0)
|
||||||
|
cu.set_brightness(0.7)
|
||||||
|
|
||||||
|
audio = WavPlayer(0, 10, 11, 9, amp_enable=22)
|
||||||
|
|
||||||
|
|
||||||
|
class Countdown(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.timer_running = False
|
||||||
|
self.total_seconds = 0
|
||||||
|
self.timer = None
|
||||||
|
|
||||||
|
def process_input(self):
|
||||||
|
if cu.is_pressed(CosmicUnicorn.SWITCH_VOLUME_UP):
|
||||||
|
self.total_seconds += 1
|
||||||
|
if cu.is_pressed(CosmicUnicorn.SWITCH_VOLUME_DOWN):
|
||||||
|
if self.total_seconds > 0:
|
||||||
|
self.total_seconds -= 1
|
||||||
|
if cu.is_pressed(CosmicUnicorn.SWITCH_SLEEP):
|
||||||
|
self.start_timer()
|
||||||
|
|
||||||
|
def display_time(self):
|
||||||
|
seconds = self.total_seconds % (24 * 3600)
|
||||||
|
seconds %= 3600
|
||||||
|
minutes = seconds // 60
|
||||||
|
seconds %= 60
|
||||||
|
|
||||||
|
# Add leading zeros to the minutes and seconds
|
||||||
|
if len(str(minutes)) == 1:
|
||||||
|
minutes = "0{}".format(minutes)
|
||||||
|
if len(str(seconds)) == 1:
|
||||||
|
seconds = "0{}".format(seconds)
|
||||||
|
|
||||||
|
return "{}:{}".format(minutes, seconds)
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
||||||
|
graphics.clear()
|
||||||
|
|
||||||
|
graphics.set_pen(BLUE)
|
||||||
|
graphics.circle(0, 0, 12)
|
||||||
|
graphics.set_pen(GREEN)
|
||||||
|
graphics.circle(25, 30, 5)
|
||||||
|
graphics.set_pen(RED)
|
||||||
|
graphics.circle(0, 32, 12)
|
||||||
|
|
||||||
|
graphics.set_pen(CLEAR)
|
||||||
|
graphics.rectangle(0, 11, CosmicUnicorn.WIDTH, 9)
|
||||||
|
|
||||||
|
graphics.set_pen(WHITE)
|
||||||
|
graphics.text(self.display_time(), 4, 12, -1, 1)
|
||||||
|
cu.update(graphics)
|
||||||
|
|
||||||
|
def start_timer(self):
|
||||||
|
if not self.timer_running:
|
||||||
|
self.timer = Timer(mode=Timer.PERIODIC, period=1000, callback=self.countdown)
|
||||||
|
self.timer_running = True
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.timer.deinit()
|
||||||
|
self.timer_running = False
|
||||||
|
|
||||||
|
def countdown(self, arg):
|
||||||
|
|
||||||
|
if self.total_seconds == 0:
|
||||||
|
audio.play_wav("doorbell.wav", False)
|
||||||
|
self.reset()
|
||||||
|
else:
|
||||||
|
self.total_seconds -= 1
|
||||||
|
|
||||||
|
|
||||||
|
count = Countdown()
|
||||||
|
|
||||||
|
while 1:
|
||||||
|
count.process_input()
|
||||||
|
count.draw()
|
||||||
|
time.sleep(0.07)
|
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,74 @@
|
||||||
|
from audio import WavPlayer
|
||||||
|
from cosmic import CosmicUnicorn
|
||||||
|
from picographics import PicoGraphics, DISPLAY_COSMIC_UNICORN as DISPLAY
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
cu = CosmicUnicorn()
|
||||||
|
graphics = PicoGraphics(DISPLAY)
|
||||||
|
|
||||||
|
audio = WavPlayer(0, 10, 11, 9, amp_enable=22)
|
||||||
|
|
||||||
|
WHITE = graphics.create_pen(255, 255, 255)
|
||||||
|
RED = graphics.create_pen(255, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
class Menu(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.items = ["Pew 1", "Pew 2", "Pew 3"]
|
||||||
|
self.selected = 0
|
||||||
|
|
||||||
|
# A function to draw only the menu elements.
|
||||||
|
# Helps to keep our main function tidy!
|
||||||
|
def draw_menu(self):
|
||||||
|
graphics.set_pen(WHITE)
|
||||||
|
|
||||||
|
for item in range(len(self.items)):
|
||||||
|
if self.selected == item:
|
||||||
|
graphics.set_pen(RED)
|
||||||
|
|
||||||
|
graphics.text(self.items[item], 0, 2 + item * 10, 31, 1)
|
||||||
|
graphics.set_pen(WHITE)
|
||||||
|
|
||||||
|
# Make changes based on the currently selected menu item
|
||||||
|
def process_selected(self):
|
||||||
|
if self.selected == 0:
|
||||||
|
audio.play_wav("Pew1.wav", False)
|
||||||
|
|
||||||
|
if self.selected == 1:
|
||||||
|
audio.play_wav("Pew2.wav", False)
|
||||||
|
|
||||||
|
if self.selected == 2:
|
||||||
|
audio.play_wav("Pew3.wav", False)
|
||||||
|
|
||||||
|
|
||||||
|
menu = Menu()
|
||||||
|
graphics.set_font("bitmap6")
|
||||||
|
cu.set_brightness(0.7)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
||||||
|
graphics.clear()
|
||||||
|
|
||||||
|
if cu.is_pressed(CosmicUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||||
|
audio.play_wav("buttonbeep.wav", False)
|
||||||
|
if menu.selected + 1 < len(menu.items):
|
||||||
|
menu.selected += 1
|
||||||
|
else:
|
||||||
|
menu.selected = 0
|
||||||
|
|
||||||
|
if cu.is_pressed(CosmicUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||||
|
audio.play_wav("buttonbeep.wav", False)
|
||||||
|
if menu.selected > 0:
|
||||||
|
menu.selected -= 1
|
||||||
|
else:
|
||||||
|
menu.selected = len(menu.items) - 1
|
||||||
|
|
||||||
|
if cu.is_pressed(CosmicUnicorn.SWITCH_SLEEP):
|
||||||
|
menu.process_selected()
|
||||||
|
|
||||||
|
menu.draw_menu()
|
||||||
|
|
||||||
|
cu.update(graphics)
|
||||||
|
sleep(0.2)
|
|
@ -0,0 +1,8 @@
|
||||||
|
from audio import WavPlayer
|
||||||
|
|
||||||
|
sound = WavPlayer(0, 10, 11, 9, amp_enable=22)
|
||||||
|
|
||||||
|
sound.play_wav("beepboop.wav", False)
|
||||||
|
|
||||||
|
while sound.is_playing():
|
||||||
|
pass
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,276 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Christopher Parrott for Pimoroni Ltd
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import os
|
||||||
|
import math
|
||||||
|
import struct
|
||||||
|
from machine import I2S, Pin
|
||||||
|
|
||||||
|
"""
|
||||||
|
A class for playing Wav files out of an I2S audio amp. It can also play pure tones.
|
||||||
|
This code is based heavily on the work of Mike Teachman, at:
|
||||||
|
https://github.com/miketeachman/micropython-i2s-examples/blob/master/examples/wavplayer.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class WavPlayer:
|
||||||
|
# Internal states
|
||||||
|
PLAY = 0
|
||||||
|
PAUSE = 1
|
||||||
|
FLUSH = 2
|
||||||
|
STOP = 3
|
||||||
|
NONE = 4
|
||||||
|
|
||||||
|
MODE_WAV = 0
|
||||||
|
MODE_TONE = 1
|
||||||
|
|
||||||
|
# Default buffer length
|
||||||
|
SILENCE_BUFFER_LENGTH = 1000
|
||||||
|
WAV_BUFFER_LENGTH = 10000
|
||||||
|
INTERNAL_BUFFER_LENGTH = 20000
|
||||||
|
|
||||||
|
TONE_SAMPLE_RATE = 44_100
|
||||||
|
TONE_BITS_PER_SAMPLE = 16
|
||||||
|
TONE_FULL_WAVES = 2
|
||||||
|
|
||||||
|
def __init__(self, id, sck_pin, ws_pin, sd_pin, amp_enable=None, ibuf_len=INTERNAL_BUFFER_LENGTH, root="/"):
|
||||||
|
self.__id = id
|
||||||
|
self.__sck_pin = sck_pin
|
||||||
|
self.__ws_pin = ws_pin
|
||||||
|
self.__sd_pin = sd_pin
|
||||||
|
self.__ibuf_len = ibuf_len
|
||||||
|
self.__enable = None
|
||||||
|
|
||||||
|
if amp_enable is not None:
|
||||||
|
self.__enable = Pin(amp_enable, Pin.OUT)
|
||||||
|
|
||||||
|
# Set the directory to search for files in
|
||||||
|
self.set_root(root)
|
||||||
|
|
||||||
|
self.__state = WavPlayer.NONE
|
||||||
|
self.__mode = WavPlayer.MODE_WAV
|
||||||
|
self.__wav_file = None
|
||||||
|
self.__loop_wav = False
|
||||||
|
self.__first_sample_offset = None
|
||||||
|
self.__flush_count = 0
|
||||||
|
self.__audio_out = None
|
||||||
|
|
||||||
|
# Allocate a small array of blank audio samples used for silence
|
||||||
|
self.__silence_samples = bytearray(self.SILENCE_BUFFER_LENGTH)
|
||||||
|
|
||||||
|
# Allocate a larger array for WAV audio samples, using a memoryview for more efficient access
|
||||||
|
self.__wav_samples_mv = memoryview(bytearray(self.WAV_BUFFER_LENGTH))
|
||||||
|
|
||||||
|
# Reserve a variable for audio samples used for tones
|
||||||
|
self.__tone_samples = None
|
||||||
|
self.__queued_samples = None
|
||||||
|
|
||||||
|
def set_root(self, root):
|
||||||
|
self.__root = root.rstrip("/") + "/"
|
||||||
|
|
||||||
|
def play_wav(self, wav_file, loop=False):
|
||||||
|
if os.listdir(self.__root).count(wav_file) == 0:
|
||||||
|
raise ValueError(f"'{wav_file}' not found")
|
||||||
|
|
||||||
|
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
|
||||||
|
|
||||||
|
self.__wav_file = open(self.__root + wav_file, "rb") # Open the chosen WAV file in read-only, binary mode
|
||||||
|
self.__loop_wav = loop # Record if the user wants the file to loop
|
||||||
|
|
||||||
|
# Parse the WAV file, returning the necessary parameters to initialise I2S communication
|
||||||
|
format, sample_rate, bits_per_sample, self.__first_sample_offset, self.sample_size = WavPlayer.__parse_wav(self.__wav_file)
|
||||||
|
|
||||||
|
# Keep a track of total bytes read from WAV File
|
||||||
|
self.total_bytes_read = 0
|
||||||
|
|
||||||
|
self.__wav_file.seek(self.__first_sample_offset) # Advance to first byte of sample data
|
||||||
|
|
||||||
|
self.__start_i2s(bits=bits_per_sample,
|
||||||
|
format=format,
|
||||||
|
rate=sample_rate,
|
||||||
|
state=WavPlayer.PLAY,
|
||||||
|
mode=WavPlayer.MODE_WAV)
|
||||||
|
|
||||||
|
def play_tone(self, frequency, amplitude):
|
||||||
|
if frequency < 20.0 or frequency > 20_000:
|
||||||
|
raise ValueError("frequency out of range. Expected between 20Hz and 20KHz")
|
||||||
|
|
||||||
|
if amplitude < 0.0 or amplitude > 1.0:
|
||||||
|
raise ValueError("amplitude out of range. Expected 0.0 to 1.0")
|
||||||
|
|
||||||
|
# Create a buffer containing the pure tone samples
|
||||||
|
samples_per_cycle = self.TONE_SAMPLE_RATE // frequency
|
||||||
|
sample_size_in_bytes = self.TONE_BITS_PER_SAMPLE // 8
|
||||||
|
samples = bytearray(self.TONE_FULL_WAVES * samples_per_cycle * sample_size_in_bytes)
|
||||||
|
range = pow(2, self.TONE_BITS_PER_SAMPLE) // 2
|
||||||
|
|
||||||
|
format = "<h" if self.TONE_BITS_PER_SAMPLE == 16 else "<l"
|
||||||
|
|
||||||
|
# Populate the buffer with multiple cycles to avoid it completing too quickly and causing drop outs
|
||||||
|
for i in range(samples_per_cycle * self.TONE_FULL_WAVES):
|
||||||
|
sample = int((range - 1) * (math.sin(2 * math.pi * i / samples_per_cycle)) * amplitude)
|
||||||
|
struct.pack_into(format, samples, i * sample_size_in_bytes, sample)
|
||||||
|
|
||||||
|
# Are we not already playing tones?
|
||||||
|
if not (self.__mode == WavPlayer.MODE_TONE and (self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE)):
|
||||||
|
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
|
||||||
|
self.__tone_samples = samples
|
||||||
|
self.__start_i2s(bits=self.TONE_BITS_PER_SAMPLE,
|
||||||
|
format=I2S.MONO,
|
||||||
|
rate=self.TONE_SAMPLE_RATE,
|
||||||
|
state=WavPlayer.PLAY,
|
||||||
|
mode=WavPlayer.MODE_TONE)
|
||||||
|
else:
|
||||||
|
self.__queued_samples = samples
|
||||||
|
self.__state = WavPlayer.PLAY
|
||||||
|
|
||||||
|
def pause(self):
|
||||||
|
if self.__state == WavPlayer.PLAY:
|
||||||
|
self.__state = WavPlayer.PAUSE # Enter the pause state on the next callback
|
||||||
|
|
||||||
|
def resume(self):
|
||||||
|
if self.__state == WavPlayer.PAUSE:
|
||||||
|
self.__state = WavPlayer.PLAY # Enter the play state on the next callback
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
if self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE:
|
||||||
|
if self.__mode == WavPlayer.MODE_WAV:
|
||||||
|
# Enter the flush state on the next callback and close the file
|
||||||
|
# It is done in this order to prevent the callback entering the play
|
||||||
|
# state after we close the file but before we change the state)
|
||||||
|
self.__state = WavPlayer.FLUSH
|
||||||
|
self.__wav_file.close()
|
||||||
|
else:
|
||||||
|
self.__state = WavPlayer.STOP
|
||||||
|
|
||||||
|
def is_playing(self):
|
||||||
|
return self.__state != WavPlayer.NONE and self.__state != WavPlayer.STOP
|
||||||
|
|
||||||
|
def is_paused(self):
|
||||||
|
return self.__state == WavPlayer.PAUSE
|
||||||
|
|
||||||
|
def __start_i2s(self, bits=16, format=I2S.MONO, rate=44_100, state=STOP, mode=MODE_WAV):
|
||||||
|
import gc
|
||||||
|
gc.collect()
|
||||||
|
self.__audio_out = I2S(
|
||||||
|
self.__id,
|
||||||
|
sck=self.__sck_pin,
|
||||||
|
ws=self.__ws_pin,
|
||||||
|
sd=self.__sd_pin,
|
||||||
|
mode=I2S.TX,
|
||||||
|
bits=bits,
|
||||||
|
format=format,
|
||||||
|
rate=rate,
|
||||||
|
ibuf=self.__ibuf_len,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.__state = state
|
||||||
|
self.__mode = mode
|
||||||
|
self.__flush_count = self.__ibuf_len // self.SILENCE_BUFFER_LENGTH + 1
|
||||||
|
self.__audio_out.irq(self.__i2s_callback)
|
||||||
|
self.__audio_out.write(self.__silence_samples)
|
||||||
|
|
||||||
|
if self.__enable is not None:
|
||||||
|
self.__enable.on()
|
||||||
|
|
||||||
|
def __stop_i2s(self):
|
||||||
|
self.stop() # Stop any active playback
|
||||||
|
while self.is_playing(): # and wait for it to complete
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self.__enable is not None:
|
||||||
|
self.__enable.off()
|
||||||
|
|
||||||
|
if self.__audio_out is not None:
|
||||||
|
self.__audio_out.deinit() # Deinit any active I2S comms
|
||||||
|
|
||||||
|
self.__state == WavPlayer.NONE # Return to the none state
|
||||||
|
|
||||||
|
def __i2s_callback(self, arg):
|
||||||
|
# PLAY
|
||||||
|
if self.__state == WavPlayer.PLAY:
|
||||||
|
if self.__mode == WavPlayer.MODE_WAV:
|
||||||
|
num_read = self.__wav_file.readinto(self.__wav_samples_mv) # Read the next section of the WAV file
|
||||||
|
self.total_bytes_read += num_read
|
||||||
|
# Have we reached the end of the file?
|
||||||
|
if num_read == 0:
|
||||||
|
# Do we want to loop the WAV playback?
|
||||||
|
if self.__loop_wav:
|
||||||
|
_ = self.__wav_file.seek(self.__first_sample_offset) # Play again, so advance to first byte of sample data
|
||||||
|
else:
|
||||||
|
self.__wav_file.close() # Stop playing, so close the file
|
||||||
|
self.__state = WavPlayer.FLUSH # and enter the flush state on the next callback
|
||||||
|
|
||||||
|
self.__audio_out.write(self.__silence_samples) # In both cases play silence to end this callback
|
||||||
|
else:
|
||||||
|
if num_read > 0 and num_read < self.WAV_BUFFER_LENGTH:
|
||||||
|
num_read = num_read - (self.total_bytes_read - self.sample_size)
|
||||||
|
self.__audio_out.write(self.__wav_samples_mv[: num_read]) # We are within the file, so write out the next audio samples
|
||||||
|
else:
|
||||||
|
if self.__queued_samples is not None:
|
||||||
|
self.__tone_samples = self.__queued_samples
|
||||||
|
self.__queued_samples = None
|
||||||
|
self.__audio_out.write(self.__tone_samples)
|
||||||
|
|
||||||
|
# PAUSE or STOP
|
||||||
|
elif self.__state == WavPlayer.PAUSE or self.__state == WavPlayer.STOP:
|
||||||
|
self.__audio_out.write(self.__silence_samples) # Play silence
|
||||||
|
|
||||||
|
# FLUSH
|
||||||
|
elif self.__state == WavPlayer.FLUSH:
|
||||||
|
# Flush is used to allow the residual audio samples in the internal buffer to be written
|
||||||
|
# to the I2S peripheral. This step avoids part of the sound file from being cut off
|
||||||
|
if self.__flush_count > 0:
|
||||||
|
self.__flush_count -= 1
|
||||||
|
else:
|
||||||
|
self.__state = WavPlayer.STOP # Enter the stop state on the next callback
|
||||||
|
self.__audio_out.write(self.__silence_samples) # Play silence
|
||||||
|
|
||||||
|
# NONE
|
||||||
|
elif self.__state == WavPlayer.NONE:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __parse_wav(wav_file):
|
||||||
|
chunk_ID = wav_file.read(4)
|
||||||
|
if chunk_ID != b"RIFF":
|
||||||
|
raise ValueError("WAV chunk ID invalid")
|
||||||
|
_ = wav_file.read(4) # chunk_size
|
||||||
|
format = wav_file.read(4)
|
||||||
|
if format != b"WAVE":
|
||||||
|
raise ValueError("WAV format invalid")
|
||||||
|
sub_chunk1_ID = wav_file.read(4)
|
||||||
|
if sub_chunk1_ID != b"fmt ":
|
||||||
|
raise ValueError("WAV sub chunk 1 ID invalid")
|
||||||
|
_ = wav_file.read(4) # sub_chunk1_size
|
||||||
|
_ = struct.unpack("<H", wav_file.read(2))[0] # audio_format
|
||||||
|
num_channels = struct.unpack("<H", wav_file.read(2))[0]
|
||||||
|
|
||||||
|
if num_channels == 1:
|
||||||
|
format = I2S.MONO
|
||||||
|
else:
|
||||||
|
format = I2S.STEREO
|
||||||
|
|
||||||
|
sample_rate = struct.unpack("<I", wav_file.read(4))[0]
|
||||||
|
# if sample_rate != 44_100 and sample_rate != 48_000:
|
||||||
|
# raise ValueError(f"WAV sample rate of {sample_rate} invalid. Only 44.1KHz or 48KHz audio are supported")
|
||||||
|
|
||||||
|
_ = struct.unpack("<I", wav_file.read(4))[0] # byte_rate
|
||||||
|
_ = struct.unpack("<H", wav_file.read(2))[0] # block_align
|
||||||
|
bits_per_sample = struct.unpack("<H", wav_file.read(2))[0]
|
||||||
|
|
||||||
|
# usually the sub chunk2 ID ("data") comes next, but
|
||||||
|
# some online MP3->WAV converters add
|
||||||
|
# binary data before "data". So, read a fairly large
|
||||||
|
# block of bytes and search for "data".
|
||||||
|
|
||||||
|
binary_block = wav_file.read(200)
|
||||||
|
offset = binary_block.find(b"data")
|
||||||
|
if offset == -1:
|
||||||
|
raise ValueError("WAV sub chunk 2 ID not found")
|
||||||
|
|
||||||
|
wav_file.seek(40)
|
||||||
|
sub_chunk2_size = struct.unpack("<I", wav_file.read(4))[0]
|
||||||
|
|
||||||
|
return (format, sample_rate, bits_per_sample, 44 + offset, sub_chunk2_size)
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,86 @@
|
||||||
|
from machine import Timer
|
||||||
|
from audio import WavPlayer
|
||||||
|
from galactic import GalacticUnicorn
|
||||||
|
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY
|
||||||
|
import time
|
||||||
|
|
||||||
|
gu = GalacticUnicorn()
|
||||||
|
graphics = PicoGraphics(DISPLAY)
|
||||||
|
|
||||||
|
graphics.set_font("bitmap6")
|
||||||
|
WHITE = graphics.create_pen(255, 255, 255)
|
||||||
|
BLUE = graphics.create_pen(0, 0, 255)
|
||||||
|
CLEAR = graphics.create_pen(0, 0, 0)
|
||||||
|
RED = graphics.create_pen(255, 0, 0)
|
||||||
|
GREEN = graphics.create_pen(0, 255, 0)
|
||||||
|
gu.set_brightness(0.7)
|
||||||
|
|
||||||
|
audio = WavPlayer(0, 10, 11, 9, amp_enable=22)
|
||||||
|
|
||||||
|
|
||||||
|
class Countdown(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.timer_running = False
|
||||||
|
self.total_seconds = 0
|
||||||
|
self.timer = None
|
||||||
|
|
||||||
|
def process_input(self):
|
||||||
|
if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_UP):
|
||||||
|
self.total_seconds += 1
|
||||||
|
if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_DOWN):
|
||||||
|
if self.total_seconds > 0:
|
||||||
|
self.total_seconds -= 1
|
||||||
|
if gu.is_pressed(GalacticUnicorn.SWITCH_SLEEP):
|
||||||
|
self.start_timer()
|
||||||
|
|
||||||
|
def display_time(self):
|
||||||
|
seconds = self.total_seconds % (24 * 3600)
|
||||||
|
seconds %= 3600
|
||||||
|
minutes = seconds // 60
|
||||||
|
seconds %= 60
|
||||||
|
|
||||||
|
# Add leading zeros to the minutes and seconds
|
||||||
|
if len(str(minutes)) == 1:
|
||||||
|
minutes = "0{}".format(minutes)
|
||||||
|
if len(str(seconds)) == 1:
|
||||||
|
seconds = "0{}".format(seconds)
|
||||||
|
|
||||||
|
return "{}:{}".format(minutes, seconds)
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
||||||
|
graphics.clear()
|
||||||
|
|
||||||
|
graphics.set_pen(BLUE)
|
||||||
|
graphics.circle(0, 0, 12)
|
||||||
|
graphics.set_pen(GREEN)
|
||||||
|
graphics.circle(50, 7, 6)
|
||||||
|
|
||||||
|
graphics.set_pen(WHITE)
|
||||||
|
graphics.text(self.display_time(), 15, 2, -1, 1)
|
||||||
|
gu.update(graphics)
|
||||||
|
|
||||||
|
def start_timer(self):
|
||||||
|
if not self.timer_running:
|
||||||
|
self.timer = Timer(mode=Timer.PERIODIC, period=1000, callback=self.countdown)
|
||||||
|
self.timer_running = True
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.timer.deinit()
|
||||||
|
self.timer_running = False
|
||||||
|
|
||||||
|
def countdown(self, arg):
|
||||||
|
|
||||||
|
if self.total_seconds == 0:
|
||||||
|
audio.play_wav("doorbell.wav", False)
|
||||||
|
self.reset()
|
||||||
|
else:
|
||||||
|
self.total_seconds -= 1
|
||||||
|
|
||||||
|
|
||||||
|
count = Countdown()
|
||||||
|
|
||||||
|
while 1:
|
||||||
|
count.process_input()
|
||||||
|
count.draw()
|
||||||
|
time.sleep(0.07)
|
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,80 @@
|
||||||
|
from audio import WavPlayer
|
||||||
|
from galactic import GalacticUnicorn
|
||||||
|
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
gu = GalacticUnicorn()
|
||||||
|
graphics = PicoGraphics(DISPLAY)
|
||||||
|
|
||||||
|
audio = WavPlayer(0, 10, 11, 9, amp_enable=22)
|
||||||
|
|
||||||
|
WHITE = graphics.create_pen(255, 255, 255)
|
||||||
|
RED = graphics.create_pen(255, 0, 0)
|
||||||
|
GREEN = graphics.create_pen(0, 255, 150)
|
||||||
|
CLEAR = graphics.create_pen(0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
class Menu(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.items = ["Pew 1", "Pew 2", "Pew 3"]
|
||||||
|
self.selected = 0
|
||||||
|
|
||||||
|
# A function to draw only the menu elements.
|
||||||
|
# Helps to keep our main function tidy!
|
||||||
|
def draw_menu(self):
|
||||||
|
graphics.set_pen(WHITE)
|
||||||
|
|
||||||
|
graphics.set_pen(GREEN)
|
||||||
|
graphics.line(0, 5, GalacticUnicorn.WIDTH, 5)
|
||||||
|
|
||||||
|
graphics.set_pen(CLEAR)
|
||||||
|
graphics.rectangle(13, 2, 26, 5)
|
||||||
|
|
||||||
|
for item in range(len(self.items)):
|
||||||
|
if self.selected == item:
|
||||||
|
graphics.set_pen(WHITE)
|
||||||
|
graphics.text(self.items[self.selected], 14, 2, 31, 1)
|
||||||
|
|
||||||
|
# Make changes based on the currently selected menu item
|
||||||
|
def process_selected(self):
|
||||||
|
if self.selected == 0:
|
||||||
|
audio.play_wav("Pew1.wav", False)
|
||||||
|
|
||||||
|
if self.selected == 1:
|
||||||
|
audio.play_wav("Pew2.wav", False)
|
||||||
|
|
||||||
|
if self.selected == 2:
|
||||||
|
audio.play_wav("Pew3.wav", False)
|
||||||
|
|
||||||
|
|
||||||
|
menu = Menu()
|
||||||
|
graphics.set_font("bitmap6")
|
||||||
|
gu.set_brightness(0.7)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
||||||
|
graphics.clear()
|
||||||
|
|
||||||
|
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||||
|
audio.play_wav("buttonbeep.wav", False)
|
||||||
|
if menu.selected + 1 < len(menu.items):
|
||||||
|
menu.selected += 1
|
||||||
|
else:
|
||||||
|
menu.selected = 0
|
||||||
|
|
||||||
|
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||||
|
audio.play_wav("buttonbeep.wav", False)
|
||||||
|
if menu.selected > 0:
|
||||||
|
menu.selected -= 1
|
||||||
|
else:
|
||||||
|
menu.selected = len(menu.items) - 1
|
||||||
|
|
||||||
|
if gu.is_pressed(GalacticUnicorn.SWITCH_SLEEP):
|
||||||
|
menu.process_selected()
|
||||||
|
|
||||||
|
menu.draw_menu()
|
||||||
|
|
||||||
|
gu.update(graphics)
|
||||||
|
sleep(0.2)
|
|
@ -0,0 +1,8 @@
|
||||||
|
from audio import WavPlayer
|
||||||
|
|
||||||
|
sound = WavPlayer(0, 10, 11, 9, amp_enable=22)
|
||||||
|
|
||||||
|
sound.play_wav("beepboop.wav", False)
|
||||||
|
|
||||||
|
while sound.is_playing():
|
||||||
|
pass
|
|
@ -0,0 +1,36 @@
|
||||||
|
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY # 5.7"
|
||||||
|
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_4 as DISPLAY # 4.0"
|
||||||
|
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_7 as DISPLAY # 7.3"
|
||||||
|
import pngdec
|
||||||
|
|
||||||
|
# Create a PicoGraphics instance
|
||||||
|
graphics = PicoGraphics(DISPLAY)
|
||||||
|
WIDTH, HEIGHT = graphics.get_bounds()
|
||||||
|
|
||||||
|
# Set the font
|
||||||
|
graphics.set_font("bitmap8")
|
||||||
|
|
||||||
|
# Create an instance of the PNG Decoder
|
||||||
|
png = pngdec.PNG(graphics)
|
||||||
|
|
||||||
|
# Clear the screen
|
||||||
|
graphics.set_pen(1)
|
||||||
|
graphics.clear()
|
||||||
|
graphics.set_pen(0)
|
||||||
|
|
||||||
|
# Few lines of text.
|
||||||
|
graphics.text("PNG Pencil", 70, 100, WIDTH, 3)
|
||||||
|
|
||||||
|
# Open our PNG File from flash. In this example we're using a cartoon pencil.
|
||||||
|
# You can use Thonny to transfer PNG Images to your Inky Frame.
|
||||||
|
try:
|
||||||
|
png.open_file("pencil_256x256.png")
|
||||||
|
|
||||||
|
# Decode our PNG file and set the X and Y
|
||||||
|
png.decode(200, 100)
|
||||||
|
|
||||||
|
except OSError:
|
||||||
|
graphics.text("Unable to find PNG file! Copy 'pencil_256x256.png' to your Inky Frame using Thonny :)", 10, 70, WIDTH, 3)
|
||||||
|
|
||||||
|
# Start the screen update
|
||||||
|
graphics.update()
|
|
@ -31,7 +31,7 @@ Alternatively, you can transfer them using Thonny, but you will have to mount th
|
||||||
```python
|
```python
|
||||||
import os
|
import os
|
||||||
import sdcard
|
import sdcard
|
||||||
from machine import Pin
|
from machine import Pin, SPI
|
||||||
sd_spi = SPI(0, sck=Pin(18, Pin.OUT), mosi=Pin(19, Pin.OUT), miso=Pin(16, Pin.OUT))
|
sd_spi = SPI(0, sck=Pin(18, Pin.OUT), mosi=Pin(19, Pin.OUT), miso=Pin(16, Pin.OUT))
|
||||||
sd = sdcard.SDCard(sd_spi, Pin(22))
|
sd = sdcard.SDCard(sd_spi, Pin(22))
|
||||||
os.mount(sd, "/sd")
|
os.mount(sd, "/sd")
|
||||||
|
|
|
@ -17,18 +17,16 @@ HEIGHT = None
|
||||||
code = qrcode.QRCode()
|
code = qrcode.QRCode()
|
||||||
|
|
||||||
|
|
||||||
def read_until(stream, char):
|
def read_until(stream, find):
|
||||||
result = b""
|
result = b""
|
||||||
while True:
|
while len(c := stream.read(1)) > 0:
|
||||||
c = stream.read(1)
|
if c == find:
|
||||||
if c == char:
|
|
||||||
return result
|
return result
|
||||||
result += c
|
result += c
|
||||||
|
|
||||||
|
|
||||||
def discard_until(stream, c):
|
def discard_until(stream, find):
|
||||||
while stream.read(1) != c:
|
_ = read_until(stream, find)
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def parse_xml_stream(s, accept_tags, group_by, max_items=3):
|
def parse_xml_stream(s, accept_tags, group_by, max_items=3):
|
||||||
|
@ -36,6 +34,7 @@ def parse_xml_stream(s, accept_tags, group_by, max_items=3):
|
||||||
text = b""
|
text = b""
|
||||||
count = 0
|
count = 0
|
||||||
current = {}
|
current = {}
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
char = s.read(1)
|
char = s.read(1)
|
||||||
if len(char) == 0:
|
if len(char) == 0:
|
||||||
|
@ -79,9 +78,9 @@ def parse_xml_stream(s, accept_tags, group_by, max_items=3):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
current_tag = read_until(s, b">")
|
current_tag = read_until(s, b">")
|
||||||
tag += [next_char + current_tag.split(b" ")[0]]
|
if not current_tag.endswith(b"/"):
|
||||||
text = b""
|
tag += [next_char + current_tag.split(b" ")[0]]
|
||||||
gc.collect()
|
text = b""
|
||||||
|
|
||||||
else:
|
else:
|
||||||
text += char
|
text += char
|
||||||
|
@ -113,7 +112,7 @@ def get_rss():
|
||||||
|
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
print(e)
|
print(e)
|
||||||
return False
|
return []
|
||||||
|
|
||||||
|
|
||||||
feed = None
|
feed = None
|
||||||
|
@ -135,7 +134,7 @@ def draw():
|
||||||
graphics.set_pen(0)
|
graphics.set_pen(0)
|
||||||
|
|
||||||
# Draws 2 articles from the feed if they're available.
|
# Draws 2 articles from the feed if they're available.
|
||||||
if feed:
|
if len(feed) > 0:
|
||||||
|
|
||||||
# Title
|
# Title
|
||||||
graphics.set_pen(graphics.create_pen(200, 0, 0))
|
graphics.set_pen(graphics.create_pen(200, 0, 0))
|
||||||
|
|
|
@ -95,7 +95,7 @@ def draw():
|
||||||
y += line_space
|
y += line_space
|
||||||
x = default_x
|
x = default_x
|
||||||
|
|
||||||
graphics.text(letter.upper(), x, y, 640, scale, spacing)
|
graphics.text(letter.upper(), x, y, 640, scale=scale, spacing=spacing)
|
||||||
x += letter_space
|
x += letter_space
|
||||||
|
|
||||||
graphics.update()
|
graphics.update()
|
||||||
|
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 5.4 KiB |
|
@ -4,6 +4,7 @@
|
||||||
- [Read ADCs](#read-adcs)
|
- [Read ADCs](#read-adcs)
|
||||||
- [Read GPIOs](#read-gpios)
|
- [Read GPIOs](#read-gpios)
|
||||||
- [Read Encoders](#read-encoders)
|
- [Read Encoders](#read-encoders)
|
||||||
|
- [Read Speeds](#read-speeds)
|
||||||
- [LED Rainbow](#led-rainbow)
|
- [LED Rainbow](#led-rainbow)
|
||||||
- [Reset Inventor](#reset-inventor)
|
- [Reset Inventor](#reset-inventor)
|
||||||
- [Motor Examples](#motor-examples)
|
- [Motor Examples](#motor-examples)
|
||||||
|
@ -22,13 +23,14 @@
|
||||||
- [Velocity Tuning](#velocity-tuning)
|
- [Velocity Tuning](#velocity-tuning)
|
||||||
- [Position on Velocity Tuning](#position-on-velocity-tuning)
|
- [Position on Velocity Tuning](#position-on-velocity-tuning)
|
||||||
- [Servo Examples](#servo-examples)
|
- [Servo Examples](#servo-examples)
|
||||||
- [Single Servos](#single-servo)
|
- [Single Servo](#single-servo)
|
||||||
- [Multiple Servos](#multiple-servos)
|
- [Multiple Servos](#multiple-servos)
|
||||||
- [Simple Easing](#simple-easing)
|
- [Simple Easing](#simple-easing)
|
||||||
- [Servo Wave](#servo-wave)
|
- [Servo Wave](#servo-wave)
|
||||||
- [Calibration](#calibration)
|
- [Calibration](#calibration)
|
||||||
- [Audio Examples](#audio-examples)
|
- [Audio Examples](#audio-examples)
|
||||||
- [Tone Song](#tone-song)
|
- [Tone Song](#tone-song)
|
||||||
|
- [Motor Song](#motor-song)
|
||||||
|
|
||||||
## Function Examples
|
## Function Examples
|
||||||
|
|
||||||
|
@ -50,6 +52,12 @@ Shows how to initialise and read the 6 GPIO headers of Inventor 2040 W.
|
||||||
Demonstrates how to read the angles of Inventor 2040 W's two encoders.
|
Demonstrates how to read the angles of Inventor 2040 W's two encoders.
|
||||||
|
|
||||||
|
|
||||||
|
### Read Speeds
|
||||||
|
[read_speeds.py](read_speeds.py)
|
||||||
|
|
||||||
|
Demonstrates how to read the speeds of Inventor 2040 W's two encoders.
|
||||||
|
|
||||||
|
|
||||||
### LED Rainbow
|
### LED Rainbow
|
||||||
[led_rainbow.py](led_rainbow.py)
|
[led_rainbow.py](led_rainbow.py)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import time
|
||||||
|
from inventor import Inventor2040W, NUM_MOTORS # , MOTOR_A, MOTOR_B
|
||||||
|
# from pimoroni import REVERSED_DIR
|
||||||
|
|
||||||
|
"""
|
||||||
|
Demonstrates how to read the speeds of Inventor 2040 W's two encoders.
|
||||||
|
|
||||||
|
Press "User" to exit the program.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Wheel friendly names
|
||||||
|
NAMES = ["LEFT", "RIGHT"]
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
GEAR_RATIO = 50 # The gear ratio of the motor
|
||||||
|
SPEED = 1.0 # The speed to drive the motors at
|
||||||
|
SLEEP = 0.1 # The time to sleep between each capture
|
||||||
|
|
||||||
|
# Create a new Inventor2040W
|
||||||
|
board = Inventor2040W(motor_gear_ratio=GEAR_RATIO)
|
||||||
|
|
||||||
|
# Uncomment the below lines (and the top imports) to
|
||||||
|
# reverse the counting direction of an encoder
|
||||||
|
# encoders[MOTOR_A].direction(REVERSED_DIR)
|
||||||
|
# encoders[MOTOR_B].direction(REVERSED_DIR)
|
||||||
|
|
||||||
|
# Set both motors driving
|
||||||
|
for motor in board.motors:
|
||||||
|
motor.speed(SPEED)
|
||||||
|
|
||||||
|
# Variables for storing encoder captures
|
||||||
|
captures = [None] * NUM_MOTORS
|
||||||
|
|
||||||
|
# Read the encoders until the user button is pressed
|
||||||
|
while not board.switch_pressed():
|
||||||
|
|
||||||
|
# Capture the state of all the encoders since the last capture, SLEEP seconds ago
|
||||||
|
for i in range(NUM_MOTORS):
|
||||||
|
captures[i] = board.encoders[i].capture()
|
||||||
|
|
||||||
|
# Print out the speeds from each encoder
|
||||||
|
for i in range(NUM_MOTORS):
|
||||||
|
print(NAMES[i], "=", captures[i].revolutions_per_second, end=", ")
|
||||||
|
print()
|
||||||
|
|
||||||
|
time.sleep(SLEEP)
|
|
@ -0,0 +1,236 @@
|
||||||
|
# Borrows heavily from : https://github.com/adafruit/Adafruit_CircuitPython_IS31FL3731/blob/main/adafruit_is31fl3731/keybow2040.py
|
||||||
|
# and : https://github.com/adafruit/micropython-adafruit-is31fl3731/blob/master/is31fl3731.py
|
||||||
|
import math
|
||||||
|
import utime
|
||||||
|
|
||||||
|
|
||||||
|
_MODE_REGISTER = const(0x00) # noqa: F821
|
||||||
|
_FRAME_REGISTER = const(0x01) # noqa: F821
|
||||||
|
_AUTOPLAY1_REGISTER = const(0x02) # noqa: F821
|
||||||
|
_AUTOPLAY2_REGISTER = const(0x03) # noqa: F821
|
||||||
|
_BLINK_REGISTER = const(0x05) # noqa: F821
|
||||||
|
_AUDIOSYNC_REGISTER = const(0x06) # noqa: F821
|
||||||
|
_BREATH1_REGISTER = const(0x08) # noqa: F821
|
||||||
|
_BREATH2_REGISTER = const(0x09) # noqa: F821
|
||||||
|
_SHUTDOWN_REGISTER = const(0x0a) # noqa: F821
|
||||||
|
_GAIN_REGISTER = const(0x0b) # noqa: F821
|
||||||
|
_ADC_REGISTER = const(0x0c) # noqa: F821
|
||||||
|
|
||||||
|
_CONFIG_BANK = const(0x0b) # noqa: F821
|
||||||
|
_BANK_ADDRESS = const(0xfd) # noqa: F821
|
||||||
|
|
||||||
|
_PICTURE_MODE = const(0x00) # noqa: F821
|
||||||
|
_AUTOPLAY_MODE = const(0x08) # noqa: F821
|
||||||
|
_AUDIOPLAY_MODE = const(0x18) # noqa: F821
|
||||||
|
|
||||||
|
_ENABLE_OFFSET = const(0x00) # noqa: F821
|
||||||
|
_BLINK_OFFSET = const(0x12) # noqa: F821
|
||||||
|
_COLOR_OFFSET = const(0x24) # noqa: F821
|
||||||
|
|
||||||
|
|
||||||
|
class Matrix:
|
||||||
|
width = 16
|
||||||
|
height = 9
|
||||||
|
|
||||||
|
def __init__(self, i2c, address=0x74):
|
||||||
|
self.i2c = i2c
|
||||||
|
self.address = address
|
||||||
|
self.reset()
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
def _bank(self, bank=None):
|
||||||
|
if bank is None:
|
||||||
|
return self.i2c.readfrom_mem(self.address, _BANK_ADDRESS, 1)[0]
|
||||||
|
self.i2c.writeto_mem(self.address, _BANK_ADDRESS, bytearray([bank]))
|
||||||
|
|
||||||
|
def _register(self, bank, register, value=None):
|
||||||
|
self._bank(bank)
|
||||||
|
if value is None:
|
||||||
|
return self.i2c.readfrom_mem(self.address, register, 1)[0]
|
||||||
|
self.i2c.writeto_mem(self.address, register, bytearray([value]))
|
||||||
|
|
||||||
|
def _mode(self, mode=None):
|
||||||
|
return self._register(_CONFIG_BANK, _MODE_REGISTER, mode)
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self._mode(_PICTURE_MODE)
|
||||||
|
self.frame(0)
|
||||||
|
for frame in range(8):
|
||||||
|
self.fill(0, False, frame=frame)
|
||||||
|
for col in range(18):
|
||||||
|
self._register(frame, _ENABLE_OFFSET + col, 0xff)
|
||||||
|
self.audio_sync(False)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.sleep(True)
|
||||||
|
utime.sleep_us(10)
|
||||||
|
self.sleep(False)
|
||||||
|
|
||||||
|
def sleep(self, value):
|
||||||
|
return self._register(_CONFIG_BANK, _SHUTDOWN_REGISTER, not value)
|
||||||
|
|
||||||
|
def autoplay(self, delay=0, loops=0, frames=0):
|
||||||
|
if delay == 0:
|
||||||
|
self._mode(_PICTURE_MODE)
|
||||||
|
return
|
||||||
|
delay //= 11
|
||||||
|
if not 0 <= loops <= 7:
|
||||||
|
raise ValueError("Loops out of range")
|
||||||
|
if not 0 <= frames <= 7:
|
||||||
|
raise ValueError("Frames out of range")
|
||||||
|
if not 1 <= delay <= 64:
|
||||||
|
raise ValueError("Delay out of range")
|
||||||
|
self._register(_CONFIG_BANK, _AUTOPLAY1_REGISTER, loops << 4 | frames)
|
||||||
|
self._register(_CONFIG_BANK, _AUTOPLAY2_REGISTER, delay % 64)
|
||||||
|
self._mode(_AUTOPLAY_MODE | self._frame)
|
||||||
|
|
||||||
|
def fade(self, fade_in=None, fade_out=None, pause=0):
|
||||||
|
if fade_in is None and fade_out is None:
|
||||||
|
self._register(_CONFIG_BANK, _BREATH2_REGISTER, 0)
|
||||||
|
elif fade_in is None:
|
||||||
|
fade_in = fade_out
|
||||||
|
elif fade_out is None:
|
||||||
|
fade_out = fade_in
|
||||||
|
fade_in = int(math.log(fade_in / 26, 2))
|
||||||
|
fade_out = int(math.log(fade_out / 26, 2))
|
||||||
|
pause = int(math.log(pause / 26, 2))
|
||||||
|
if not 0 <= fade_in <= 7:
|
||||||
|
raise ValueError("Fade in out of range")
|
||||||
|
if not 0 <= fade_out <= 7:
|
||||||
|
raise ValueError("Fade out out of range")
|
||||||
|
if not 0 <= pause <= 7:
|
||||||
|
raise ValueError("Pause out of range")
|
||||||
|
self._register(_CONFIG_BANK, _BREATH1_REGISTER, fade_out << 4 | fade_in)
|
||||||
|
self._register(_CONFIG_BANK, _BREATH2_REGISTER, 1 << 4 | pause)
|
||||||
|
|
||||||
|
def frame(self, frame=None, show=True):
|
||||||
|
if frame is None:
|
||||||
|
return self._frame
|
||||||
|
if not 0 <= frame <= 8:
|
||||||
|
raise ValueError("Frame out of range")
|
||||||
|
self._frame = frame
|
||||||
|
if show:
|
||||||
|
self._register(_CONFIG_BANK, _FRAME_REGISTER, frame)
|
||||||
|
|
||||||
|
def audio_sync(self, value=None):
|
||||||
|
return self._register(_CONFIG_BANK, _AUDIOSYNC_REGISTER, value)
|
||||||
|
|
||||||
|
def audio_play(self, sample_rate, audio_gain=0,
|
||||||
|
agc_enable=False, agc_fast=False):
|
||||||
|
if sample_rate == 0:
|
||||||
|
self._mode(_PICTURE_MODE)
|
||||||
|
return
|
||||||
|
sample_rate //= 46
|
||||||
|
if not 1 <= sample_rate <= 256:
|
||||||
|
raise ValueError("Sample rate out of range")
|
||||||
|
self._register(_CONFIG_BANK, _ADC_REGISTER, sample_rate % 256)
|
||||||
|
audio_gain //= 3
|
||||||
|
if not 0 <= audio_gain <= 7:
|
||||||
|
raise ValueError("Audio gain out of range")
|
||||||
|
self._register(_CONFIG_BANK, _GAIN_REGISTER,
|
||||||
|
bool(agc_enable) << 3 | bool(agc_fast) << 4 | audio_gain)
|
||||||
|
self._mode(_AUDIOPLAY_MODE)
|
||||||
|
|
||||||
|
def blink(self, rate=None):
|
||||||
|
if rate is None:
|
||||||
|
return (self._register(_CONFIG_BANK, _BLINK_REGISTER) & 0x07) * 270
|
||||||
|
elif rate == 0:
|
||||||
|
self._register(_CONFIG_BANK, _BLINK_REGISTER, 0x00)
|
||||||
|
return
|
||||||
|
rate //= 270
|
||||||
|
self._register(_CONFIG_BANK, _BLINK_REGISTER, rate & 0x07 | 0x08)
|
||||||
|
|
||||||
|
def fill(self, color=None, blink=None, frame=None):
|
||||||
|
if frame is None:
|
||||||
|
frame = self._frame
|
||||||
|
self._bank(frame)
|
||||||
|
if color is not None:
|
||||||
|
if not 0 <= color <= 255:
|
||||||
|
raise ValueError("Color out of range")
|
||||||
|
data = bytearray([color] * 24)
|
||||||
|
for row in range(6):
|
||||||
|
self.i2c.writeto_mem(self.address,
|
||||||
|
_COLOR_OFFSET + row * 24, data)
|
||||||
|
if blink is not None:
|
||||||
|
data = bool(blink) * 0xff
|
||||||
|
for col in range(18):
|
||||||
|
self._register(frame, _BLINK_OFFSET + col, data)
|
||||||
|
|
||||||
|
def write_frame(self, data, frame=None):
|
||||||
|
if len(data) > 144:
|
||||||
|
raise ValueError("Bytearray too large for frame")
|
||||||
|
if frame is None:
|
||||||
|
frame = self._frame
|
||||||
|
self._bank(frame)
|
||||||
|
self.i2c.writeto_mem(self.address, _COLOR_OFFSET, data)
|
||||||
|
|
||||||
|
def _pixel_addr(self, x, y):
|
||||||
|
return x + y * 16
|
||||||
|
|
||||||
|
def pixel(self, x, y, color=None, blink=None, frame=None):
|
||||||
|
if not 0 <= x <= self.width:
|
||||||
|
return
|
||||||
|
if not 0 <= y <= self.height:
|
||||||
|
return
|
||||||
|
pixel = self._pixel_addr(x, y)
|
||||||
|
if color is None and blink is None:
|
||||||
|
return self._register(self._frame, pixel)
|
||||||
|
if frame is None:
|
||||||
|
frame = self._frame
|
||||||
|
if color is not None:
|
||||||
|
if not 0 <= color <= 255:
|
||||||
|
raise ValueError("Color out of range")
|
||||||
|
self._register(frame, _COLOR_OFFSET + pixel, color)
|
||||||
|
if blink is not None:
|
||||||
|
addr, bit = divmod(pixel, 8)
|
||||||
|
bits = self._register(frame, _BLINK_OFFSET + addr)
|
||||||
|
if blink:
|
||||||
|
bits |= 1 << bit
|
||||||
|
else:
|
||||||
|
bits &= ~(1 << bit)
|
||||||
|
self._register(frame, _BLINK_OFFSET + addr, bits)
|
||||||
|
|
||||||
|
|
||||||
|
class Matrix_Keybow2040(Matrix):
|
||||||
|
width = 16
|
||||||
|
height = 3
|
||||||
|
|
||||||
|
def pixelrgb(self, x, y, r, g, b, blink=None, frame=None):
|
||||||
|
"""
|
||||||
|
Blink or brightness for x, y-pixel
|
||||||
|
|
||||||
|
:param x: horizontal pixel position
|
||||||
|
:param y: vertical pixel position
|
||||||
|
:param r: red brightness value 0->255
|
||||||
|
:param g: green brightness value 0->255
|
||||||
|
:param b: blue brightness value 0->255
|
||||||
|
:param blink: True to blink
|
||||||
|
:param frame: the frame to set the pixel
|
||||||
|
"""
|
||||||
|
x = (4 * (3 - x)) + y
|
||||||
|
|
||||||
|
super().pixel(x, 0, r, blink, frame)
|
||||||
|
super().pixel(x, 1, g, blink, frame)
|
||||||
|
super().pixel(x, 2, b, blink, frame)
|
||||||
|
|
||||||
|
def _pixel_addr(self, x, y):
|
||||||
|
lookup = [
|
||||||
|
(120, 88, 104), # 0, 0
|
||||||
|
(136, 40, 72), # 1, 0
|
||||||
|
(112, 80, 96), # 2, 0
|
||||||
|
(128, 32, 64), # 3, 0
|
||||||
|
(121, 89, 105), # 0, 1
|
||||||
|
(137, 41, 73), # 1, 1
|
||||||
|
(113, 81, 97), # 2, 1
|
||||||
|
(129, 33, 65), # 3, 1
|
||||||
|
(122, 90, 106), # 0, 2
|
||||||
|
(138, 25, 74), # 1, 2
|
||||||
|
(114, 82, 98), # 2, 2
|
||||||
|
(130, 17, 66), # 3, 2
|
||||||
|
(123, 91, 107), # 0, 3
|
||||||
|
(139, 26, 75), # 1, 3
|
||||||
|
(115, 83, 99), # 2, 3
|
||||||
|
(131, 18, 67), # 3, 3
|
||||||
|
]
|
||||||
|
|
||||||
|
return lookup[x][y]
|
|
@ -0,0 +1,67 @@
|
||||||
|
import time
|
||||||
|
import usb.device
|
||||||
|
from usb.device.keyboard import KeyboardInterface, KeyCode
|
||||||
|
from pimoroni import Button
|
||||||
|
|
||||||
|
# A very basic HID Keyboard example for Keybow 2040
|
||||||
|
# Inspired by: https://github.com/micropython/micropython-lib/blob/master/micropython/usb/examples/device/keyboard_example.py
|
||||||
|
|
||||||
|
# This example requires a Pico USB compatible firmware, eg: pico_usb-1.23.0-pimoroni-micropython.uf2
|
||||||
|
|
||||||
|
# The pin order for Keybow 2040 is weird,
|
||||||
|
# But the below is laid out to match the pad with USB up
|
||||||
|
# from top left to bottom right.
|
||||||
|
KEYS = {
|
||||||
|
18: KeyCode.A, 14: KeyCode.B, 10: KeyCode.C, 6: KeyCode.D,
|
||||||
|
19: KeyCode.E, 15: KeyCode.F, 11: KeyCode.G, 7: KeyCode.H,
|
||||||
|
20: KeyCode.I, 16: KeyCode.J, 12: KeyCode.K, 8: KeyCode.L,
|
||||||
|
21: KeyCode.M, 17: KeyCode.N, 13: KeyCode.O, 9: KeyCode.P,
|
||||||
|
}
|
||||||
|
|
||||||
|
# We'll fill this with Button instances
|
||||||
|
BUTTONS = {}
|
||||||
|
|
||||||
|
|
||||||
|
class Keybow2040(KeyboardInterface):
|
||||||
|
def on_led_update(self, led_mask):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
for pin, keycode in KEYS.items():
|
||||||
|
BUTTONS[pin] = Button(pin)
|
||||||
|
|
||||||
|
k = Keybow2040()
|
||||||
|
usb.device.get().init(k, builtin_driver=True)
|
||||||
|
|
||||||
|
keys = [0, 0, 0, 0, 0, 0]
|
||||||
|
|
||||||
|
while True:
|
||||||
|
changed = False
|
||||||
|
if k.is_open():
|
||||||
|
for pin, code in KEYS.items():
|
||||||
|
pressed = BUTTONS[pin].read()
|
||||||
|
|
||||||
|
# If the key is pressed then try to insert
|
||||||
|
# it at the first zero. Otherwise (try to) replace
|
||||||
|
# its keycode with 0 to clear that press.
|
||||||
|
c = code if pressed else 0
|
||||||
|
i = 0 if pressed else code
|
||||||
|
|
||||||
|
try:
|
||||||
|
keys[keys.index(i)] = c
|
||||||
|
changed = True
|
||||||
|
except ValueError:
|
||||||
|
# Either our 6-key list is full
|
||||||
|
# or we're releasing a key that's not in it!
|
||||||
|
# Or handling a key that isn't pressed.
|
||||||
|
pass
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
k.send_keys(keys)
|
||||||
|
time.sleep_ms(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("Starting Keybow 2040...")
|
||||||
|
main()
|
|
@ -0,0 +1,116 @@
|
||||||
|
import machine
|
||||||
|
import usb.device
|
||||||
|
from usb.device.keyboard import KeyboardInterface, KeyCode
|
||||||
|
from pimoroni import Button
|
||||||
|
from is31fl3731 import Matrix_Keybow2040
|
||||||
|
|
||||||
|
# A very basic HID Keyboard example for Keybow 2040
|
||||||
|
# Inspired by: https://github.com/micropython/micropython-lib/blob/master/micropython/usb/examples/device/keyboard_example.py
|
||||||
|
|
||||||
|
# This example requires a Pico USB compatible firmware, eg: pico_usb-1.23.0-pimoroni-micropython.uf2
|
||||||
|
|
||||||
|
# Don't forget to copy over is31fl3731.py too!
|
||||||
|
|
||||||
|
# The pin order for Keybow 2040 is weird,
|
||||||
|
# But the below is laid out to match the pad with USB up
|
||||||
|
# from top left to bottom right.
|
||||||
|
KEYS = {
|
||||||
|
18: KeyCode.A, 14: KeyCode.B, 10: KeyCode.C, 6: KeyCode.D,
|
||||||
|
19: KeyCode.E, 15: KeyCode.F, 11: KeyCode.G, 7: KeyCode.H,
|
||||||
|
20: KeyCode.I, 16: KeyCode.J, 12: KeyCode.K, 8: KeyCode.L,
|
||||||
|
21: KeyCode.M, 17: KeyCode.N, 13: KeyCode.O, 9: KeyCode.P,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Another look up table for the balmy LED positioning
|
||||||
|
PIXELS = {
|
||||||
|
18: (3, 0), 14: (3, 1), 10: (3, 2), 6: (3, 3),
|
||||||
|
19: (2, 0), 15: (2, 1), 11: (2, 2), 7: (2, 3),
|
||||||
|
20: (1, 0), 16: (1, 1), 12: (1, 2), 8: (1, 3),
|
||||||
|
21: (0, 0), 17: (0, 1), 13: (0, 2), 9: (0, 3),
|
||||||
|
}
|
||||||
|
|
||||||
|
# We'll fill this with Button instances
|
||||||
|
BUTTONS = {}
|
||||||
|
VALUES = {}
|
||||||
|
MAX_ON_TIME = 20.0
|
||||||
|
|
||||||
|
|
||||||
|
# From CPython Lib/colorsys.py
|
||||||
|
def hsv_to_rgb(h, s, v):
|
||||||
|
if s == 0.0:
|
||||||
|
return v, v, v
|
||||||
|
i = int(h * 6.0)
|
||||||
|
f = (h * 6.0) - i
|
||||||
|
p = v * (1.0 - s)
|
||||||
|
q = v * (1.0 - s * f)
|
||||||
|
t = v * (1.0 - s * (1.0 - f))
|
||||||
|
i = i % 6
|
||||||
|
if i == 0:
|
||||||
|
return v, t, p
|
||||||
|
if i == 1:
|
||||||
|
return q, v, p
|
||||||
|
if i == 2:
|
||||||
|
return p, v, t
|
||||||
|
if i == 3:
|
||||||
|
return p, q, v
|
||||||
|
if i == 4:
|
||||||
|
return t, p, v
|
||||||
|
if i == 5:
|
||||||
|
return v, p, q
|
||||||
|
|
||||||
|
|
||||||
|
class Keybow2040(KeyboardInterface):
|
||||||
|
def on_led_update(self, led_mask):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
for pin, keycode in KEYS.items():
|
||||||
|
BUTTONS[pin] = Button(pin, repeat_time=0)
|
||||||
|
VALUES[pin] = 0
|
||||||
|
|
||||||
|
m = Matrix_Keybow2040(machine.I2C(0, 400000))
|
||||||
|
k = Keybow2040()
|
||||||
|
usb.device.get().init(k, builtin_driver=True)
|
||||||
|
|
||||||
|
keys = [0, 0, 0, 0, 0, 0]
|
||||||
|
|
||||||
|
while True:
|
||||||
|
changed = False
|
||||||
|
if k.is_open():
|
||||||
|
for pin, code in KEYS.items():
|
||||||
|
pressed = BUTTONS[pin].read()
|
||||||
|
|
||||||
|
if pressed:
|
||||||
|
VALUES[pin] = MAX_ON_TIME
|
||||||
|
|
||||||
|
# If the key is pressed then try to insert
|
||||||
|
# it at the first zero. Otherwise (try to) replace
|
||||||
|
# its keycode with 0 to clear that press.
|
||||||
|
c = code if pressed else 0
|
||||||
|
i = 0 if pressed else code
|
||||||
|
|
||||||
|
try:
|
||||||
|
keys[keys.index(i)] = c
|
||||||
|
changed = True
|
||||||
|
except ValueError:
|
||||||
|
# Either our 6-key list is full
|
||||||
|
# or we're releasing a key that's not in it!
|
||||||
|
# Or handling a key that isn't pressed.
|
||||||
|
pass
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
k.send_keys(keys)
|
||||||
|
|
||||||
|
for pin, code in KEYS.items():
|
||||||
|
x, y = PIXELS[pin]
|
||||||
|
v = VALUES[pin] / MAX_ON_TIME
|
||||||
|
r, g, b = [int(c * 255) for c in hsv_to_rgb(x / 4.0, 0.7, 1.0 - v)]
|
||||||
|
m.pixelrgb(x, y, r, g, b)
|
||||||
|
if VALUES[pin] > 0:
|
||||||
|
VALUES[pin] -= 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("Starting Keybow 2040...")
|
||||||
|
main()
|
|
@ -0,0 +1,276 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Christopher Parrott for Pimoroni Ltd
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import os
|
||||||
|
import math
|
||||||
|
import struct
|
||||||
|
from machine import I2S, Pin
|
||||||
|
|
||||||
|
"""
|
||||||
|
A class for playing Wav files out of an I2S audio amp. It can also play pure tones.
|
||||||
|
This code is based heavily on the work of Mike Teachman, at:
|
||||||
|
https://github.com/miketeachman/micropython-i2s-examples/blob/master/examples/wavplayer.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class WavPlayer:
|
||||||
|
# Internal states
|
||||||
|
PLAY = 0
|
||||||
|
PAUSE = 1
|
||||||
|
FLUSH = 2
|
||||||
|
STOP = 3
|
||||||
|
NONE = 4
|
||||||
|
|
||||||
|
MODE_WAV = 0
|
||||||
|
MODE_TONE = 1
|
||||||
|
|
||||||
|
# Default buffer length
|
||||||
|
SILENCE_BUFFER_LENGTH = 1000
|
||||||
|
WAV_BUFFER_LENGTH = 10000
|
||||||
|
INTERNAL_BUFFER_LENGTH = 20000
|
||||||
|
|
||||||
|
TONE_SAMPLE_RATE = 44_100
|
||||||
|
TONE_BITS_PER_SAMPLE = 16
|
||||||
|
TONE_FULL_WAVES = 2
|
||||||
|
|
||||||
|
def __init__(self, id, sck_pin, ws_pin, sd_pin, amp_enable=None, ibuf_len=INTERNAL_BUFFER_LENGTH, root="/"):
|
||||||
|
self.__id = id
|
||||||
|
self.__sck_pin = sck_pin
|
||||||
|
self.__ws_pin = ws_pin
|
||||||
|
self.__sd_pin = sd_pin
|
||||||
|
self.__ibuf_len = ibuf_len
|
||||||
|
self.__enable = None
|
||||||
|
|
||||||
|
if amp_enable is not None:
|
||||||
|
self.__enable = Pin(amp_enable, Pin.OUT)
|
||||||
|
|
||||||
|
# Set the directory to search for files in
|
||||||
|
self.set_root(root)
|
||||||
|
|
||||||
|
self.__state = WavPlayer.NONE
|
||||||
|
self.__mode = WavPlayer.MODE_WAV
|
||||||
|
self.__wav_file = None
|
||||||
|
self.__loop_wav = False
|
||||||
|
self.__first_sample_offset = None
|
||||||
|
self.__flush_count = 0
|
||||||
|
self.__audio_out = None
|
||||||
|
|
||||||
|
# Allocate a small array of blank audio samples used for silence
|
||||||
|
self.__silence_samples = bytearray(self.SILENCE_BUFFER_LENGTH)
|
||||||
|
|
||||||
|
# Allocate a larger array for WAV audio samples, using a memoryview for more efficient access
|
||||||
|
self.__wav_samples_mv = memoryview(bytearray(self.WAV_BUFFER_LENGTH))
|
||||||
|
|
||||||
|
# Reserve a variable for audio samples used for tones
|
||||||
|
self.__tone_samples = None
|
||||||
|
self.__queued_samples = None
|
||||||
|
|
||||||
|
def set_root(self, root):
|
||||||
|
self.__root = root.rstrip("/") + "/"
|
||||||
|
|
||||||
|
def play_wav(self, wav_file, loop=False):
|
||||||
|
if os.listdir(self.__root).count(wav_file) == 0:
|
||||||
|
raise ValueError(f"'{wav_file}' not found")
|
||||||
|
|
||||||
|
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
|
||||||
|
|
||||||
|
self.__wav_file = open(self.__root + wav_file, "rb") # Open the chosen WAV file in read-only, binary mode
|
||||||
|
self.__loop_wav = loop # Record if the user wants the file to loop
|
||||||
|
|
||||||
|
# Parse the WAV file, returning the necessary parameters to initialise I2S communication
|
||||||
|
format, sample_rate, bits_per_sample, self.__first_sample_offset, self.sample_size = WavPlayer.__parse_wav(self.__wav_file)
|
||||||
|
|
||||||
|
# Keep a track of total bytes read from WAV File
|
||||||
|
self.total_bytes_read = 0
|
||||||
|
|
||||||
|
self.__wav_file.seek(self.__first_sample_offset) # Advance to first byte of sample data
|
||||||
|
|
||||||
|
self.__start_i2s(bits=bits_per_sample,
|
||||||
|
format=format,
|
||||||
|
rate=sample_rate,
|
||||||
|
state=WavPlayer.PLAY,
|
||||||
|
mode=WavPlayer.MODE_WAV)
|
||||||
|
|
||||||
|
def play_tone(self, frequency, amplitude):
|
||||||
|
if frequency < 20.0 or frequency > 20_000:
|
||||||
|
raise ValueError("frequency out of range. Expected between 20Hz and 20KHz")
|
||||||
|
|
||||||
|
if amplitude < 0.0 or amplitude > 1.0:
|
||||||
|
raise ValueError("amplitude out of range. Expected 0.0 to 1.0")
|
||||||
|
|
||||||
|
# Create a buffer containing the pure tone samples
|
||||||
|
samples_per_cycle = self.TONE_SAMPLE_RATE // frequency
|
||||||
|
sample_size_in_bytes = self.TONE_BITS_PER_SAMPLE // 8
|
||||||
|
samples = bytearray(self.TONE_FULL_WAVES * samples_per_cycle * sample_size_in_bytes)
|
||||||
|
range = pow(2, self.TONE_BITS_PER_SAMPLE) // 2
|
||||||
|
|
||||||
|
format = "<h" if self.TONE_BITS_PER_SAMPLE == 16 else "<l"
|
||||||
|
|
||||||
|
# Populate the buffer with multiple cycles to avoid it completing too quickly and causing drop outs
|
||||||
|
for i in range(samples_per_cycle * self.TONE_FULL_WAVES):
|
||||||
|
sample = int((range - 1) * (math.sin(2 * math.pi * i / samples_per_cycle)) * amplitude)
|
||||||
|
struct.pack_into(format, samples, i * sample_size_in_bytes, sample)
|
||||||
|
|
||||||
|
# Are we not already playing tones?
|
||||||
|
if not (self.__mode == WavPlayer.MODE_TONE and (self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE)):
|
||||||
|
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
|
||||||
|
self.__tone_samples = samples
|
||||||
|
self.__start_i2s(bits=self.TONE_BITS_PER_SAMPLE,
|
||||||
|
format=I2S.MONO,
|
||||||
|
rate=self.TONE_SAMPLE_RATE,
|
||||||
|
state=WavPlayer.PLAY,
|
||||||
|
mode=WavPlayer.MODE_TONE)
|
||||||
|
else:
|
||||||
|
self.__queued_samples = samples
|
||||||
|
self.__state = WavPlayer.PLAY
|
||||||
|
|
||||||
|
def pause(self):
|
||||||
|
if self.__state == WavPlayer.PLAY:
|
||||||
|
self.__state = WavPlayer.PAUSE # Enter the pause state on the next callback
|
||||||
|
|
||||||
|
def resume(self):
|
||||||
|
if self.__state == WavPlayer.PAUSE:
|
||||||
|
self.__state = WavPlayer.PLAY # Enter the play state on the next callback
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
if self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE:
|
||||||
|
if self.__mode == WavPlayer.MODE_WAV:
|
||||||
|
# Enter the flush state on the next callback and close the file
|
||||||
|
# It is done in this order to prevent the callback entering the play
|
||||||
|
# state after we close the file but before we change the state)
|
||||||
|
self.__state = WavPlayer.FLUSH
|
||||||
|
self.__wav_file.close()
|
||||||
|
else:
|
||||||
|
self.__state = WavPlayer.STOP
|
||||||
|
|
||||||
|
def is_playing(self):
|
||||||
|
return self.__state != WavPlayer.NONE and self.__state != WavPlayer.STOP
|
||||||
|
|
||||||
|
def is_paused(self):
|
||||||
|
return self.__state == WavPlayer.PAUSE
|
||||||
|
|
||||||
|
def __start_i2s(self, bits=16, format=I2S.MONO, rate=44_100, state=STOP, mode=MODE_WAV):
|
||||||
|
import gc
|
||||||
|
gc.collect()
|
||||||
|
self.__audio_out = I2S(
|
||||||
|
self.__id,
|
||||||
|
sck=self.__sck_pin,
|
||||||
|
ws=self.__ws_pin,
|
||||||
|
sd=self.__sd_pin,
|
||||||
|
mode=I2S.TX,
|
||||||
|
bits=bits,
|
||||||
|
format=format,
|
||||||
|
rate=rate,
|
||||||
|
ibuf=self.__ibuf_len,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.__state = state
|
||||||
|
self.__mode = mode
|
||||||
|
self.__flush_count = self.__ibuf_len // self.SILENCE_BUFFER_LENGTH + 1
|
||||||
|
self.__audio_out.irq(self.__i2s_callback)
|
||||||
|
self.__audio_out.write(self.__silence_samples)
|
||||||
|
|
||||||
|
if self.__enable is not None:
|
||||||
|
self.__enable.on()
|
||||||
|
|
||||||
|
def __stop_i2s(self):
|
||||||
|
self.stop() # Stop any active playback
|
||||||
|
while self.is_playing(): # and wait for it to complete
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self.__enable is not None:
|
||||||
|
self.__enable.off()
|
||||||
|
|
||||||
|
if self.__audio_out is not None:
|
||||||
|
self.__audio_out.deinit() # Deinit any active I2S comms
|
||||||
|
|
||||||
|
self.__state == WavPlayer.NONE # Return to the none state
|
||||||
|
|
||||||
|
def __i2s_callback(self, arg):
|
||||||
|
# PLAY
|
||||||
|
if self.__state == WavPlayer.PLAY:
|
||||||
|
if self.__mode == WavPlayer.MODE_WAV:
|
||||||
|
num_read = self.__wav_file.readinto(self.__wav_samples_mv) # Read the next section of the WAV file
|
||||||
|
self.total_bytes_read += num_read
|
||||||
|
# Have we reached the end of the file?
|
||||||
|
if num_read == 0:
|
||||||
|
# Do we want to loop the WAV playback?
|
||||||
|
if self.__loop_wav:
|
||||||
|
_ = self.__wav_file.seek(self.__first_sample_offset) # Play again, so advance to first byte of sample data
|
||||||
|
else:
|
||||||
|
self.__wav_file.close() # Stop playing, so close the file
|
||||||
|
self.__state = WavPlayer.FLUSH # and enter the flush state on the next callback
|
||||||
|
|
||||||
|
self.__audio_out.write(self.__silence_samples) # In both cases play silence to end this callback
|
||||||
|
else:
|
||||||
|
if num_read > 0 and num_read < self.WAV_BUFFER_LENGTH:
|
||||||
|
num_read = num_read - (self.total_bytes_read - self.sample_size)
|
||||||
|
self.__audio_out.write(self.__wav_samples_mv[: num_read]) # We are within the file, so write out the next audio samples
|
||||||
|
else:
|
||||||
|
if self.__queued_samples is not None:
|
||||||
|
self.__tone_samples = self.__queued_samples
|
||||||
|
self.__queued_samples = None
|
||||||
|
self.__audio_out.write(self.__tone_samples)
|
||||||
|
|
||||||
|
# PAUSE or STOP
|
||||||
|
elif self.__state == WavPlayer.PAUSE or self.__state == WavPlayer.STOP:
|
||||||
|
self.__audio_out.write(self.__silence_samples) # Play silence
|
||||||
|
|
||||||
|
# FLUSH
|
||||||
|
elif self.__state == WavPlayer.FLUSH:
|
||||||
|
# Flush is used to allow the residual audio samples in the internal buffer to be written
|
||||||
|
# to the I2S peripheral. This step avoids part of the sound file from being cut off
|
||||||
|
if self.__flush_count > 0:
|
||||||
|
self.__flush_count -= 1
|
||||||
|
else:
|
||||||
|
self.__state = WavPlayer.STOP # Enter the stop state on the next callback
|
||||||
|
self.__audio_out.write(self.__silence_samples) # Play silence
|
||||||
|
|
||||||
|
# NONE
|
||||||
|
elif self.__state == WavPlayer.NONE:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __parse_wav(wav_file):
|
||||||
|
chunk_ID = wav_file.read(4)
|
||||||
|
if chunk_ID != b"RIFF":
|
||||||
|
raise ValueError("WAV chunk ID invalid")
|
||||||
|
_ = wav_file.read(4) # chunk_size
|
||||||
|
format = wav_file.read(4)
|
||||||
|
if format != b"WAVE":
|
||||||
|
raise ValueError("WAV format invalid")
|
||||||
|
sub_chunk1_ID = wav_file.read(4)
|
||||||
|
if sub_chunk1_ID != b"fmt ":
|
||||||
|
raise ValueError("WAV sub chunk 1 ID invalid")
|
||||||
|
_ = wav_file.read(4) # sub_chunk1_size
|
||||||
|
_ = struct.unpack("<H", wav_file.read(2))[0] # audio_format
|
||||||
|
num_channels = struct.unpack("<H", wav_file.read(2))[0]
|
||||||
|
|
||||||
|
if num_channels == 1:
|
||||||
|
format = I2S.MONO
|
||||||
|
else:
|
||||||
|
format = I2S.STEREO
|
||||||
|
|
||||||
|
sample_rate = struct.unpack("<I", wav_file.read(4))[0]
|
||||||
|
# if sample_rate != 44_100 and sample_rate != 48_000:
|
||||||
|
# raise ValueError(f"WAV sample rate of {sample_rate} invalid. Only 44.1KHz or 48KHz audio are supported")
|
||||||
|
|
||||||
|
_ = struct.unpack("<I", wav_file.read(4))[0] # byte_rate
|
||||||
|
_ = struct.unpack("<H", wav_file.read(2))[0] # block_align
|
||||||
|
bits_per_sample = struct.unpack("<H", wav_file.read(2))[0]
|
||||||
|
|
||||||
|
# usually the sub chunk2 ID ("data") comes next, but
|
||||||
|
# some online MP3->WAV converters add
|
||||||
|
# binary data before "data". So, read a fairly large
|
||||||
|
# block of bytes and search for "data".
|
||||||
|
|
||||||
|
binary_block = wav_file.read(200)
|
||||||
|
offset = binary_block.find(b"data")
|
||||||
|
if offset == -1:
|
||||||
|
raise ValueError("WAV sub chunk 2 ID not found")
|
||||||
|
|
||||||
|
wav_file.seek(40)
|
||||||
|
sub_chunk2_size = struct.unpack("<I", wav_file.read(4))[0]
|
||||||
|
|
||||||
|
return (format, sample_rate, bits_per_sample, 44 + offset, sub_chunk2_size)
|
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,8 @@
|
||||||
|
from audio import WavPlayer
|
||||||
|
|
||||||
|
sound = WavPlayer(0, 10, 11, 9, amp_enable=29)
|
||||||
|
|
||||||
|
sound.play_wav("pirate-arrrr.wav", False)
|
||||||
|
|
||||||
|
while sound.is_playing():
|
||||||
|
pass
|
|
@ -0,0 +1,40 @@
|
||||||
|
from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY, PEN_RGB332
|
||||||
|
import pngdec
|
||||||
|
|
||||||
|
# Create a PicoGraphics instance
|
||||||
|
display = PicoGraphics(display=DISPLAY_PICO_DISPLAY, pen_type=PEN_RGB332)
|
||||||
|
|
||||||
|
# Set the backlight so we can see it!
|
||||||
|
display.set_backlight(1.0)
|
||||||
|
|
||||||
|
# Create an instance of the PNG Decoder
|
||||||
|
png = pngdec.PNG(display)
|
||||||
|
|
||||||
|
# Create some pens for use later.
|
||||||
|
BG = display.create_pen(200, 200, 200)
|
||||||
|
TEXT = display.create_pen(0, 0, 0)
|
||||||
|
|
||||||
|
# Clear the screen
|
||||||
|
display.set_pen(BG)
|
||||||
|
display.clear()
|
||||||
|
|
||||||
|
display.set_pen(TEXT)
|
||||||
|
display.text("PNG Pencil", 15, 80)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Open our PNG File from flash. In this example we're using an image of a cartoon pencil.
|
||||||
|
# You can use Thonny to transfer PNG Images to your Pico.
|
||||||
|
png.open_file("pencil.png")
|
||||||
|
|
||||||
|
# Decode our PNG file and set the X and Y
|
||||||
|
png.decode(20, 100, scale=3)
|
||||||
|
|
||||||
|
# Handle the error if the image doesn't exist on the flash.
|
||||||
|
except OSError:
|
||||||
|
print("Error: PNG File missing. Copy the PNG file from the example folder to your Pico using Thonny and run the example again.")
|
||||||
|
|
||||||
|
display.update()
|
||||||
|
|
||||||
|
# We're not doing anything else with the display now but we want to keep the program running!
|
||||||
|
while True:
|
||||||
|
pass
|
|
@ -0,0 +1,66 @@
|
||||||
|
from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY_2, PEN_P8
|
||||||
|
import pngdec
|
||||||
|
|
||||||
|
# Create a PicoGraphics instance
|
||||||
|
display = PicoGraphics(display=DISPLAY_PICO_DISPLAY_2, pen_type=PEN_P8, rotate=270)
|
||||||
|
|
||||||
|
# Get the display width/height so we can position the PNGs
|
||||||
|
width, height = display.get_bounds()
|
||||||
|
|
||||||
|
# Set the backlight so we can see it!
|
||||||
|
display.set_backlight(1.0)
|
||||||
|
|
||||||
|
# Create an instance of the PNG Decoder
|
||||||
|
png = pngdec.PNG(display)
|
||||||
|
|
||||||
|
# Create some pens for use later.
|
||||||
|
BG = display.create_pen(200, 200, 200)
|
||||||
|
|
||||||
|
# 16 Reds
|
||||||
|
for i in range(16):
|
||||||
|
display.create_pen(i * 16, 0, 0)
|
||||||
|
|
||||||
|
# 16 Greens
|
||||||
|
for i in range(16):
|
||||||
|
display.create_pen(0, i * 16, 0)
|
||||||
|
|
||||||
|
# 16 Blues
|
||||||
|
for i in range(16):
|
||||||
|
display.create_pen(0, 0, i * 16)
|
||||||
|
|
||||||
|
# Adding in an white background colour at the beginning of each offset.
|
||||||
|
for i in range(3):
|
||||||
|
display.update_pen(i * 16, 200, 200, 200)
|
||||||
|
|
||||||
|
# Clear the screen
|
||||||
|
display.set_pen(BG)
|
||||||
|
display.clear()
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Open our PNG File from flash. In this example we're using an image of a cartoon pencil.
|
||||||
|
# You can use Thonny to transfer PNG Images to your Pico.
|
||||||
|
png.open_file("pencil_gray.png")
|
||||||
|
|
||||||
|
# Horizontally/vertically center the three PNG Images.
|
||||||
|
png_w = png.get_width()
|
||||||
|
png_h = png.get_height()
|
||||||
|
|
||||||
|
offset_x = (width - png_w * 2) // 2
|
||||||
|
height_y = (height // 3)
|
||||||
|
offset_y = (height_y - png_h * 2) // 2
|
||||||
|
|
||||||
|
# Decode our PNG file and set the X and Y
|
||||||
|
png.decode(offset_x, offset_y, scale=2, mode=pngdec.PNG_COPY, palette_offset=0)
|
||||||
|
png.decode(offset_x, offset_y + height_y, scale=2, mode=pngdec.PNG_COPY, palette_offset=16)
|
||||||
|
png.decode(offset_x, offset_y + (height_y * 2), scale=2, mode=pngdec.PNG_COPY, palette_offset=32)
|
||||||
|
|
||||||
|
# Handle the error if the image doesn't exist on the flash.
|
||||||
|
except OSError:
|
||||||
|
print("Error: PNG File missing. Copy the PNG file from the example folder to your Pico using Thonny and run the example again.")
|
||||||
|
|
||||||
|
display.update()
|
||||||
|
|
||||||
|
# We're not doing anything else with the display now but we want to keep the program running!
|
||||||
|
while True:
|
||||||
|
pass
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 1.1 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 497 B |
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,276 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Christopher Parrott for Pimoroni Ltd
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import os
|
||||||
|
import math
|
||||||
|
import struct
|
||||||
|
from machine import I2S, Pin
|
||||||
|
|
||||||
|
"""
|
||||||
|
A class for playing Wav files out of an I2S audio amp. It can also play pure tones.
|
||||||
|
This code is based heavily on the work of Mike Teachman, at:
|
||||||
|
https://github.com/miketeachman/micropython-i2s-examples/blob/master/examples/wavplayer.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class WavPlayer:
|
||||||
|
# Internal states
|
||||||
|
PLAY = 0
|
||||||
|
PAUSE = 1
|
||||||
|
FLUSH = 2
|
||||||
|
STOP = 3
|
||||||
|
NONE = 4
|
||||||
|
|
||||||
|
MODE_WAV = 0
|
||||||
|
MODE_TONE = 1
|
||||||
|
|
||||||
|
# Default buffer length
|
||||||
|
SILENCE_BUFFER_LENGTH = 1000
|
||||||
|
WAV_BUFFER_LENGTH = 10000
|
||||||
|
INTERNAL_BUFFER_LENGTH = 20000
|
||||||
|
|
||||||
|
TONE_SAMPLE_RATE = 44_100
|
||||||
|
TONE_BITS_PER_SAMPLE = 16
|
||||||
|
TONE_FULL_WAVES = 2
|
||||||
|
|
||||||
|
def __init__(self, id, sck_pin, ws_pin, sd_pin, amp_enable=None, ibuf_len=INTERNAL_BUFFER_LENGTH, root="/"):
|
||||||
|
self.__id = id
|
||||||
|
self.__sck_pin = sck_pin
|
||||||
|
self.__ws_pin = ws_pin
|
||||||
|
self.__sd_pin = sd_pin
|
||||||
|
self.__ibuf_len = ibuf_len
|
||||||
|
self.__enable = None
|
||||||
|
|
||||||
|
if amp_enable is not None:
|
||||||
|
self.__enable = Pin(amp_enable, Pin.OUT)
|
||||||
|
|
||||||
|
# Set the directory to search for files in
|
||||||
|
self.set_root(root)
|
||||||
|
|
||||||
|
self.__state = WavPlayer.NONE
|
||||||
|
self.__mode = WavPlayer.MODE_WAV
|
||||||
|
self.__wav_file = None
|
||||||
|
self.__loop_wav = False
|
||||||
|
self.__first_sample_offset = None
|
||||||
|
self.__flush_count = 0
|
||||||
|
self.__audio_out = None
|
||||||
|
|
||||||
|
# Allocate a small array of blank audio samples used for silence
|
||||||
|
self.__silence_samples = bytearray(self.SILENCE_BUFFER_LENGTH)
|
||||||
|
|
||||||
|
# Allocate a larger array for WAV audio samples, using a memoryview for more efficient access
|
||||||
|
self.__wav_samples_mv = memoryview(bytearray(self.WAV_BUFFER_LENGTH))
|
||||||
|
|
||||||
|
# Reserve a variable for audio samples used for tones
|
||||||
|
self.__tone_samples = None
|
||||||
|
self.__queued_samples = None
|
||||||
|
|
||||||
|
def set_root(self, root):
|
||||||
|
self.__root = root.rstrip("/") + "/"
|
||||||
|
|
||||||
|
def play_wav(self, wav_file, loop=False):
|
||||||
|
if os.listdir(self.__root).count(wav_file) == 0:
|
||||||
|
raise ValueError(f"'{wav_file}' not found")
|
||||||
|
|
||||||
|
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
|
||||||
|
|
||||||
|
self.__wav_file = open(self.__root + wav_file, "rb") # Open the chosen WAV file in read-only, binary mode
|
||||||
|
self.__loop_wav = loop # Record if the user wants the file to loop
|
||||||
|
|
||||||
|
# Parse the WAV file, returning the necessary parameters to initialise I2S communication
|
||||||
|
format, sample_rate, bits_per_sample, self.__first_sample_offset, self.sample_size = WavPlayer.__parse_wav(self.__wav_file)
|
||||||
|
|
||||||
|
# Keep a track of total bytes read from WAV File
|
||||||
|
self.total_bytes_read = 0
|
||||||
|
|
||||||
|
self.__wav_file.seek(self.__first_sample_offset) # Advance to first byte of sample data
|
||||||
|
|
||||||
|
self.__start_i2s(bits=bits_per_sample,
|
||||||
|
format=format,
|
||||||
|
rate=sample_rate,
|
||||||
|
state=WavPlayer.PLAY,
|
||||||
|
mode=WavPlayer.MODE_WAV)
|
||||||
|
|
||||||
|
def play_tone(self, frequency, amplitude):
|
||||||
|
if frequency < 20.0 or frequency > 20_000:
|
||||||
|
raise ValueError("frequency out of range. Expected between 20Hz and 20KHz")
|
||||||
|
|
||||||
|
if amplitude < 0.0 or amplitude > 1.0:
|
||||||
|
raise ValueError("amplitude out of range. Expected 0.0 to 1.0")
|
||||||
|
|
||||||
|
# Create a buffer containing the pure tone samples
|
||||||
|
samples_per_cycle = self.TONE_SAMPLE_RATE // frequency
|
||||||
|
sample_size_in_bytes = self.TONE_BITS_PER_SAMPLE // 8
|
||||||
|
samples = bytearray(self.TONE_FULL_WAVES * samples_per_cycle * sample_size_in_bytes)
|
||||||
|
range = pow(2, self.TONE_BITS_PER_SAMPLE) // 2
|
||||||
|
|
||||||
|
format = "<h" if self.TONE_BITS_PER_SAMPLE == 16 else "<l"
|
||||||
|
|
||||||
|
# Populate the buffer with multiple cycles to avoid it completing too quickly and causing drop outs
|
||||||
|
for i in range(samples_per_cycle * self.TONE_FULL_WAVES):
|
||||||
|
sample = int((range - 1) * (math.sin(2 * math.pi * i / samples_per_cycle)) * amplitude)
|
||||||
|
struct.pack_into(format, samples, i * sample_size_in_bytes, sample)
|
||||||
|
|
||||||
|
# Are we not already playing tones?
|
||||||
|
if not (self.__mode == WavPlayer.MODE_TONE and (self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE)):
|
||||||
|
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
|
||||||
|
self.__tone_samples = samples
|
||||||
|
self.__start_i2s(bits=self.TONE_BITS_PER_SAMPLE,
|
||||||
|
format=I2S.MONO,
|
||||||
|
rate=self.TONE_SAMPLE_RATE,
|
||||||
|
state=WavPlayer.PLAY,
|
||||||
|
mode=WavPlayer.MODE_TONE)
|
||||||
|
else:
|
||||||
|
self.__queued_samples = samples
|
||||||
|
self.__state = WavPlayer.PLAY
|
||||||
|
|
||||||
|
def pause(self):
|
||||||
|
if self.__state == WavPlayer.PLAY:
|
||||||
|
self.__state = WavPlayer.PAUSE # Enter the pause state on the next callback
|
||||||
|
|
||||||
|
def resume(self):
|
||||||
|
if self.__state == WavPlayer.PAUSE:
|
||||||
|
self.__state = WavPlayer.PLAY # Enter the play state on the next callback
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
if self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE:
|
||||||
|
if self.__mode == WavPlayer.MODE_WAV:
|
||||||
|
# Enter the flush state on the next callback and close the file
|
||||||
|
# It is done in this order to prevent the callback entering the play
|
||||||
|
# state after we close the file but before we change the state)
|
||||||
|
self.__state = WavPlayer.FLUSH
|
||||||
|
self.__wav_file.close()
|
||||||
|
else:
|
||||||
|
self.__state = WavPlayer.STOP
|
||||||
|
|
||||||
|
def is_playing(self):
|
||||||
|
return self.__state != WavPlayer.NONE and self.__state != WavPlayer.STOP
|
||||||
|
|
||||||
|
def is_paused(self):
|
||||||
|
return self.__state == WavPlayer.PAUSE
|
||||||
|
|
||||||
|
def __start_i2s(self, bits=16, format=I2S.MONO, rate=44_100, state=STOP, mode=MODE_WAV):
|
||||||
|
import gc
|
||||||
|
gc.collect()
|
||||||
|
self.__audio_out = I2S(
|
||||||
|
self.__id,
|
||||||
|
sck=self.__sck_pin,
|
||||||
|
ws=self.__ws_pin,
|
||||||
|
sd=self.__sd_pin,
|
||||||
|
mode=I2S.TX,
|
||||||
|
bits=bits,
|
||||||
|
format=format,
|
||||||
|
rate=rate,
|
||||||
|
ibuf=self.__ibuf_len,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.__state = state
|
||||||
|
self.__mode = mode
|
||||||
|
self.__flush_count = self.__ibuf_len // self.SILENCE_BUFFER_LENGTH + 1
|
||||||
|
self.__audio_out.irq(self.__i2s_callback)
|
||||||
|
self.__audio_out.write(self.__silence_samples)
|
||||||
|
|
||||||
|
if self.__enable is not None:
|
||||||
|
self.__enable.on()
|
||||||
|
|
||||||
|
def __stop_i2s(self):
|
||||||
|
self.stop() # Stop any active playback
|
||||||
|
while self.is_playing(): # and wait for it to complete
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self.__enable is not None:
|
||||||
|
self.__enable.off()
|
||||||
|
|
||||||
|
if self.__audio_out is not None:
|
||||||
|
self.__audio_out.deinit() # Deinit any active I2S comms
|
||||||
|
|
||||||
|
self.__state == WavPlayer.NONE # Return to the none state
|
||||||
|
|
||||||
|
def __i2s_callback(self, arg):
|
||||||
|
# PLAY
|
||||||
|
if self.__state == WavPlayer.PLAY:
|
||||||
|
if self.__mode == WavPlayer.MODE_WAV:
|
||||||
|
num_read = self.__wav_file.readinto(self.__wav_samples_mv) # Read the next section of the WAV file
|
||||||
|
self.total_bytes_read += num_read
|
||||||
|
# Have we reached the end of the file?
|
||||||
|
if num_read == 0:
|
||||||
|
# Do we want to loop the WAV playback?
|
||||||
|
if self.__loop_wav:
|
||||||
|
_ = self.__wav_file.seek(self.__first_sample_offset) # Play again, so advance to first byte of sample data
|
||||||
|
else:
|
||||||
|
self.__wav_file.close() # Stop playing, so close the file
|
||||||
|
self.__state = WavPlayer.FLUSH # and enter the flush state on the next callback
|
||||||
|
|
||||||
|
self.__audio_out.write(self.__silence_samples) # In both cases play silence to end this callback
|
||||||
|
else:
|
||||||
|
if num_read > 0 and num_read < self.WAV_BUFFER_LENGTH:
|
||||||
|
num_read = num_read - (self.total_bytes_read - self.sample_size)
|
||||||
|
self.__audio_out.write(self.__wav_samples_mv[: num_read]) # We are within the file, so write out the next audio samples
|
||||||
|
else:
|
||||||
|
if self.__queued_samples is not None:
|
||||||
|
self.__tone_samples = self.__queued_samples
|
||||||
|
self.__queued_samples = None
|
||||||
|
self.__audio_out.write(self.__tone_samples)
|
||||||
|
|
||||||
|
# PAUSE or STOP
|
||||||
|
elif self.__state == WavPlayer.PAUSE or self.__state == WavPlayer.STOP:
|
||||||
|
self.__audio_out.write(self.__silence_samples) # Play silence
|
||||||
|
|
||||||
|
# FLUSH
|
||||||
|
elif self.__state == WavPlayer.FLUSH:
|
||||||
|
# Flush is used to allow the residual audio samples in the internal buffer to be written
|
||||||
|
# to the I2S peripheral. This step avoids part of the sound file from being cut off
|
||||||
|
if self.__flush_count > 0:
|
||||||
|
self.__flush_count -= 1
|
||||||
|
else:
|
||||||
|
self.__state = WavPlayer.STOP # Enter the stop state on the next callback
|
||||||
|
self.__audio_out.write(self.__silence_samples) # Play silence
|
||||||
|
|
||||||
|
# NONE
|
||||||
|
elif self.__state == WavPlayer.NONE:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __parse_wav(wav_file):
|
||||||
|
chunk_ID = wav_file.read(4)
|
||||||
|
if chunk_ID != b"RIFF":
|
||||||
|
raise ValueError("WAV chunk ID invalid")
|
||||||
|
_ = wav_file.read(4) # chunk_size
|
||||||
|
format = wav_file.read(4)
|
||||||
|
if format != b"WAVE":
|
||||||
|
raise ValueError("WAV format invalid")
|
||||||
|
sub_chunk1_ID = wav_file.read(4)
|
||||||
|
if sub_chunk1_ID != b"fmt ":
|
||||||
|
raise ValueError("WAV sub chunk 1 ID invalid")
|
||||||
|
_ = wav_file.read(4) # sub_chunk1_size
|
||||||
|
_ = struct.unpack("<H", wav_file.read(2))[0] # audio_format
|
||||||
|
num_channels = struct.unpack("<H", wav_file.read(2))[0]
|
||||||
|
|
||||||
|
if num_channels == 1:
|
||||||
|
format = I2S.MONO
|
||||||
|
else:
|
||||||
|
format = I2S.STEREO
|
||||||
|
|
||||||
|
sample_rate = struct.unpack("<I", wav_file.read(4))[0]
|
||||||
|
# if sample_rate != 44_100 and sample_rate != 48_000:
|
||||||
|
# raise ValueError(f"WAV sample rate of {sample_rate} invalid. Only 44.1KHz or 48KHz audio are supported")
|
||||||
|
|
||||||
|
_ = struct.unpack("<I", wav_file.read(4))[0] # byte_rate
|
||||||
|
_ = struct.unpack("<H", wav_file.read(2))[0] # block_align
|
||||||
|
bits_per_sample = struct.unpack("<H", wav_file.read(2))[0]
|
||||||
|
|
||||||
|
# usually the sub chunk2 ID ("data") comes next, but
|
||||||
|
# some online MP3->WAV converters add
|
||||||
|
# binary data before "data". So, read a fairly large
|
||||||
|
# block of bytes and search for "data".
|
||||||
|
|
||||||
|
binary_block = wav_file.read(200)
|
||||||
|
offset = binary_block.find(b"data")
|
||||||
|
if offset == -1:
|
||||||
|
raise ValueError("WAV sub chunk 2 ID not found")
|
||||||
|
|
||||||
|
wav_file.seek(40)
|
||||||
|
sub_chunk2_size = struct.unpack("<I", wav_file.read(4))[0]
|
||||||
|
|
||||||
|
return (format, sample_rate, bits_per_sample, 44 + offset, sub_chunk2_size)
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,93 @@
|
||||||
|
# Countdown Timer with Alarm
|
||||||
|
# i2s Audio Example
|
||||||
|
# Use VOL +/- to increase/decrease the amount of time
|
||||||
|
# Use the Sleep ZZZ button to start the countdown
|
||||||
|
|
||||||
|
from machine import Timer
|
||||||
|
from audio import WavPlayer
|
||||||
|
from stellar import StellarUnicorn
|
||||||
|
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||||
|
import time
|
||||||
|
|
||||||
|
su = StellarUnicorn()
|
||||||
|
graphics = PicoGraphics(DISPLAY)
|
||||||
|
|
||||||
|
graphics.set_font("bitmap6")
|
||||||
|
WHITE = graphics.create_pen(255, 255, 255)
|
||||||
|
BLUE = graphics.create_pen(0, 0, 255)
|
||||||
|
CLEAR = graphics.create_pen(0, 0, 0)
|
||||||
|
RED = graphics.create_pen(255, 0, 0)
|
||||||
|
GREEN = graphics.create_pen(0, 255, 0)
|
||||||
|
su.set_brightness(0.5)
|
||||||
|
|
||||||
|
audio = WavPlayer(0, 10, 11, 9, amp_enable=22)
|
||||||
|
|
||||||
|
|
||||||
|
class Countdown(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.timer_running = False
|
||||||
|
self.total_seconds = 0
|
||||||
|
self.timer = None
|
||||||
|
|
||||||
|
def process_input(self):
|
||||||
|
if su.is_pressed(StellarUnicorn.SWITCH_VOLUME_UP):
|
||||||
|
self.total_seconds += 1
|
||||||
|
if su.is_pressed(StellarUnicorn.SWITCH_VOLUME_DOWN):
|
||||||
|
if self.total_seconds > 0:
|
||||||
|
self.total_seconds -= 1
|
||||||
|
if su.is_pressed(StellarUnicorn.SWITCH_SLEEP):
|
||||||
|
self.start_timer()
|
||||||
|
|
||||||
|
def display_time(self):
|
||||||
|
seconds = self.total_seconds % (24 * 3600)
|
||||||
|
seconds %= 3600
|
||||||
|
minutes = seconds // 60
|
||||||
|
seconds %= 60
|
||||||
|
|
||||||
|
# Add leading zeros to the minutes and seconds
|
||||||
|
if len(str(minutes)) == 1:
|
||||||
|
minutes = "0{}".format(minutes)
|
||||||
|
if len(str(seconds)) == 1:
|
||||||
|
seconds = "0{}".format(seconds)
|
||||||
|
|
||||||
|
return "{}\n{}".format(minutes, seconds)
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
||||||
|
graphics.clear()
|
||||||
|
|
||||||
|
graphics.set_pen(RED)
|
||||||
|
graphics.rectangle(0, 0, 16, 16)
|
||||||
|
|
||||||
|
graphics.set_pen(CLEAR)
|
||||||
|
graphics.rectangle(2, 2, StellarUnicorn.WIDTH - 4, 12)
|
||||||
|
|
||||||
|
graphics.set_pen(WHITE)
|
||||||
|
graphics.text(self.display_time(), 2, 1, -1, 1)
|
||||||
|
su.update(graphics)
|
||||||
|
|
||||||
|
def start_timer(self):
|
||||||
|
if not self.timer_running:
|
||||||
|
self.timer = Timer(mode=Timer.PERIODIC, period=1000, callback=self.countdown)
|
||||||
|
self.timer_running = True
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
time.sleep(0.2)
|
||||||
|
self.timer.deinit()
|
||||||
|
self.timer_running = False
|
||||||
|
|
||||||
|
def countdown(self, arg):
|
||||||
|
|
||||||
|
if self.total_seconds == 0:
|
||||||
|
audio.play_wav("doorbell.wav", False)
|
||||||
|
self.reset()
|
||||||
|
else:
|
||||||
|
self.total_seconds -= 1
|
||||||
|
|
||||||
|
|
||||||
|
count = Countdown()
|
||||||
|
|
||||||
|
while 1:
|
||||||
|
count.process_input()
|
||||||
|
count.draw()
|
||||||
|
time.sleep(0.07)
|
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,86 @@
|
||||||
|
# Example Menu with Sound
|
||||||
|
# i2s Audio Example
|
||||||
|
# Use Brightness +/- to move up and down
|
||||||
|
# Press Sleep to play the selected sound
|
||||||
|
|
||||||
|
from audio import WavPlayer
|
||||||
|
from stellar import StellarUnicorn
|
||||||
|
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
su = StellarUnicorn()
|
||||||
|
graphics = PicoGraphics(DISPLAY)
|
||||||
|
|
||||||
|
audio = WavPlayer(0, 10, 11, 9, amp_enable=22)
|
||||||
|
|
||||||
|
WHITE = graphics.create_pen(255, 255, 255)
|
||||||
|
RED = graphics.create_pen(255, 0, 0)
|
||||||
|
GREEN = graphics.create_pen(0, 200, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class Menu(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.items = ["1", "2", "3"]
|
||||||
|
self.selected = 0
|
||||||
|
|
||||||
|
# A function to draw only the menu elements.
|
||||||
|
# Helps to keep our main function tidy!
|
||||||
|
def draw_menu(self):
|
||||||
|
graphics.set_pen(GREEN)
|
||||||
|
|
||||||
|
graphics.circle(0, 0, 3)
|
||||||
|
graphics.circle(16, 5, 4)
|
||||||
|
graphics.circle(3, 16, 3)
|
||||||
|
|
||||||
|
graphics.set_pen(WHITE)
|
||||||
|
|
||||||
|
for item in range(len(self.items)):
|
||||||
|
if self.selected == item:
|
||||||
|
graphics.set_pen(WHITE)
|
||||||
|
graphics.text(self.items[self.selected], 6, 4, 31, 1)
|
||||||
|
|
||||||
|
# Make changes based on the currently selected menu item
|
||||||
|
def process_selected(self):
|
||||||
|
if self.selected == 0:
|
||||||
|
audio.play_wav("Pew1.wav", False)
|
||||||
|
|
||||||
|
if self.selected == 1:
|
||||||
|
audio.play_wav("Pew2.wav", False)
|
||||||
|
|
||||||
|
if self.selected == 2:
|
||||||
|
audio.play_wav("Pew3.wav", False)
|
||||||
|
|
||||||
|
|
||||||
|
menu = Menu()
|
||||||
|
|
||||||
|
graphics.set_font("bitmap6")
|
||||||
|
|
||||||
|
su.set_brightness(0.7)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
||||||
|
graphics.clear()
|
||||||
|
|
||||||
|
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||||
|
audio.play_wav("buttonbeep.wav", False)
|
||||||
|
if menu.selected + 1 < len(menu.items):
|
||||||
|
menu.selected += 1
|
||||||
|
else:
|
||||||
|
menu.selected = 0
|
||||||
|
|
||||||
|
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||||
|
audio.play_wav("buttonbeep.wav", False)
|
||||||
|
if menu.selected > 0:
|
||||||
|
menu.selected -= 1
|
||||||
|
else:
|
||||||
|
menu.selected = len(menu.items) - 1
|
||||||
|
|
||||||
|
if su.is_pressed(StellarUnicorn.SWITCH_SLEEP):
|
||||||
|
menu.process_selected()
|
||||||
|
|
||||||
|
menu.draw_menu()
|
||||||
|
|
||||||
|
su.update(graphics)
|
||||||
|
sleep(0.2)
|
|
@ -0,0 +1,8 @@
|
||||||
|
from audio import WavPlayer
|
||||||
|
|
||||||
|
sound = WavPlayer(0, 10, 11, 9, amp_enable=22)
|
||||||
|
|
||||||
|
sound.play_wav("beepboop.wav", False)
|
||||||
|
|
||||||
|
while sound.is_playing():
|
||||||
|
pass
|
|
@ -0,0 +1,36 @@
|
||||||
|
from picographics import PicoGraphics, DISPLAY_TUFTY_2040
|
||||||
|
import pngdec
|
||||||
|
|
||||||
|
display = PicoGraphics(display=DISPLAY_TUFTY_2040)
|
||||||
|
|
||||||
|
# Create an instance of the PNG Decoder
|
||||||
|
png = pngdec.PNG(display)
|
||||||
|
|
||||||
|
# Create some pens for use later.
|
||||||
|
BG = display.create_pen(200, 200, 200)
|
||||||
|
TEXT = display.create_pen(0, 0, 0)
|
||||||
|
|
||||||
|
# Clear the screen
|
||||||
|
display.set_pen(BG)
|
||||||
|
display.clear()
|
||||||
|
|
||||||
|
display.set_pen(TEXT)
|
||||||
|
display.text("PNG Pencil", 105, 80)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Open our PNG File from flash. In this example we're using an image of a cartoon pencil.
|
||||||
|
# You can use Thonny to transfer PNG Images to your Pico.
|
||||||
|
png.open_file("pencil.png")
|
||||||
|
|
||||||
|
# Decode our PNG file and set the X and Y
|
||||||
|
png.decode(140, 100)
|
||||||
|
|
||||||
|
# Handle the error if the image doesn't exist on the flash.
|
||||||
|
except OSError:
|
||||||
|
print("Error: PNG File missing. Copy the PNG file from the example folder to your Pico using Thonny and run the example again.")
|
||||||
|
|
||||||
|
display.update()
|
||||||
|
|
||||||
|
# We're not doing anything else with the display now but we want to keep the program running!
|
||||||
|
while True:
|
||||||
|
pass
|
|
@ -0,0 +1,54 @@
|
||||||
|
from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_P8
|
||||||
|
import pngdec
|
||||||
|
|
||||||
|
display = PicoGraphics(display=DISPLAY_TUFTY_2040, pen_type=PEN_P8)
|
||||||
|
|
||||||
|
# Create an instance of the PNG Decoder
|
||||||
|
png = pngdec.PNG(display)
|
||||||
|
|
||||||
|
# Create some pens for use later.
|
||||||
|
BG = display.create_pen(200, 200, 200)
|
||||||
|
TEXT = display.create_pen(0, 0, 0)
|
||||||
|
|
||||||
|
# 16 Reds
|
||||||
|
for i in range(15):
|
||||||
|
display.create_pen(i * 16, 0, 0)
|
||||||
|
|
||||||
|
# 16 Greens
|
||||||
|
for i in range(16):
|
||||||
|
display.create_pen(0, i * 16, 0)
|
||||||
|
|
||||||
|
# 16 Blues
|
||||||
|
for i in range(15):
|
||||||
|
display.create_pen(0, 0, i * 16)
|
||||||
|
|
||||||
|
# Adding in an white background colour at the beginning of each offset.
|
||||||
|
for i in range(3):
|
||||||
|
display.update_pen(i * 16, 200, 200, 200)
|
||||||
|
|
||||||
|
# Clear the screen
|
||||||
|
display.set_pen(BG)
|
||||||
|
display.clear()
|
||||||
|
|
||||||
|
display.set_pen(TEXT)
|
||||||
|
display.text("PNG Pencil \n& Offset Palette", 125, 115)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Open our PNG File from flash. In this example we're using an image of a cartoon pencil.
|
||||||
|
# You can use Thonny to transfer PNG Images to your Pico.
|
||||||
|
png.open_file("pencil_gray.png")
|
||||||
|
|
||||||
|
# Decode our PNG file and set the X and Y
|
||||||
|
png.decode(35, 10, scale=2, mode=pngdec.PNG_COPY, palette_offset=0)
|
||||||
|
png.decode(35, 90, scale=2, mode=pngdec.PNG_COPY, palette_offset=16)
|
||||||
|
png.decode(35, 170, scale=2, mode=pngdec.PNG_COPY, palette_offset=32)
|
||||||
|
|
||||||
|
# Handle the error if the image doesn't exist on the flash.
|
||||||
|
except OSError:
|
||||||
|
print("Error: PNG File missing. Copy the PNG file from the example folder to your Pico using Thonny and run the example again.")
|
||||||
|
|
||||||
|
display.update()
|
||||||
|
|
||||||
|
# We're not doing anything else with the display now but we want to keep the program running!
|
||||||
|
while True:
|
||||||
|
pass
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 1.1 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 497 B |
|
@ -1,9 +1,9 @@
|
||||||
diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt
|
diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt
|
||||||
index 281b0c3bc..7e04bb549 100644
|
index fcc435b7b..efabcb3a3 100644
|
||||||
--- a/ports/rp2/CMakeLists.txt
|
--- a/ports/rp2/CMakeLists.txt
|
||||||
+++ b/ports/rp2/CMakeLists.txt
|
+++ b/ports/rp2/CMakeLists.txt
|
||||||
@@ -453,6 +453,16 @@ target_link_options(${MICROPY_TARGET} PRIVATE
|
@@ -464,6 +464,16 @@ set_source_files_properties(
|
||||||
-Wl,--wrap=dcd_event_handler
|
COMPILE_OPTIONS "-O2"
|
||||||
)
|
)
|
||||||
|
|
||||||
+# Do not include stack unwinding & exception handling for C++ user modules
|
+# Do not include stack unwinding & exception handling for C++ user modules
|
||||||
|
|
|
@ -5,13 +5,13 @@ MP_DEFINE_CONST_FUN_OBJ_1(adcfft_update_obj, adcfft_update);
|
||||||
MP_DEFINE_CONST_FUN_OBJ_3(adcfft_get_scaled_obj, adcfft_get_scaled);
|
MP_DEFINE_CONST_FUN_OBJ_3(adcfft_get_scaled_obj, adcfft_get_scaled);
|
||||||
MP_DEFINE_CONST_FUN_OBJ_1(adcfft__del__obj, adcfft__del__);
|
MP_DEFINE_CONST_FUN_OBJ_1(adcfft__del__obj, adcfft__del__);
|
||||||
|
|
||||||
STATIC const mp_rom_map_elem_t adcfft_locals_dict_table[] = {
|
static const mp_rom_map_elem_t adcfft_locals_dict_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&adcfft__del__obj) },
|
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&adcfft__del__obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&adcfft_update_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&adcfft_update_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_get_scaled), MP_ROM_PTR(&adcfft_get_scaled_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_get_scaled), MP_ROM_PTR(&adcfft_get_scaled_obj) },
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC MP_DEFINE_CONST_DICT(adcfft_locals_dict, adcfft_locals_dict_table);
|
static MP_DEFINE_CONST_DICT(adcfft_locals_dict, adcfft_locals_dict_table);
|
||||||
|
|
||||||
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
||||||
MP_DEFINE_CONST_OBJ_TYPE(
|
MP_DEFINE_CONST_OBJ_TYPE(
|
||||||
|
@ -31,11 +31,11 @@ const mp_obj_type_t adcfft_type = {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Module
|
// Module
|
||||||
STATIC const mp_map_elem_t adcfft_globals_table[] = {
|
static const mp_map_elem_t adcfft_globals_table[] = {
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_adcfft) }, // Module name
|
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_adcfft) }, // Module name
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ADCFFT), (mp_obj_t)&adcfft_type }, // Class name & type
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_ADCFFT), (mp_obj_t)&adcfft_type }, // Class name & type
|
||||||
};
|
};
|
||||||
STATIC MP_DEFINE_CONST_DICT(mp_module_adcfft_globals, adcfft_globals_table);
|
static MP_DEFINE_CONST_DICT(mp_module_adcfft_globals, adcfft_globals_table);
|
||||||
|
|
||||||
|
|
||||||
const mp_obj_module_t adcfft_user_cmodule = {
|
const mp_obj_module_t adcfft_user_cmodule = {
|
||||||
|
|
|
@ -21,8 +21,7 @@ mp_obj_t adcfft_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
|
||||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||||
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||||
|
|
||||||
adcfft_obj_t *self = m_new_obj_with_finaliser(adcfft_obj_t);
|
adcfft_obj_t *self = mp_obj_malloc_with_finaliser(adcfft_obj_t, &adcfft_type);
|
||||||
self->base.type = &adcfft_type;
|
|
||||||
|
|
||||||
unsigned int adc_channel = args[ARG_adc_channel].u_int;
|
unsigned int adc_channel = args[ARG_adc_channel].u_int;
|
||||||
unsigned int adc_gpio = args[ARG_adc_gpio].u_int;
|
unsigned int adc_gpio = args[ARG_adc_gpio].u_int;
|
||||||
|
|
|
@ -38,7 +38,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(Badger2040_woken_by_button_obj, Badger2040_woken_by_bu
|
||||||
MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_system_speed_obj, Badger2040_system_speed);
|
MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_system_speed_obj, Badger2040_system_speed);
|
||||||
|
|
||||||
/***** Binding of Methods *****/
|
/***** Binding of Methods *****/
|
||||||
STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = {
|
static const mp_rom_map_elem_t Badger2040_locals_dict_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&Badger2040___del___obj) },
|
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&Badger2040___del___obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_is_busy), MP_ROM_PTR(&Badger2040_is_busy_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_is_busy), MP_ROM_PTR(&Badger2040_is_busy_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_update_speed), MP_ROM_PTR(&Badger2040_update_speed_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_update_speed), MP_ROM_PTR(&Badger2040_update_speed_obj) },
|
||||||
|
@ -72,7 +72,7 @@ STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR_command), MP_ROM_PTR(&Badger2040_command_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_command), MP_ROM_PTR(&Badger2040_command_obj) },
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC MP_DEFINE_CONST_DICT(Badger2040_locals_dict, Badger2040_locals_dict_table);
|
static MP_DEFINE_CONST_DICT(Badger2040_locals_dict, Badger2040_locals_dict_table);
|
||||||
|
|
||||||
/***** Class Definition *****/
|
/***** Class Definition *****/
|
||||||
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
||||||
|
@ -94,7 +94,7 @@ const mp_obj_type_t Badger2040_type = {
|
||||||
|
|
||||||
/***** Globals Table *****/
|
/***** Globals Table *****/
|
||||||
|
|
||||||
STATIC const mp_rom_map_elem_t badger2040_globals_table[] = {
|
static const mp_rom_map_elem_t badger2040_globals_table[] = {
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_badger2040) },
|
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_badger2040) },
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_Badger2040), (mp_obj_t)&Badger2040_type },
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_Badger2040), (mp_obj_t)&Badger2040_type },
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ STATIC const mp_rom_map_elem_t badger2040_globals_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR_PIN_BATTERY), MP_ROM_INT(29) },
|
{ MP_ROM_QSTR(MP_QSTR_PIN_BATTERY), MP_ROM_INT(29) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_PIN_ENABLE_3V3), MP_ROM_INT(10) },
|
{ MP_ROM_QSTR(MP_QSTR_PIN_ENABLE_3V3), MP_ROM_INT(10) },
|
||||||
};
|
};
|
||||||
STATIC MP_DEFINE_CONST_DICT(mp_module_badger2040_globals, badger2040_globals_table);
|
static MP_DEFINE_CONST_DICT(mp_module_badger2040_globals, badger2040_globals_table);
|
||||||
|
|
||||||
/***** Module Definition *****/
|
/***** Module Definition *****/
|
||||||
const mp_obj_module_t badger2040_user_cmodule = {
|
const mp_obj_module_t badger2040_user_cmodule = {
|
||||||
|
|
|
@ -98,8 +98,7 @@ mp_obj_t Badger2040_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
|
||||||
buffer = m_new(uint8_t, width * height / 8);
|
buffer = m_new(uint8_t, width * height / 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
badger2040_obj = m_new_obj_with_finaliser(_Badger2040_obj_t);
|
badger2040_obj = mp_obj_malloc_with_finaliser(_Badger2040_obj_t, &Badger2040_type);
|
||||||
badger2040_obj->base.type = &Badger2040_type;
|
|
||||||
badger2040_obj->buf = buffer;
|
badger2040_obj->buf = buffer;
|
||||||
badger2040_obj->badger2040 = m_new_class(pimoroni::Badger2040, buffer);
|
badger2040_obj->badger2040 = m_new_class(pimoroni::Badger2040, buffer);
|
||||||
badger2040_obj->badger2040->init();
|
badger2040_obj->badger2040->init();
|
||||||
|
|
|
@ -19,7 +19,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(BreakoutAS7262_set_integration_time_obj, BreakoutAS726
|
||||||
MP_DEFINE_CONST_FUN_OBJ_3(BreakoutAS7262_set_leds_obj, BreakoutAS7262_set_leds);
|
MP_DEFINE_CONST_FUN_OBJ_3(BreakoutAS7262_set_leds_obj, BreakoutAS7262_set_leds);
|
||||||
|
|
||||||
/***** Binding of Methods *****/
|
/***** Binding of Methods *****/
|
||||||
STATIC const mp_rom_map_elem_t BreakoutAS7262_locals_dict_table[] = {
|
static const mp_rom_map_elem_t BreakoutAS7262_locals_dict_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&BreakoutAS7262_reset_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&BreakoutAS7262_reset_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_device_type), MP_ROM_PTR(&BreakoutAS7262_device_type_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_device_type), MP_ROM_PTR(&BreakoutAS7262_device_type_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_hardware_version), MP_ROM_PTR(&BreakoutAS7262_hardware_version_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_hardware_version), MP_ROM_PTR(&BreakoutAS7262_hardware_version_obj) },
|
||||||
|
@ -54,7 +54,7 @@ STATIC const mp_rom_map_elem_t BreakoutAS7262_locals_dict_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR_CONT_ROYGBR), MP_ROM_INT(MP_CONT_ROYGBR) },
|
{ MP_ROM_QSTR(MP_QSTR_CONT_ROYGBR), MP_ROM_INT(MP_CONT_ROYGBR) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_ONESHOT), MP_ROM_INT(MP_ONESHOT) },
|
{ MP_ROM_QSTR(MP_QSTR_ONESHOT), MP_ROM_INT(MP_ONESHOT) },
|
||||||
};
|
};
|
||||||
STATIC MP_DEFINE_CONST_DICT(BreakoutAS7262_locals_dict, BreakoutAS7262_locals_dict_table);
|
static MP_DEFINE_CONST_DICT(BreakoutAS7262_locals_dict, BreakoutAS7262_locals_dict_table);
|
||||||
|
|
||||||
/***** Class Definition *****/
|
/***** Class Definition *****/
|
||||||
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
||||||
|
@ -80,11 +80,11 @@ const mp_obj_type_t breakout_as7262_BreakoutAS7262_type = {
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/***** Globals Table *****/
|
/***** Globals Table *****/
|
||||||
STATIC const mp_map_elem_t breakout_as7262_globals_table[] = {
|
static const mp_map_elem_t breakout_as7262_globals_table[] = {
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_as7262) },
|
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_as7262) },
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutAS7262), (mp_obj_t)&breakout_as7262_BreakoutAS7262_type },
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutAS7262), (mp_obj_t)&breakout_as7262_BreakoutAS7262_type },
|
||||||
};
|
};
|
||||||
STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_as7262_globals, breakout_as7262_globals_table);
|
static MP_DEFINE_CONST_DICT(mp_module_breakout_as7262_globals, breakout_as7262_globals_table);
|
||||||
|
|
||||||
/***** Module Definition *****/
|
/***** Module Definition *****/
|
||||||
const mp_obj_module_t breakout_as7262_user_cmodule = {
|
const mp_obj_module_t breakout_as7262_user_cmodule = {
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
#include "breakout_as7343.h"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// BreakoutAS7343 Class
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/***** Methods *****/
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7343_reset_obj, BreakoutAS7343_reset);
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7343_version_obj, BreakoutAS7343_version);
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7343_read_obj, BreakoutAS7343_read);
|
||||||
|
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(BreakoutAS7343_set_gain_obj, BreakoutAS7343_set_gain);
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(BreakoutAS7343_set_integration_time_obj, BreakoutAS7343_set_integration_time);
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(BreakoutAS7343_set_measurement_time_obj, BreakoutAS7343_set_measurement_time);
|
||||||
|
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(BreakoutAS7343_set_illumination_current_obj, BreakoutAS7343_set_illumination_current);
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(BreakoutAS7343_set_illumination_led_obj, BreakoutAS7343_set_illumination_led);
|
||||||
|
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(BreakoutAS7343_set_channels_obj, BreakoutAS7343_set_channels);
|
||||||
|
|
||||||
|
/***** Binding of Methods *****/
|
||||||
|
static const mp_rom_map_elem_t BreakoutAS7343_locals_dict_table[] = {
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&BreakoutAS7343_reset_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_version), MP_ROM_PTR(&BreakoutAS7343_version_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutAS7343_read_obj) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_set_gain), MP_ROM_PTR(&BreakoutAS7343_set_gain_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_set_measurement_time), MP_ROM_PTR(&BreakoutAS7343_set_measurement_time_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_set_integration_time), MP_ROM_PTR(&BreakoutAS7343_set_integration_time_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_set_illumination_current), MP_ROM_PTR(&BreakoutAS7343_set_illumination_current_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_set_illumination_led), MP_ROM_PTR(&BreakoutAS7343_set_illumination_led_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_set_channels), MP_ROM_PTR(&BreakoutAS7343_set_channels_obj) },
|
||||||
|
};
|
||||||
|
static MP_DEFINE_CONST_DICT(BreakoutAS7343_locals_dict, BreakoutAS7343_locals_dict_table);
|
||||||
|
|
||||||
|
/***** Class Definition *****/
|
||||||
|
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
||||||
|
MP_DEFINE_CONST_OBJ_TYPE(
|
||||||
|
breakout_as7343_BreakoutAS7343_type,
|
||||||
|
MP_QSTR_BreakoutAS7343,
|
||||||
|
MP_TYPE_FLAG_NONE,
|
||||||
|
make_new, BreakoutAS7343_make_new,
|
||||||
|
locals_dict, (mp_obj_dict_t*)&BreakoutAS7343_locals_dict
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
const mp_obj_type_t breakout_as7343_BreakoutAS7343_type = {
|
||||||
|
{ &mp_type_type },
|
||||||
|
.name = MP_QSTR_BreakoutAS7343,
|
||||||
|
.make_new = BreakoutAS7343_make_new,
|
||||||
|
.locals_dict = (mp_obj_dict_t*)&BreakoutAS7343_locals_dict,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// breakout_as7343 Module
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/***** Globals Table *****/
|
||||||
|
static const mp_map_elem_t breakout_as7343_globals_table[] = {
|
||||||
|
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_as7343) },
|
||||||
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutAS7343), (mp_obj_t)&breakout_as7343_BreakoutAS7343_type },
|
||||||
|
};
|
||||||
|
static MP_DEFINE_CONST_DICT(mp_module_breakout_as7343_globals, breakout_as7343_globals_table);
|
||||||
|
|
||||||
|
/***** Module Definition *****/
|
||||||
|
const mp_obj_module_t breakout_as7343_user_cmodule = {
|
||||||
|
.base = { &mp_type_module },
|
||||||
|
.globals = (mp_obj_dict_t*)&mp_module_breakout_as7343_globals,
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
#if MICROPY_VERSION <= 70144
|
||||||
|
MP_REGISTER_MODULE(MP_QSTR_breakout_as7343, breakout_as7343_user_cmodule, MODULE_BREAKOUT_AS7343_ENABLED);
|
||||||
|
#else
|
||||||
|
MP_REGISTER_MODULE(MP_QSTR_breakout_as7343, breakout_as7343_user_cmodule);
|
||||||
|
#endif
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
@ -0,0 +1,140 @@
|
||||||
|
#include "drivers/as7343/as7343.hpp"
|
||||||
|
#include "common/pimoroni_i2c.hpp"
|
||||||
|
#include "micropython/modules/util.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
using namespace pimoroni;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "breakout_as7343.h"
|
||||||
|
#include "pimoroni_i2c.h"
|
||||||
|
|
||||||
|
/***** Variables Struct *****/
|
||||||
|
typedef struct _breakout_as7343_BreakoutAS7343_obj_t {
|
||||||
|
mp_obj_base_t base;
|
||||||
|
AS7343 *breakout;
|
||||||
|
_PimoroniI2C_obj_t *i2c;
|
||||||
|
} breakout_as7343_BreakoutAS7343_obj_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***** Constructor *****/
|
||||||
|
mp_obj_t BreakoutAS7343_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||||
|
breakout_as7343_BreakoutAS7343_obj_t *self = nullptr;
|
||||||
|
|
||||||
|
enum { ARG_i2c, ARG_int };
|
||||||
|
static const mp_arg_t allowed_args[] = {
|
||||||
|
{ MP_QSTR_i2c, MP_ARG_OBJ, {.u_obj = nullptr} },
|
||||||
|
{ MP_QSTR_interrupt, MP_ARG_INT, {.u_int = PIN_UNUSED} },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse args.
|
||||||
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||||
|
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||||
|
|
||||||
|
self = m_new_obj(breakout_as7343_BreakoutAS7343_obj_t);
|
||||||
|
self->base.type = &breakout_as7343_BreakoutAS7343_type;
|
||||||
|
|
||||||
|
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
|
||||||
|
|
||||||
|
self->breakout = m_new_class(AS7343, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_int].u_int);
|
||||||
|
|
||||||
|
if(!self->breakout->init()) {
|
||||||
|
mp_raise_msg(&mp_type_RuntimeError, "BreakoutAS7343: breakout not found when initialising");
|
||||||
|
}
|
||||||
|
|
||||||
|
return MP_OBJ_FROM_PTR(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Methods *****/
|
||||||
|
mp_obj_t BreakoutAS7343_reset(mp_obj_t self_in) {
|
||||||
|
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
|
||||||
|
self->breakout->reset();
|
||||||
|
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t BreakoutAS7343_version(mp_obj_t self_in) {
|
||||||
|
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
|
||||||
|
|
||||||
|
uint8_t aux_id, revision_id, hardware_id;
|
||||||
|
self->breakout->get_version(aux_id, revision_id, hardware_id);
|
||||||
|
|
||||||
|
mp_obj_t tuple[3];
|
||||||
|
tuple[0] = mp_obj_new_int(aux_id);
|
||||||
|
tuple[1] = mp_obj_new_int(revision_id);
|
||||||
|
tuple[2] = mp_obj_new_int(hardware_id);
|
||||||
|
return mp_obj_new_tuple(3, tuple);
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t BreakoutAS7343_read(mp_obj_t self_in) {
|
||||||
|
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
|
||||||
|
AS7343::reading reading = self->breakout->read();
|
||||||
|
|
||||||
|
mp_obj_t tuple[12];
|
||||||
|
tuple[0] = mp_obj_new_float(reading.FZ);
|
||||||
|
tuple[1] = mp_obj_new_float(reading.FY);
|
||||||
|
tuple[2] = mp_obj_new_float(reading.FXL);
|
||||||
|
tuple[3] = mp_obj_new_float(reading.NIR);
|
||||||
|
|
||||||
|
tuple[4] = mp_obj_new_float(reading.F2);
|
||||||
|
tuple[5] = mp_obj_new_float(reading.F3);
|
||||||
|
tuple[6] = mp_obj_new_float(reading.F4);
|
||||||
|
tuple[7] = mp_obj_new_float(reading.F6);
|
||||||
|
|
||||||
|
tuple[8] = mp_obj_new_float(reading.F1);
|
||||||
|
tuple[9] = mp_obj_new_float(reading.F5);
|
||||||
|
tuple[10] = mp_obj_new_float(reading.F7);
|
||||||
|
tuple[11] = mp_obj_new_float(reading.F8);
|
||||||
|
|
||||||
|
return mp_obj_new_tuple(12, tuple);
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t BreakoutAS7343_set_gain(mp_obj_t self_in, mp_obj_t value_in) {
|
||||||
|
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
|
||||||
|
|
||||||
|
self->breakout->set_gain(mp_obj_get_int(value_in));
|
||||||
|
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t BreakoutAS7343_set_measurement_time(mp_obj_t self_in, mp_obj_t value_in) {
|
||||||
|
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
|
||||||
|
|
||||||
|
self->breakout->set_measurement_time(mp_obj_get_float(value_in));
|
||||||
|
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t BreakoutAS7343_set_integration_time(mp_obj_t self_in, mp_obj_t value_in) {
|
||||||
|
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
|
||||||
|
|
||||||
|
self->breakout->set_integration_time(mp_obj_get_float(value_in));
|
||||||
|
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t BreakoutAS7343_set_illumination_current(mp_obj_t self_in, mp_obj_t value_in) {
|
||||||
|
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
|
||||||
|
|
||||||
|
self->breakout->set_illumination_current(mp_obj_get_int(value_in));
|
||||||
|
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t BreakoutAS7343_set_illumination_led(mp_obj_t self_in, mp_obj_t value_in) {
|
||||||
|
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
|
||||||
|
|
||||||
|
self->breakout->set_illumination_led(value_in == mp_const_true);
|
||||||
|
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t BreakoutAS7343_set_channels(mp_obj_t self_in, mp_obj_t value_in) {
|
||||||
|
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
|
||||||
|
|
||||||
|
self->breakout->set_channels((AS7343::channel_count)mp_obj_get_int(value_in));
|
||||||
|
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Include MicroPython API.
|
||||||
|
#include "py/runtime.h"
|
||||||
|
|
||||||
|
/***** Constants *****/
|
||||||
|
|
||||||
|
|
||||||
|
/***** Extern of Class Definition *****/
|
||||||
|
extern const mp_obj_type_t breakout_as7343_BreakoutAS7343_type;
|
||||||
|
|
||||||
|
/***** Extern of Class Methods *****/
|
||||||
|
extern mp_obj_t BreakoutAS7343_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
|
||||||
|
extern mp_obj_t BreakoutAS7343_reset(mp_obj_t self_in);
|
||||||
|
extern mp_obj_t BreakoutAS7343_version(mp_obj_t self_in);
|
||||||
|
extern mp_obj_t BreakoutAS7343_read(mp_obj_t self_in);
|
||||||
|
|
||||||
|
extern mp_obj_t BreakoutAS7343_set_gain(mp_obj_t self_in, mp_obj_t value_in);
|
||||||
|
extern mp_obj_t BreakoutAS7343_set_measurement_time(mp_obj_t self_in, mp_obj_t value_in);
|
||||||
|
extern mp_obj_t BreakoutAS7343_set_integration_time(mp_obj_t self_in, mp_obj_t value_in);
|
||||||
|
|
||||||
|
extern mp_obj_t BreakoutAS7343_set_illumination_current(mp_obj_t self_in, mp_obj_t value_in);
|
||||||
|
extern mp_obj_t BreakoutAS7343_set_illumination_led(mp_obj_t self_in, mp_obj_t value_in);
|
||||||
|
|
||||||
|
extern mp_obj_t BreakoutAS7343_set_channels(mp_obj_t self_in, mp_obj_t value_in);
|
|
@ -0,0 +1,19 @@
|
||||||
|
set(MOD_NAME breakout_as7343)
|
||||||
|
string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER)
|
||||||
|
add_library(usermod_${MOD_NAME} INTERFACE)
|
||||||
|
|
||||||
|
target_sources(usermod_${MOD_NAME} INTERFACE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/as7343/as7343.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(usermod_${MOD_NAME} INTERFACE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(usermod_${MOD_NAME} INTERFACE
|
||||||
|
-DMODULE_${MOD_NAME_UPPER}_ENABLED=1
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(usermod INTERFACE usermod_${MOD_NAME})
|
|
@ -15,7 +15,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutBH1745_threshold_obj, 3, BreakoutBH1745_thres
|
||||||
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutBH1745_measurement_time_ms_obj, 2, BreakoutBH1745_measurement_time_ms);
|
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutBH1745_measurement_time_ms_obj, 2, BreakoutBH1745_measurement_time_ms);
|
||||||
|
|
||||||
/***** Binding of Methods *****/
|
/***** Binding of Methods *****/
|
||||||
STATIC const mp_rom_map_elem_t BreakoutBH1745_locals_dict_table[] = {
|
static const mp_rom_map_elem_t BreakoutBH1745_locals_dict_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR_chip_id), MP_ROM_PTR(&BreakoutBH1745_chip_id_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_chip_id), MP_ROM_PTR(&BreakoutBH1745_chip_id_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_manufacturer_id), MP_ROM_PTR(&BreakoutBH1745_manufacturer_id_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_manufacturer_id), MP_ROM_PTR(&BreakoutBH1745_manufacturer_id_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_rgbc_raw), MP_ROM_PTR(&BreakoutBH1745_rgbc_raw_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_rgbc_raw), MP_ROM_PTR(&BreakoutBH1745_rgbc_raw_obj) },
|
||||||
|
@ -25,7 +25,7 @@ STATIC const mp_rom_map_elem_t BreakoutBH1745_locals_dict_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR_measurement_time_ms), MP_ROM_PTR(&BreakoutBH1745_measurement_time_ms_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_measurement_time_ms), MP_ROM_PTR(&BreakoutBH1745_measurement_time_ms_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_leds), MP_ROM_PTR(&BreakoutBH1745_leds_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_leds), MP_ROM_PTR(&BreakoutBH1745_leds_obj) },
|
||||||
};
|
};
|
||||||
STATIC MP_DEFINE_CONST_DICT(BreakoutBH1745_locals_dict, BreakoutBH1745_locals_dict_table);
|
static MP_DEFINE_CONST_DICT(BreakoutBH1745_locals_dict, BreakoutBH1745_locals_dict_table);
|
||||||
|
|
||||||
/***** Class Definition *****/
|
/***** Class Definition *****/
|
||||||
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
||||||
|
@ -51,14 +51,14 @@ const mp_obj_type_t breakout_bh1745_BreakoutBH1745_type = {
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/***** Globals Table *****/
|
/***** Globals Table *****/
|
||||||
STATIC const mp_map_elem_t breakout_bh1745_globals_table[] = {
|
static const mp_map_elem_t breakout_bh1745_globals_table[] = {
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_bh1745) },
|
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_bh1745) },
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutBH1745), (mp_obj_t)&breakout_bh1745_BreakoutBH1745_type },
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutBH1745), (mp_obj_t)&breakout_bh1745_BreakoutBH1745_type },
|
||||||
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_I2C_ADDRESS_DEFAULT), MP_ROM_INT(BH1745_I2C_ADDRESS_DEFAULT) },
|
{ MP_ROM_QSTR(MP_QSTR_I2C_ADDRESS_DEFAULT), MP_ROM_INT(BH1745_I2C_ADDRESS_DEFAULT) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_I2C_ADDRESS_ALT), MP_ROM_INT(BH1745_I2C_ADDRESS_ALTERNATE) },
|
{ MP_ROM_QSTR(MP_QSTR_I2C_ADDRESS_ALT), MP_ROM_INT(BH1745_I2C_ADDRESS_ALTERNATE) },
|
||||||
};
|
};
|
||||||
STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_bh1745_globals, breakout_bh1745_globals_table);
|
static MP_DEFINE_CONST_DICT(mp_module_breakout_bh1745_globals, breakout_bh1745_globals_table);
|
||||||
|
|
||||||
/***** Module Definition *****/
|
/***** Module Definition *****/
|
||||||
const mp_obj_module_t breakout_bh1745_user_cmodule = {
|
const mp_obj_module_t breakout_bh1745_user_cmodule = {
|
||||||
|
|
|
@ -9,11 +9,11 @@ MP_DEFINE_CONST_FUN_OBJ_1(BreakoutBME280_read_obj, BreakoutBME280_read);
|
||||||
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutBME280_configure_obj, 1, BreakoutBME280_configure);
|
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutBME280_configure_obj, 1, BreakoutBME280_configure);
|
||||||
|
|
||||||
/***** Binding of Methods *****/
|
/***** Binding of Methods *****/
|
||||||
STATIC const mp_rom_map_elem_t BreakoutBME280_locals_dict_table[] = {
|
static const mp_rom_map_elem_t BreakoutBME280_locals_dict_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutBME280_read_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutBME280_read_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_configure), MP_ROM_PTR(&BreakoutBME280_configure_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_configure), MP_ROM_PTR(&BreakoutBME280_configure_obj) },
|
||||||
};
|
};
|
||||||
STATIC MP_DEFINE_CONST_DICT(BreakoutBME280_locals_dict, BreakoutBME280_locals_dict_table);
|
static MP_DEFINE_CONST_DICT(BreakoutBME280_locals_dict, BreakoutBME280_locals_dict_table);
|
||||||
|
|
||||||
/***** Class Definition *****/
|
/***** Class Definition *****/
|
||||||
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
||||||
|
@ -39,7 +39,7 @@ const mp_obj_type_t breakout_bme280_BreakoutBME280_type = {
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/***** Globals Table *****/
|
/***** Globals Table *****/
|
||||||
STATIC const mp_map_elem_t breakout_bme280_globals_table[] = {
|
static const mp_map_elem_t breakout_bme280_globals_table[] = {
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_bme280) },
|
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_bme280) },
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutBME280), (mp_obj_t)&breakout_bme280_BreakoutBME280_type },
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutBME280), (mp_obj_t)&breakout_bme280_BreakoutBME280_type },
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ STATIC const mp_map_elem_t breakout_bme280_globals_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_10_MS), MP_ROM_INT(BME280_STANDBY_TIME_10_MS) },
|
{ MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_10_MS), MP_ROM_INT(BME280_STANDBY_TIME_10_MS) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_20_MS), MP_ROM_INT(BME280_STANDBY_TIME_20_MS) },
|
{ MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_20_MS), MP_ROM_INT(BME280_STANDBY_TIME_20_MS) },
|
||||||
};
|
};
|
||||||
STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_bme280_globals, breakout_bme280_globals_table);
|
static MP_DEFINE_CONST_DICT(mp_module_breakout_bme280_globals, breakout_bme280_globals_table);
|
||||||
|
|
||||||
/***** Module Definition *****/
|
/***** Module Definition *****/
|
||||||
const mp_obj_module_t breakout_bme280_user_cmodule = {
|
const mp_obj_module_t breakout_bme280_user_cmodule = {
|
||||||
|
|
|
@ -9,11 +9,11 @@ MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutBME68X_read_obj, 1, BreakoutBME68X_read);
|
||||||
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutBME68X_configure_obj, 1, BreakoutBME68X_configure);
|
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutBME68X_configure_obj, 1, BreakoutBME68X_configure);
|
||||||
|
|
||||||
/***** Binding of Methods *****/
|
/***** Binding of Methods *****/
|
||||||
STATIC const mp_rom_map_elem_t BreakoutBME68X_locals_dict_table[] = {
|
static const mp_rom_map_elem_t BreakoutBME68X_locals_dict_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutBME68X_read_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutBME68X_read_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_configure), MP_ROM_PTR(&BreakoutBME68X_configure_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_configure), MP_ROM_PTR(&BreakoutBME68X_configure_obj) },
|
||||||
};
|
};
|
||||||
STATIC MP_DEFINE_CONST_DICT(BreakoutBME68X_locals_dict, BreakoutBME68X_locals_dict_table);
|
static MP_DEFINE_CONST_DICT(BreakoutBME68X_locals_dict, BreakoutBME68X_locals_dict_table);
|
||||||
|
|
||||||
/***** Class Definition *****/
|
/***** Class Definition *****/
|
||||||
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
||||||
|
@ -38,7 +38,7 @@ const mp_obj_type_t breakout_bme68x_BreakoutBME68X_type = {
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/***** Globals Table *****/
|
/***** Globals Table *****/
|
||||||
STATIC const mp_map_elem_t breakout_bme68x_globals_table[] = {
|
static const mp_map_elem_t breakout_bme68x_globals_table[] = {
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_bme68x) },
|
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_bme68x) },
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutBME68X), (mp_obj_t)&breakout_bme68x_BreakoutBME68X_type },
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutBME68X), (mp_obj_t)&breakout_bme68x_BreakoutBME68X_type },
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ STATIC const mp_map_elem_t breakout_bme68x_globals_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR_I2C_ADDRESS_DEFAULT), MP_ROM_INT(BME68X_I2C_ADDR_LOW) },
|
{ MP_ROM_QSTR(MP_QSTR_I2C_ADDRESS_DEFAULT), MP_ROM_INT(BME68X_I2C_ADDR_LOW) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_I2C_ADDRESS_ALT), MP_ROM_INT(BME68X_I2C_ADDR_HIGH) },
|
{ MP_ROM_QSTR(MP_QSTR_I2C_ADDRESS_ALT), MP_ROM_INT(BME68X_I2C_ADDR_HIGH) },
|
||||||
};
|
};
|
||||||
STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_bme68x_globals, breakout_bme68x_globals_table);
|
static MP_DEFINE_CONST_DICT(mp_module_breakout_bme68x_globals, breakout_bme68x_globals_table);
|
||||||
|
|
||||||
/***** Module Definition *****/
|
/***** Module Definition *****/
|
||||||
const mp_obj_module_t breakout_bme68x_user_cmodule = {
|
const mp_obj_module_t breakout_bme68x_user_cmodule = {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Ładowanie…
Reference in New Issue