kopia lustrzana https://github.com/pimoroni/pimoroni-pico
Added C++ port of accelerometer example
rodzic
03a7cbee06
commit
bb23ba22db
|
@ -1,3 +1,4 @@
|
|||
include(plasma2040_level.cmake)
|
||||
include(plasma2040_monitor.cmake)
|
||||
include(plasma2040_rainbow.cmake)
|
||||
include(plasma2040_rotary.cmake)
|
||||
|
|
|
@ -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})
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -58,10 +58,10 @@ 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)
|
||||
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)
|
||||
# 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)
|
||||
|
@ -149,7 +149,7 @@ while True:
|
|||
measured_angle = ((new_measured_angle - measured_angle) * SMOOTHING_FACTOR) + measured_angle
|
||||
|
||||
if mode == ANGLE:
|
||||
# Apply the measured angle directly to the used angle, clamping it between -1 and +1
|
||||
# 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))
|
||||
|
||||
|
@ -158,6 +158,7 @@ while True:
|
|||
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))
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue