Ported most encoder wheel examples to C++

pull/774/head
ZodiusInfuser 2023-05-03 17:22:58 +01:00
rodzic e3f9f14dcf
commit 9f925b5259
19 zmienionych plików z 904 dodań i 4 usunięć

Wyświetl plik

@ -1,5 +1,6 @@
add_subdirectory(breakout_dotmatrix)
add_subdirectory(breakout_encoder)
add_subdirectory(breakout_encoder_wheel)
add_subdirectory(breakout_ioexpander)
add_subdirectory(breakout_ltr559)
add_subdirectory(breakout_colourlcd160x80)

Wyświetl plik

@ -0,0 +1,8 @@
add_subdirectory(buttons)
add_subdirectory(chase_game)
add_subdirectory(clock)
add_subdirectory(colour_picker)
add_subdirectory(encoder)
#add_subdirectory(gpio_pwm)
add_subdirectory(led_rainbow)
add_subdirectory(stop_watch)

Wyświetl plik

@ -0,0 +1,13 @@
set(OUTPUT_NAME encoderwheel_buttons)
add_executable(${OUTPUT_NAME} buttons.cpp)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME}
pico_stdlib
breakout_encoder_wheel
)
# enable usb output
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,95 @@
#include <math.h>
#include <string>
#include "breakout_encoder_wheel.hpp"
#include "time.h"
using namespace pimoroni;
using namespace encoderwheel;
/*
A demonstration of reading the 5 buttons on Encoder Wheel.
*/
// Constants
const std::string BUTTON_NAMES[] = {"Up", "Down", "Left", "Right", "Centre"};
// Create a new BreakoutEncoderWheel
I2C i2c(BOARD::BREAKOUT_GARDEN);
BreakoutEncoderWheel wheel(&i2c);
// Variables
bool last_pressed[NUM_BUTTONS] = {false, false, false, false, false};
bool pressed[NUM_BUTTONS] = {false, false, false, false, false};
int main() {
stdio_init_all();
// Attempt to initialise the encoder wheel
if(wheel.init()) {
// Loop forever
while(true) {
// Read all of the encoder wheel's buttons
for(int b = 0 ; b < NUM_BUTTONS; b++) {
pressed[b] = wheel.pressed(b);
if(pressed[b] != last_pressed[b]) {
printf("%s %s\n", BUTTON_NAMES[b].c_str(), pressed[b] ? "Pressed" : "Released");
}
last_pressed[b] = pressed[b];
}
// Clear the LED ring
wheel.clear();
for(int i = 0; i < NUM_LEDS; i++) {
if(i % 6 == 3) {
wheel.set_rgb(i, 64, 64, 64);
}
}
// If up is pressed, set the top LEDs to yellow
if(pressed[UP]) {
int mid = NUM_LEDS;
for(int i = mid - 2; i < mid + 3; i++) {
wheel.set_rgb(i % NUM_LEDS, 255, 255, 0);
}
}
// If right is pressed, set the right LEDs to red
if(pressed[RIGHT]) {
int mid = NUM_LEDS / 4;
for(int i = mid - 2; i < mid + 3; i++) {
wheel.set_rgb(i % NUM_LEDS, 255, 0, 0);
}
}
// If down is pressed, set the bottom LEDs to green
if(pressed[DOWN]) {
int mid = NUM_LEDS / 2;
for(int i = mid - 2; i < mid + 3; i++) {
wheel.set_rgb(i % NUM_LEDS, 0, 255, 0);
}
}
// If left is pressed, set the left LEDs to blue
if(pressed[LEFT]) {
int mid = (NUM_LEDS * 3) / 4;
for(int i = mid - 2; i < mid + 3; i++) {
wheel.set_rgb(i % NUM_LEDS, 0, 0, 255);
}
}
// If centre is pressed, set the diagonal LEDs to half white
if(pressed[CENTRE]) {
for(int i = 0; i < NUM_LEDS; i++) {
if(i % 6 >= 2 && i % 6 <= 4) {
wheel.set_rgb(i, 128, 128, 128);
}
}
}
wheel.show();
}
}
return 0;
}

Wyświetl plik

@ -0,0 +1,13 @@
set(OUTPUT_NAME encoderwheel_chase_game)
add_executable(${OUTPUT_NAME} chase_game.cpp)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME}
pico_stdlib
breakout_encoder_wheel
)
# enable usb output
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,147 @@
#include <math.h>
#include <string>
#include "breakout_encoder_wheel.hpp"
#include "time.h"
using namespace pimoroni;
using namespace encoderwheel;
/*
A simple alignment game. Use Encoder Wheel's rotary dial to align the coloured band
to the white goal. The closer to the goal, the greener your coloured band will be.
When you reach the goal, the goal will move to a new random position.
*/
// The band colour hues to show in Angle mode
constexpr float GOAL_HUE = 0.333f;
constexpr float FAR_HUE = 0.0f;
// The width and colour settings for the band
constexpr float BAND_WIDTH = 5.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_MARGIN = 1.0f;
constexpr float GOAL_WIDTH = BAND_WIDTH + (2.0f * GOAL_MARGIN);
constexpr float GOAL_BRIGHTNESS = 0.4f;
// Create a new BreakoutEncoderWheel
I2C i2c(BOARD::BREAKOUT_GARDEN);
BreakoutEncoderWheel wheel(&i2c);
// Variables
float goal_position = 0.0f;
int16_t band_position = 0;
// 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.0f && goal_width > 0.0) {
float band_start = centre_position - (width / 2);
float band_end = centre_position + (width / 2);
float band_centre = centre_position;
float goal_start = goal_position - (goal_width / 2);
float goal_end = goal_position + (goal_width / 2);
// Go through each led in the strip
for(int i = 0; i < NUM_LEDS; i++) {
// Set saturation and brightness values for if the led is inside or outside of the goal
float saturation = BAND_SATURATION;
float brightness = 0.0f;
if(i >= goal_start && i < goal_end) {
saturation = BAND_IN_GOAL_SATURATION;
brightness = GOAL_BRIGHTNESS;
}
if(goal_end >= NUM_LEDS && i + NUM_LEDS < goal_end) {
saturation = BAND_IN_GOAL_SATURATION;
brightness = GOAL_BRIGHTNESS;
}
if(goal_start < 0 && i - NUM_LEDS >= goal_start) {
saturation = BAND_IN_GOAL_SATURATION;
brightness = GOAL_BRIGHTNESS;
}
float val = brightness;
float sat = 0.0f;
if(i >= band_start && i < band_end) {
// Inside the band
if(i < band_centre) {
// Transition into the band
val = map(i, band_centre, band_start, BAND_BRIGHTNESS, brightness);
sat = map(i, band_centre, band_start, BAND_SATURATION, saturation);
}
else {
val = map(i, band_centre, band_end, BAND_BRIGHTNESS, brightness);
sat = map(i, band_centre, band_end, BAND_SATURATION, saturation);
}
}
else if(band_end >= NUM_LEDS && i + NUM_LEDS < band_end && i < band_centre) {
val = map(i + NUM_LEDS, band_centre, band_end, BAND_BRIGHTNESS, brightness);
sat = map(i + NUM_LEDS, band_centre, band_end, BAND_SATURATION, saturation);
}
else if(band_start < 0 && i - NUM_LEDS >= band_start && i >= band_centre) {
val = map(i - NUM_LEDS, band_centre, band_start, BAND_BRIGHTNESS, brightness);
sat = map(i - NUM_LEDS, band_centre, band_start, BAND_SATURATION, saturation);
}
//else {
// Outside of the band
//}
wheel.set_hsv(i, hue, sat, val);
}
wheel.show();
}
}
int main() {
stdio_init_all();
// Attempt to initialise the encoder wheel
if(wheel.init()) {
// Loop forever
while(true) {
band_position = wheel.step();
// Convert the difference between the band and goal positions into a colour hue
float diff1, diff2;
if(band_position > goal_position) {
diff1 = band_position - goal_position;
diff2 = (goal_position + NUM_LEDS) - band_position;
}
else {
diff1 = goal_position - band_position;
diff2 = (band_position + NUM_LEDS) - goal_position;
}
float position_diff = MIN(diff1, diff2);
float hue = map(position_diff, 0, NUM_LEDS / 2.0f, GOAL_HUE, FAR_HUE);
// Convert the band and goal positions to positions on the LED strip
float strip_band_position = map(band_position, 0, NUM_LEDS, 0.0f, (float)NUM_LEDS);
float strip_goal_position = map(goal_position, 0, NUM_LEDS, 0.0f, (float)NUM_LEDS);
// Draw the band and goal
colour_band(strip_band_position, BAND_WIDTH, strip_goal_position, GOAL_WIDTH, hue);
// Check if the band is within the goal, and if so, set a new goal
if(band_position >= goal_position - GOAL_MARGIN && band_position <= goal_position + GOAL_MARGIN)
goal_position = rand() % NUM_LEDS;
if(band_position >= NUM_LEDS && band_position + NUM_LEDS < goal_position + GOAL_MARGIN)
goal_position = rand() % NUM_LEDS;
if(goal_position - GOAL_MARGIN < 0 && band_position - NUM_LEDS >= goal_position + GOAL_MARGIN)
goal_position = rand() % NUM_LEDS;
}
}
return 0;
}

