-
PoC Firmware Feature Complete
11/03/2024 at 19:48 • 0 comments2 days of hacking at Supercon8 tables and what do we have?!
... PoC Feature-Complete!
- RP2040 Slave PoC firmware feature-complete? Yes.
- RP2040 Master PoC firmware feature-complete? Yes.
- FUFF transport-layer protocol over-I2C implemented? Yes.
- payloads (file xfer) working from RP2040 master to slaves? Yes.
- RPC commands working from master to slave (activates SAO features)? Yes.- Circuit mounted on Supercon8 SAO protoboard, demoing on badge? In-progress...
- Custom PCB? No. Focused on the comm stack, as is the theme of the SAO contest.
Git repo (includes example code for RP2040 I2C Slave): -
Porting from Arduino (C++) to RP2040 (MicroPython)
10/25/2024 at 06:24 • 0 commentsI needed to port my Arduino design onto the RP2040. I was going to use a boost converter, but I don't have one, so I just decided to use the 5V rail on the RP2040-One that gets "populated" on the RP2040 when powered over USB. If I take care of the 5V rail on the circuit later, the code should be the same either way.
We can see I have another rp2040 "SAO" connected via a directly soldered I2C bus ;) I'm working on the I2C communication -- the disconnected rp2040-Matrix you see connected will be pretending to be the conference badge (the I2C master), for now.
Pics of the updated prototype circuit:
A video of the same animations playing, but now through micropython:
The ported code is as follows:
# Arduino code ported to RP2040 micropython # ----------------------------------------- import machine import neopixel import time import random LED_PIN = 8 LED_COUNT = 14 LED_BRIGHTNESS = 0.05 # Start with 0.1 to check visibility; adjust to preferred level LED_CENTER_LEFT = 0 LED_CENTER_RIGHT = 7 LED_LEFT_MIN = 1 # Exclude center LED (left spool) LED_LEFT_MAX = 6 # Exclude center LED (left spool) LED_RIGHT_MIN = 8 # Exclude center LED (right spool) LED_RIGHT_MAX = 13 # Exclude center LED (right spool) # Initialize NeoPixel np = neopixel.NeoPixel(machine.Pin(LED_PIN), LED_COUNT) LED_LEFT_INDEX = random.randint(LED_LEFT_MIN, LED_LEFT_MAX) LED_RIGHT_INDEX = random.randint(LED_RIGHT_MIN, LED_RIGHT_MAX) # Original colors FOREGROUND_COLOR = (0, 0, 0) # Black BACKGROUND_COLOR = (255, 165, 0) # Orange CENTER_COLOR = (255, 0, 0) # Red # Function to adjust brightness with minimum scaling effect def set_neopixel_color(color, brightness_factor): return tuple(max(int(c * brightness_factor), 1) for c in color) # Animation functions with brightness adjustment # ---------------------------------------------- def animation_play(millis_delay=130): global LED_LEFT_INDEX, LED_RIGHT_INDEX # Play animation for left spool if LED_LEFT_INDEX < LED_LEFT_MAX: np[LED_LEFT_INDEX] = set_neopixel_color(BACKGROUND_COLOR, LED_BRIGHTNESS) np[LED_LEFT_INDEX + 1] = set_neopixel_color(FOREGROUND_COLOR, LED_BRIGHTNESS) LED_LEFT_INDEX += 1 else: np[LED_LEFT_MAX] = set_neopixel_color(BACKGROUND_COLOR, LED_BRIGHTNESS) np[LED_LEFT_MIN] = set_neopixel_color(FOREGROUND_COLOR, LED_BRIGHTNESS) LED_LEFT_INDEX = LED_LEFT_MIN # Play animation for right spool if LED_RIGHT_INDEX < LED_RIGHT_MAX: np[LED_RIGHT_INDEX] = set_neopixel_color(BACKGROUND_COLOR, LED_BRIGHTNESS) np[LED_RIGHT_INDEX + 1] = set_neopixel_color(FOREGROUND_COLOR, LED_BRIGHTNESS) LED_RIGHT_INDEX += 1 else: np[LED_RIGHT_MAX] = set_neopixel_color(BACKGROUND_COLOR, LED_BRIGHTNESS) np[LED_RIGHT_MIN] = set_neopixel_color(FOREGROUND_COLOR, LED_BRIGHTNESS) LED_RIGHT_INDEX = LED_RIGHT_MIN np.write() time.sleep_ms(millis_delay) def animation_rewind(millis_delay=50): global LED_LEFT_INDEX, LED_RIGHT_INDEX # Rewind animation for left spool if LED_LEFT_INDEX > LED_LEFT_MIN: np[LED_LEFT_INDEX] = set_neopixel_color(BACKGROUND_COLOR, LED_BRIGHTNESS) np[LED_LEFT_INDEX - 1] = set_neopixel_color(FOREGROUND_COLOR, LED_BRIGHTNESS) LED_LEFT_INDEX -= 1 else: np[LED_LEFT_MIN] = set_neopixel_color(BACKGROUND_COLOR, LED_BRIGHTNESS) np[LED_LEFT_MAX] = set_neopixel_color(FOREGROUND_COLOR, LED_BRIGHTNESS) LED_LEFT_INDEX = LED_LEFT_MAX # Rewind animation for right spool if LED_RIGHT_INDEX > LED_RIGHT_MIN: np[LED_RIGHT_INDEX] = set_neopixel_color(BACKGROUND_COLOR, LED_BRIGHTNESS) np[LED_RIGHT_INDEX - 1] = set_neopixel_color(FOREGROUND_COLOR, LED_BRIGHTNESS) LED_RIGHT_INDEX -= 1 else: np[LED_RIGHT_MIN] = set_neopixel_color(BACKGROUND_COLOR, LED_BRIGHTNESS) np[LED_RIGHT_MAX] = set_neopixel_color(FOREGROUND_COLOR, LED_BRIGHTNESS) LED_RIGHT_INDEX = LED_RIGHT_MAX np.write() time.sleep_ms(millis_delay) # Blink to indicate function triggered def animation_function_triggered(color): np[LED_CENTER_LEFT] = set_neopixel_color(color, LED_BRIGHTNESS) np[LED_CENTER_RIGHT] = set_neopixel_color(color, LED_BRIGHTNESS) np.write() time.sleep_ms(100) np[LED_CENTER_LEFT] = set_neopixel_color((0, 0, 0), LED_BRIGHTNESS) np[LED_CENTER_RIGHT] = set_neopixel_color((0, 0, 0), LED_BRIGHTNESS) np.write() def animation_pause(): np[LED_LEFT_INDEX] = set_neopixel_color(BACKGROUND_COLOR, LED_BRIGHTNESS) np[LED_RIGHT_INDEX] = set_neopixel_color(BACKGROUND_COLOR, LED_BRIGHTNESS) np.write() time.sleep_ms(100) def animation_bootup(): # Illuminate left spool for i in range(LED_LEFT_MIN, LED_LEFT_MAX + 1): if i == LED_CENTER_LEFT: continue np[i] = set_neopixel_color(BACKGROUND_COLOR, LED_BRIGHTNESS) np.write() time.sleep_ms(50) # Illuminate right spool for i in range(LED_RIGHT_MIN, LED_RIGHT_MAX + 1): if i == LED_CENTER_RIGHT: continue np[i] = set_neopixel_color(BACKGROUND_COLOR, LED_BRIGHTNESS) np.write() time.sleep_ms(50) def animation_fastforward(): animation_play(40) # Main setup # ---------- animation_bootup() # Main loop # --------- while True: animation_function_triggered((255, 255, 255)) # White for _ in range(20): animation_pause() animation_function_triggered((255, 255, 255)) for _ in range(50): animation_play() animation_function_triggered((255, 255, 255)) for _ in range(150): animation_fastforward() animation_function_triggered((255, 255, 255)) for _ in range(50): animation_play() animation_function_triggered((255, 255, 255)) for _ in range(50): animation_rewind()
-
Coding The Animations
10/16/2024 at 09:19 • 0 commentsI enhanced the prototype code to implement animations for PLAY, PAUSE, REWIND, and FASTFORWARD -- video of latest functionality is at the bottom of this project log.
I included an indexing offset to imitate the asymmetric spinning of the rotational indicator on tape spools, as seen here:
Here is the code:
// neopixel code // -------------- #include <FastLED.h> #define LED_PIN 5 #define LED_COUNT 14 #define LED_BRIGHTNESS 5 // max 255 which is blinding #define LED_CENTER_LEFT 0 #define LED_CENTER_RIGHT 7 CRGB leds[LED_COUNT]; // must be lowercase and this variable name or the compilation fails int LED_LEFT_CENTER = 0; int LED_RIGHT_CENTER = 7; int LED_LEFT_MIN = 1; // exclude center LED (left spool) int LED_LEFT_MAX = 6; // exclude center LED (left spool) int LED_RIGHT_MIN = 8; // exclude center LED (right spool) int LED_RIGHT_MAX = 13; // exclude center LED (right spool) int LED_LEFT_INDEX = random(LED_LEFT_MIN, LED_LEFT_MAX); // index for rotation animations (left spool) int LED_RIGHT_INDEX = random(LED_RIGHT_MIN, LED_RIGHT_MAX); // index for rotation animations (right spool) CRGB FOREGROUND_COLOR = CRGB::Black; CRGB BACKGROUND_COLOR = CRGB::Orange; CRGB CENTER_COLOR = CRGB::Red; // function prototypes // -------------- void animation_play(int millisDelay = 130); // function prototype to accept optional argument void animation_rewind(int millisDelay = 50); // function prototype to accept optional argument // main setup // -------------- void setup() { Serial.begin(9600); FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, LED_COUNT); FastLED.setBrightness(LED_BRIGHTNESS); animation_bootup(); } // main loop // -------------- void loop() { animation_function_triggered(CRGB::White); for (int i=0; i < 20; i++) { animation_pause(); } animation_function_triggered(CRGB::White); for (int i=0; i < 50; i++) { animation_play(); } animation_function_triggered(CRGB::White); for (int i=0; i < 150; i++) { animation_fastforward(); } animation_function_triggered(CRGB::White); for (int i=0; i < 50; i++) { animation_play(); } animation_function_triggered(CRGB::White); for (int i=0; i < 50; i++) { animation_rewind(); } } // blink to indicate function triggered void animation_function_triggered(CRGB color) { // on leds[LED_LEFT_CENTER] = color; leds[LED_RIGHT_CENTER] = color; FastLED.show(); // sleep delay(100); // off leds[LED_LEFT_CENTER] = CRGB::Black; leds[LED_RIGHT_CENTER] = CRGB::Black; FastLED.show(); } void animation_pause() { leds[LED_LEFT_INDEX] = BACKGROUND_COLOR; leds[LED_RIGHT_INDEX] = BACKGROUND_COLOR; FastLED.show(); delay(100); } void animation_bootup() { // illuminate left spool for (int i = LED_LEFT_MIN; i <= LED_LEFT_MAX; i++) { if (i == LED_CENTER_LEFT) { continue; } leds[i] = BACKGROUND_COLOR; FastLED.show(); delay(50); } // illuminate right spool for (int i = LED_RIGHT_MIN; i <= LED_RIGHT_MAX; i++) { if (i == LED_CENTER_RIGHT) { continue; } leds[i] = BACKGROUND_COLOR; FastLED.show(); delay(50); } } void animation_fastforward() { animation_play(40); } // speed is delay, so more is slower void animation_play(int millisDelay) { // animation: PLAY (left spool) if (LED_LEFT_INDEX < LED_LEFT_MAX) { leds[LED_LEFT_INDEX] = BACKGROUND_COLOR; leds[LED_LEFT_INDEX+1] = FOREGROUND_COLOR; LED_LEFT_INDEX++; } else { leds[LED_LEFT_MAX] = BACKGROUND_COLOR; leds[LED_LEFT_MIN] = FOREGROUND_COLOR; LED_LEFT_INDEX = LED_LEFT_MIN; } // animation: PLAY (right spool) if (LED_RIGHT_INDEX < LED_RIGHT_MAX) { leds[LED_RIGHT_INDEX] = BACKGROUND_COLOR; leds[LED_RIGHT_INDEX+1] = FOREGROUND_COLOR; LED_RIGHT_INDEX++; } else { leds[LED_RIGHT_MAX] = BACKGROUND_COLOR; leds[LED_RIGHT_MIN] = FOREGROUND_COLOR; LED_RIGHT_INDEX = LED_RIGHT_MIN; } FastLED.show(); delay(millisDelay); } // speed is delay, so more is slower void animation_rewind(int millisDelay) { // animation: REWIND (left spool) if (LED_LEFT_INDEX > LED_LEFT_MIN) { leds[LED_LEFT_INDEX] = BACKGROUND_COLOR; leds[LED_LEFT_INDEX-1] = FOREGROUND_COLOR; LED_LEFT_INDEX--; Serial.println("decrement index"); } else { leds[LED_LEFT_MIN] = BACKGROUND_COLOR; leds[LED_LEFT_MAX] = FOREGROUND_COLOR; LED_LEFT_INDEX = LED_LEFT_MAX; Serial.println("restart index"); } // animation: REWIND (right spool) if (LED_RIGHT_INDEX > LED_RIGHT_MIN) { leds[LED_RIGHT_INDEX] = BACKGROUND_COLOR; leds[LED_RIGHT_INDEX-1] = FOREGROUND_COLOR; LED_RIGHT_INDEX--; } else { leds[LED_RIGHT_MIN] = BACKGROUND_COLOR; leds[LED_RIGHT_MAX] = FOREGROUND_COLOR; LED_RIGHT_INDEX = LED_RIGHT_MAX; } Serial.print("LED_LEFT_INDEX: "); Serial.print(LED_LEFT_INDEX); Serial.println(); Serial.print("LED_RIGHT_INDEX: "); Serial.print(LED_RIGHT_INDEX); Serial.println(); FastLED.show(); delay(millisDelay); }
... and the updated animations:
-
Prototype Assembly: Part 3
10/16/2024 at 04:15 • 0 commentsNow I just need to code the demo:
// neopixel code // -------------- #include <FastLED.h> // neopixel code // -------------- #define LED_PIN 5 #define LED_COUNT 14 #define LED_BRIGHTNESS 5 #define LED_CENTER_LEFT 0 #define LED_CENTER_RIGHT 7 // neopixel code // -------------- // must be lowercase and this variable name or the compilation fails CRGB leds[LED_COUNT]; // di0_demo1 code // -------------- // exclude center LED int LED_LEFT_MIN = 1; int LED_LEFT_MAX = 6; // exclude center LED int LED_RIGHT_MIN = 8; int LED_RIGHT_MAX = 13; // keep the indexes for which LEDs are off (CRGB::Black) int LED_LEFT_OFF_INDEX = LED_LEFT_MIN; int LED_RIGHT_OFF_INDEX = LED_RIGHT_MIN; // main setup // -------------- void setup() { Serial.begin(9600); FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, LED_COUNT); FastLED.setBrightness(LED_BRIGHTNESS); di0_demo1_setup(); } // main loop // -------------- void loop() { //di0_demo0(); di0_demo1_loop(); } // di0_demo1 code // -------------- void di0_demo1_setup() { // illuminate left spool for (int i = LED_LEFT_MIN; i <= LED_LEFT_MAX; i++) { if (i == LED_CENTER_LEFT) { continue; } leds[i] = CRGB::WhiteSmoke; FastLED.show(); delay(50); } // illuminate right spool for (int i = LED_RIGHT_MIN; i <= LED_RIGHT_MAX; i++) { if (i == LED_CENTER_RIGHT) { continue; } leds[i] = CRGB::WhiteSmoke; FastLED.show(); delay(50); } } // di0_demo1 code // -------------- void di0_demo1_loop() { // animation: PLAY (left spool) if (LED_LEFT_OFF_INDEX < LED_LEFT_MAX) { leds[LED_LEFT_OFF_INDEX] = CRGB::Blue; leds[LED_LEFT_OFF_INDEX+1] = CRGB::WhiteSmoke; LED_LEFT_OFF_INDEX++; } else { leds[LED_LEFT_MAX] = CRGB::Blue; leds[LED_LEFT_MIN] = CRGB::WhiteSmoke; LED_LEFT_OFF_INDEX = LED_LEFT_MIN; } // animation: PLAY (right spool) if (LED_RIGHT_OFF_INDEX < LED_RIGHT_MAX) { leds[LED_RIGHT_OFF_INDEX] = CRGB::Blue; leds[LED_RIGHT_OFF_INDEX+1] = CRGB::WhiteSmoke; LED_RIGHT_OFF_INDEX++; } else { leds[LED_RIGHT_MAX] = CRGB::Blue; leds[LED_RIGHT_MIN] = CRGB::WhiteSmoke; LED_RIGHT_OFF_INDEX = LED_RIGHT_MIN; } FastLED.show(); delay(100); }
... and voila:
-
Prototype Assembly: Part 2
10/16/2024 at 01:09 • 0 commentsI've completed my test circuit as follows:
Arduino
- Digital Pin 5 --> LED Module 1: DIN
- 5V --> Breadboard Positive Rail
- Gnd --> Breadboard Negative Rail
LED Module 1:
- DIN --> Arduino: Digital Pin 5
- DOUT --> LED Module 2: DIN
- PWR --> Breadboard Positive Rail
- GND --> Breadboard Negative Rail
LED Module 2:
- DIN --> LED Module 1: DOUT
- DOUT --> Disconnected
- PWR --> Breadboard Positive Rail
- GND --> Breadboard Negative Rail
-
Prototype Assembly: Part 1
10/15/2024 at 22:57 • 0 commentsLet's keep it stupid-simple with the big ole trusty Arduino Uno for the PoC / breadboarding, especially since these RGB arrays want 5V. I will port this prototype to an RP2040-based design with a boost converter on the custom-printed PCB for the con, as time / blockers allow.
The pinouts on these WS2812B modules fit exactly across the breadboard gap. There is an extra middle pin for ground, but I don't think I need it.
Adding this log on the GPD Win Max 2 (2024) and messing with my RP2040-based Thumby. Also, to the left we can see the RP2040-One connected to an RP2040-Matrix, which are my other silly SAO prototypes, which I'd like to use as placeholder SAOs to demo on the Jacana FUFF Mux (https://hackaday.io/project/198240-fuff-mux-sao).