Progress on encoder wheel C++ and MP

pull/774/head
ZodiusInfuser 2023-05-02 16:19:22 +01:00
rodzic 928c28b677
commit d4d6cd1936
9 zmienionych plików z 214 dodań i 65 usunięć

Wyświetl plik

@ -1,5 +1,6 @@
#include "breakout_encoder_wheel.hpp" #include "breakout_encoder_wheel.hpp"
#include <algorithm> #include <algorithm>
#include <cmath>
namespace pimoroni { namespace pimoroni {
@ -13,7 +14,21 @@ namespace pimoroni {
ioe.setup_rotary_encoder(ENC_CHANNEL, ENC_TERM_A, ENC_TERM_B); ioe.setup_rotary_encoder(ENC_CHANNEL, ENC_TERM_A, ENC_TERM_B);
success = true; if(led_ring.init()) {
led_ring.enable({
0b00000000, 0b10111111,
0b00111110, 0b00111110,
0b00111111, 0b10111110,
0b00000111, 0b10000110,
0b00110000, 0b00110000,
0b00111111, 0b10111110,
0b00111111, 0b10111110,
0b01111111, 0b11111110,
0b01111111, 0b00000000
}, 0);
success = true;
}
} }
return success; return success;
@ -55,22 +70,127 @@ namespace pimoroni {
ioe.clear_interrupt_flag(); ioe.clear_interrupt_flag();
} }
BreakoutEncoderWheel::Direction BreakoutEncoderWheel::get_encoder_direction() { bool BreakoutEncoderWheel::pressed(uint button) {
//return direction; return 0; // TODO
return DEFAULT_DIRECTION;
} }
void BreakoutEncoderWheel::set_encoder_direction(Direction direction) { int BreakoutEncoderWheel::count() {
//this->direction = direction; return 0; // TODO
} }
void BreakoutEncoderWheel::set_pixel(uint8_t index, uint8_t r, uint8_t g, uint8_t b) { int BreakoutEncoderWheel::delta() {
return 0; // TODO
}
int BreakoutEncoderWheel::step() {
return 0; // TODO
}
int BreakoutEncoderWheel::turn() {
return 0; // TODO
}
void BreakoutEncoderWheel::zero() {
}
float BreakoutEncoderWheel::revolutions() {
return 0; // TODO
}
float BreakoutEncoderWheel::degrees() {
return 0; // TODO
}
float BreakoutEncoderWheel::radians() {
return 0; // TODO
}
Direction BreakoutEncoderWheel::direction() {
return enc_direction;
}
void BreakoutEncoderWheel::direction(Direction direction) {
enc_direction = direction;
}
void BreakoutEncoderWheel::set_rgb(int index, int r, int g, int b) {
RGBLookup rgb = lookup_table[index]; RGBLookup rgb = lookup_table[index];
led_ring.set(rgb.r, r); led_ring.set(rgb.r, r);
led_ring.set(rgb.g, g); led_ring.set(rgb.g, g);
led_ring.set(rgb.b, b); led_ring.set(rgb.b, b);
} }
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);
}
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: r = bv; g = t; b = p; break;
case 1: r = q; g = bv; b = p; break;
case 2: r = p; g = bv; b = t; break;
case 3: r = p; g = q; b = bv; break;
case 4: r = t; g = p; b = bv; break;
case 5: r = bv; g = p; b = q; break;
}
set_rgb(index, r, g, b);
}
void BreakoutEncoderWheel::clear() {
led_ring.clear();
}
void BreakoutEncoderWheel::show() {
led_ring.update();
}
int BreakoutEncoderWheel::gpio_pin_mode(int gpio) {
return 0; // TODO
}
void BreakoutEncoderWheel::gpio_pin_mode(int gpio, int mode) {
}
int BreakoutEncoderWheel::gpio_pin_value(int gpio) {
return 0; // TODO
}
float BreakoutEncoderWheel::gpio_pin_value_as_voltage(int gpio) {
return 0; // TODO
}
void BreakoutEncoderWheel::gpio_pin_value(int gpio, int value, bool load, bool wait_for_load) {
}
void BreakoutEncoderWheel::gpio_pwm_load(bool wait_for_load) {
}
int BreakoutEncoderWheel::gpio_pwm_frequency(float frequency, bool load, bool wait_for_load) {
return 0; // TODO
}
/*
bool BreakoutEncoderWheel::wheel_available() { bool BreakoutEncoderWheel::wheel_available() {
return (ioe.get_interrupt_flag() > 0); return (ioe.get_interrupt_flag() > 0);
} }
@ -84,8 +204,5 @@ namespace pimoroni {
//return count; //return count;
return 0; return 0;
} }
*/
void BreakoutEncoderWheel::clear_wheel() {
ioe.clear_rotary_encoder(ENC_CHANNEL);
}
} }