Wyświetl plik

@ -0,0 +1,14 @@
set(OUTPUT_NAME encoderwheel_clock)
add_executable(${OUTPUT_NAME} clock.cpp)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME}
pico_stdlib
hardware_rtc
breakout_encoder_wheel
)
# enable usb output
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,113 @@
#include <math.h>
#include <string>
#include "breakout_encoder_wheel.hpp"
#include "time.h"
#include "hardware/rtc.h"
#include "pico/util/datetime.h"
using namespace pimoroni;
using namespace encoderwheel;
/*
Displays a 12 hour clock on Encoder Wheel's LED ring, getting time from the system.
*/
// Datetime Indices
const uint HOUR = 4;
const uint MINUTE = 5;
const uint SECOND = 6;
// Constants
constexpr float BRIGHTNESS = 1.0f; // The brightness of the LEDs
const uint UPDATES = 50; // How many times the LEDs will be updated per second
const uint UPDATE_RATE_US = 1000000 / UPDATES;
// Handy values for the number of milliseconds
constexpr float MILLIS_PER_SECOND = 1000;
constexpr float MILLIS_PER_MINUTE = MILLIS_PER_SECOND * 60;
constexpr float MILLIS_PER_HOUR = MILLIS_PER_MINUTE * 60;
constexpr float MILLIS_PER_HALF_DAY = MILLIS_PER_HOUR * 12;
// Create a new BreakoutEncoderWheel
I2C i2c(BOARD::BREAKOUT_GARDEN);
BreakoutEncoderWheel wheel(&i2c);
// Calculates the brightness of an LED based on its index and a position along the LED ring
int led_brightness_at(int led, float position, float half_width = 1.0f, float span = 1.0f) {
float brightness = 0.0f;
float upper = position + half_width;
float lower = position - half_width;
if(led > position)
brightness = CLAMP((upper - led) / span, 0.0f, 1.0f);
else
brightness = CLAMP((led - lower) / span, 0.0f, 1.0f);
// Handle the LEDs being in a circle
if(upper >= NUM_LEDS)
brightness = CLAMP(((upper - NUM_LEDS) - led) / span, brightness, 1.0f);
else if(lower < 0.0f)
brightness = CLAMP((led - (lower + NUM_LEDS)) / span, brightness, 1.0f);
return (int)(brightness * BRIGHTNESS * 255);
}
int main() {
stdio_init_all();
// Start on Thursday 4th of May 2023 14:20:00
datetime_t now = {
.year = 2023,
.month = 05,
.day = 04,
.dotw = 4, // 0 is Sunday, so 4 is Thursday
.hour = 14,
.min = 20,
.sec = 00
};
// Start the RTC
rtc_init();
rtc_set_datetime(&now);
// clk_sys is >2000x faster than clk_rtc, so datetime is not updated immediately when rtc_get_datetime() is called.
// tbe delay is up to 3 RTC clock cycles (which is 64us with the default clock settings)
sleep_us(64);
// Attempt to initialise the encoder wheel
if(wheel.init()) {
// Loop forever
while(true) {
// Record the start time of this loop
absolute_time_t start_time = get_absolute_time();
// Get the current system time
rtc_get_datetime(&now);
// Convert the seconds, minutes, and hours into milliseconds (this is done to give a smoother animation, particularly for the seconds hand)
uint sec_as_millis = (now.sec * MILLIS_PER_SECOND);
uint min_as_millis = (now.min * MILLIS_PER_MINUTE) + sec_as_millis;
uint hour_as_millis = ((now.hour % 12) * MILLIS_PER_HOUR) + min_as_millis;
// Calculate the position on the LED ring that the, second, minute, and hour hands should be
float sec_pos = MIN(sec_as_millis / MILLIS_PER_MINUTE, 1.0f) * NUM_LEDS;
float min_pos = MIN(min_as_millis / MILLIS_PER_HOUR, 1.0f) * NUM_LEDS;
float hour_pos = MIN(hour_as_millis / MILLIS_PER_HALF_DAY, 1.0f) * NUM_LEDS;
for(int i = 0; i < NUM_LEDS; i++) {
// Turn on the LEDs close to the position of the current second, minute, and hour
int r = led_brightness_at(i, sec_pos);
int g = led_brightness_at(i, min_pos);
int b = led_brightness_at(i, hour_pos);
wheel.set_rgb(i, r, g, b);
}
wheel.show();
// Sleep until the next update, accounting for how long the above operations took to perform
sleep_until(delayed_by_us(start_time, UPDATE_RATE_US));
}
}
return 0;
}

