Merge pull request #194 from pimoroni/patch_plasma2040_examples

Extra examples and bug fixes for Plasma2040
pull/195/head
Philip Howard 2021-08-26 14:36:22 +01:00 zatwierdzone przez GitHub
commit 07f3d65a14
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
23 zmienionych plików z 1327 dodań i 150 usunięć

Wyświetl plik

@ -33,7 +33,7 @@ namespace pimoroni {
}
bool BME68X::configure(uint8_t filter, uint8_t odr, uint8_t os_humidity, uint8_t os_pressure, uint8_t os_temp) {
int8_t result;
int8_t result = 0;
conf.filter = filter;
conf.odr = odr;

Wyświetl plik

@ -3,14 +3,14 @@
namespace plasma {
APA102::APA102(uint num_leds, PIO pio, uint sm, uint pin_dat, uint pin_clk, uint freq, RGB* buffer) : buffer(buffer), num_leds(num_leds), pio(pio), sm(sm) {
uint offset = pio_add_program(pio, &apa102_program);
pio_program_offset = pio_add_program(pio, &apa102_program);
pio_sm_set_pins_with_mask(pio, sm, 0, (1u << pin_clk) | (1u << pin_dat));
pio_sm_set_pindirs_with_mask(pio, sm, ~0u, (1u << pin_clk) | (1u << pin_dat));
pio_gpio_init(pio, pin_clk);
pio_gpio_init(pio, pin_dat);
pio_sm_config c = apa102_program_get_default_config(offset);
pio_sm_config c = apa102_program_get_default_config(pio_program_offset);
sm_config_set_out_pins(&c, pin_dat, 1);
sm_config_set_sideset_pins(&c, pin_clk);
@ -21,7 +21,7 @@ APA102::APA102(uint num_leds, PIO pio, uint sm, uint pin_dat, uint pin_clk, uint
float div = (float)clock_get_hz(clk_sys) / (2 * freq);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_init(pio, sm, pio_program_offset, &c);
pio_sm_set_enabled(pio, sm, true);
dma_channel = dma_claim_unused_channel(true);
@ -34,6 +34,7 @@ APA102::APA102(uint num_leds, PIO pio, uint sm, uint pin_dat, uint pin_clk, uint
if(this->buffer == nullptr) {
this->buffer = new RGB[num_leds];
managed_buffer = true;
}
}

Wyświetl plik

@ -60,8 +60,16 @@ namespace plasma {
clear();
update(true);
dma_channel_unclaim(dma_channel);
pio_sm_set_enabled(pio, sm, false);
pio_remove_program(pio, &apa102_program, pio_program_offset);
#ifndef MICROPY_BUILD_TYPE
// pio_sm_unclaim seems to hardfault in MicroPython
pio_sm_unclaim(pio, sm);
delete[] buffer;
#endif
if(managed_buffer) {
// Only delete buffers we have allocated ourselves.
delete[] buffer;
}
}
bool start(uint fps=60);
bool stop();
@ -78,7 +86,9 @@ namespace plasma {
uint32_t fps;
PIO pio;
uint sm;
uint pio_program_offset;
int dma_channel;
struct repeating_timer timer;
bool managed_buffer = false;
};
}

Wyświetl plik

@ -3,12 +3,12 @@
namespace plasma {
WS2812::WS2812(uint num_leds, PIO pio, uint sm, uint pin, uint freq, bool rgbw, COLOR_ORDER color_order, RGB* buffer) : buffer(buffer), num_leds(num_leds), color_order(color_order), pio(pio), sm(sm) {
uint offset = pio_add_program(pio, &ws2812_program);
pio_program_offset = pio_add_program(pio, &ws2812_program);
pio_gpio_init(pio, pin);
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
pio_sm_config c = ws2812_program_get_default_config(offset);
pio_sm_config c = ws2812_program_get_default_config(pio_program_offset);
sm_config_set_sideset_pins(&c, pin);
sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); // Discard first (APA102 global brightness) byte. TODO support RGBW WS281X LEDs
@ -18,7 +18,7 @@ WS2812::WS2812(uint num_leds, PIO pio, uint sm, uint pin, uint freq, bool rgbw,
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_init(pio, sm, pio_program_offset, &c);
pio_sm_set_enabled(pio, sm, true);
dma_channel = dma_claim_unused_channel(true);
@ -33,6 +33,7 @@ WS2812::WS2812(uint num_leds, PIO pio, uint sm, uint pin, uint freq, bool rgbw,
if(!this->buffer) {
this->buffer = new RGB[num_leds];
managed_buffer = true;
}
}

Wyświetl plik

@ -63,14 +63,22 @@ namespace plasma {
uint32_t num_leds;
COLOR_ORDER color_order;
WS2812(uint num_leds, PIO pio, uint sm, uint pin, uint freq=DEFAULT_SERIAL_FREQ, bool rgbw=false, COLOR_ORDER color_order=COLOR_ORDER::RGB, RGB* buffer=nullptr);
WS2812(uint num_leds, PIO pio, uint sm, uint pin, uint freq=DEFAULT_SERIAL_FREQ, bool rgbw=false, COLOR_ORDER color_order=COLOR_ORDER::GRB, RGB* buffer=nullptr);
~WS2812() {
stop();
clear();
update(true);
dma_channel_unclaim(dma_channel);
pio_sm_set_enabled(pio, sm, false);
pio_remove_program(pio, &ws2812_program, pio_program_offset);
#ifndef MICROPY_BUILD_TYPE
// pio_sm_unclaim seems to hardfault in MicroPython
pio_sm_unclaim(pio, sm);
delete[] buffer;
#endif
if(managed_buffer) {
// Only delete buffers we have allocated ourselves.
delete[] buffer;
}
}
bool start(uint fps=60);
bool stop();
@ -87,7 +95,9 @@ namespace plasma {
uint32_t fps;
PIO pio;
uint sm;
uint pio_program_offset;
int dma_channel;
struct repeating_timer timer;
bool managed_buffer = false;
};
}

Wyświetl plik

@ -1,3 +1,5 @@
include(plasma2040_rotary.cmake)
include(plasma2040_level.cmake)
include(plasma2040_monitor.cmake)
include(plasma2040_rainbow.cmake)
include(plasma2040_rotary.cmake)
include(plasma2040_stacker.cmake)

Wyświetl plik

@ -0,0 +1,17 @@
set(OUTPUT_NAME plasma2040_level)
add_executable(${OUTPUT_NAME} plasma2040_level.cpp)
target_link_libraries(${OUTPUT_NAME}
pico_stdlib
plasma2040
breakout_msa301
hardware_i2c
pimoroni_i2c
rgbled
button
)
# enable usb output
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,245 @@
#include <stdio.h>
#include <math.h>
#include <cstdint>
#include "pico/stdlib.h"
#include "plasma2040.hpp"
#include "common/pimoroni_common.hpp"
#include "breakout_msa301.hpp"
#include "rgbled.hpp"
#include "button.hpp"
/*
A simple balancing game, where you use the MSA301 accelerometer to line up a band with a goal on the strip.
This can either be done using:
- Angle mode: Where position on the strip directly matches the accelerometer's angle
- Velocity mode: Where tilting the accelerometer changes the speed the band moves at
When the goal position is reached, a new position is randomly selected
Press "A" to change the game mode.
Press "B" to start or stop the game mode.
Press "Boot" to invert the direction of the accelerometer tilt
*/
using namespace pimoroni;
using namespace plasma;
// Set how many LEDs you have
const uint N_LEDS = 30;
// How many times the LEDs will be updated per second
const uint UPDATES = 60;
// The sensitivity of the accelerometer input
constexpr float ANGLE_SENSITIVITY = 0.05f;
constexpr float VELOCITY_SENSITIVITY = 0.2f / UPDATES;
// The band colour hues to show in Angle mode
constexpr float ANGLE_MODE_GOAL_HUE = 0.333f;
constexpr float ANGLE_MODE_EDGE_HUE = 0.0f;
// The band colour hues to show in Velocity mode
constexpr float VELOCITY_MODE_GOAL_HUE = 0.667f;
constexpr float VELOCITY_MODE_EDGE_HUE = 1.0f;
// The width and colour settings for the band
constexpr float BAND_PIXEL_WIDTH = 2.0f;
constexpr float BAND_SATURATION = 1.0f;
constexpr float BAND_IN_GOAL_SATURATION = 0.5f;
constexpr float BAND_BRIGHTNESS = 1.0f;
// The width and colour settings for the goal
// Goal should be wider than the band by a small amount
constexpr float GOAL_PIXEL_WIDTH = BAND_PIXEL_WIDTH + 2.0f;
constexpr float GOAL_BRIGHTNESS = 0.1f;
// The percentage of the new angle (between 0.0 and 1.0) to apply to the last angle
// Has the effect of smoothing out the reading, at the cost of making it slower to react
constexpr float SMOOTHING_FACTOR = 0.1f;
// Pick *one* LED type by uncommenting the relevant line below:
// APA102-style LEDs with Data/Clock lines. AKA DotStar
//APA102 led_strip(N_LEDS, pio0, 0, plasma2040::DAT, plasma2040::CLK);
// WS28X-style LEDs with a single signal line. AKA NeoPixel
WS2812 led_strip(N_LEDS, pio0, 0, plasma2040::DAT);
Button user_sw(plasma2040::USER_SW, Polarity::ACTIVE_LOW, 0);
Button button_a(plasma2040::BUTTON_A, Polarity::ACTIVE_LOW, 0);
Button button_b(plasma2040::BUTTON_B, Polarity::ACTIVE_LOW, 0);
RGBLED led(plasma2040::LED_R, plasma2040::LED_G, plasma2040::LED_B);
I2C i2c(BOARD::PICO_EXPLORER);
BreakoutMSA301 msa(&i2c);
enum LEVEL_MODE {
ANGLE,
VELOCITY
};
// Maps a value from one range to another
float map(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// Shows a band and goal with the given widths at the positions on the strip
void colour_band(float centre_position, float width, float goal_position, float goal_width, float hue) {
if((centre_position >= 0.0f) && (width > 0.0) && (goal_width > 0.0)) {
float band_pixels_start = centre_position - (width / 2);
float band_pixels_end = centre_position + (width / 2);
float goal_pixels_start = goal_position - (goal_width / 2);
float goal_pixels_end = goal_position + (goal_width / 2);
// Go through each led in the strip
uint i2;
float saturation, brightness, sat, val;
for(uint i = 0; i < led_strip.num_leds; i++) {
// Set saturation and brightness values for if the led is inside or outside of the goal
saturation = BAND_SATURATION;
brightness = 0.0f;
if((i >= goal_pixels_start) && (i < goal_pixels_end)) {
saturation = BAND_IN_GOAL_SATURATION;
brightness = GOAL_BRIGHTNESS;
}
i2 = i + 1;
if(i2 <= band_pixels_end) {
if(i2 <= band_pixels_start) {
// Outside of the band
led_strip.set_hsv(i, hue, 0.0, brightness);
}
else if(i <= band_pixels_start) {
// Transition into the band
val = map(band_pixels_start, (float)i, (float)i2, BAND_BRIGHTNESS, brightness);
sat = map(band_pixels_start, (float)i, (float)i2, BAND_SATURATION, saturation);
led_strip.set_hsv(i, hue, sat, val);
}
else {
// Inside the band
led_strip.set_hsv(i, hue, 1.0, 1.0);
}
}
else if(i <= band_pixels_end) {
// Transition out of the band
val = map(band_pixels_end, (float)i, (float)i2, brightness, BAND_BRIGHTNESS);
sat = map(band_pixels_end, (float)i, (float)i2, saturation, BAND_SATURATION);
led_strip.set_hsv(i, hue, sat, val);
}
else {
// Outside of the band
led_strip.set_hsv(i, hue, 0.0, brightness);
}
}
}
}
int main() {
stdio_init_all();
led_strip.start(UPDATES);
bool accel_detected = msa.init();
float band_position = 0.0f;
float goal_position = 0.0f;
float measured_angle = 0.0f;
bool invert = false;
bool game_mode = false;
LEVEL_MODE mode = LEVEL_MODE::ANGLE;
while(true) {
if(accel_detected) {
// Read the x and y axes of the accelerometer
float x = msa.get_x_axis();
float y = msa.get_y_axis();
// Convert those values to an angle in degrees, and invert if selected
float new_measured_angle = (atan2(x, -y) * 180.0f) / M_PI;
if(invert)
new_measured_angle = -new_measured_angle;
printf("Angle: %f deg\n", new_measured_angle);
// Smooth out the measured angle
measured_angle = ((new_measured_angle - measured_angle) * SMOOTHING_FACTOR) + measured_angle;
float hue = 0.0;
float position_diff;
switch(mode) {
case LEVEL_MODE::ANGLE:
// Apply the measured angle directly to the band position, clamping it between -1 and +1
band_position = measured_angle * ANGLE_SENSITIVITY;
band_position = std::min(1.0f, std::max(-1.0f, band_position));
// Convert the difference between the band and goal positions into a colour hue
position_diff = std::min(abs(band_position - goal_position), 1.0f);
hue = map(position_diff, 0.0f, 1.0f, ANGLE_MODE_GOAL_HUE, ANGLE_MODE_EDGE_HUE);
break;
case LEVEL_MODE::VELOCITY:
// Apply the measured angle as a velocity to the band position, clamping it between -1 and +1
band_position += measured_angle * VELOCITY_SENSITIVITY;
band_position = std::min(1.0f, std::max(-1.0f, band_position));
// Convert the difference between the band and goal positions into a colour hue
position_diff = std::min(abs(band_position - goal_position), 1.0f);
hue = map(position_diff, 0.0f, 1.0f, VELOCITY_MODE_GOAL_HUE, VELOCITY_MODE_EDGE_HUE);
break;
}
// Convert the band and goal positions to positions on the LED strip
float strip_band_position = map(band_position, -1.0f, 1.0f, 0.0f, float(led_strip.num_leds));
float strip_goal_position = map(goal_position, -1.0f, 1.0f, 0.0f, float(led_strip.num_leds));
// Draw the band and goal
colour_band(strip_band_position, BAND_PIXEL_WIDTH, strip_goal_position, GOAL_PIXEL_WIDTH, hue);
bool sw_pressed = user_sw.read();
bool a_pressed = button_a.read();
bool b_pressed = button_b.read();
if(b_pressed)
game_mode = !game_mode;
if(sw_pressed)
invert = !invert;
switch(mode) {
case ANGLE:
if(game_mode)
led.set_rgb(255, 255, 0);
else
led.set_rgb(0, 255, 0);
if(a_pressed)
mode = VELOCITY;
break;
case VELOCITY:
if(game_mode)
led.set_rgb(255, 0, 255);
else
led.set_rgb(0, 0, 255);
if(a_pressed)
mode = ANGLE;
break;
}
if(game_mode) {
// Check if the band is within the goal, and if so, set a new goal
bool above_lower = strip_band_position >= strip_goal_position - (GOAL_PIXEL_WIDTH - BAND_PIXEL_WIDTH) / 2;
bool below_upper = strip_band_position <= strip_goal_position + (GOAL_PIXEL_WIDTH - BAND_PIXEL_WIDTH) / 2;
if(above_lower && below_upper)
goal_position = map((float)rand(), 0.0f, (float)RAND_MAX, -1.0f, 1.0f);
}
}
// Sleep time controls the rate at which the LED buffer is updated
// but *not* the actual framerate at which the buffer is sent to the LEDs
sleep_ms(1000 / UPDATES);
}
}

Wyświetl plik

@ -0,0 +1,17 @@
set(OUTPUT_NAME plasma2040_monitor)
add_executable(${OUTPUT_NAME} plasma2040_monitor.cpp)
target_link_libraries(${OUTPUT_NAME}
pico_stdlib
plasma2040
bme68x
hardware_i2c
pimoroni_i2c
rgbled
button
)
# enable usb output
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,188 @@
#include <stdio.h>
#include <math.h>
#include <cstdint>
#include "pico/stdlib.h"
#include "plasma2040.hpp"
#include "common/pimoroni_common.hpp"
#include "bme68x.hpp"
#include "rgbled.hpp"
#include "button.hpp"
/*
Uses a BME68x to monitor the ambient temperature, pressure and humidity, and show them as bars on an LED strip.
Press "A" to cycle to the next mode.
Press "B" to cycle to the previous mode.
*/
using namespace pimoroni;
using namespace plasma;
// Set how many LEDs you have
const uint N_LEDS = 30;
// How many times the LEDs will be updated per second
const uint UPDATES = 60;
// The temperature range to show (in degrees celsius)
constexpr float TEMPERATURE_C_MIN = 20.0f;
constexpr float TEMPERATURE_C_MAX = 35.0f;
// The pressure range to show (in pascals)
constexpr float PRESSURE_PA_MIN = 87000.0f;
constexpr float PRESSURE_PA_MAX = 108500.0f;
// The humidity range to show (in percent)
constexpr float HUMIDITY_MIN = 0.0f;
constexpr float HUMIDITY_MAX = 100.0f;
// The start and end hues for the temperature range
constexpr float TEMPERATURE_HUE_START = 0.667f;
constexpr float TEMPERATURE_HUE_END = 1.0f;
// The start and end hues for the pressure range
constexpr float PRESSURE_HUE_START = 0.333f;
constexpr float PRESSURE_HUE_END = 0.0f;
// The start and end hues for the humidity range
constexpr float HUMIDITY_HUE_START = 0.333f;
constexpr float HUMIDITY_HUE_END = 0.667f;
// Pick *one* LED type by uncommenting the relevant line below:
// APA102-style LEDs with Data/Clock lines. AKA DotStar
//APA102 led_strip(N_LEDS, pio0, 0, plasma2040::DAT, plasma2040::CLK);
// WS28X-style LEDs with a single signal line. AKA NeoPixel
WS2812 led_strip(N_LEDS, pio0, 0, plasma2040::DAT);
Button button_a(plasma2040::BUTTON_A, ACTIVE_LOW, 0);
Button button_b(plasma2040::BUTTON_B, ACTIVE_LOW, 0);
RGBLED led(plasma2040::LED_R, plasma2040::LED_G, plasma2040::LED_B);
I2C i2c(BOARD::PICO_EXPLORER);
BME68X bme68x(&i2c);
enum DisplayMode {
ALL,
TEMPERATURE,
PRESSURE,
HUMIDITY
};
// Maps a value from one range to another
float map(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// Sets a section of the led strip to show a hue gradient based on the provided percent
void colour_gauge(float percent, uint start_led, uint end_led, float start_hue, float end_hue) {
if(end_led > start_led) {
uint range = end_led - start_led;
float light_pixels = percent * range;
for(uint i = 0; i < range; i++) {
float h = map(i, 0.0f, (float)range - 1, start_hue, end_hue);
uint i2 = i + 1;
if(i2 <= light_pixels) {
led_strip.set_hsv(i + start_led, h, 1.0f, 1.0f);
}
else if(i <= light_pixels) {
float scale = map(light_pixels, (float)i, (float)i2, 0.0f, 1.0f);
led_strip.set_hsv(i + start_led, h, 1.0f, scale);
}
else {
led_strip.set_hsv(i + start_led, 0.0f, 0.0f, 0.0f);
}
}
}
}
int main() {
stdio_init_all();
led_strip.start(UPDATES);
bool bme_detected = bme68x.init();
uint first_third = led_strip.num_leds / 3;
uint second_third = (led_strip.num_leds * 2) / 3;
float t = 0.0f;
DisplayMode mode = DisplayMode::ALL;
while(true) {
if(bme_detected) {
bme68x_data data;
auto result = bme68x.read_forced(&data);
(void)result;
printf("%.2fc, %.2fPa, %.2f%%\n", data.temperature, data.pressure, data.humidity);
switch(mode) {
case DisplayMode::ALL:
t = map(data.temperature, TEMPERATURE_C_MIN, TEMPERATURE_C_MAX, 0.0f, 1.0f);
colour_gauge(t, 0, first_third, TEMPERATURE_HUE_START, TEMPERATURE_HUE_END);
t = map(data.pressure, PRESSURE_PA_MIN, PRESSURE_PA_MAX, 0.0f, 1.0f);
colour_gauge(t, first_third, second_third, PRESSURE_HUE_START, PRESSURE_HUE_END);
t = map(data.humidity, HUMIDITY_MIN, HUMIDITY_MAX, 0.0f, 1.0f);
colour_gauge(t, second_third, led_strip.num_leds, HUMIDITY_HUE_START, HUMIDITY_HUE_END);
break;
case DisplayMode::TEMPERATURE:
t = map(data.temperature, TEMPERATURE_C_MIN, TEMPERATURE_C_MAX, 0.0f, 1.0f);
colour_gauge(t, 0, led_strip.num_leds, TEMPERATURE_HUE_START, TEMPERATURE_HUE_END);
break;
case DisplayMode::PRESSURE:
t = map(data.pressure, PRESSURE_PA_MIN, PRESSURE_PA_MAX, 0.0f, 1.0f);
colour_gauge(t, 0, led_strip.num_leds, PRESSURE_HUE_START, PRESSURE_HUE_END);
break;
case DisplayMode::HUMIDITY:
t = map(data.humidity, HUMIDITY_MIN, HUMIDITY_MAX, 0.0f, 1.0f);
colour_gauge(t, 0, led_strip.num_leds, HUMIDITY_HUE_START, HUMIDITY_HUE_END);
break;
}
}
bool a_pressed = button_a.read();
bool b_pressed = button_b.read();
switch(mode) {
case DisplayMode::ALL:
led.set_rgb(127, 127, 127);
if(a_pressed) mode = DisplayMode::TEMPERATURE;
else if(b_pressed) mode = DisplayMode::HUMIDITY;
break;
case DisplayMode::TEMPERATURE:
led.set_rgb(255, 0, 255);
if(a_pressed) mode = DisplayMode::PRESSURE;
else if(b_pressed) mode = DisplayMode::ALL;
break;
case DisplayMode::PRESSURE:
led.set_rgb(255, 255, 0);
if(a_pressed) mode = DisplayMode::HUMIDITY;
else if(b_pressed) mode = DisplayMode::TEMPERATURE;
break;
case DisplayMode::HUMIDITY:
led.set_rgb(0, 255, 255);
if(a_pressed) mode = DisplayMode::ALL;
else if(b_pressed) mode = DisplayMode::PRESSURE;
break;
}
}
}

Wyświetl plik

@ -47,46 +47,46 @@ Analog sense(plasma2040::CURRENT_SENSE, plasma2040::ADC_GAIN, plasma2040::SHUNT_
int main() {
stdio_init_all();
stdio_init_all();
led_strip.start(UPDATES);
led_strip.start(UPDATES);
int speed = DEFAULT_SPEED;
float offset = 0.0f;
int speed = DEFAULT_SPEED;
float offset = 0.0f;
uint count = 0;
while (true) {
bool sw = user_sw.read();
bool a = button_a.read();
bool b = button_b.read();
uint count = 0;
while(true) {
bool sw_pressed = user_sw.read();
bool a_pressed = button_a.read();
bool b_pressed = button_b.read();
if(sw) {
speed = DEFAULT_SPEED;
}
else {
if(a) speed--;
if(b) speed++;
}
speed = std::min((int)255, std::max((int)1, speed));
offset += float(speed) / 2000.0f;
for(auto i = 0u; i < led_strip.num_leds; ++i) {
float hue = float(i) / led_strip.num_leds;
led_strip.set_hsv(i, hue + offset, 1.0f, 1.0f);
}
led.set_rgb(speed, 0, 255 - speed);
count += 1;
if(count >= UPDATES) {
// Display the current value once every second
printf("Current = %f A\n", sense.read_current());
count = 0;
}
// Sleep time controls the rate at which the LED buffer is updated
// but *not* the actual framerate at which the buffer is sent to the LEDs
sleep_ms(1000 / UPDATES);
if(sw_pressed) {
speed = DEFAULT_SPEED;
}
else {
if(a_pressed) speed--;
if(b_pressed) speed++;
}
speed = std::min((int)255, std::max((int)1, speed));
offset += float(speed) / 2000.0f;
for(auto i = 0u; i < led_strip.num_leds; ++i) {
float hue = float(i) / led_strip.num_leds;
led_strip.set_hsv(i, hue + offset, 1.0f, 1.0f);
}
led.set_rgb(speed, 0, 255 - speed);
count += 1;
if(count >= UPDATES) {
// Display the current value once every second
printf("Current = %f A\n", sense.read_current());
count = 0;
}
// Sleep time controls the rate at which the LED buffer is updated
// but *not* the actual framerate at which the buffer is sent to the LEDs
sleep_ms(1000 / UPDATES);
}
}

Wyświetl plik

@ -11,15 +11,34 @@
#include "rgbled.hpp"
#include "button.hpp"
/*
Press "B" to enable cycling.
Press "A" to change the encoder mode.
Press "Boot" to reset the effects back to default.
*/
using namespace pimoroni;
using namespace plasma;
// Set how many LEDs you have
const uint N_LEDS = 30;
// The speed that the LEDs will start cycling at
const int16_t DEFAULT_SPEED = 20;
// The hue (in degrees) that the LEDs will start at
const int16_t DEFAULT_HUE = 0;
// The angle (in degrees) from the hue, that the LEDs will end at
const int16_t DEFAULT_ANGLE = 120;
// The brightness (between 0 and 31) to set the LEDs to
const int16_t DEFAULT_BRIGHTNESS = 16;
// How many times the LEDs will be updated per second
const uint UPDATES = 60;
// Pick *one* LED type by uncommenting the relevant line below:
// APA102-style LEDs with Data/Clock lines. AKA DotStar
@ -29,9 +48,9 @@ const uint UPDATES = 60;
WS2812 led_strip(N_LEDS, pio0, 0, plasma2040::DAT);
Button button_a(plasma2040::BUTTON_A);
Button button_b(plasma2040::BUTTON_B);
Button user_sw(plasma2040::USER_SW, Polarity::ACTIVE_LOW, 0);
Button button_a(plasma2040::BUTTON_A, Polarity::ACTIVE_LOW, 0);
Button button_b(plasma2040::BUTTON_B, Polarity::ACTIVE_LOW, 0);
RGBLED led(plasma2040::LED_R, plasma2040::LED_G, plasma2040::LED_B);
@ -39,118 +58,161 @@ I2C i2c(BOARD::PICO_EXPLORER);
BreakoutEncoder enc(&i2c);
enum ENCODER_MODE {
COLOUR,
ANGLE,
BRIGHTNESS,
TIME
COLOUR,
ANGLE,
BRIGHTNESS,
SPEED
};
float wrap(float v, float min, float max) {
if(v <= min)
v += (max - min);
if(v > max)
v -= (max - min);
return v;
}
void colour_cycle(float hue, float t, float angle) {
t /= 200.0f;
t /= 200.0f;
for (auto i = 0u; i < led_strip.num_leds; ++i) {
float offset = (M_PI * i) / led_strip.num_leds;
offset = sinf(offset + t) * angle;
led_strip.set_hsv(i, (hue + offset) / 360.0f, 1.0f, 1.0f);
}
for(auto i = 0u; i < led_strip.num_leds; ++i) {
float percent_along = (float)i / led_strip.num_leds;
float offset = sinf((percent_along + 0.5f + t) * M_PI) * angle;
float h = wrap((hue + offset) / 360.0f, 0.0f, 1.0f);
led_strip.set_hsv(i, h, 1.0f, 1.0f);
}
}
void gauge(uint v, uint vmax = 100) {
uint light_pixels = led_strip.num_leds * v / vmax;
void speed_gauge(uint v, uint vmax = 100) {
uint light_pixels = led_strip.num_leds * v / vmax;
for (auto i = 0u; i < led_strip.num_leds; ++i) {
if(i < light_pixels) {
led_strip.set_rgb(i, 0, 255, 0);
} else {
led_strip.set_rgb(i, 255, 0, 0);
}
for(auto i = 0u; i < led_strip.num_leds; ++i) {
if(i < light_pixels) {
led_strip.set_rgb(i, 0, 255, 0);
}
else {
led_strip.set_rgb(i, 255, 0, 0);
}
}
}
void brightness_gauge(uint v, uint vmax = 100) {
uint light_pixels = led_strip.num_leds * v / vmax;
for(auto i = 0u; i < led_strip.num_leds; ++i) {
if(i < light_pixels) {
led_strip.set_rgb(i, 64, 64, 64);
}
else {
led_strip.set_rgb(i, 0, 0, 0);
}
}
}
int main() {
stdio_init_all();
stdio_init_all();
led_strip.start(UPDATES);
led_strip.start(UPDATES);
bool encoder_detected = enc.init();
enc.clear_interrupt_flag();
bool encoder_detected = enc.init();
enc.clear_interrupt_flag();
int speed = 50;
float hue = 0;
int angle = 120;
int8_t brightness = 16;
bool cycle = true;
ENCODER_MODE mode = ENCODER_MODE::COLOUR;
while (true) {
uint32_t t = millis();
if(encoder_detected) {
if(enc.get_interrupt_flag()) {
int count = enc.read();
enc.clear_interrupt_flag();
enc.clear();
//Initialise the default values
int16_t speed = DEFAULT_SPEED;
int16_t hue = DEFAULT_HUE;
int16_t angle = DEFAULT_ANGLE;
int16_t brightness = DEFAULT_BRIGHTNESS;
cycle = false;
switch(mode) {
case ENCODER_MODE::COLOUR:
hue += count;
brightness = std::min((int8_t)359, brightness);
brightness = std::max((int8_t)0, brightness);
colour_cycle(hue, 0, (float)angle);
break;
case ENCODER_MODE::ANGLE:
angle += count;
angle = std::min((int)359, angle);
angle = std::max((int)0, angle);
colour_cycle(hue, 0, (float)angle);
break;
case ENCODER_MODE::BRIGHTNESS:
brightness += count;
brightness = std::min((int8_t)31, brightness);
brightness = std::max((int8_t)0, brightness);
led_strip.set_brightness(brightness);
gauge(brightness, 31);
break;
case ENCODER_MODE::TIME:
speed += count;
speed = std::min((int)100, speed);
speed = std::max((int)0, speed);
gauge(speed, 100);
break;
}
}
}
bool a_pressed = button_a.read();
bool b_pressed = button_b.read();
if(b_pressed) cycle = true;
bool cycle = true;
ENCODER_MODE mode = ENCODER_MODE::COLOUR;
uint32_t start_time = millis();
while(true) {
uint32_t t = millis() - start_time;
if(encoder_detected) {
if(enc.get_interrupt_flag()) {
int16_t count = enc.read();
enc.clear_interrupt_flag();
enc.clear();
cycle = false;
switch(mode) {
case ENCODER_MODE::COLOUR:
led.set_rgb(255, 0, 0);
if(a_pressed) mode = ENCODER_MODE::ANGLE;
break;
case ENCODER_MODE::ANGLE:
led.set_rgb(255, 255, 0);
if(a_pressed) mode = ENCODER_MODE::BRIGHTNESS;
break;
case ENCODER_MODE::BRIGHTNESS:
led.set_rgb(0, 255, 0);
if(a_pressed) mode = ENCODER_MODE::TIME;
break;
case ENCODER_MODE::TIME:
led.set_rgb(0, 0, 255);
if(a_pressed) mode = ENCODER_MODE::COLOUR;
break;
case ENCODER_MODE::COLOUR:
hue += count;
hue = std::min((int16_t)359, std::max((int16_t)0, hue));
colour_cycle((float)hue, 0, (float)angle);
break;
case ENCODER_MODE::ANGLE:
angle += count;
angle = std::min((int16_t)359, std::max((int16_t)0, angle));
colour_cycle((float)hue, 0, (float)angle);
break;
case ENCODER_MODE::BRIGHTNESS:
brightness += count;
brightness = std::min((int16_t)31, std::max((int16_t)0, brightness));
led_strip.set_brightness((uint8_t)brightness);
brightness_gauge(brightness, 31);
break;
case ENCODER_MODE::SPEED:
speed += count;
speed = std::min((int16_t)100, std::max((int16_t)0, speed));
speed_gauge(speed, 100);
break;
}
if(cycle) colour_cycle(hue, t * speed / 100, (float)angle);
auto first_led = led_strip.get(0);
enc.set_led(first_led.r, first_led.g, first_led.b);
// Sleep time controls the rate at which the LED buffer is updated
// but *not* the actual framerate at which the buffer is sent to the LEDs
sleep_ms(1000 / UPDATES);
}
}
bool sw_pressed = user_sw.read();
bool a_pressed = button_a.read();
bool b_pressed = button_b.read();
if(sw_pressed) {
speed = DEFAULT_SPEED;
hue = DEFAULT_HUE;
angle = DEFAULT_ANGLE;
brightness = DEFAULT_BRIGHTNESS;
}
if(b_pressed) {
if(!cycle)
start_time = millis();
cycle = true;
}
switch(mode) {
case ENCODER_MODE::COLOUR:
led.set_rgb(255, 0, 0);
if(a_pressed) mode = ENCODER_MODE::ANGLE;
break;
case ENCODER_MODE::ANGLE:
led.set_rgb(255, 255, 0);
if(a_pressed) mode = ENCODER_MODE::BRIGHTNESS;
break;
case ENCODER_MODE::BRIGHTNESS:
led.set_rgb(0, 255, 0);
if(a_pressed) mode = ENCODER_MODE::SPEED;
break;
case ENCODER_MODE::SPEED:
led.set_rgb(0, 0, 255);
if(a_pressed) mode = ENCODER_MODE::COLOUR;
break;
}
if(cycle)
colour_cycle(hue, (float)(t * speed) / 100.0f, (float)angle);
auto mid_led = led_strip.get(led_strip.num_leds / 2);
enc.set_led(mid_led.r, mid_led.g, mid_led.b);
// Sleep time controls the rate at which the LED buffer is updated
// but *not* the actual framerate at which the buffer is sent to the LEDs
sleep_ms(1000 / UPDATES);
}
}

Wyświetl plik

@ -0,0 +1,209 @@
import plasma
from plasma import plasma2040
import time
import math
import random
# Import helpers for RGB LEDs and Buttons
from pimoroni import RGBLED, Button
# Import msa301 and I2C helper
from breakout_msa301 import BreakoutMSA301
from pimoroni_i2c import PimoroniI2C
# A simple balancing game, where you use the MSA301 accelerometer to line up a band with a goal on the strip.
# This can either be done using:
# - Angle mode: Where position on the strip directly matches the accelerometer's angle
# - Velocity mode: Where tilting the accelerometer changes the speed the band moves at
# When the goal position is reached, a new position is randomly selected
# Press "A" to change the game mode.
# Press "B" to start or stop the game mode.
# Press "Boot" to invert the direction of the accelerometer tilt
# Set how many LEDs you have
NUM_LEDS = 30
# How many times the LEDs will be updated per second
UPDATES = 60
# The sensitivity of the accelerometer input
ANGLE_SENSITIVITY = 0.05
VELOCITY_SENSITIVITY = 0.2 / UPDATES
# The band colour hues to show in Angle mode
ANGLE_MODE_GOAL_HUE = 0.333
ANGLE_MODE_EDGE_HUE = 0.0
# The band colour hues to show in Velocity mode
VELOCITY_MODE_GOAL_HUE = 0.667
VELOCITY_MODE_EDGE_HUE = 1.0
# The width and colour settings for the band
BAND_PIXEL_WIDTH = 2.0
BAND_SATURATION = 1.0
BAND_IN_GOAL_SATURATION = 0.5
BAND_BRIGHTNESS = 1.0
# The width and colour settings for the goal
# Goal should be wider than the band by a small amount
GOAL_PIXEL_WIDTH = BAND_PIXEL_WIDTH + 2.0
GOAL_BRIGHTNESS = 0.1
# The percentage of the new angle (between 0.0 and 1.0) to apply to the last angle
# Has the effect of smoothing out the reading, at the cost of making it slower to react
SMOOTHING_FACTOR = 0.1
# Pick *one* LED type by uncommenting the relevant line below:
# APA102 / DotStar™ LEDs
led_strip = plasma.APA102(NUM_LEDS, 0, 0, plasma2040.DAT, plasma2040.CLK)
# WS2812 / NeoPixel™ LEDs
# led_strip = plasma.WS2812(NUM_LEDS, 0, 0, plasma2040.DAT)
user_sw = Button(plasma2040.USER_SW, repeat_time=0)
button_a = Button(plasma2040.BUTTON_A, repeat_time=0)
button_b = Button(plasma2040.BUTTON_B, repeat_time=0)
led = RGBLED(plasma2040.LED_R, plasma2040.LED_G, plasma2040.LED_B)
PINS_PLASMA2040 = {"sda": plasma2040.SDA, "scl": plasma2040.SCL}
i2c = PimoroniI2C(**PINS_PLASMA2040)
msa = BreakoutMSA301(i2c)
ANGLE, VELOCITY = range(2)
# Maps a value from one range to another
def map(x, in_min, in_max, out_min, out_max):
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
# Shows a band and goal with the given widths at the positions on the strip
def colour_band(centre_position, width, goal_position, goal_width, hue):
if centre_position >= 0.0 and width > 0.0 and goal_width > 0.0:
band_pixels_start = centre_position - (width / 2)
band_pixels_end = centre_position + (width / 2)
goal_pixels_start = goal_position - (goal_width / 2)
goal_pixels_end = goal_position + (goal_width / 2)
# Go through each led in the strip
for i in range(NUM_LEDS):
# Set saturation and brightness values for if the led is inside or outside of the goal
saturation = BAND_SATURATION
brightness = 0.0
if i >= goal_pixels_start and i < goal_pixels_end:
saturation = BAND_IN_GOAL_SATURATION
brightness = GOAL_BRIGHTNESS
i2 = i + 1
if i2 <= band_pixels_end:
if i2 <= band_pixels_start:
# Outside of the band
led_strip.set_hsv(i, hue, 0.0, brightness)
elif i <= band_pixels_start:
# Transition into the band
val = map(band_pixels_start, float(i), float(i2), BAND_BRIGHTNESS, brightness)
sat = map(band_pixels_start, float(i), float(i2), BAND_SATURATION, saturation)
led_strip.set_hsv(i, hue, sat, val)
else:
# Inside the band
led_strip.set_hsv(i, hue, 1.0, 1.0)
elif i <= band_pixels_end:
# Transition out of the band
val = map(band_pixels_end, float(i), float(i2), brightness, BAND_BRIGHTNESS)
sat = map(band_pixels_end, float(i), float(i2), saturation, BAND_SATURATION)
led_strip.set_hsv(i, hue, sat, val)
else:
# Outside of the band
led_strip.set_hsv(i, hue, 0.0, brightness)
mode = ANGLE
goal_position = 0.0
measured_angle = 0.0
invert = False
game_mode = False
# Start updating the LED strip
led_strip.start()
while True:
# Read the x and y axes of the accelerometer
x = msa.get_x_axis()
y = msa.get_y_axis()
# Convert those values to an angle in degrees, and invert if selected
new_measured_angle = (math.atan2(x, -y) * 180.0) / math.pi
if invert:
new_measured_angle = -new_measured_angle
print("Angle:", new_measured_angle, "deg")
# Smooth out the measured angle
measured_angle = ((new_measured_angle - measured_angle) * SMOOTHING_FACTOR) + measured_angle
if mode == ANGLE:
# Apply the measured angle directly to the band position, clamping it between -1 and +1
band_position = measured_angle * ANGLE_SENSITIVITY
band_position = min(1.0, max(-1.0, band_position))
# Convert the difference between the band and goal positions into a colour hue
position_diff = min(abs(band_position - goal_position), 1.0)
hue = map(position_diff, 0.0, 1.0, ANGLE_MODE_GOAL_HUE, ANGLE_MODE_EDGE_HUE)
elif mode == VELOCITY:
# Apply the measured angle as a velocity to the band position, clamping it between -1 and +1
band_position += measured_angle * VELOCITY_SENSITIVITY
band_position = min(1.0, max(-1.0, band_position))
# Convert the difference between the band and goal positions into a colour hue
position_diff = min(abs(band_position - goal_position), 1.0)
hue = map(position_diff, 0.0, 1.0, VELOCITY_MODE_GOAL_HUE, VELOCITY_MODE_EDGE_HUE)
# Convert the band and goal positions to positions on the LED strip
strip_band_position = map(band_position, -1.0, 1.0, 0.0, float(NUM_LEDS))
strip_goal_position = map(goal_position, -1.0, 1.0, 0.0, float(NUM_LEDS))
# Draw the band and goal
colour_band(strip_band_position, BAND_PIXEL_WIDTH, strip_goal_position, GOAL_PIXEL_WIDTH, hue)
sw_pressed = user_sw.read()
a_pressed = button_a.read()
b_pressed = button_b.read()
if b_pressed:
game_mode = not game_mode
if sw_pressed:
invert = not invert
if mode == ANGLE:
if game_mode:
led.set_rgb(255, 255, 0)
else:
led.set_rgb(0, 255, 0)
if a_pressed:
mode = VELOCITY
elif mode == VELOCITY:
if game_mode:
led.set_rgb(255, 0, 255)
else:
led.set_rgb(0, 0, 255)
if a_pressed:
mode = ANGLE
if game_mode:
# Check if the band is within the goal, and if so, set a new goal
above_lower = strip_band_position >= strip_goal_position - (GOAL_PIXEL_WIDTH - BAND_PIXEL_WIDTH) / 2
below_upper = strip_band_position <= strip_goal_position + (GOAL_PIXEL_WIDTH - BAND_PIXEL_WIDTH) / 2
if above_lower and below_upper:
goal_position = random.uniform(-1.0, 1.0)
time.sleep(1.0 / UPDATES)

Wyświetl plik

@ -0,0 +1,157 @@
import plasma
from plasma import plasma2040
# Import helpers for RGB LEDs and Buttons
from pimoroni import RGBLED, Button
# Import bme68x and I2C helper
from breakout_bme68x import BreakoutBME68X
from pimoroni_i2c import PimoroniI2C
# Uses a BME68x to monitor the ambient temperature, pressure and humidity, and show them as bars on an LED strip.
# Press "A" to cycle to the next mode.
# Press "B" to cycle to the previous mode.
# Set how many LEDs you have
NUM_LEDS = 30
# How many times the LEDs will be updated per second
UPDATES = 60
# The temperature range to show (in degrees celsius)
TEMPERATURE_C_MIN = 20.0
TEMPERATURE_C_MAX = 35.0
# The pressure range to show (in pascals)
PRESSURE_PA_MIN = 87000.0
PRESSURE_PA_MAX = 108500.0
# The humidity range to show (in percent)
HUMIDITY_MIN = 0.0
HUMIDITY_MAX = 100.0
# The start and end hues for the temperature range
TEMPERATURE_HUE_START = 0.667
TEMPERATURE_HUE_END = 1.0
# The start and end hues for the pressure range
PRESSURE_HUE_START = 0.333
PRESSURE_HUE_END = 0.0
# The start and end hues for the humidity range
HUMIDITY_HUE_START = 0.333
HUMIDITY_HUE_END = 0.667
# Pick *one* LED type by uncommenting the relevant line below:
# APA102 / DotStar™ LEDs
# led_strip = plasma.APA102(NUM_LEDS, 0, 0, plasma2040.DAT, plasma2040.CLK)
# WS2812 / NeoPixel™ LEDs
led_strip = plasma.WS2812(NUM_LEDS, 0, 0, plasma2040.DAT)
button_a = Button(plasma2040.BUTTON_A, repeat_time=0)
button_b = Button(plasma2040.BUTTON_B, repeat_time=0)
led = RGBLED(plasma2040.LED_R, plasma2040.LED_G, plasma2040.LED_B)
PINS_PLASMA2040 = {"sda": plasma2040.SDA, "scl": plasma2040.SCL}
i2c = PimoroniI2C(**PINS_PLASMA2040)
bme = BreakoutBME68X(i2c)
ALL, TEMPERATURE, PRESSURE, HUMIDITY = range(4)
# Maps a value from one range to another
def map(x, in_min, in_max, out_min, out_max):
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
# Sets a section of the led strip to show a hue gradient based on the provided percent
def colour_gauge(percent, start_led, end_led, start_hue, end_hue):
if end_led > start_led:
length = end_led - start_led
light_pixels = percent * float(length)
for i in range(length):
h = map(i, 0.0, float(length - 1), start_hue, end_hue)
i2 = i + 1
if i2 <= light_pixels:
led_strip.set_hsv(i + start_led, h, 1.0, 1.0)
elif i <= light_pixels:
scale = map(light_pixels, float(i), float(i2), 0.0, 1.0)
led_strip.set_hsv(i + start_led, h, 1.0, scale)
else:
led_strip.set_hsv(i + start_led, 0.0, 0.0, 0.0)
first_third = int(NUM_LEDS / 3)
second_third = int((NUM_LEDS * 2) / 3)
mode = ALL
# Start updating the LED strip
led_strip.start()
while True:
temperature, pressure, humidity, _, _, _, _ = bme.read()
print("{:0.2f}c, {:0.2f}Pa, {:0.2f}%".format(
temperature, pressure, humidity))
if mode == ALL:
t = map(temperature, TEMPERATURE_C_MIN, TEMPERATURE_C_MAX, 0.0, 1.0)
colour_gauge(t, 0, first_third, TEMPERATURE_HUE_START, TEMPERATURE_HUE_END)
t = map(pressure, PRESSURE_PA_MIN, PRESSURE_PA_MAX, 0.0, 1.0)
colour_gauge(t, first_third, second_third, PRESSURE_HUE_START, PRESSURE_HUE_END)
t = map(humidity, HUMIDITY_MIN, HUMIDITY_MAX, 0.0, 1.0)
colour_gauge(t, second_third, NUM_LEDS, HUMIDITY_HUE_START, HUMIDITY_HUE_END)
elif mode == TEMPERATURE:
t = map(temperature, TEMPERATURE_C_MIN, TEMPERATURE_C_MAX, 0.0, 1.0)
colour_gauge(t, 0, NUM_LEDS, TEMPERATURE_HUE_START, TEMPERATURE_HUE_END)
elif mode == PRESSURE:
t = map(pressure, PRESSURE_PA_MIN, PRESSURE_PA_MAX, 0.0, 1.0)
colour_gauge(t, 0, NUM_LEDS, PRESSURE_HUE_START, PRESSURE_HUE_END)
elif mode == HUMIDITY:
t = map(humidity, HUMIDITY_MIN, HUMIDITY_MAX, 0.0, 1.0)
colour_gauge(t, 0, NUM_LEDS, HUMIDITY_HUE_START, HUMIDITY_HUE_END)
a_pressed = button_a.read()
b_pressed = button_b.read()
if mode == ALL:
led.set_rgb(127, 127, 127)
if a_pressed:
mode = TEMPERATURE
elif b_pressed:
mode = HUMIDITY
elif mode == TEMPERATURE:
led.set_rgb(255, 0, 255)
if a_pressed:
mode = PRESSURE
elif b_pressed:
mode = ALL
elif mode == PRESSURE:
led.set_rgb(255, 255, 0)
if a_pressed:
mode = HUMIDITY
elif b_pressed:
mode = TEMPERATURE
elif mode == HUMIDITY:
led.set_rgb(0, 255, 255)
if a_pressed:
mode = ALL
elif b_pressed:
mode = PRESSURE

Wyświetl plik

@ -0,0 +1,187 @@
import plasma
from plasma import plasma2040
import time
import math
# Import helpers for RGB LEDs and Buttons
from pimoroni import RGBLED, Button
# Import bme68x and I2C helper
from breakout_encoder import BreakoutEncoder
from pimoroni_i2c import PimoroniI2C
# Press "B" to enable cycling.
# Press "A" to change the encoder mode.
# Press "Boot" to reset the effects back to default.
# Set how many LEDs you have
NUM_LEDS = 30
# The speed that the LEDs will start cycling at
DEFAULT_SPEED = 20
# The hue (in degrees) that the LEDs will start at
DEFAULT_HUE = 0
# The angle (in degrees) from the hue, that the LEDs will end at
DEFAULT_ANGLE = 120
# The brightness (between 0 and 31) to set the LEDs to
DEFAULT_BRIGHTNESS = 16
# How many times the LEDs will be updated per second
UPDATES = 60
# Pick *one* LED type by uncommenting the relevant line below:
# APA102 / DotStar™ LEDs
led_strip = plasma.APA102(NUM_LEDS, 0, 0, plasma2040.DAT, plasma2040.CLK)
# WS2812 / NeoPixel™ LEDs
# led_strip = plasma.WS2812(NUM_LEDS, 0, 0, plasma2040.DAT)
user_sw = Button(plasma2040.USER_SW, repeat_time=0)
button_a = Button(plasma2040.BUTTON_A, repeat_time=0)
button_b = Button(plasma2040.BUTTON_B, repeat_time=0)
led = RGBLED(plasma2040.LED_R, plasma2040.LED_G, plasma2040.LED_B)
PINS_PLASMA2040 = {"sda": plasma2040.SDA, "scl": plasma2040.SCL}
i2c = PimoroniI2C(**PINS_PLASMA2040)
enc = BreakoutEncoder(i2c)
enc.set_brightness(1.0)
COLOUR, ANGLE, BRIGHTNESS, SPEED = range(4)
def wrap(v, mn, mx):
if v <= mn:
v += mx - mn
if v > mx:
v -= mx - mn
return v
def colour_cycle(hue, t, angle):
t /= 200.0
for i in range(NUM_LEDS):
percent_along = float(i) / NUM_LEDS
offset = math.sin((percent_along + 0.5 + t) * math.pi) * angle
h = wrap((hue + offset) / 360.0, 0.0, 1.0)
led_strip.set_hsv(i, h, 1.0, 1.0)
def speed_gauge(v, vmax=100):
light_pixels = NUM_LEDS * v / vmax
for i in range(NUM_LEDS):
if i < light_pixels:
led_strip.set_rgb(i, 0, 255, 0)
else:
led_strip.set_rgb(i, 255, 0, 0)
def brightness_gauge(v, vmax=100):
light_pixels = NUM_LEDS * v / vmax
for i in range(NUM_LEDS):
if i < light_pixels:
led_strip.set_rgb(i, 64, 64, 64)
else:
led_strip.set_rgb(i, 0, 0, 0)
# Start updating the LED strip
led_strip.start()
enc.clear_interrupt_flag()
# Initialise the default values
speed = DEFAULT_SPEED
hue = DEFAULT_HUE
angle = DEFAULT_ANGLE
brightness = DEFAULT_BRIGHTNESS
cycle = True
mode = COLOUR
start_time = time.ticks_ms()
while True:
t = time.ticks_ms() - start_time
if enc.get_interrupt_flag():
count = enc.read()
enc.clear_interrupt_flag()
enc.clear()
cycle = False
if mode == COLOUR:
hue += count
hue = min(359, max(0, hue))
colour_cycle(hue, 0, angle)
elif mode == ANGLE:
angle += count
angle = min(359, max(0, angle))
colour_cycle(hue, 0, angle)
elif mode == BRIGHTNESS:
brightness += count
brightness = min(31, max(0, brightness))
led_strip.set_brightness(brightness)
brightness_gauge(brightness, 31)
elif mode == SPEED:
speed += count
speed = min(100, max(0, speed))
speed_gauge(speed, 100)
sw_pressed = user_sw.read()
a_pressed = button_a.read()
b_pressed = button_b.read()
if sw_pressed:
speed = DEFAULT_SPEED
hue = DEFAULT_HUE
angle = DEFAULT_ANGLE
brightness = DEFAULT_BRIGHTNESS
if b_pressed:
if not cycle:
start_time = time.ticks_ms()
cycle = True
speed = min(255, max(1, speed))
if mode == COLOUR:
led.set_rgb(255, 0, 0)
if a_pressed:
mode = ANGLE
elif mode == ANGLE:
led.set_rgb(255, 255, 0)
if a_pressed:
mode = BRIGHTNESS
elif mode == BRIGHTNESS:
led.set_rgb(0, 255, 0)
if a_pressed:
mode = SPEED
elif mode == SPEED:
led.set_rgb(0, 0, 255)
if a_pressed:
mode = COLOUR
if cycle:
colour_cycle(hue, float(t * speed) / 100.0, angle)
mid_led = led_strip.get(int(NUM_LEDS / 2))
enc.set_led(int(mid_led[0]), int(mid_led[1]), int(mid_led[2]))
time.sleep(1.0 / UPDATES)

Wyświetl plik

@ -14,6 +14,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutEncoder_set_brightness_obj, 2, BreakoutEncode
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutEncoder_set_led_obj, 4, BreakoutEncoder_set_led);
MP_DEFINE_CONST_FUN_OBJ_1(BreakoutEncoder_available_obj, BreakoutEncoder_available);
MP_DEFINE_CONST_FUN_OBJ_1(BreakoutEncoder_read_obj, BreakoutEncoder_read);
MP_DEFINE_CONST_FUN_OBJ_1(BreakoutEncoder_clear_obj, BreakoutEncoder_clear);
/***** Binding of Methods *****/
STATIC const mp_rom_map_elem_t BreakoutEncoder_locals_dict_table[] = {
@ -26,6 +27,7 @@ STATIC const mp_rom_map_elem_t BreakoutEncoder_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_set_led), MP_ROM_PTR(&BreakoutEncoder_set_led_obj) },
{ MP_ROM_QSTR(MP_QSTR_available), MP_ROM_PTR(&BreakoutEncoder_available_obj) },
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutEncoder_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&BreakoutEncoder_clear_obj) },
{ MP_ROM_QSTR(MP_QSTR_DIRECTION_CW), MP_ROM_INT(1) },
{ MP_ROM_QSTR(MP_QSTR_DIRECTION_CCW), MP_ROM_INT(0) },
};

Wyświetl plik

@ -199,4 +199,11 @@ mp_obj_t BreakoutEncoder_read(mp_obj_t self_in) {
breakout_encoder_BreakoutEncoder_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_encoder_BreakoutEncoder_obj_t);
return mp_obj_new_int(self->breakout->read());
}
mp_obj_t BreakoutEncoder_clear(mp_obj_t self_in) {
breakout_encoder_BreakoutEncoder_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_encoder_BreakoutEncoder_obj_t);
self->breakout->clear();
return mp_const_none;
}
}

Wyświetl plik

@ -15,4 +15,5 @@ extern mp_obj_t BreakoutEncoder_set_direction(size_t n_args, const mp_obj_t *pos
extern mp_obj_t BreakoutEncoder_set_brightness(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t BreakoutEncoder_set_led(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t BreakoutEncoder_available(mp_obj_t self_in);
extern mp_obj_t BreakoutEncoder_read(mp_obj_t self_in);
extern mp_obj_t BreakoutEncoder_read(mp_obj_t self_in);
extern mp_obj_t BreakoutEncoder_clear(mp_obj_t self_in);

Wyświetl plik

@ -7,12 +7,14 @@ MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaAPA102_set_rgb_obj, 5, PlasmaAPA102_set_rgb);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaAPA102_set_hsv_obj, 3, PlasmaAPA102_set_hsv);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaAPA102_set_brightness_obj, 2, PlasmaAPA102_set_brightness);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaAPA102_start_obj, 1, PlasmaAPA102_start);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaAPA102_get_obj, 2, PlasmaAPA102_get);
MP_DEFINE_CONST_FUN_OBJ_1(PlasmaAPA102_clear_obj, PlasmaAPA102_clear);
MP_DEFINE_CONST_FUN_OBJ_1(PlasmaWS2812___del___obj, PlasmaWS2812___del__);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaWS2812_set_rgb_obj, 5, PlasmaWS2812_set_rgb);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaWS2812_set_hsv_obj, 3, PlasmaWS2812_set_hsv);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaWS2812_start_obj, 1, PlasmaWS2812_start);
MP_DEFINE_CONST_FUN_OBJ_KW(PlasmaWS2812_get_obj, 2, PlasmaAPA102_get);
MP_DEFINE_CONST_FUN_OBJ_1(PlasmaWS2812_clear_obj, PlasmaWS2812_clear);
/***** Binding of Methods *****/
@ -22,6 +24,7 @@ STATIC const mp_rom_map_elem_t PlasmaAPA102_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_set_hsv), MP_ROM_PTR(&PlasmaAPA102_set_hsv_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_brightness), MP_ROM_PTR(&PlasmaAPA102_set_brightness_obj) },
{ MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&PlasmaAPA102_start_obj) },
{ MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&PlasmaAPA102_get_obj) },
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&PlasmaAPA102_clear_obj) },
};
STATIC const mp_rom_map_elem_t PlasmaWS2812_locals_dict_table[] = {
@ -29,6 +32,7 @@ STATIC const mp_rom_map_elem_t PlasmaWS2812_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_set_rgb), MP_ROM_PTR(&PlasmaWS2812_set_rgb_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_hsv), MP_ROM_PTR(&PlasmaWS2812_set_hsv_obj) },
{ MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&PlasmaWS2812_start_obj) },
{ MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&PlasmaWS2812_get_obj) },
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&PlasmaWS2812_clear_obj) },
};
@ -70,6 +74,8 @@ STATIC const mp_map_elem_t plasma2040_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_CLK), MP_ROM_INT(14) },
{ MP_ROM_QSTR(MP_QSTR_DAT), MP_ROM_INT(15) },
{ MP_ROM_QSTR(MP_QSTR_CURRENT_SENSE), MP_ROM_INT(29) },
{ MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_INT(20) },
{ MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_INT(21) },
{ MP_ROM_QSTR(MP_QSTR_SHUNT_RESISTOR), MP_ROM_PTR(&shunt_resistor) },
{ MP_ROM_QSTR(MP_QSTR_ADC_GAIN), MP_ROM_INT(50) },