Wyświetl plik

@ -13,15 +13,6 @@ namespace pimoroni {
uint8_t b; uint8_t b;
}; };
//--------------------------------------------------
// Enums
//--------------------------------------------------
public:
enum Direction : bool {
DIRECTION_CW = true,
DIRECTION_CCW = false
};
//-------------------------------------------------- //--------------------------------------------------
// Constants // Constants
@ -29,22 +20,21 @@ namespace pimoroni {
public: public:
static const uint8_t DEFAULT_IOE_I2C_ADDRESS = 0x13; static const uint8_t DEFAULT_IOE_I2C_ADDRESS = 0x13;
static const uint8_t DEFAULT_LED_I2C_ADDRESS = 0x77; static const uint8_t DEFAULT_LED_I2C_ADDRESS = 0x77;
static const uint8_t LED_I2C_ADDRESS_ALTERNATE = 0x74; static const uint8_t ALTERNATE_LED_I2C_ADDRESS = 0x74;
static const Direction DEFAULT_DIRECTION = DIRECTION_CW; static const Direction DEFAULT_DIRECTION = NORMAL_DIR;
static const uint32_t DEFAULT_TIMEOUT = 1; static const uint32_t DEFAULT_TIMEOUT = 1;
private: private:
static const uint8_t SWITCH_CENTRE = 1; static const uint8_t ENC_CHANNEL = 1;
static const uint8_t SWITCH_UP = 13;
static const uint8_t SWITCH_LEFT = 11;
static const uint8_t SWITCH_DOWN = 4;
static const uint8_t SWITCH_RIGHT = 2;
static const uint8_t ENC_TERM_A = 12; static const uint8_t ENC_TERM_A = 12;
static const uint8_t ENC_TERM_B = 3; static const uint8_t ENC_TERM_B = 3;
static const uint8_t ENC_CHANNEL = 1; static const uint8_t SW_UP = 13;
static const uint8_t SW_DOWN = 4;
static const uint8_t SW_LEFT = 11;
static const uint8_t SW_RIGHT = 2;
static const uint8_t SW_CENTRE = 1;
// This wonderful lookup table maps the LEDs on the encoder wheel // This wonderful lookup table maps the LEDs on the encoder wheel
// from their 3x24 (remember, they're RGB) configuration to // from their 3x24 (remember, they're RGB) configuration to
@ -85,7 +75,7 @@ namespace pimoroni {
IS31FL3731 led_ring; IS31FL3731 led_ring;
//Direction direction = DEFAULT_DIRECTION; //Direction direction = DEFAULT_DIRECTION;
uint interrupt_pin = PIN_UNUSED; // A local copy of the value passed to the IOExpander, used in initialisation uint interrupt_pin = PIN_UNUSED; // A local copy of the value passed to the IOExpander, used in initialisation
Direction enc_direction = DEFAULT_DIRECTION;
//-------------------------------------------------- //--------------------------------------------------
// Constructors/Destructor // Constructors/Destructor
@ -118,17 +108,29 @@ namespace pimoroni {
void clear_interrupt_flag(); void clear_interrupt_flag();
// Encoder breakout specific // Encoder breakout specific
Direction get_encoder_direction(); bool pressed(uint button);
void set_encoder_direction(Direction direction); int count();
int delta();
int step();
int turn();
void zero();
float revolutions();
float degrees();
float radians();
Direction direction();
void direction(Direction direction);
void set_rgb(int index, int r, int g, int b);
void set_hsv(int index, float h, float s = 1.0f, float v = 1.0f);
void clear();
void show();
void set_pixel(uint8_t index, uint8_t r, uint8_t g, uint8_t b); int gpio_pin_mode(int gpio);
//void update(uint8_t frame = 0); void gpio_pin_mode(int gpio, int mode);
//void clear(); int gpio_pin_value(int gpio);
float gpio_pin_value_as_voltage(int gpio);
bool wheel_available(); void gpio_pin_value(int gpio, int value, bool load = true, bool wait_for_load = false);
int16_t read_wheel(); void gpio_pwm_load(bool wait_for_load = false);
void clear_wheel(); int gpio_pwm_frequency(float frequency, bool load = true, bool wait_for_load = false);
bool read_switch();
}; };
} }