Wyświetl plik

@ -0,0 +1,13 @@
set(OUTPUT_NAME encoderwheel_colour_picker)
add_executable(${OUTPUT_NAME} colour_picker.cpp)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME}
pico_stdlib
breakout_encoder_wheel
)
# enable usb output
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,167 @@
#include <math.h>
#include <string>
#include "breakout_encoder_wheel.hpp"
#include "time.h"
using namespace pimoroni;
using namespace encoderwheel;
/*
Create a colour wheel on the Encoder Wheel's LED ring, and use all functions of the wheel to interact with it.
Rotate the wheel to select a Hue
Press the up direction to increase Brightness
Press the down direction to decrease Brightness
Press the left direction to decrease Saturation
Press the right direction to increase Saturation
Press the centre to hide the selection marker
*/
// Constants
constexpr float BRIGHTNESS_STEP = 0.02f; // How much to increase or decrease the brightness each update
constexpr float SATURATION_STEP = 0.02f; // How much to increase or decrease the saturation each update
const uint UPDATES = 50; // How many times to update the LEDs per second
const uint UPDATE_RATE_US = 1000000 / UPDATES;
// Create a new BreakoutEncoderWheel
I2C i2c(BOARD::BREAKOUT_GARDEN);
BreakoutEncoderWheel wheel(&i2c);
// Variables
float brightness = 1.0f;
float saturation = 1.0f;
int position = 0;
bool changed = true;
bool last_centre_pressed = false;
// Struct for storing RGB values
struct Pixel {
uint8_t r;
uint8_t g;
uint8_t b;
Pixel() : r(0), g(0), b(0) {};
Pixel(uint8_t r, uint8_t g, uint8_t b) : r(r), g(g), b(b) {};
};
// Basic function to convert Hue, Saturation and Value to an RGB colour
Pixel hsv_to_rgb(float h, float s, float v) {
if(h < 0.0f) {
h = 1.0f + fmodf(h, 1.0f);
}
int i = int(h * 6);
float f = h * 6 - i;
v = v * 255.0f;
float sv = s * v;
float fsv = f * sv;
auto p = uint8_t(-sv + v);
auto q = uint8_t(-fsv + v);
auto t = uint8_t(fsv - sv + v);
uint8_t bv = uint8_t(v);
switch (i % 6) {
default:
case 0: return Pixel(bv, t, p);
case 1: return Pixel(q, bv, p);
case 2: return Pixel(p, bv, t);
case 3: return Pixel(p, q, bv);
case 4: return Pixel(t, p, bv);
case 5: return Pixel(bv, p, q);
}
}
// Simple function to clamp a value between 0.0 and 1.0
float clamp01(float value) {
return MAX(MIN(value, 1.0f), 0.0f);
}
int main() {
stdio_init_all();
// Attempt to initialise the encoder wheel
if(wheel.init()) {
// Loop forever
while(true) {
// Record the start time of this loop
absolute_time_t start_time = get_absolute_time();
// If up is pressed, increase the brightness
if(wheel.pressed(UP)) {
brightness += BRIGHTNESS_STEP;
changed = true; // Trigger a change
}
// If down is pressed, decrease the brightness
if(wheel.pressed(DOWN)) {
brightness -= BRIGHTNESS_STEP;
changed = true; // Trigger a change
}
// If right is pressed, increase the saturation
if(wheel.pressed(RIGHT)) {
saturation += SATURATION_STEP;
changed = true; // Trigger a change
}
// If left is pressed, decrease the saturation
if(wheel.pressed(LEFT)) {
saturation -= SATURATION_STEP;
changed = true; // Trigger a change
}
// Limit the brightness and saturation between 0.0 and 1.0
brightness = clamp01(brightness);
saturation = clamp01(saturation);
// Check if the encoder has been turned
if(wheel.delta() != 0) {
// Update the position based on the count change
position = wheel.step();
changed = true; // Trigger a change
}
// If centre is pressed, trigger a change
bool centre_pressed = wheel.pressed(CENTRE);
if(centre_pressed != last_centre_pressed) {
changed = true;
}
last_centre_pressed = centre_pressed;
// Was a change triggered?
if(changed) {
// Print the colour at the current hue, saturation, and brightness
Pixel pixel = hsv_to_rgb((float)position / NUM_LEDS, saturation, brightness);
printf("Colour Code = #%02x%02x%02x\n", pixel.r, pixel.g, pixel.b);
// Set the LED at the current position to either the actual colour,
// or an inverted version to show a "selection marker"
if(centre_pressed)
wheel.set_rgb(position, pixel.r, pixel.g, pixel.b);
else
wheel.set_rgb(position, 255 - pixel.r, 255 - pixel.g, 255 - pixel.b);
// Set the LEDs below the current position
for(int i = 0; i < position; i++) {
wheel.set_hsv(i, (float)i / NUM_LEDS, saturation, brightness);
}
// Set the LEDs after the current position
for(int i = position + 1; i < NUM_LEDS; i++) {
wheel.set_hsv(i, (float)i / NUM_LEDS, saturation, brightness);
}
wheel.show();
changed = false;
}
// Sleep until the next update, accounting for how long the above operations took to perform
sleep_until(delayed_by_us(start_time, UPDATE_RATE_US));
}
}
return 0;
}

