kopia lustrzana https://github.com/pimoroni/pimoroni-pico
Porównaj commity
99 Commity
Autor | SHA1 | Data |
---|---|---|
Hel Gibbons | f18f1ba259 | |
John Bintz | f495fb8b70 | |
Philip Howard | 7a2ebe5c0d | |
Phil Howard | 48f2446f6c | |
Phil Howard | 6eef96d9c9 | |
Philip Howard | 9771274e90 | |
Phil Howard | 7c05770a44 | |
Philip Howard | 3440ab232c | |
Phil Howard | 7d0bb04d6d | |
Phil Howard | 392890e99b | |
Phil Howard | 7b7d9fe383 | |
Phil Howard | ccd00f6289 | |
Philip Howard | 10fed7edaf | |
Phil Howard | 517f8ae1cd | |
thirdr | 5b6903ce72 | |
Phil Howard | 84cabe077d | |
Phil Howard | 64e26dc6f4 | |
Phil Howard | 47e3aed88f | |
Philip Howard | bf6fa4bc45 | |
Philip Howard | 0b133c750e | |
Philip Howard | 5691573f89 | |
Philip Howard | b41bb4abf7 | |
Philip Howard | 8b928d8891 | |
Philip Howard | 56e6d66c9b | |
thirdr | ffa12c2165 | |
thirdr | b9f3990995 | |
thirdr | cca4f3f7f1 | |
Philip Howard | 981a38b989 | |
Philip Howard | 25786def35 | |
thirdr | a387e45098 | |
thirdr | 452d700ba1 | |
Hel Gibbons | 9d7d651565 | |
Scott Dutton | b3a0c4fb9d | |
thirdr | f962c3cd3c | |
thirdr | b5a040f6cc | |
thirdr | d790d3d344 | |
thirdr | ba7b8388bf | |
thirdr | 829f688285 | |
thirdr | 8fb17a3c26 | |
thirdr | 4db7cc61ab | |
thirdr | c510a3c875 | |
thirdr | 425f132096 | |
thirdr | 76683acb0d | |
thirdr | 7c287192de | |
thirdr | f15c657b44 | |
Philip Howard | f3b53b6b5f | |
coadkins | 37c4d22527 | |
Philip Howard | 616b1cc8d6 | |
Phil Howard | 45a9925072 | |
thirdr | 3998b0c8bf | |
Connor Linfoot | 32c10482d9 | |
Phil Howard | 6ed743b32c | |
Phil Howard | 17d59f4729 | |
Philip Howard | 4c44b77193 | |
Phil Howard | 5510c82564 | |
Philip Howard | 3a10b29f54 | |
Phil Howard | 8cf276b992 | |
Phil Howard | 8bb17087d9 | |
Phil Howard | 6fcbaf5616 | |
Phil Howard | 6803f00b31 | |
Phil Howard | 6e71a62c65 | |
Philip Howard | f1ea35fbbf | |
Philip Howard | c066325ca0 | |
Philip Howard | fd4eb165f8 | |
Phil Howard | 8fc8a8ee06 | |
Phil Howard | 3bfb548686 | |
Philip Howard | 9edcdcc126 | |
Philip Howard | e8e550b18b | |
thirdr | cdb7b4bf2c | |
Philip Howard | 4fc3095433 | |
Phil Howard | 9c5b529754 | |
ZodiusInfuser | a87d5581aa | |
ZodiusInfuser | 44d7875f7e | |
ZodiusInfuser | a90c31fb3b | |
ZodiusInfuser | 458b0ac209 | |
Phil Howard | a537672dd4 | |
Phil Howard | d34e692f51 | |
Phil Howard | 27b913124c | |
Phil Howard | c7b788cd1d | |
Philip Howard | c386b3e9cf | |
thirdr | b499296867 | |
thirdr | 193adaca72 | |
Philip Howard | a7a2e2bee0 | |
thirdr | b0babcfe9f | |
Phil Howard | 19fa8864cf | |
thirdr | e34b2420c6 | |
Phil Howard | 964cf5eedf | |
Phil Howard | eab1595352 | |
Phil Howard | 5dd76ed31b | |
Philip Howard | 6eb0f90e53 | |
Phil Howard | b0d53dadb3 | |
Phil Howard | ad518064e9 | |
Philip Howard | d83107474e | |
Phil Howard | c4f70df1cf | |
Phil Howard | 10221066dd | |
Philip Howard | ab64fcaccc | |
Hel Gibbons | 32c63c343d | |
Hel Gibbons | 8d964bce2c | |
Skyler Mansfield | b23a71b889 |
|
@ -25,7 +25,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Compiler Cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /home/runner/.ccache
|
||||
key: ccache-cmake-${{github.ref}}-${{matrix.board}}-${{github.sha}}
|
||||
|
@ -34,13 +34,13 @@ jobs:
|
|||
ccache-cmake-${{github.ref}}
|
||||
ccache-cmake
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
# Check out the Pico SDK
|
||||
- name: Checkout Pico SDK
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: raspberrypi/pico-sdk
|
||||
path: pico-sdk
|
||||
|
@ -48,7 +48,7 @@ jobs:
|
|||
|
||||
# Check out the Pico Extras
|
||||
- name: Checkout Pico Extras
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: raspberrypi/pico-extras
|
||||
path: pico-extras
|
||||
|
|
|
@ -7,20 +7,23 @@ on:
|
|||
types: [created]
|
||||
|
||||
env:
|
||||
MICROPYTHON_VERSION: v1.22.1
|
||||
MICROPYTHON_VERSION: v1.23.0
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.name }} (${{ matrix.board }})
|
||||
runs-on: ubuntu-20.04
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: pico
|
||||
board: RPI_PICO
|
||||
- name: pico_usb
|
||||
board: RPI_PICO_USB
|
||||
- name: picow
|
||||
board: RPI_PICO_W
|
||||
- name: tiny2040
|
||||
- name: tiny2040_8mb
|
||||
board: PIMORONI_TINY2040
|
||||
- name: picolipo_4mb
|
||||
board: PIMORONI_PICOLIPO_4MB
|
||||
|
@ -41,7 +44,7 @@ jobs:
|
|||
|
||||
env:
|
||||
# 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 }}"
|
||||
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"
|
||||
|
@ -52,7 +55,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Compiler Cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /home/runner/.ccache
|
||||
key: ccache-micropython-${{ matrix.name }}-${{ github.ref }}-${{ github.sha }}
|
||||
|
@ -80,6 +83,13 @@ jobs:
|
|||
source $BUILD_TOOLS
|
||||
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
|
||||
run: |
|
||||
source $BUILD_TOOLS
|
||||
|
@ -110,8 +120,13 @@ jobs:
|
|||
source $BUILD_TOOLS
|
||||
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
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.RELEASE_FILE }}.uf2
|
||||
path: build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2
|
||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
|||
name: Python Linting
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Python Deps
|
||||
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)
|
||||
|
||||
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++
|
||||
|
||||
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 {
|
||||
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
|
||||
git checkout $MICROPYTHON_VERSION
|
||||
git cherry-pick -n 932f76c6ba64c5a3e68de3324556d9979f09303b
|
||||
git submodule update --init lib/pico-sdk
|
||||
git submodule update --init lib/cyw43-driver
|
||||
git submodule update --init lib/lwip
|
||||
|
@ -72,4 +74,4 @@ function cmake_build {
|
|||
ccache --show-stats || true
|
||||
cd build-$BOARD_NAME
|
||||
cp firmware.uf2 $RELEASE_FILE.uf2
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,15 @@ namespace pimoroni {
|
|||
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 value;
|
||||
i2c_write_blocking(i2c, address, ®, 1, false);
|
||||
|
|
|
@ -63,6 +63,7 @@ namespace pimoroni {
|
|||
i2c_inst_t* pin_to_inst(uint pin);
|
||||
|
||||
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);
|
||||
uint16_t reg_read_uint16(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(sdcard)
|
||||
add_subdirectory(as7262)
|
||||
add_subdirectory(as7343)
|
||||
add_subdirectory(bh1745)
|
||||
add_subdirectory(bme68x)
|
||||
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;
|
||||
}
|
|
@ -113,12 +113,6 @@ void Hub75::FM6126A_setup() {
|
|||
|
||||
void Hub75::start(irq_handler_t handler) {
|
||||
if(handler) {
|
||||
dma_channel = 0;
|
||||
|
||||
// Try as I might, I can't seem to coax MicroPython into leaving PIO in a known state upon soft reset
|
||||
// check for claimed PIO and prepare a clean slate.
|
||||
stop(handler);
|
||||
|
||||
if (panel_type == PANEL_FM6126A) {
|
||||
FM6126A_setup();
|
||||
}
|
||||
|
@ -139,7 +133,7 @@ void Hub75::start(irq_handler_t handler) {
|
|||
// Prevent flicker in Python caused by the smaller dataset just blasting through the PIO too quickly
|
||||
pio_sm_set_clkdiv(pio, sm_data, width <= 32 ? 2.0f : 1.0f);
|
||||
|
||||
dma_channel_claim(dma_channel);
|
||||
dma_channel = dma_claim_unused_channel(true);
|
||||
dma_channel_config config = dma_channel_get_default_config(dma_channel);
|
||||
channel_config_set_transfer_data_size(&config, DMA_SIZE_32);
|
||||
channel_config_set_bswap(&config, false);
|
||||
|
@ -148,15 +142,13 @@ void Hub75::start(irq_handler_t handler) {
|
|||
|
||||
|
||||
// Same handler for both DMA channels
|
||||
irq_set_exclusive_handler(DMA_IRQ_0, handler);
|
||||
irq_set_exclusive_handler(DMA_IRQ_1, handler);
|
||||
irq_add_shared_handler(DMA_IRQ_0, handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
|
||||
|
||||
dma_channel_set_irq0_enabled(dma_channel, true);
|
||||
|
||||
irq_set_enabled(pio_get_dreq(pio, sm_data, true), true);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
|
||||
|
||||
row = 0;
|
||||
bit = 0;
|
||||
|
||||
|
@ -169,10 +161,9 @@ void Hub75::start(irq_handler_t handler) {
|
|||
void Hub75::stop(irq_handler_t handler) {
|
||||
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
irq_set_enabled(DMA_IRQ_1, false);
|
||||
irq_set_enabled(pio_get_dreq(pio, sm_data, true), false);
|
||||
|
||||
if(dma_channel_is_claimed(dma_channel)) {
|
||||
if(dma_channel != -1 && dma_channel_is_claimed(dma_channel)) {
|
||||
dma_channel_set_irq0_enabled(dma_channel, false);
|
||||
irq_remove_handler(DMA_IRQ_0, handler);
|
||||
//dma_channel_wait_for_finish_blocking(dma_channel);
|
||||
|
@ -184,17 +175,21 @@ void Hub75::stop(irq_handler_t handler) {
|
|||
if(pio_sm_is_claimed(pio, sm_data)) {
|
||||
pio_sm_set_enabled(pio, sm_data, false);
|
||||
pio_sm_drain_tx_fifo(pio, sm_data);
|
||||
pio_remove_program(pio, &hub75_data_rgb888_program, data_prog_offs);
|
||||
pio_sm_unclaim(pio, sm_data);
|
||||
}
|
||||
|
||||
if(pio_sm_is_claimed(pio, sm_row)) {
|
||||
pio_sm_set_enabled(pio, sm_row, false);
|
||||
pio_sm_drain_tx_fifo(pio, sm_row);
|
||||
if (inverted_stb) {
|
||||
pio_remove_program(pio, &hub75_row_inverted_program, row_prog_offs);
|
||||
} else {
|
||||
pio_remove_program(pio, &hub75_row_program, row_prog_offs);
|
||||
}
|
||||
pio_sm_unclaim(pio, sm_row);
|
||||
}
|
||||
|
||||
pio_clear_instruction_memory(pio);
|
||||
|
||||
// Make sure the GPIO is in a known good state
|
||||
// since we don't know what the PIO might have done with it
|
||||
gpio_put_masked(0b111111 << pin_r0, 0);
|
||||
|
|
|
@ -73,7 +73,7 @@ class Hub75 {
|
|||
Pixel background = 0;
|
||||
|
||||
// DMA & PIO
|
||||
uint dma_channel = 0;
|
||||
int dma_channel = -1;
|
||||
uint bit = 0;
|
||||
uint row = 0;
|
||||
|
||||
|
|
|
@ -47,8 +47,9 @@ namespace pimoroni {
|
|||
return !(sr.read() & 128);
|
||||
}
|
||||
|
||||
void Inky73::busy_wait() {
|
||||
while(is_busy()) {
|
||||
void Inky73::busy_wait(uint timeout_ms) {
|
||||
absolute_time_t timeout = make_timeout_time_ms(timeout_ms);
|
||||
while(is_busy() && !time_reached(timeout)) {
|
||||
tight_loop_contents();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace pimoroni {
|
|||
// Methods
|
||||
//--------------------------------------------------
|
||||
public:
|
||||
void busy_wait();
|
||||
void busy_wait(uint timeout_ms=45000);
|
||||
void reset();
|
||||
void power_off();
|
||||
|
||||
|
|
|
@ -133,8 +133,6 @@ namespace pimoroni {
|
|||
|
||||
void ST7789::configure_display(Rotation rotate) {
|
||||
|
||||
bool rotate180 = rotate == ROTATE_180 || rotate == ROTATE_90;
|
||||
|
||||
if(rotate == ROTATE_90 || rotate == ROTATE_270) {
|
||||
std::swap(width, height);
|
||||
}
|
||||
|
@ -185,20 +183,30 @@ namespace pimoroni {
|
|||
// Pico Display
|
||||
if(width == 240 && height == 135) {
|
||||
caset[0] = 40; // 240 cols
|
||||
caset[1] = 279;
|
||||
raset[0] = 53; // 135 rows
|
||||
raset[1] = 187;
|
||||
madctl = rotate180 ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
|
||||
caset[1] = 40 + width - 1;
|
||||
raset[0] = 52; // 135 rows
|
||||
raset[1] = 52 + height - 1;
|
||||
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;
|
||||
}
|
||||
|
||||
// Pico Display at 90 degree rotation
|
||||
if(width == 135 && height == 240) {
|
||||
caset[0] = 52; // 135 cols
|
||||
caset[1] = 186;
|
||||
caset[1] = 52 + width - 1;
|
||||
raset[0] = 40; // 240 rows
|
||||
raset[1] = 279;
|
||||
madctl = rotate180 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
|
||||
raset[1] = 40 + height - 1;
|
||||
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
|
||||
|
@ -207,7 +215,7 @@ namespace pimoroni {
|
|||
caset[1] = 319;
|
||||
raset[0] = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -217,7 +225,7 @@ namespace pimoroni {
|
|||
caset[1] = 239;
|
||||
raset[0] = 0;
|
||||
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
|
||||
|
|
|
@ -19,6 +19,7 @@ add_subdirectory(breakout_bme688)
|
|||
add_subdirectory(breakout_bmp280)
|
||||
add_subdirectory(breakout_bme280)
|
||||
add_subdirectory(breakout_as7262)
|
||||
add_subdirectory(as7343)
|
||||
add_subdirectory(breakout_bh1745)
|
||||
add_subdirectory(breakout_icp10125)
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ int main() {
|
|||
// }
|
||||
// }
|
||||
|
||||
graphics.set_pen(1);
|
||||
graphics.set_pen(15);
|
||||
graphics.clear();
|
||||
|
||||
float s = (sin(i / 10.0f) * 1.0f) + 1.5f;
|
||||
|
|
|
@ -45,7 +45,7 @@ int main() {
|
|||
|
||||
while(true) {
|
||||
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
|
||||
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);
|
||||
|
||||
Button button_a(Tufty2040::A);
|
||||
Button button_b(Tufty2040::B);
|
||||
Button button_c(Tufty2040::C);
|
||||
Button button_up(Tufty2040::UP);
|
||||
Button button_down(Tufty2040::DOWN);
|
||||
Button button_a(Tufty2040::A, Polarity::ACTIVE_HIGH);
|
||||
Button button_b(Tufty2040::B, Polarity::ACTIVE_HIGH);
|
||||
Button button_c(Tufty2040::C, Polarity::ACTIVE_HIGH);
|
||||
Button button_up(Tufty2040::UP, Polarity::ACTIVE_HIGH);
|
||||
Button button_down(Tufty2040::DOWN, Polarity::ACTIVE_HIGH);
|
||||
|
||||
uint32_t 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 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_OX = 13;
|
||||
static const uint8_t MICS_OX = 12;
|
||||
static const uint8_t MICS_HEATER_EN = 1;
|
||||
|
||||
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) {
|
||||
value = value < 0.0f ? 0.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);
|
||||
}
|
||||
|
||||
float CosmicUnicorn::get_brightness() {
|
||||
return this->brightness / 255.0f;
|
||||
return this->brightness / 256.0f;
|
||||
}
|
||||
|
||||
void CosmicUnicorn::adjust_brightness(float delta) {
|
||||
|
|
|
@ -488,11 +488,14 @@ namespace pimoroni {
|
|||
void GalacticUnicorn::set_brightness(float value) {
|
||||
value = value < 0.0f ? 0.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);
|
||||
}
|
||||
|
||||
float GalacticUnicorn::get_brightness() {
|
||||
return this->brightness / 255.0f;
|
||||
return this->brightness / 256.0f;
|
||||
}
|
||||
|
||||
void GalacticUnicorn::adjust_brightness(float delta) {
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace pimoroni {
|
|||
}
|
||||
|
||||
void PicoGraphics_Pen1BitY::set_pen(uint8_t r, uint8_t g, uint8_t b) {
|
||||
color = std::max(r, std::max(g, b));
|
||||
color = std::max(r, std::max(g, b)) >> 4;
|
||||
}
|
||||
|
||||
void PicoGraphics_Pen1BitY::set_pixel(const Point &p) {
|
||||
|
|
|
@ -485,11 +485,14 @@ namespace pimoroni {
|
|||
void StellarUnicorn::set_brightness(float value) {
|
||||
value = value < 0.0f ? 0.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);
|
||||
}
|
||||
|
||||
float StellarUnicorn::get_brightness() {
|
||||
return this->brightness / 255.0f;
|
||||
return this->brightness / 256.0f;
|
||||
}
|
||||
|
||||
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", "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
|
||||
import os
|
||||
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 = sdcard.SDCard(sd_spi, Pin(22))
|
||||
os.mount(sd, "/sd")
|
||||
|
|
|
@ -17,18 +17,16 @@ HEIGHT = None
|
|||
code = qrcode.QRCode()
|
||||
|
||||
|
||||
def read_until(stream, char):
|
||||
def read_until(stream, find):
|
||||
result = b""
|
||||
while True:
|
||||
c = stream.read(1)
|
||||
if c == char:
|
||||
while len(c := stream.read(1)) > 0:
|
||||
if c == find:
|
||||
return result
|
||||
result += c
|
||||
|
||||
|
||||
def discard_until(stream, c):
|
||||
while stream.read(1) != c:
|
||||
pass
|
||||
def discard_until(stream, find):
|
||||
_ = read_until(stream, find)
|
||||
|
||||
|
||||
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""
|
||||
count = 0
|
||||
current = {}
|
||||
|
||||
while True:
|
||||
char = s.read(1)
|
||||
if len(char) == 0:
|
||||
|
@ -79,9 +78,9 @@ def parse_xml_stream(s, accept_tags, group_by, max_items=3):
|
|||
|
||||
else:
|
||||
current_tag = read_until(s, b">")
|
||||
tag += [next_char + current_tag.split(b" ")[0]]
|
||||
text = b""
|
||||
gc.collect()
|
||||
if not current_tag.endswith(b"/"):
|
||||
tag += [next_char + current_tag.split(b" ")[0]]
|
||||
text = b""
|
||||
|
||||
else:
|
||||
text += char
|
||||
|
@ -113,7 +112,7 @@ def get_rss():
|
|||
|
||||
except OSError as e:
|
||||
print(e)
|
||||
return False
|
||||
return []
|
||||
|
||||
|
||||
feed = None
|
||||
|
@ -135,7 +134,7 @@ def draw():
|
|||
graphics.set_pen(0)
|
||||
|
||||
# Draws 2 articles from the feed if they're available.
|
||||
if feed:
|
||||
if len(feed) > 0:
|
||||
|
||||
# Title
|
||||
graphics.set_pen(graphics.create_pen(200, 0, 0))
|
||||
|
|
|
@ -95,7 +95,7 @@ def draw():
|
|||
y += line_space
|
||||
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
|
||||
|
||||
graphics.update()
|
||||
|
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 5.4 KiB |
|
@ -4,6 +4,7 @@
|
|||
- [Read ADCs](#read-adcs)
|
||||
- [Read GPIOs](#read-gpios)
|
||||
- [Read Encoders](#read-encoders)
|
||||
- [Read Speeds](#read-speeds)
|
||||
- [LED Rainbow](#led-rainbow)
|
||||
- [Reset Inventor](#reset-inventor)
|
||||
- [Motor Examples](#motor-examples)
|
||||
|
@ -22,13 +23,14 @@
|
|||
- [Velocity Tuning](#velocity-tuning)
|
||||
- [Position on Velocity Tuning](#position-on-velocity-tuning)
|
||||
- [Servo Examples](#servo-examples)
|
||||
- [Single Servos](#single-servo)
|
||||
- [Single Servo](#single-servo)
|
||||
- [Multiple Servos](#multiple-servos)
|
||||
- [Simple Easing](#simple-easing)
|
||||
- [Servo Wave](#servo-wave)
|
||||
- [Calibration](#calibration)
|
||||
- [Audio Examples](#audio-examples)
|
||||
- [Tone Song](#tone-song)
|
||||
- [Motor Song](#motor-song)
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
### 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.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 |
|
@ -189,3 +189,4 @@ Here are some Plasma Stick community projects and resources that you might find
|
|||
- :link: [MQTT Script for Plasma Stick](https://github.com/digitalurban/MQTT-Plasma-Stick-2040W)
|
||||
- :link: [Pimoroni Wireless Plasma Kit - Server](https://github.com/brunon/Starlight)
|
||||
- :link: [Plasma LEDs](https://github.com/bitcdr/plasma-leds)
|
||||
- :link: [Album Art Bottle LEDs](https://github.com/heavyimage/Album-Art-Bottle-LEDs)
|
||||
|
|
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
|
||||
index 281b0c3bc..7e04bb549 100644
|
||||
index fcc435b7b..efabcb3a3 100644
|
||||
--- a/ports/rp2/CMakeLists.txt
|
||||
+++ b/ports/rp2/CMakeLists.txt
|
||||
@@ -453,6 +453,16 @@ target_link_options(${MICROPY_TARGET} PRIVATE
|
||||
-Wl,--wrap=dcd_event_handler
|
||||
@@ -464,6 +464,16 @@ set_source_files_properties(
|
||||
COMPILE_OPTIONS "-O2"
|
||||
)
|
||||
|
||||
+# 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_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_update), MP_ROM_PTR(&adcfft_update_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
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
|
@ -31,11 +31,11 @@ const mp_obj_type_t adcfft_type = {
|
|||
#endif
|
||||
|
||||
// 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_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 = {
|
||||
|
|
|
@ -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_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);
|
||||
self->base.type = &adcfft_type;
|
||||
adcfft_obj_t *self = mp_obj_malloc_with_finaliser(adcfft_obj_t, &adcfft_type);
|
||||
|
||||
unsigned int adc_channel = args[ARG_adc_channel].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);
|
||||
|
||||
/***** 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_is_busy), MP_ROM_PTR(&Badger2040_is_busy_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) },
|
||||
};
|
||||
|
||||
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 *****/
|
||||
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
||||
|
@ -94,7 +94,7 @@ const mp_obj_type_t Badger2040_type = {
|
|||
|
||||
/***** 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_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_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 *****/
|
||||
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);
|
||||
}
|
||||
|
||||
badger2040_obj = m_new_obj_with_finaliser(_Badger2040_obj_t);
|
||||
badger2040_obj->base.type = &Badger2040_type;
|
||||
badger2040_obj = mp_obj_malloc_with_finaliser(_Badger2040_obj_t, &Badger2040_type);
|
||||
badger2040_obj->buf = buffer;
|
||||
badger2040_obj->badger2040 = m_new_class(pimoroni::Badger2040, buffer);
|
||||
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);
|
||||
|
||||
/***** 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_device_type), MP_ROM_PTR(&BreakoutAS7262_device_type_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_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 *****/
|
||||
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
||||
|
@ -80,11 +80,11 @@ const mp_obj_type_t breakout_as7262_BreakoutAS7262_type = {
|
|||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/***** 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_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 *****/
|
||||
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);
|
Some files were not shown because too many files have changed in this diff Show More
Ładowanie…
Reference in New Issue