Skip to content

Commit

Permalink
Overhaul Plasma LEDs to use Multiverse protocol.
Browse files Browse the repository at this point in the history
This gives us reset/bootloader features which are super useful.
  • Loading branch information
Gadgetoid committed Mar 15, 2024
1 parent 369373c commit b328141
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 10 deletions.
113 changes: 104 additions & 9 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <string_view>
#include <ctype.h> // isupper / islower

#include "bsp/board.h"
Expand All @@ -38,9 +39,19 @@

#include "hardware/clocks.h"
#include "hardware/vreg.h"
#include "pico/bootrom.h"
#include "hardware/structs/rosc.h"
#include "hardware/watchdog.h"
#include "pico/timeout_helper.h"

pimoroni::RGBLED led(17, 18, 19);

const size_t MAX_UART_PACKET = 64;

const size_t COMMAND_LEN = 4;
uint8_t command_buffer[COMMAND_LEN];
std::string_view command((const char *)command_buffer, COMMAND_LEN);


extern "C" {
void usb_serial_init(void);
Expand All @@ -60,7 +71,54 @@ enum {
};

void hid_task(void);
void cdc_task(void);
uint cdc_task(uint8_t *buf, size_t buf_len);

uint cdc_task(uint8_t *buf, size_t buf_len) {

if (tud_cdc_connected()) {
if (tud_cdc_available()) {
return tud_cdc_read(buf, buf_len);
}
}

return 0;
}

bool cdc_wait_for(std::string_view data, uint timeout_ms=50) {
timeout_state ts;
absolute_time_t until = delayed_by_ms(get_absolute_time(), timeout_ms);
check_timeout_fn check_timeout = init_single_timeout_until(&ts, until);

for(auto expected_char : data) {
char got_char;
while(1){
tud_task();
if (cdc_task((uint8_t *)&got_char, 1) == 1) break;
if(check_timeout(&ts)) return false;
}
if (got_char != expected_char) return false;
}
return true;
}

size_t cdc_get_bytes(const uint8_t *buffer, const size_t len, const uint timeout_ms=1000) {
memset((void *)buffer, len, 0);

uint8_t *p = (uint8_t *)buffer;

timeout_state ts;
absolute_time_t until = delayed_by_ms(get_absolute_time(), timeout_ms);
check_timeout_fn check_timeout = init_single_timeout_until(&ts, until);

size_t bytes_remaining = len;
while (bytes_remaining && !check_timeout(&ts)) {
tud_task(); // tinyusb device task
size_t bytes_read = cdc_task(p, std::min(bytes_remaining, MAX_UART_PACKET));
bytes_remaining -= bytes_read;
p += bytes_read;
}
return len - bytes_remaining;
}

/*------------- MAIN -------------*/
int main(void)
Expand Down Expand Up @@ -90,7 +148,43 @@ int main(void)
{
tud_task();
hid_task();
cdc_task();
//cdc_task();

if (tud_cdc_connected()) {
if (tud_cdc_available()) {
if(!cdc_wait_for("multiverse:")) {
continue; // Couldn't get 16 bytes of command
}

if(cdc_get_bytes(command_buffer, COMMAND_LEN) != COMMAND_LEN) {
//display::info("cto");
continue;
}

if(command == "data") {
if (cdc_get_bytes(led_front_buffer, sizeof(led_front_buffer)) == sizeof(led_front_buffer)) {
plasma_flip();
}
continue;
}

if(command == "_rst") {
sleep_ms(500);
save_and_disable_interrupts();
rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB;
watchdog_reboot(0, 0, 0);
continue;
}

if(command == "_usb") {
sleep_ms(500);
save_and_disable_interrupts();
rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB;
reset_usb_boot(0, 0);
continue;
}
}
}
}

return 0;
Expand Down Expand Up @@ -218,8 +312,8 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t rep
//--------------------------------------------------------------------+
// USB CDC
//--------------------------------------------------------------------+
extern uint8_t led_buffer[32 * 4 * 4]; // Uh this is plasma's buffer

/*
void cdc_task(void)
{
const uint32_t interval_ms = 500;
Expand All @@ -228,10 +322,10 @@ void cdc_task(void)
//uint8_t buf[64] = {0};
if( tud_cdc_available() ) {
ptr = tud_cdc_read(led_buffer + ptr, sizeof(led_buffer));
if(ptr >= sizeof(led_buffer)){
ptr = 0;
}
//ptr = tud_cdc_read(led_buffer + ptr, sizeof(led_buffer));
//if(ptr >= sizeof(led_buffer)){
// ptr = 0;
//}
}
if(tud_cdc_connected()) {
Expand All @@ -254,4 +348,5 @@ void cdc_task(void)
tud_cdc_write_flush();
#endif
}
}
}
*/
13 changes: 13 additions & 0 deletions plasma.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

// TODO I count 30 inputs on the board- 12 per player + 6 util so we're probably OK with 32 buttons * 4 LEDs * 4 bytes?
uint8_t led_buffer[32 * 4 * 4] = {0};
uint8_t led_front_buffer[32 * 4 * 4] = {0};

// TODO these might need dialling in but seem okay on my 4x4 rig
uint8_t apa102_sof[8] = {0x00};
Expand Down Expand Up @@ -50,6 +51,18 @@ void plasma_init() {
true);
}

void plasma_flip() {
/*
Plasma is SOF B G R
Multiverse is B G R _
*/
for(auto x = 0u; x < sizeof(led_buffer); x += 4) {
led_buffer[x + 1] = led_front_buffer[x + 0];
led_buffer[x + 2] = led_front_buffer[x + 1];
led_buffer[x + 3] = led_front_buffer[x + 2];
}
}

void plasma_set_all(uint8_t r, uint8_t g, uint8_t b, uint8_t brightness) {
for(auto x = 0u; x < sizeof(led_buffer); x += 4) {
led_buffer[x + 0] = APA102_SOF | brightness;
Expand Down
4 changes: 3 additions & 1 deletion plasma.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

const uint PLASMA_CLOCK = 22;
const uint PLASMA_DATA = 23;
extern uint8_t led_front_buffer[32 * 4 * 4];

void plasma_init();
void plasma_set_all(uint8_t r, uint8_t g, uint8_t b, uint8_t brightness=31);
void plasma_set_all(uint8_t r, uint8_t g, uint8_t b, uint8_t brightness=31);
void plasma_flip();
8 changes: 8 additions & 0 deletions reset-picade.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import multiverse
import glob

picades = glob.glob("/dev/serial/by-id/usb-Pimoroni_Picade_Max_*")

d = multiverse.Display(picades[0], 32, 32, 0, 0)
d.setup()
d.bootloader()

0 comments on commit b328141

Please sign in to comment.