Wyświetl plik

@ -0,0 +1,13 @@
set(OUTPUT_NAME encoderwheel_encoder)
add_executable(${OUTPUT_NAME} encoder.cpp)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME}
pico_stdlib
breakout_encoder_wheel
)
# enable usb output
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,59 @@
#include <math.h>
#include <string>
#include "breakout_encoder_wheel.hpp"
#include "time.h"
using namespace pimoroni;
using namespace encoderwheel;
/*
A demonstration of reading the rotary dial of the Encoder Wheel breakout.
*/
// Create a new BreakoutEncoderWheel
I2C i2c(BOARD::BREAKOUT_GARDEN);
BreakoutEncoderWheel wheel(&i2c);
// Variables
int position = 0;
float hue = 0.0f;
int main() {
stdio_init_all();
// Attempt to initialise the encoder wheel
if(wheel.init()) {
// Set the first LED
wheel.clear();
wheel.set_hsv(position, hue, 1.0f, 1.0f);
wheel.show();
// Loop forever
while(true) {
// Has the dial been turned since the last time we checked?
int16_t change = wheel.delta();
if(change != 0) {
// Print out the direction the dial was turned, and the count
if(change > 0)
printf("Clockwise, Count = %d\n", wheel.count());
else
printf("Counter Clockwise, Count = %d\n", wheel.count());
// Record the new position (from 0 to 23)
position = wheel.step();
// Record a colour hue from 0.0 to 1.0
hue = fmodf(wheel.revolutions(), 1.0f);
// Set the LED at the new position to the new hue
wheel.clear();
wheel.set_hsv(position, hue, 1.0f, 1.0f);
wheel.show();
}
}
}
return 0;
}

Wyświetl plik