Wyświetl plik

@ -1,5 +1,5 @@
import time import time
from datetime import datetime from machine import RTC
from pimoroni_i2c import PimoroniI2C from pimoroni_i2c import PimoroniI2C
from breakout_encoder_wheel import BreakoutEncoderWheel, NUM_LEDS from breakout_encoder_wheel import BreakoutEncoderWheel, NUM_LEDS
@ -13,6 +13,12 @@ Press Ctrl+C to stop the program.
PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5} PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5}
PINS_PICO_EXPLORER = {"sda": 20, "scl": 21} PINS_PICO_EXPLORER = {"sda": 20, "scl": 21}
# Datetime Indices
HOUR = 4
MINUTE = 5
SECOND = 6
MICROSECOND = 7
# Constants # Constants
BRIGHTNESS = 1.0 # The brightness of the LEDs BRIGHTNESS = 1.0 # The brightness of the LEDs
UPDATES = 50 # How many times the LEDs will be updated per second UPDATES = 50 # How many times the LEDs will be updated per second
@ -27,12 +33,13 @@ MILLIS_PER_HALF_DAY = MILLIS_PER_HOUR * 12
# Create a new BreakoutEncoderWheel # Create a new BreakoutEncoderWheel
i2c = PimoroniI2C(**PINS_BREAKOUT_GARDEN) i2c = PimoroniI2C(**PINS_BREAKOUT_GARDEN)
wheel = BreakoutEncoderWheel(i2c) wheel = BreakoutEncoderWheel(i2c)
rtc = machine.RTC()
# Sleep until a specific time in the future. Use this instead of time.sleep() to correct for # Sleep until a specific time in the future. Use this instead of time.sleep() to correct for
# inconsistent timings when dealing with complex operations or external communication # inconsistent timings when dealing with complex operations or external communication
def sleep_until(end_time): def sleep_until(end_time):
time_to_sleep = end_time - time.monotonic() time_to_sleep = end_time - (time.ticks_ms() / 1000)
if time_to_sleep > 0.0: if time_to_sleep > 0.0:
time.sleep(time_to_sleep) time.sleep(time_to_sleep)
@ -58,22 +65,22 @@ def led_brightness_at(led, position, half_width=1, span=1):
elif lower < 0.0: elif lower < 0.0:
brightness = clamp((led - (lower + NUM_LEDS)) / span, brightness, 1.0) brightness = clamp((led - (lower + NUM_LEDS)) / span, brightness, 1.0)
return brightness * BRIGHTNESS * 255 return int(brightness * BRIGHTNESS * 255)
# Make rainbows # Make rainbows
while True: while True:
# Record the start time of this loop # Record the start time of this loop
start_time = time.monotonic() start_time = time.ticks_ms() / 1000
# Get the current system time # Get the current system time
now = datetime.now() now = rtc.datetime()
# Convert the seconds, minutes, and hours into milliseconds (this is done to give a smoother animation, particularly for the seconds hand) # Convert the seconds, minutes, and hours into milliseconds (this is done to give a smoother animation, particularly for the seconds hand)
sec_as_millis = (now.second * MILLIS_PER_SECOND) + (now.microsecond // MILLIS_PER_SECOND) sec_as_millis = (now[SECOND] * MILLIS_PER_SECOND) + (now[MICROSECOND] // MILLIS_PER_SECOND)
min_as_millis = (now.minute * MILLIS_PER_MINUTE) + sec_as_millis min_as_millis = (now[MINUTE] * MILLIS_PER_MINUTE) + sec_as_millis
hour_as_millis = ((now.hour % 12) * MILLIS_PER_HOUR) + min_as_millis 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 # Calculate the position on the LED ring that the, second, minute, and hour hands should be
sec_pos = min(sec_as_millis / MILLIS_PER_MINUTE, 1.0) * NUM_LEDS sec_pos = min(sec_as_millis / MILLIS_PER_MINUTE, 1.0) * NUM_LEDS

Wyświetl plik

@ -1,5 +1,4 @@
import time import time
from colorsys import hsv_to_rgb
from pimoroni_i2c import PimoroniI2C from pimoroni_i2c import PimoroniI2C
from breakout_encoder_wheel import BreakoutEncoderWheel, UP, DOWN, LEFT, RIGHT, CENTRE, NUM_LEDS from breakout_encoder_wheel import BreakoutEncoderWheel, UP, DOWN, LEFT, RIGHT, CENTRE, NUM_LEDS
@ -38,6 +37,30 @@ changed = True
last_centre_pressed = False last_centre_pressed = False
# 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
# Simple function to clamp a value between 0.0 and 1.0 # Simple function to clamp a value between 0.0 and 1.0
def clamp01(value): def clamp01(value):
return max(min(value, 1.0), 0.0) return max(min(value, 1.0), 0.0)
@ -46,14 +69,14 @@ def clamp01(value):
# Sleep until a specific time in the future. Use this instead of time.sleep() to correct for # Sleep until a specific time in the future. Use this instead of time.sleep() to correct for
# inconsistent timings when dealing with complex operations or external communication # inconsistent timings when dealing with complex operations or external communication
def sleep_until(end_time): def sleep_until(end_time):
time_to_sleep = end_time - time.monotonic() time_to_sleep = end_time - (time.ticks_ms() / 1000)
if time_to_sleep > 0.0: if time_to_sleep > 0.0:
time.sleep(time_to_sleep) time.sleep(time_to_sleep)
while True: while True:
# Record the start time of this loop # Record the start time of this loop
start_time = time.monotonic() start_time = time.ticks_ms() / 1000
# If up is pressed, increase the brightness # If up is pressed, increase the brightness
if wheel.pressed(UP): if wheel.pressed(UP):
@ -95,7 +118,7 @@ while True:
if changed: if changed:
# Print the colour at the current hue, saturation, and brightness # Print the colour at the current hue, saturation, and brightness
r, g, b = [int(c * 255) for c in hsv_to_rgb(position / NUM_LEDS, saturation, brightness)] r, g, b = [int(c * 255) for c in hsv_to_rgb(position / NUM_LEDS, saturation, brightness)]
print("Colour Code = #", hex(r)[2:].zfill(2), hex(g)[2:].zfill(2), hex(b)[2:].zfill(2), sep="") print("Colour Code = #", '{:02x}'.format(r), '{:02x}'.format(g), '{:02x}'.format(b), sep="")
# Set the LED at the current position to either the actual colour, # Set the LED at the current position to either the actual colour,
# or an inverted version to show a "selection marker" # or an inverted version to show a "selection marker"

Wyświetl plik

@ -1,7 +1,7 @@
import math import math
import time import time
from ioexpander import PWM from breakout_ioexpander import BreakoutIOExpander
from pimoroni_i2c import PimoroniI2C from pimoroni_i2c import PimoroniI2C
from breakout_encoder_wheel import BreakoutEncoderWheel, CENTRE, GPIOS, NUM_GPIOS from breakout_encoder_wheel import BreakoutEncoderWheel, CENTRE, GPIOS, NUM_GPIOS
@ -28,7 +28,7 @@ period = wheel.gpio_pwm_frequency(FREQUENCY)
# Set the GPIO pins to PWM outputs # Set the GPIO pins to PWM outputs
for g in GPIOS: for g in GPIOS:
wheel.gpio_pin_mode(g, PWM) wheel.gpio_pin_mode(g, BreakoutIOExpander.PIN_PWM)
offset = 0.0 offset = 0.0
@ -36,7 +36,7 @@ offset = 0.0
# Sleep until a specific time in the future. Use this instead of time.sleep() to correct for # Sleep until a specific time in the future. Use this instead of time.sleep() to correct for
# inconsistent timings when dealing with complex operations or external communication # inconsistent timings when dealing with complex operations or external communication
def sleep_until(end_time): def sleep_until(end_time):
time_to_sleep = end_time - time.monotonic() time_to_sleep = end_time - (time.ticks_ms() / 1000)
if time_to_sleep > 0.0: if time_to_sleep > 0.0:
time.sleep(time_to_sleep) time.sleep(time_to_sleep)
@ -45,7 +45,7 @@ def sleep_until(end_time):
while not wheel.pressed(CENTRE): while not wheel.pressed(CENTRE):
# Record the start time of this loop # Record the start time of this loop
start_time = time.monotonic() start_time = time.ticks_ms() / 1000
offset += SPEED / 1000.0 offset += SPEED / 1000.0

Wyświetl plik

@ -29,7 +29,7 @@ offset = 0.0
# Sleep until a specific time in the future. Use this instead of time.sleep() to correct for # Sleep until a specific time in the future. Use this instead of time.sleep() to correct for
# inconsistent timings when dealing with complex operations or external communication # inconsistent timings when dealing with complex operations or external communication
def sleep_until(end_time): def sleep_until(end_time):
time_to_sleep = end_time - time.monotonic() time_to_sleep = end_time - (time.ticks_ms() / 1000)
if time_to_sleep > 0.0: if time_to_sleep > 0.0:
time.sleep(time_to_sleep) time.sleep(time_to_sleep)
@ -38,7 +38,7 @@ def sleep_until(end_time):
while True: while True:
# Record the start time of this loop # Record the start time of this loop
start_time = time.monotonic() start_time = time.ticks_ms() / 1000
offset += SPEED / 1000.0 offset += SPEED / 1000.0

Wyświetl plik

@ -49,13 +49,13 @@ def clamp(n, smallest, largest):
# Sleep until a specific time in the future. Use this instead of time.sleep() to correct for # Sleep until a specific time in the future. Use this instead of time.sleep() to correct for
# inconsistent timings when dealing with complex operations or external communication # inconsistent timings when dealing with complex operations or external communication
def sleep_until(end_time): def sleep_until(end_time):
time_to_sleep = end_time - time.monotonic() time_to_sleep = end_time - (time.ticks_ms() / 1000)
if time_to_sleep > 0.0: if time_to_sleep > 0.0:
time.sleep(time_to_sleep) time.sleep(time_to_sleep)
# Record the current time # Record the current time
current_time = time.monotonic() current_time = (time.ticks_ms() / 1000)
# Run the update loop forever # Run the update loop forever
while True: while True:

Wyświetl plik

@ -104,7 +104,7 @@ STATIC const mp_rom_map_elem_t breakout_encoder_wheel_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_DOWN), MP_ROM_INT(1) }, { MP_ROM_QSTR(MP_QSTR_DOWN), MP_ROM_INT(1) },
{ MP_ROM_QSTR(MP_QSTR_LEFT), MP_ROM_INT(2) }, { MP_ROM_QSTR(MP_QSTR_LEFT), MP_ROM_INT(2) },
{ MP_ROM_QSTR(MP_QSTR_RIGHT), MP_ROM_INT(3) }, { MP_ROM_QSTR(MP_QSTR_RIGHT), MP_ROM_INT(3) },
{ MP_ROM_QSTR(MP_QSTR_CENTRE), MP_ROM_INT(5) }, { MP_ROM_QSTR(MP_QSTR_CENTRE), MP_ROM_INT(4) },
{ MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_INT(7) }, { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_INT(7) },
{ MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_INT(8) }, { MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_INT(8) },

Wyświetl plik

@ -81,7 +81,7 @@ extern mp_obj_t BreakoutEncoderWheel_pressed(mp_obj_t self_in, mp_obj_t button_i
int button = mp_obj_get_int(button_in); int button = mp_obj_get_int(button_in);
if(button < 0 || button >= 5) { if(button < 0 || button >= 5) {
mp_raise_ValueError("button out of range. Expected 0 to 4") mp_raise_ValueError("button out of range. Expected 0 to 4");
} }
return mp_obj_new_bool(self->breakout->pressed(button)); return mp_obj_new_bool(self->breakout->pressed(button));
@ -195,9 +195,9 @@ extern mp_obj_t BreakoutEncoderWheel_set_hsv(size_t n_args, const mp_obj_t *pos_
static const mp_arg_t allowed_args[] = { static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_index, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_index, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_s, MP_ARG_INT, { .u_obj = mp_const_none }}, { MP_QSTR_s, MP_ARG_OBJ, { .u_obj = mp_const_none }},
{ MP_QSTR_v, MP_ARG_INT, { .u_obj = mp_const_none }}, { MP_QSTR_v, MP_ARG_OBJ, { .u_obj = mp_const_none }},
}; };
// Parse args. // Parse args.