Wyświetl plik

@ -73,7 +73,7 @@ mp_obj_t PlasmaWS2812_make_new(const mp_obj_type_t *type, size_t n_args, size_t
{ MP_QSTR_freq, MP_ARG_INT, {.u_int = WS2812::DEFAULT_SERIAL_FREQ} },
{ MP_QSTR_buffer, MP_ARG_OBJ, {.u_obj = nullptr} },
{ MP_QSTR_rgbw, MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_color_order, MP_ARG_INT, {.u_int = (uint8_t)WS2812::COLOR_ORDER::RGB} },
{ MP_QSTR_color_order, MP_ARG_INT, {.u_int = (uint8_t)WS2812::COLOR_ORDER::GRB} },
};
// Parse args.
@ -182,6 +182,29 @@ mp_obj_t PlasmaWS2812_set_hsv(size_t n_args, const mp_obj_t *pos_args, mp_map_t
return mp_const_none;
}
mp_obj_t PlasmaWS2812_get(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_index };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_index, MP_ARG_REQUIRED | MP_ARG_INT },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int index = args[ARG_index].u_int;
_PlasmaWS2812_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _PlasmaWS2812_obj_t);
WS2812::RGB rgb = self->ws2812->get(index);
mp_obj_t tuple[4];
tuple[0] = mp_obj_new_int(rgb.r);
tuple[1] = mp_obj_new_float(rgb.g);
tuple[2] = mp_obj_new_float(rgb.b);
tuple[3] = mp_obj_new_float(rgb.w);
return mp_obj_new_tuple(4, tuple);
}
/********** APA102 **********/
/***** Variables Struct *****/
@ -245,22 +268,29 @@ mp_obj_t PlasmaAPA102_make_new(const mp_obj_type_t *type, size_t n_args, size_t
int clk = args[ARG_clk].u_int;
int freq = args[ARG_freq].u_int;
void *buffer = nullptr;
APA102::RGB *buffer = nullptr;
if (args[ARG_buffer].u_obj) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW);
buffer = bufinfo.buf;
buffer = (APA102::RGB *)bufinfo.buf;
if(bufinfo.len < (size_t)(num_leds * 4)) {
mp_raise_ValueError("Supplied buffer is too small for LED count!");
}
// If a bytearray is supplied it'll be raw, uninitialized bytes
// iterate through the RGB elements and call "brightness"
// to set up the SOF bytes, otherwise a flickery mess will happen!
// Oh for such niceties as "placement new"...
for(auto i = 0; i < num_leds; i++) {
buffer[i].brightness(15);
}
}
self = m_new_obj_with_finaliser(_PlasmaAPA102_obj_t);
self->base.type = &PlasmaAPA102_type;
self->buf = buffer;
self->apa102 = new APA102(num_leds, pio, sm, dat, clk, freq, (APA102::RGB *)buffer);
self->apa102 = new APA102(num_leds, pio, sm, dat, clk, freq, buffer);
return MP_OBJ_FROM_PTR(self);
}
@ -357,4 +387,27 @@ mp_obj_t PlasmaAPA102_set_hsv(size_t n_args, const mp_obj_t *pos_args, mp_map_t
return mp_const_none;
}
mp_obj_t PlasmaAPA102_get(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_index };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_index, MP_ARG_REQUIRED | MP_ARG_INT },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int index = args[ARG_index].u_int;
_PlasmaAPA102_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _PlasmaAPA102_obj_t);
APA102::RGB rgb = self->apa102->get(index);
mp_obj_t tuple[4];
tuple[0] = mp_obj_new_int(rgb.r);
tuple[1] = mp_obj_new_float(rgb.g);
tuple[2] = mp_obj_new_float(rgb.b);
tuple[3] = mp_obj_new_float(rgb.sof);
return mp_obj_new_tuple(4, tuple);
}
}

Wyświetl plik

@ -13,6 +13,7 @@ extern mp_obj_t PlasmaAPA102_start(size_t n_args, const mp_obj_t *pos_args, mp_m
extern mp_obj_t PlasmaAPA102_set_rgb(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaAPA102_set_hsv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaAPA102_set_brightness(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaAPA102_get(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaAPA102_clear(mp_obj_t self_in);
extern void PlasmaWS2812_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind);
@ -21,6 +22,7 @@ extern mp_obj_t PlasmaWS2812___del__(mp_obj_t self_in);
extern mp_obj_t PlasmaWS2812_start(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaWS2812_set_rgb(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaWS2812_set_hsv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaWS2812_get(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t PlasmaWS2812_clear(mp_obj_t self_in);
extern bool Pimoroni_mp_obj_to_i2c(mp_obj_t in, void *out);