From 59d57a193bf41f87f9782a8f7501aeaebee66bb4 Mon Sep 17 00:00:00 2001 From: ZodiusInfuser Date: Mon, 12 Sep 2022 15:47:07 +0100 Subject: [PATCH] Initial support for Encoder wheel breakout --- libraries/CMakeLists.txt | 1 + .../breakout_encoder_wheel/CMakeLists.txt | 1 + .../breakout_encoder_wheel.cmake | 11 + .../breakout_encoder_wheel.cpp | 89 +++++++++ .../breakout_encoder_wheel.hpp | 134 +++++++++++++ .../breakout_encoder_wheel/button_test.py | 188 ++++++++++++++++++ .../breakout_encoder_wheel.c | 69 +++++++ .../breakout_encoder_wheel.cpp | 168 ++++++++++++++++ .../breakout_encoder_wheel.h | 18 ++ .../breakout_encoder_wheel/micropython.cmake | 21 ++ .../micropython-common-breakouts.cmake | 1 + 11 files changed, 701 insertions(+) create mode 100644 libraries/breakout_encoder_wheel/CMakeLists.txt create mode 100644 libraries/breakout_encoder_wheel/breakout_encoder_wheel.cmake create mode 100644 libraries/breakout_encoder_wheel/breakout_encoder_wheel.cpp create mode 100644 libraries/breakout_encoder_wheel/breakout_encoder_wheel.hpp create mode 100644 micropython/examples/breakout_encoder_wheel/button_test.py create mode 100644 micropython/modules/breakout_encoder_wheel/breakout_encoder_wheel.c create mode 100644 micropython/modules/breakout_encoder_wheel/breakout_encoder_wheel.cpp create mode 100644 micropython/modules/breakout_encoder_wheel/breakout_encoder_wheel.h create mode 100644 micropython/modules/breakout_encoder_wheel/micropython.cmake diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 317ba021..e5bca9d3 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(hershey_fonts) add_subdirectory(bitmap_fonts) add_subdirectory(breakout_dotmatrix) add_subdirectory(breakout_encoder) +add_subdirectory(breakout_encoder_wheel) add_subdirectory(breakout_ioexpander) add_subdirectory(breakout_ltr559) add_subdirectory(breakout_rgbmatrix5x5) diff --git a/libraries/breakout_encoder_wheel/CMakeLists.txt b/libraries/breakout_encoder_wheel/CMakeLists.txt new file mode 100644 index 00000000..8373bfa4 --- /dev/null +++ b/libraries/breakout_encoder_wheel/CMakeLists.txt @@ -0,0 +1 @@ +include(breakout_encoder_wheel.cmake) diff --git a/libraries/breakout_encoder_wheel/breakout_encoder_wheel.cmake b/libraries/breakout_encoder_wheel/breakout_encoder_wheel.cmake new file mode 100644 index 00000000..73d9e8ae --- /dev/null +++ b/libraries/breakout_encoder_wheel/breakout_encoder_wheel.cmake @@ -0,0 +1,11 @@ +set(LIB_NAME breakout_encoder_wheel) +add_library(${LIB_NAME} INTERFACE) + +target_sources(${LIB_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp +) + +target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) + +# Pull in pico libraries that we need +target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib ioexpander is31fl3731) diff --git a/libraries/breakout_encoder_wheel/breakout_encoder_wheel.cpp b/libraries/breakout_encoder_wheel/breakout_encoder_wheel.cpp new file mode 100644 index 00000000..44bef6f3 --- /dev/null +++ b/libraries/breakout_encoder_wheel/breakout_encoder_wheel.cpp @@ -0,0 +1,89 @@ +#include "breakout_encoder_wheel.hpp" +#include + +namespace pimoroni { + + bool BreakoutEncoderWheel::init(bool skip_chip_id_check) { + bool success = false; + if(ioe.init(skip_chip_id_check)) { + + if(interrupt_pin != PIN_UNUSED) { + ioe.enable_interrupt_out(true); + } + + ioe.setup_rotary_encoder(ENC_CHANNEL, ENC_TERM_A, ENC_TERM_B); + + success = true; + } + + return success; + } + + i2c_inst_t* BreakoutEncoderWheel::get_i2c() const { + return ioe.get_i2c(); + } + + int BreakoutEncoderWheel::get_ioe_address() const { + return ioe.get_address(); + } + + int BreakoutEncoderWheel::get_led_address() const { + return led_ring.get_address(); + } + + int BreakoutEncoderWheel::get_sda() const { + return ioe.get_sda(); + } + + int BreakoutEncoderWheel::get_scl() const { + return ioe.get_scl(); + } + + int BreakoutEncoderWheel::get_int() const { + return ioe.get_int(); + } + + void BreakoutEncoderWheel::set_ioe_address(uint8_t address) { + ioe.set_address(address); + } + + bool BreakoutEncoderWheel::get_interrupt_flag() { + return ioe.get_interrupt_flag(); + } + + void BreakoutEncoderWheel::clear_interrupt_flag() { + ioe.clear_interrupt_flag(); + } + + BreakoutEncoderWheel::Direction BreakoutEncoderWheel::get_encoder_direction() { + return direction; + } + + void BreakoutEncoderWheel::set_encoder_direction(Direction direction) { + this->direction = direction; + } + + void BreakoutEncoderWheel::set_pixel(uint8_t index, uint8_t r, uint8_t g, uint8_t b) { + RGBLookup rgb = lookup_table[index]; + led_ring.set(rgb.r, r); + led_ring.set(rgb.g, g); + led_ring.set(rgb.b, b); + } + + bool BreakoutEncoderWheel::wheel_available() { + return (ioe.get_interrupt_flag() > 0); + } + + int16_t BreakoutEncoderWheel::read_wheel() { + int16_t count = ioe.read_rotary_encoder(ENC_CHANNEL); + if(direction != DIRECTION_CW) + count = 0 - count; + + ioe.clear_interrupt_flag(); + return count; + } + + void BreakoutEncoderWheel::clear_wheel() { + ioe.clear_rotary_encoder(ENC_CHANNEL); + } +} \ No newline at end of file diff --git a/libraries/breakout_encoder_wheel/breakout_encoder_wheel.hpp b/libraries/breakout_encoder_wheel/breakout_encoder_wheel.hpp new file mode 100644 index 00000000..ec17cb78 --- /dev/null +++ b/libraries/breakout_encoder_wheel/breakout_encoder_wheel.hpp @@ -0,0 +1,134 @@ +#pragma once + +#include "drivers/ioexpander/ioexpander.hpp" +#include "drivers/is31fl3731/is31fl3731.hpp" +#include "common/pimoroni_common.hpp" + +namespace pimoroni { + + class BreakoutEncoderWheel { + struct RGBLookup { + uint8_t r; + uint8_t g; + uint8_t b; + }; + + //-------------------------------------------------- + // Enums + //-------------------------------------------------- + public: + enum Direction : bool { + DIRECTION_CW = true, + DIRECTION_CCW = false + }; + + + //-------------------------------------------------- + // Constants + //-------------------------------------------------- + public: + static const uint8_t DEFAULT_IOE_I2C_ADDRESS = 0x13; + static const uint8_t DEFAULT_LED_I2C_ADDRESS = 0x77; + static const uint8_t LED_I2C_ADDRESS_ALTERNATE = 0x74; + + static const Direction DEFAULT_DIRECTION = DIRECTION_CW; + static const uint32_t DEFAULT_TIMEOUT = 1; + + private: + static const uint8_t SWITCH_CENTRE = 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_B = 3; + + static const uint8_t ENC_CHANNEL = 1; + + // This wonderful lookup table maps the LEDs on the encoder wheel + // from their 3x24 (remember, they're RGB) configuration to + // their specific location in the 144 pixel buffer. + static constexpr RGBLookup lookup_table[24] = { + {128, 32, 48}, + {129, 33, 49}, + {130, 17, 50}, + {131, 18, 34}, + {132, 19, 35}, + {133, 20, 36}, + {134, 21, 37}, + {112, 80, 96}, + {113, 81, 97}, + {114, 82, 98}, + {115, 83, 99}, + {116, 84, 100}, + {117, 68, 101}, + {118, 69, 85}, + {127, 47, 63}, + {121, 41, 57}, + {122, 25, 58}, + {123, 26, 42}, + {124, 27, 43}, + {125, 28, 44}, + {126, 29, 45}, + {15, 95, 111}, + {8, 89, 105}, + {9, 90, 106}, + }; + + + //-------------------------------------------------- + // Variables + //-------------------------------------------------- + private: + IOExpander ioe; + IS31FL3731 led_ring; + Direction direction = DEFAULT_DIRECTION; + uint interrupt_pin = PIN_UNUSED; // A local copy of the value passed to the IOExpander, used in initialisation + + + //-------------------------------------------------- + // Constructors/Destructor + //-------------------------------------------------- + public: + BreakoutEncoderWheel(uint8_t ioe_address = DEFAULT_IOE_I2C_ADDRESS, uint8_t led_address = DEFAULT_LED_I2C_ADDRESS) + : BreakoutEncoderWheel(new I2C(), ioe_address, led_address) {} + + BreakoutEncoderWheel(I2C *i2c, uint8_t ioe_address = DEFAULT_IOE_I2C_ADDRESS, uint8_t led_address = DEFAULT_LED_I2C_ADDRESS, uint interrupt = PIN_UNUSED, uint32_t timeout = DEFAULT_TIMEOUT, bool debug = false) + : ioe(i2c, ioe_address, interrupt, timeout, debug), led_ring(i2c, led_address) {} + + + //-------------------------------------------------- + // Methods + //-------------------------------------------------- + public: + bool init(bool skip_chip_id_check = false); + + // For print access in micropython + i2c_inst_t* get_i2c() const; + int get_ioe_address() const; + int get_led_address() const; + int get_sda() const; + int get_scl() const; + int get_int() const; + + // Calls through to IOExpander class + void set_ioe_address(uint8_t address); + bool get_interrupt_flag(); + void clear_interrupt_flag(); + + // Encoder breakout specific + Direction get_encoder_direction(); + void set_encoder_direction(Direction direction); + + void set_pixel(uint8_t index, uint8_t r, uint8_t g, uint8_t b); + //void update(uint8_t frame = 0); + //void clear(); + + bool wheel_available(); + int16_t read_wheel(); + void clear_wheel(); + bool read_switch(); + }; + +} \ No newline at end of file diff --git a/micropython/examples/breakout_encoder_wheel/button_test.py b/micropython/examples/breakout_encoder_wheel/button_test.py new file mode 100644 index 00000000..4da930ee --- /dev/null +++ b/micropython/examples/breakout_encoder_wheel/button_test.py @@ -0,0 +1,188 @@ +import time +from pimoroni_i2c import PimoroniI2C +from breakout_ioexpander import BreakoutIOExpander +from adafruit_is31fl3731 import IS31FL3731 +import sys + +PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5} +PINS_PICO_EXPLORER = {"sda": 20, "scl": 21} + +s1_pin = 1 +s2_pin = 13 +s3_pin = 11 +s4_pin = 4 +s5_pin = 2 + +ENC_TERM_A = 3 +ENC_TERM_B = 12 + +ENC_CHANNEL = 1 + +i2c = PimoroniI2C(**PINS_BREAKOUT_GARDEN) +ioe = BreakoutIOExpander(i2c, address=0x18) + +ioe.set_mode(s1_pin, BreakoutIOExpander.PIN_IN_PU) +ioe.set_mode(s2_pin, BreakoutIOExpander.PIN_IN_PU) +ioe.set_mode(s3_pin, BreakoutIOExpander.PIN_IN_PU) +ioe.set_mode(s4_pin, BreakoutIOExpander.PIN_IN_PU) +ioe.set_mode(s5_pin, BreakoutIOExpander.PIN_IN_PU) + +ioe.setup_rotary_encoder(ENC_CHANNEL, ENC_TERM_A, ENC_TERM_B, 6, count_microsteps=True) +#ioe.set_mode(ENC_TERM_A, BreakoutIOExpander.PIN_IN_PU) +#ioe.set_mode(ENC_TERM_B, BreakoutIOExpander.PIN_IN_PU) + +display = IS31FL3731(i2c, address=0x77) +#display.fill(10) + +mapping = ((128, 32, 48), + (129, 33, 49), + (130, 17, 50), + (131, 18, 34), + (132, 19, 35), + (133, 20, 36), + (134, 21, 37), + (112, 80, 96), + (113, 81, 97), + (114, 82, 98), + (115, 83, 99), + (116, 84, 100), + (117, 68, 101), + (118, 69, 85), + (127, 47, 63), + (121, 41, 57), + (122, 25, 58), + (123, 26, 42), + (124, 27, 43), + (125, 28, 44), + (126, 29, 45), + (15, 95, 111), + (8, 89, 105), + (9, 90, 106)) + + +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 + +''' +while True: + for x in range(0, 24): + single = mapping[x] + display.pixel(single[0], 0, 255) + display.pixel(single[1], 0, 255) + display.pixel(single[2], 0, 255) + print(x) + time.sleep(0.05) + display.pixel(single[0], 0, 0) + display.pixel(single[1], 0, 0) + display.pixel(single[2], 0, 0) +''' + +last_s1 = True +last_s2 = True +last_s3 = True +last_s4 = True +last_s5 = True +last_count = -1 + +last_enc_a = False +last_enc_b = False + + +while True: + s1 = bool(ioe.input(s1_pin)) + s2 = bool(ioe.input(s2_pin)) + s3 = bool(ioe.input(s3_pin)) + s4 = bool(ioe.input(s4_pin)) + s5 = bool(ioe.input(s5_pin)) + if s1 is not last_s1: + if s1: + print("Centre (S1) has been released") + else: + print("Centre (S1) has been pressed") + last_s1 = s1 + + if s2 is not last_s2: + if s2: + print("Up (S2) has been released") + else: + print("Up (S2) has been pressed") + last_s2 = s2 + + if s3 is not last_s3: + if s3: + print("Left (S3) has been released") + else: + print("Left (S3) has been pressed") + last_s3 = s3 + + if s4 is not last_s4: + if s4: + print("Down (S4) has been released") + else: + print("Down (S4) has been pressed") + last_s4 = s4 + + if s5 is not last_s5: + if s5: + print("Right (S5) has been released") + else: + print("Right (S5) has been pressed") + last_s5 = s5 + + count = ioe.read_rotary_encoder(ENC_CHANNEL) // 2 + if count != last_count: + if count - last_count > 0: + print("Clockwise, Count = ", count) + else: + print("Counter Clockwise, Count = ", count) + + last_single = mapping[last_count % 24] + display.pixel(last_single[0], 0, 0) + display.pixel(last_single[1], 0, 0) + display.pixel(last_single[2], 0, 0) + single = mapping[count % 24] + r, g, b = hsv_to_rgb(count / 24, 1.0, 1.0) + display.pixel(single[0], 0, int(255 * r)) + display.pixel(single[1], 0, int(255 * g)) + display.pixel(single[2], 0, int(255 * b)) + last_count = count + + ''' + enc_a = bool(ioe.input(ENC_TERM_A)) + enc_b = bool(ioe.input(ENC_TERM_B)) + + if enc_a is not last_enc_a: + if enc_a: + print("ENC A high") + else: + print("ENC A low") + last_enc_a = enc_a + + if enc_b is not last_enc_b: + if enc_b: + print("ENC B high") + else: + print("ENC B low") + last_enc_b = enc_b + ''' + + time.sleep(0.005) \ No newline at end of file diff --git a/micropython/modules/breakout_encoder_wheel/breakout_encoder_wheel.c b/micropython/modules/breakout_encoder_wheel/breakout_encoder_wheel.c new file mode 100644 index 00000000..468d83f4 --- /dev/null +++ b/micropython/modules/breakout_encoder_wheel/breakout_encoder_wheel.c @@ -0,0 +1,69 @@ +#include "breakout_encoder_wheel.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// BreakoutEncoderWheel Class +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Methods *****/ +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutEncoderWheel_set_address_obj, 2, BreakoutEncoderWheel_set_address); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutEncoderWheel_get_interrupt_flag_obj, BreakoutEncoderWheel_get_interrupt_flag); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutEncoderWheel_clear_interrupt_flag_obj, BreakoutEncoderWheel_clear_interrupt_flag); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutEncoderWheel_get_direction_obj, BreakoutEncoderWheel_get_direction); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutEncoderWheel_set_direction_obj, 2, BreakoutEncoderWheel_set_direction); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutEncoderWheel_set_brightness_obj, 2, BreakoutEncoderWheel_set_brightness); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutEncoderWheel_set_led_obj, 4, BreakoutEncoderWheel_set_led); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutEncoderWheel_available_obj, BreakoutEncoderWheel_available); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutEncoderWheel_read_obj, BreakoutEncoderWheel_read); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutEncoderWheel_clear_obj, BreakoutEncoderWheel_clear); + +/***** Binding of Methods *****/ +STATIC const mp_rom_map_elem_t BreakoutEncoderWheel_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_set_address), MP_ROM_PTR(&BreakoutEncoderWheel_set_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_interrupt_flag), MP_ROM_PTR(&BreakoutEncoderWheel_get_interrupt_flag_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear_interrupt_flag), MP_ROM_PTR(&BreakoutEncoderWheel_clear_interrupt_flag_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_direction), MP_ROM_PTR(&BreakoutEncoderWheel_get_direction_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_direction), MP_ROM_PTR(&BreakoutEncoderWheel_set_direction_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_brightness), MP_ROM_PTR(&BreakoutEncoderWheel_set_brightness_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_led), MP_ROM_PTR(&BreakoutEncoderWheel_set_led_obj) }, + { MP_ROM_QSTR(MP_QSTR_available), MP_ROM_PTR(&BreakoutEncoderWheel_available_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutEncoderWheel_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&BreakoutEncoderWheel_clear_obj) }, + { MP_ROM_QSTR(MP_QSTR_DIRECTION_CW), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_DIRECTION_CCW), MP_ROM_INT(0) }, +}; +STATIC MP_DEFINE_CONST_DICT(BreakoutEncoderWheel_locals_dict, BreakoutEncoderWheel_locals_dict_table); + +/***** Class Definition *****/ +const mp_obj_type_t breakout_encoder_wheel_BreakoutEncoderWheel_type = { + { &mp_type_type }, + .name = MP_QSTR_BreakoutEncoderWheel, + .make_new = BreakoutEncoderWheel_make_new, + .locals_dict = (mp_obj_dict_t*)&BreakoutEncoderWheel_locals_dict, +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// breakout_encoder_wheel Module +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Globals Table *****/ +STATIC const mp_map_elem_t breakout_encoder_wheel_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_encoder_wheel) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutEncoderWheel), (mp_obj_t)&breakout_encoder_wheel_BreakoutEncoderWheel_type }, +}; +STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_encoder_wheel_globals, breakout_encoder_wheel_globals_table); + +/***** Module Definition *****/ +const mp_obj_module_t breakout_encoder_wheel_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_breakout_encoder_wheel_globals, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +#if MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_breakout_encoder_wheel, breakout_encoder_wheel_user_cmodule, MODULE_BREAKOUT_ENCODER_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_breakout_encoder_wheel, breakout_encoder_wheel_user_cmodule); +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_encoder_wheel/breakout_encoder_wheel.cpp b/micropython/modules/breakout_encoder_wheel/breakout_encoder_wheel.cpp new file mode 100644 index 00000000..89d67ed4 --- /dev/null +++ b/micropython/modules/breakout_encoder_wheel/breakout_encoder_wheel.cpp @@ -0,0 +1,168 @@ +#include "libraries/breakout_encoder_wheel/breakout_encoder_wheel.hpp" +#include "micropython/modules/util.hpp" +#include + + +using namespace pimoroni; + +extern "C" { +#include "breakout_encoder_wheel.h" +#include "pimoroni_i2c.h" + +/***** Variables Struct *****/ +typedef struct _breakout_encoder_wheel_BreakoutEncoderWheel_obj_t { + mp_obj_base_t base; + BreakoutEncoderWheel *breakout; + _PimoroniI2C_obj_t *i2c; +} breakout_encoder_wheel_BreakoutEncoderWheel_obj_t; + + +/***** Constructor *****/ +mp_obj_t BreakoutEncoderWheel_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + breakout_encoder_wheel_BreakoutEncoderWheel_obj_t *self = nullptr; + + enum { ARG_i2c, ARG_address, ARG_interrupt }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c, MP_ARG_OBJ, {.u_obj = nullptr} }, + { MP_QSTR_address, MP_ARG_INT, {.u_int = BreakoutEncoderWheel::DEFAULT_I2C_ADDRESS} }, + { MP_QSTR_interrupt, MP_ARG_INT, {.u_int = PIN_UNUSED} }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + self = m_new_obj(breakout_encoder_wheel_BreakoutEncoderWheel_obj_t); + self->base.type = &breakout_encoder_wheel_BreakoutEncoderWheel_type; + + self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj); + + self->breakout = m_new_class(BreakoutEncoderWheel, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_address].u_int, args[ARG_interrupt].u_int); + + if(!self->breakout->init()) { + mp_raise_msg(&mp_type_RuntimeError, "BreakoutEncoderWheel: breakout not found when initialising"); + } + + return MP_OBJ_FROM_PTR(self); +} + +/***** Methods *****/ +mp_obj_t BreakoutEncoderWheel_set_address(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_address }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_address, 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); + + breakout_encoder_wheel_BreakoutEncoderWheel_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_encoder_wheel_BreakoutEncoderWheel_obj_t); + + self->breakout->set_address(args[ARG_address].u_int); + + return mp_const_none; +} + +mp_obj_t BreakoutEncoderWheel_get_interrupt_flag(mp_obj_t self_in) { + breakout_encoder_wheel_BreakoutEncoderWheel_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_encoder_wheel_BreakoutEncoderWheel_obj_t); + return mp_obj_new_bool(self->breakout->get_interrupt_flag()); +} + +mp_obj_t BreakoutEncoderWheel_clear_interrupt_flag(mp_obj_t self_in) { + breakout_encoder_wheel_BreakoutEncoderWheel_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_encoder_wheel_BreakoutEncoderWheel_obj_t); + self->breakout->clear_interrupt_flag(); + + return mp_const_none; +} + +mp_obj_t BreakoutEncoderWheel_get_direction(mp_obj_t self_in) { + breakout_encoder_wheel_BreakoutEncoderWheel_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_encoder_wheel_BreakoutEncoderWheel_obj_t); + return mp_obj_new_bool(self->breakout->get_direction()); +} + +mp_obj_t BreakoutEncoderWheel_set_direction(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_clockwise }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_clockwise, MP_ARG_REQUIRED | MP_ARG_BOOL }, + }; + + 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); + + breakout_encoder_wheel_BreakoutEncoderWheel_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_encoder_wheel_BreakoutEncoderWheel_obj_t); + + self->breakout->set_direction(args[ARG_clockwise].u_bool ? BreakoutEncoderWheel::DIRECTION_CW : BreakoutEncoderWheel::DIRECTION_CCW); + + return mp_const_none; +} + +mp_obj_t BreakoutEncoderWheel_set_brightness(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_brightness }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_brightness, MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + + 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); + + breakout_encoder_wheel_BreakoutEncoderWheel_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_encoder_wheel_BreakoutEncoderWheel_obj_t); + + float brightness = mp_obj_get_float(args[ARG_brightness].u_obj); + if(brightness < 0 || brightness > 1.0f) + mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0"); + else + self->breakout->set_brightness(brightness); + + return mp_const_none; +} + +mp_obj_t BreakoutEncoderWheel_set_led(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_r, ARG_g, ARG_b, ARG_w }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_b, 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); + + breakout_encoder_wheel_BreakoutEncoderWheel_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_encoder_wheel_BreakoutEncoderWheel_obj_t); + + int r = args[ARG_r].u_int; + int g = args[ARG_g].u_int; + int b = args[ARG_b].u_int; + + if(r < 0 || r > 255) + mp_raise_ValueError("r out of range. Expected 0 to 255"); + else if(g < 0 || g > 255) + mp_raise_ValueError("g out of range. Expected 0 to 255"); + else if(b < 0 || b > 255) + mp_raise_ValueError("b out of range. Expected 0 to 255"); + else + self->breakout->set_led(r, g, b); + + return mp_const_none; +} + +mp_obj_t BreakoutEncoderWheel_available(mp_obj_t self_in) { + breakout_encoder_wheel_BreakoutEncoderWheel_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_encoder_wheel_BreakoutEncoderWheel_obj_t); + return mp_obj_new_bool(self->breakout->available()); +} + +mp_obj_t BreakoutEncoderWheel_read(mp_obj_t self_in) { + breakout_encoder_wheel_BreakoutEncoderWheel_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_encoder_wheel_BreakoutEncoderWheel_obj_t); + return mp_obj_new_int(self->breakout->read()); +} + +mp_obj_t BreakoutEncoderWheel_clear(mp_obj_t self_in) { + breakout_encoder_wheel_BreakoutEncoderWheel_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_encoder_wheel_BreakoutEncoderWheel_obj_t); + self->breakout->clear(); + + return mp_const_none; +} +} \ No newline at end of file diff --git a/micropython/modules/breakout_encoder_wheel/breakout_encoder_wheel.h b/micropython/modules/breakout_encoder_wheel/breakout_encoder_wheel.h new file mode 100644 index 00000000..c85739e4 --- /dev/null +++ b/micropython/modules/breakout_encoder_wheel/breakout_encoder_wheel.h @@ -0,0 +1,18 @@ +// Include MicroPython API. +#include "py/runtime.h" + +/***** Extern of Class Definition *****/ +extern const mp_obj_type_t breakout_encoder_wheel_BreakoutEncoderWheel_type; + +/***** Extern of Class Methods *****/ +extern mp_obj_t BreakoutEncoderWheel_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); +extern mp_obj_t BreakoutEncoderWheel_set_address(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutEncoderWheel_get_interrupt_flag(mp_obj_t self_in); +extern mp_obj_t BreakoutEncoderWheel_clear_interrupt_flag(mp_obj_t self_in); +extern mp_obj_t BreakoutEncoderWheel_get_direction(mp_obj_t self_in); +extern mp_obj_t BreakoutEncoderWheel_set_direction(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutEncoderWheel_set_brightness(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutEncoderWheel_set_led(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutEncoderWheel_available(mp_obj_t self_in); +extern mp_obj_t BreakoutEncoderWheel_read(mp_obj_t self_in); +extern mp_obj_t BreakoutEncoderWheel_clear(mp_obj_t self_in); \ No newline at end of file diff --git a/micropython/modules/breakout_encoder_wheel/micropython.cmake b/micropython/modules/breakout_encoder_wheel/micropython.cmake new file mode 100644 index 00000000..de763c17 --- /dev/null +++ b/micropython/modules/breakout_encoder_wheel/micropython.cmake @@ -0,0 +1,21 @@ +set(MOD_NAME breakout_encoder_wheel) +string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER) +add_library(usermod_${MOD_NAME} INTERFACE) + +target_sources(usermod_${MOD_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c + ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/${MOD_NAME}/${MOD_NAME}.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/ioexpander/ioexpander.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/is31fl3731/is31fl3731.cpp +) + +target_include_directories(usermod_${MOD_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR} +) + +target_compile_definitions(usermod_${MOD_NAME} INTERFACE + -DMODULE_${MOD_NAME_UPPER}_ENABLED=1 +) + +target_link_libraries(usermod INTERFACE usermod_${MOD_NAME}) \ No newline at end of file diff --git a/micropython/modules/micropython-common-breakouts.cmake b/micropython/modules/micropython-common-breakouts.cmake index 17958fa4..f18c3fb0 100644 --- a/micropython/modules/micropython-common-breakouts.cmake +++ b/micropython/modules/micropython-common-breakouts.cmake @@ -1,5 +1,6 @@ include(breakout_dotmatrix/micropython) include(breakout_encoder/micropython) +include(breakout_encoder_wheel/micropython) include(breakout_ioexpander/micropython) include(breakout_ltr559/micropython) include(breakout_as7262/micropython)