diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2e8c12a..fcecf32 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -11,7 +11,7 @@ jobs: build: strategy: matrix: - teensy: ['32', '40', '40_blinker', '40_fan_hub', '32_fan_hub'] + teensy: ['32', '40', '40_blinker', '40_fan_hub', '32_fan_hub', '40_neopixel', '40_neopixel_example'] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -31,8 +31,9 @@ jobs: cp .pio/build/teensy${{ matrix.teensy }}/firmware.hex firmware_teensy${{ matrix.teensy }}.hex - name: Upload artifact if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: + name: firmware_teensy${{ matrix.teensy }} path: | firmware_teensy${{ matrix.teensy }}.hex - name: Upload firmware to release diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cdc49b..6d80d78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### 0.7.0 (2025/03/03) + +* Provide the ability to use Neopixels. + ### 0.6.0 (2024/12/11) * Fix the usecase where an infinite loop would occur if no demo commands. diff --git a/platformio.ini b/platformio.ini index 0771623..448b222 100644 --- a/platformio.ini +++ b/platformio.ini @@ -46,6 +46,22 @@ build_src_filter = +<*.hpp> - +; neopixel is LGPL, so just make a dedicated build for it +[env:teensy40_neopixel] +extends = env:teensy40 +lib_deps = + https://github.com/ramonaoptics/teensy4_i2c.git#our_mods + https://github.com/adafruit/Adafruit_NeoPixel.git +build_flags = + !python versioneer.py + -DTEENSYTOANY_USE_NEOPIXEL +build_src_filter = + +<*.c> + +<*.h> + +<*.cpp> + +<*.hpp> + - + [env:teensy40_blinker] extends = env:teensy40 build_src_filter = @@ -86,3 +102,19 @@ build_src_filter = +<*.hpp> - + + +[env:teensy40_neopixel_example] +extends = env:teensy40 +lib_deps = + https://github.com/ramonaoptics/teensy4_i2c.git#our_mods + https://github.com/adafruit/Adafruit_NeoPixel.git +build_flags = + !python versioneer.py + -DTEENSYTOANY_USE_NEOPIXEL +build_src_filter = + +<*.c> + +<*.h> + +<*.cpp> + +<*.hpp> + - + + diff --git a/src/commandconstants.hpp b/src/commandconstants.hpp index 5c4af2a..c8350ce 100644 --- a/src/commandconstants.hpp +++ b/src/commandconstants.hpp @@ -63,6 +63,16 @@ int register_write_uint32(CommandRouter *cmd, int argc, const char **argv); int eeprom_read_uint8(CommandRouter *cmd, int argc, const char **argv); int eeprom_write_uint8(CommandRouter *cmd, int argc, const char **argv); +#ifdef TEENSYTOANY_USE_NEOPIXEL +int neopixel_init(CommandRouter *cmd, int argc, const char **argv); +int neopixel_begin(CommandRouter *cmd, int argc, const char **argv); +int neopixel_update_length(CommandRouter *cmd, int argc, const char **argv); +int neopixel_update_pin(CommandRouter *cmd, int argc, const char **argv); +int neopixel_update_type(CommandRouter *cmd, int argc, const char **argv); +int neopixel_show(CommandRouter *cmd, int argc, const char **argv); +int neopixel_set_pixel_color(CommandRouter *cmd, int argc, const char **argv); +#endif + // Mostly for debugging and startup scripts int sleep_seconds(CommandRouter *cmd, int argc, const char **argv); int startup_commands_available(CommandRouter *cmd, int argc, const char **argv); @@ -183,6 +193,22 @@ const command_item_t command_list[] = { "eeprom_read_uint8 address", eeprom_read_uint8}, {"eeprom_write_uint8", "Write to an EEPROM address.", "eeprom_write_uint8 address data", eeprom_write_uint8}, +#ifdef TEENSYTOANY_USE_NEOPIXEL + {"neopixel_init", "Initialize the neopixel library", + "neopixel_init num_pixels pin type", neopixel_init}, + {"neopixel_begin", "Begin the neopixel buffer", + "neopixel_begin", neopixel_begin}, + {"neopixel_update_length", "Update the length of the neopixel buffer", + "neopixel_update_length num_pixels", neopixel_update_length}, + {"neopixel_update_pin", "Update the pin of the neopixel buffer", + "neopixel_update_pin pin", neopixel_update_pin}, + {"neopixel_update_type", "Update the type of the neopixel buffer", + "neopixel_update_type type", neopixel_update_type}, + {"neopixel_show", "Show the current neopixel buffer", + "neopixel_show", neopixel_show}, + {"neopixel_set_pixel_color", "Set the color of a pixel in the neopixel buffer", + "neopixel_set_pixel_color pixel red green blue", neopixel_set_pixel_color}, +#endif {"sleep", "Sleep (and block) for the desired duration", "sleep duration", sleep_seconds}, {"startup_commands_available", "Number of startup commands available", diff --git a/src/examples/neopixel_demo/neopixel_demo.cpp b/src/examples/neopixel_demo/neopixel_demo.cpp new file mode 100644 index 0000000..e235e8d --- /dev/null +++ b/src/examples/neopixel_demo/neopixel_demo.cpp @@ -0,0 +1,193 @@ +/* A small demo to show how the teensy-to-any can be used to control 5 neopixel LEDs. + * + * Ramona Optics - 2025 + */ +const char *teensy_to_any_startup_commands[] = { + // Setup the LED as an indicator pin + "neopixel_update_pin 19", + "neopixel_update_length 5", + + // set color to orange to indicate that we aren't ready yet + // It takes about 2-3 seconds for the serial module to boot up + // The teensy will be unresponsive during this time. + "neopixel_set_pixel_color 0 56 5 1 0", + "neopixel_set_pixel_color 1 56 5 1 0", + "neopixel_set_pixel_color 2 56 5 1 0", + "neopixel_set_pixel_color 3 56 5 1 0", + "neopixel_set_pixel_color 4 56 5 1 0", + "neopixel_begin", + "neopixel_show", + nullptr, +}; + + +#define _SLEEP_1000ms ("sleep 0.2", "sleep 0.2", "sleep 0.2", "sleep 0.2", "sleep 0.2") +#define _SLEEP_200ms ("sleep 0.2") +#define _SLEEP_100ms ("sleep 0.1") +#define _SLEEP_50ms ("sleep 0.05") + +#define _SLEEP_PATTERN _SLEEP_100ms + +// Change the fan speed +const char *teensy_to_any_demo_commands[] = { + "neopixel_set_pixel_color 0 0 0 0 40", + "neopixel_set_pixel_color 1 40 0 0 0", + "neopixel_set_pixel_color 2 0 40 0 0", + "neopixel_set_pixel_color 3 0 0 40 0", + "neopixel_set_pixel_color 4 40 40 40 40", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 30", + "neopixel_set_pixel_color 1 30 0 0 0", + "neopixel_set_pixel_color 2 0 30 0 0", + "neopixel_set_pixel_color 3 0 0 30 0", + "neopixel_set_pixel_color 4 30 30 30 30", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 20", + "neopixel_set_pixel_color 1 20 0 0 0", + "neopixel_set_pixel_color 2 0 20 0 0", + "neopixel_set_pixel_color 3 0 0 20 0", + "neopixel_set_pixel_color 4 20 20 20 20", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 10", + "neopixel_set_pixel_color 1 10 0 0 0", + "neopixel_set_pixel_color 2 0 10 0 0", + "neopixel_set_pixel_color 3 0 0 10 0", + "neopixel_set_pixel_color 4 10 10 10 10", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 5", + "neopixel_set_pixel_color 1 5 0 0 0", + "neopixel_set_pixel_color 2 0 5 0 0", + "neopixel_set_pixel_color 3 0 0 5 0", + "neopixel_set_pixel_color 4 5 5 5 5", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 4", + "neopixel_set_pixel_color 1 4 0 0 0", + "neopixel_set_pixel_color 2 0 4 0 0", + "neopixel_set_pixel_color 3 0 0 4 0", + "neopixel_set_pixel_color 4 4 4 4 4", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 3", + "neopixel_set_pixel_color 1 3 0 0 0", + "neopixel_set_pixel_color 2 0 3 0 0", + "neopixel_set_pixel_color 3 0 0 3 0", + "neopixel_set_pixel_color 4 3 3 3 3", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 2", + "neopixel_set_pixel_color 1 2 0 0 0", + "neopixel_set_pixel_color 2 0 2 0 0", + "neopixel_set_pixel_color 3 0 0 2 0", + "neopixel_set_pixel_color 4 2 2 2 2", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 1", + "neopixel_set_pixel_color 1 1 0 0 0", + "neopixel_set_pixel_color 2 0 1 0 0", + "neopixel_set_pixel_color 3 0 0 1 0", + "neopixel_set_pixel_color 4 1 1 1 1", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 00", + "neopixel_set_pixel_color 1 00 0 0 0", + "neopixel_set_pixel_color 2 0 00 0 0", + "neopixel_set_pixel_color 3 0 0 00 0", + "neopixel_set_pixel_color 4 00 00 00 00", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 1", + "neopixel_set_pixel_color 1 1 0 0 0", + "neopixel_set_pixel_color 2 0 1 0 0", + "neopixel_set_pixel_color 3 0 0 1 0", + "neopixel_set_pixel_color 4 1 1 1 1", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 2", + "neopixel_set_pixel_color 1 2 0 0 0", + "neopixel_set_pixel_color 2 0 2 0 0", + "neopixel_set_pixel_color 3 0 0 2 0", + "neopixel_set_pixel_color 4 2 2 2 2", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 3", + "neopixel_set_pixel_color 1 3 0 0 0", + "neopixel_set_pixel_color 2 0 3 0 0", + "neopixel_set_pixel_color 3 0 0 3 0", + "neopixel_set_pixel_color 4 3 3 3 3", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 4", + "neopixel_set_pixel_color 1 4 0 0 0", + "neopixel_set_pixel_color 2 0 4 0 0", + "neopixel_set_pixel_color 3 0 0 4 0", + "neopixel_set_pixel_color 4 4 4 4 4", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 5", + "neopixel_set_pixel_color 1 5 0 0 0", + "neopixel_set_pixel_color 2 0 5 0 0", + "neopixel_set_pixel_color 3 0 0 5 0", + "neopixel_set_pixel_color 4 5 5 5 5", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 10", + "neopixel_set_pixel_color 1 10 0 0 0", + "neopixel_set_pixel_color 2 0 10 0 0", + "neopixel_set_pixel_color 3 0 0 10 0", + "neopixel_set_pixel_color 4 10 10 10 10", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 20", + "neopixel_set_pixel_color 1 20 0 0 0", + "neopixel_set_pixel_color 2 0 20 0 0", + "neopixel_set_pixel_color 3 0 0 20 0", + "neopixel_set_pixel_color 4 20 20 20 20", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 30", + "neopixel_set_pixel_color 1 30 0 0 0", + "neopixel_set_pixel_color 2 0 30 0 0", + "neopixel_set_pixel_color 3 0 0 30 0", + "neopixel_set_pixel_color 4 30 30 30 30", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 40", + "neopixel_set_pixel_color 1 40 0 0 0", + "neopixel_set_pixel_color 2 0 40 0 0", + "neopixel_set_pixel_color 3 0 0 40 0", + "neopixel_set_pixel_color 4 40 40 40 40", + "neopixel_show", + _SLEEP_PATTERN, + + "neopixel_set_pixel_color 0 0 0 0 50", + "neopixel_set_pixel_color 1 50 0 0 0", + "neopixel_set_pixel_color 2 0 50 0 0", + "neopixel_set_pixel_color 3 0 0 50 0", + "neopixel_set_pixel_color 4 50 50 50 50", + "neopixel_show", + _SLEEP_PATTERN, + nullptr, +}; diff --git a/src/main.cpp b/src/main.cpp index b2e8671..03ea946 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,6 +32,19 @@ inline SPISettings my_spi_settings() { return SPISettings(spi_baudrate, spi_bit_order, spi_data_mode); } +#ifdef TEENSYTOANY_USE_NEOPIXEL +#include +#define NEOPIXEL_PIN 19 +#define NEOPIXEL_NUMLEDS 5 +// #define NEOPIXEL_BRIGHTNESS 50 // Set BRIGHTNESS to about 1/5 (max = 255) + +Adafruit_NeoPixel neopixel_strip( + NEOPIXEL_NUMLEDS, + 19, // -1 = nopin + NEO_GRBW + NEO_KHZ800 +); +#endif + #if TEENSY_TO_ANY_HAS_I2C_T3 I2CMaster i2c; #endif @@ -1104,6 +1117,84 @@ int eeprom_write_uint8(CommandRouter *cmd, int argc, const char **argv) { return 0; } +#ifdef TEENSYTOANY_USE_NEOPIXEL +int neopixel_init(CommandRouter *cmd, int argc, const char **argv) { + if (argc != 4) { + return EINVAL; + } + + uint16_t num_pixels = (uint16_t) strtol(argv[1], nullptr, 0); + int16_t pin = (int16_t) strtol(argv[2], nullptr, 0); + neoPixelType type = (neoPixelType) strtol(argv[3], nullptr, 0); + + neopixel_strip.updateType(type); + neopixel_strip.updateLength(num_pixels); + neopixel_strip.setPin(pin); + + return 0; +} + +int neopixel_begin(CommandRouter *cmd, int argc, const char **argv) { + if (argc != 1) { + return EINVAL; + } + neopixel_strip.begin(); + return 0; +} + + +int neopixel_update_length(CommandRouter *cmd, int argc, const char **argv) { + if (argc != 2) { + return EINVAL; + } + uint16_t num_pixels = (uint16_t) strtol(argv[1], nullptr, 0); + neopixel_strip.updateLength(num_pixels); + return 0; +} +int neopixel_update_pin(CommandRouter *cmd, int argc, const char **argv) { + if (argc != 2) { + return EINVAL; + } + + int16_t pin = (int16_t) strtol(argv[1], nullptr, 0); + neopixel_strip.setPin(pin); + return 0; +} +int neopixel_update_type(CommandRouter *cmd, int argc, const char **argv) { + if (argc != 2) { + return EINVAL; + } + neoPixelType type = (neoPixelType) strtol(argv[1], nullptr, 0); + neopixel_strip.updateType(type); + return 0; +} +int neopixel_show(CommandRouter *cmd, int argc, const char **argv){ + if (argc != 1) { + return EINVAL; + } + neopixel_strip.show(); + return 0; +} +int neopixel_set_pixel_color(CommandRouter *cmd, int argc, const char **argv) { + if (argc > 6 || argc < 5) { + return EINVAL; + } + + uint16_t n = (uint16_t) strtol(argv[1], nullptr, 0); + uint16_t r = (uint16_t) strtol(argv[2], nullptr, 0); + uint16_t g = (uint16_t) strtol(argv[3], nullptr, 0); + uint16_t b = (uint16_t) strtol(argv[4], nullptr, 0); + if (argc == 6) { + uint16_t w = (uint16_t) strtol(argv[5], nullptr, 0); + neopixel_strip.setPixelColor(n, r, g, b, w); + } else { + neopixel_strip.setPixelColor(n, r, g, b); + } + + return 0; +} +#endif + void loop() { // TODO: remove this check on if Serial is available. diff --git a/versioneer.py b/versioneer.py index 8ff1e54..cd4e3e7 100644 --- a/versioneer.py +++ b/versioneer.py @@ -3,7 +3,7 @@ revision = subprocess.check_output(["git", "describe", "--tags", "--dirty"]).strip() revision = revision.decode() revision = revision.replace('-dirty', '.dirty', 1) -revision = revision.replace('-', '.dev', 1) +revision = revision.replace('-', '.post', 1) revision = revision.replace('-g', '+g', 1) print('-DGIT_DESCRIBE=\'"%s"\'' % revision)