@ -0,0 +1,13 @@
set(OUTPUT_NAME encoderwheel_led_rainbow)
add_executable(${OUTPUT_NAME} led_rainbow.cpp)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME}
pico_stdlib
breakout_encoder_wheel
)
# enable usb output
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,53 @@
#include <math.h>
#include <string>
#include "breakout_encoder_wheel.hpp"
#include "time.h"
using namespace pimoroni;
using namespace encoderwheel;
/*
Displays a rotating rainbow pattern on Encoder Wheel's LED ring.
*/
// Constants
constexpr float SPEED = 5.0f; // The speed that the LEDs will cycle at
constexpr float BRIGHTNESS = 1.0f; // The brightness of the LEDs
const uint UPDATES = 50; // How many times the LEDs will be updated per second
const uint UPDATE_RATE_US = 1000000 / UPDATES;
// Create a new BreakoutEncoderWheel
I2C i2c(BOARD::BREAKOUT_GARDEN);
BreakoutEncoderWheel wheel(&i2c);
// Variables
float offset = 0.0;
int main() {
stdio_init_all();
// Attempt to initialise the encoder wheel
if(wheel.init()) {
// Loop forever
while(true) {
// Record the start time of this loop
absolute_time_t start_time = get_absolute_time();
offset += SPEED / 1000.0f;
// Update all the LEDs
for(int i = 0; i < NUM_LEDS; i++) {
float hue = (float)i / NUM_LEDS;
wheel.set_hsv(i, hue + offset, 1.0, BRIGHTNESS);
}
wheel.show();
// Sleep until the next update, accounting for how long the above operations took to perform
sleep_until(delayed_by_us(start_time, UPDATE_RATE_US));
}
}
return 0;
}

Wyświetl plik

@ -0,0 +1,13 @@
set(OUTPUT_NAME encoderwheel_stop_watch)
add_executable(${OUTPUT_NAME} stop_watch.cpp)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME}
pico_stdlib
breakout_encoder_wheel
)
# enable usb output
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,150 @@
#include <math.h>
#include <string>
#include "breakout_encoder_wheel.hpp"
#include "time.h"
using namespace pimoroni;
using namespace encoderwheel;
/*
Display a circular stop-watch on the Encoder Wheel's LED ring.
Press the centre button to start the stopwatch, then again to pause and resume.
*/
// Constants
constexpr float BRIGHTNESS = 1.0f; // The brightness of the LEDs
const uint UPDATES = 50; // How many times the LEDs will be updated per second
const uint MINUTE_UPDATES = UPDATES * 60; // How many times the LEDs will be updated per minute
const uint HOUR_UPDATES = MINUTE_UPDATES * 60; // How many times the LEDs will be updated per hour
const uint UPDATE_RATE_US = 1000000 / UPDATES;
constexpr float IDLE_PULSE_MIN = 0.2f; // The brightness (between 0.0 and 1.0) that the idle pulse will go down to
constexpr float IDLE_PULSE_MAX = 0.5f; // The brightness (between 0.0 and 1.0) that the idle pulse will go up to
constexpr float IDLE_PULSE_TIME = 2.0f; // The time (in seconds) to perform a complete idle pulse
constexpr uint UPDATES_PER_PULSE = IDLE_PULSE_TIME * UPDATES;
// The state constants used for program flow
enum State {
IDLE = 0,
COUNTING,
PAUSED
};
// Create a new BreakoutEncoderWheel
I2C i2c(BOARD::BREAKOUT_GARDEN);
BreakoutEncoderWheel wheel(&i2c);
// Variables
State state = IDLE;
uint idle_update = 0;
uint second_update = 0;
uint minute_update = 0;
uint hour_update = 0;
bool last_centre_pressed = false;
int main() {
stdio_init_all();
// Attempt to initialise the encoder wheel
if(wheel.init()) {
// Record the current time
absolute_time_t current_time = get_absolute_time();
// Run the update loop forever
while(true) {
// Record the start time of this loop
absolute_time_t start_time = get_absolute_time();
// Read whether or not the wheen centre has been pressed
bool centre_pressed = wheel.pressed(CENTRE);
if(centre_pressed && centre_pressed != last_centre_pressed) {
switch(state) {
case IDLE: // If we're currently idle, switch to counting
second_update = 0;
minute_update = 0;
hour_update = 0;
state = COUNTING;
break;
case COUNTING: // If we're counting, switch to paused
state = PAUSED;
break;
case PAUSED: // If we're paused, switch back to counting
state = COUNTING;
}
}
last_centre_pressed = centre_pressed;
switch(state) {
// If we're idle, perform a pulsing animation to show the stopwatch is ready to go
case IDLE:
{
float percent_along = MIN((float)idle_update / (float)UPDATES_PER_PULSE, 1.0f);
float brightness = ((cosf(percent_along * M_PI * 2.0f) + 1.0f) / 2.0f) * ((IDLE_PULSE_MAX - IDLE_PULSE_MIN)) + IDLE_PULSE_MIN;
// Update all the LEDs
for(int i = 0; i < NUM_LEDS; i++) {
wheel.set_hsv(i, 0.0, 0.0, brightness);
}
wheel.show();
// Advance to the next update, wrapping around to zero if at the end
idle_update += 1;
if(idle_update >= UPDATES_PER_PULSE) {
idle_update = 0;
}
}
break;
// If we're counting, perform the stopwatch animation
case COUNTING:
{
// Calculate how many LED channels should light, as a proportion of a second, minute, and hour
float r_to_light = MIN((float)second_update / UPDATES, 1.0f) * 24.0f;
float g_to_light = MIN((float)minute_update / MINUTE_UPDATES, 1.0f) * 24.0f;
float b_to_light = MIN((float)hour_update / HOUR_UPDATES, 1.0f) * 24.0f;
// Set each LED, such that ones below the current time are fully lit, ones after
// are off, and the one at the transition is at a percentage of the brightness
for(int i = 0; i < NUM_LEDS; i++) {
int r = (int)(CLAMP(r_to_light - i, 0.0f, 1.0f) * BRIGHTNESS * 255.0f);
int g = (int)(CLAMP(g_to_light - i, 0.0f, 1.0f) * BRIGHTNESS * 255.0f);
int b = (int)(CLAMP(b_to_light - i, 0.0f, 1.0f) * BRIGHTNESS * 255.0f);
wheel.set_rgb(i, r, g, b);
}
wheel.show();
// Advance the second updates count, wrapping around to zero if at the end
second_update += 1;
if(second_update >= UPDATES) {
second_update = 0;
}
// Advance the minute updates count, wrapping around to zero if at the end
minute_update += 1;
if(minute_update >= MINUTE_UPDATES) {
minute_update = 0;
}
// Advance the hour updates count, wrapping around to zero if at the end
hour_update += 1;
if(hour_update >= HOUR_UPDATES) {
hour_update = 0;
}
}
break;
case PAUSED:
// Do nothing
break;
}
// Sleep until the next update, accounting for how long the above operations took to perform
current_time = delayed_by_us(start_time, UPDATE_RATE_US);
sleep_until(current_time);
}
}
return 0;
}

Wyświetl plik

@ -3,6 +3,7 @@
#include <cmath>
namespace pimoroni {
namespace encoderwheel {
bool BreakoutEncoderWheel::init(bool skip_chip_id_check) {
bool success = false;
@ -151,7 +152,7 @@ namespace pimoroni {
void BreakoutEncoderWheel::set_hsv(int index, float h, float s, float v) {
int r, g, b;
if(h < 0.0f) {
h = 1.0f + fmod(h, 1.0f);
h = 1.0f + fmodf(h, 1.0f);
}
int i = int(h * 6);
@ -244,4 +245,5 @@ namespace pimoroni {
}
}
}
}
}

Wyświetl plik

@ -5,6 +5,21 @@
#include "common/pimoroni_common.hpp"
namespace pimoroni {
namespace encoderwheel {
static const uint8_t NUM_LEDS = 24;
static const uint8_t NUM_BUTTONS = 5;
static const uint8_t NUM_GPIOS = 3;
static const uint8_t UP = 0;
static const uint8_t DOWN = 1;
static const uint8_t LEFT = 2;
static const uint8_t RIGHT = 3;
static const uint8_t CENTRE = 4;
static const uint8_t GP7 = 7;
static const uint8_t GP8 = 8;
static const uint8_t GP9 = 9;
static const uint8_t GPIOS[] = {GP7, GP8, GP9};
class BreakoutEncoderWheel {
struct RGBLookup {
@ -142,5 +157,5 @@ namespace pimoroni {
private:
void take_encoder_reading();
};
}
}

Wyświetl plik

@ -12,8 +12,6 @@ PINS_PICO_EXPLORER = {"sda": 20, "scl": 21}
# Constants
BUTTON_NAMES = ["Up", "Down", "Left", "Right", "Centre"]
UPDATES = 50 # How many times the buttons will be checked and LEDs updated, per second
UPDATE_RATE = 1 / UPDATES
# Create a new BreakoutEncoderWheel
i2c = PimoroniI2C(**PINS_BREAKOUT_GARDEN)