forked from jappel/VersaMCU
Added Macro functionality, updated readme
This commit is contained in:
parent
b49984b9c0
commit
59b3cb4dd1
9 changed files with 354 additions and 33 deletions
|
|
@ -31,7 +31,7 @@
|
|||
#include "hal/usb_serial.h"
|
||||
#include "config/pins.h"
|
||||
#include "config/nvm_config.h"
|
||||
#include <string.h>
|
||||
#include "config/macro_config.h"
|
||||
|
||||
// ─── Static Bridge: HAL-Callbacks → EventQueue ───────────────────────────────
|
||||
//
|
||||
|
|
@ -70,13 +70,18 @@ static void encoder_cb(uint8_t enc, int8_t dir)
|
|||
CMainController::CMainController()
|
||||
: m_cfg_chunks_expected(0)
|
||||
, m_cfg_receiving(false)
|
||||
, m_macro_chunks_expected(0)
|
||||
, m_macro_receiving(false)
|
||||
{
|
||||
memset(m_cfg_buf, 0, sizeof(m_cfg_buf));
|
||||
memset(m_cfg_buf, 0, sizeof(m_cfg_buf));
|
||||
memset(m_macro_buf, 0, sizeof(m_macro_buf));
|
||||
memset(&m_macros, 0, sizeof(m_macros));
|
||||
}
|
||||
|
||||
void CMainController::setup()
|
||||
{
|
||||
init_buttons(); // Buttons aus NVM laden (oder Defaults)
|
||||
macro_config_load(m_macros); // Makro-Tabelle aus NVM laden (oder leere Tabelle)
|
||||
init_buttons(); // Buttons aus NVM laden (oder Defaults)
|
||||
s_queue = &m_queue; // Queue-Pointer setzen bevor Callbacks registriert werden
|
||||
usb_hid_init(); // HID-Descriptor registriert sich via globalem Konstruktor,
|
||||
// usb_hid_init() ist hier ein No-Op aber verdeutlicht die Abhängigkeit
|
||||
|
|
@ -102,13 +107,8 @@ void CMainController::init_buttons()
|
|||
}
|
||||
|
||||
// MX-Buttons: LED-Index aus serpentiner Verdrahtung berechnen,
|
||||
// Aktion + Base-Farbe aus NVM.
|
||||
// Aktion + Base-Farbe + Animation aus NVM.
|
||||
// mx_actions[0] ↔ key_id 5 (COL_1/ROW_0), mx_actions[19] ↔ key_id 24 (COL_4/ROW_4)
|
||||
//
|
||||
// Idle-Animation: Regenbogen-Sweep über alle 20 LEDs.
|
||||
// Jede LED bekommt einen gleichmäßigen Hue-Versatz (phase = idx * period / 20),
|
||||
// sodass immer ein voller Regenbogen auf dem Pad liegt und sich langsam dreht.
|
||||
const uint16_t k_rainbow_period = 4000; // 4s pro volle Runde
|
||||
|
||||
for (uint8_t key = 5; key < MATRIX_KEYS; key++) {
|
||||
uint8_t col = key / MATRIX_ROWS;
|
||||
|
|
@ -118,9 +118,16 @@ void CMainController::init_buttons()
|
|||
RGB base(cfg.led_r[mx_idx], cfg.led_g[mx_idx], cfg.led_b[mx_idx]);
|
||||
m_buttons[key].init(key, led, cfg.mx_actions[mx_idx], base);
|
||||
|
||||
// Phase gleichmäßig verteilen: LED 0 = Hue 0, LED 19 = Hue ~242 (fast voll)
|
||||
uint16_t phase = (uint16_t)((uint32_t)mx_idx * k_rainbow_period / 20);
|
||||
m_buttons[key].set_anim(LEDAnim::COLOR_CYCLE, k_rainbow_period, phase);
|
||||
LEDAnim anim = static_cast<LEDAnim>(cfg.led_anim[mx_idx]);
|
||||
uint16_t period = cfg.led_period_ms[mx_idx] > 0 ? cfg.led_period_ms[mx_idx] : 4000;
|
||||
|
||||
if (anim == LEDAnim::COLOR_CYCLE) {
|
||||
// Phase gleichmäßig verteilen → stehender Regenbogen dreht sich
|
||||
uint16_t phase = (uint16_t)((uint32_t)mx_idx * period / 20);
|
||||
m_buttons[key].set_anim(LEDAnim::COLOR_CYCLE, period, phase);
|
||||
} else {
|
||||
m_buttons[key].set_anim(anim, period);
|
||||
}
|
||||
}
|
||||
|
||||
// Encoder CW/CCW-Aktionen separat merken – Encoder haben kein CButton-Objekt
|
||||
|
|
@ -246,6 +253,58 @@ void CMainController::poll_vendor()
|
|||
}
|
||||
break;
|
||||
|
||||
// ── Makro-Übertragung: BEGIN → n×DATA → COMMIT ──────────────────
|
||||
case USB_CMD_MACRO_BEGIN:
|
||||
m_macro_chunks_expected = pkt.key_id();
|
||||
m_macro_receiving = true;
|
||||
memset(m_macro_buf, 0, sizeof(m_macro_buf));
|
||||
break;
|
||||
|
||||
case USB_CMD_MACRO_DATA:
|
||||
if (m_macro_receiving) {
|
||||
uint16_t offset = (uint16_t)pkt.key_id() * 6;
|
||||
if (offset < sizeof(m_macro_buf)) {
|
||||
uint8_t count = (uint8_t)(sizeof(m_macro_buf) - offset);
|
||||
if (count > 6) count = 6;
|
||||
memcpy(m_macro_buf + offset, &pkt.data[2], count);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_CMD_MACRO_COMMIT:
|
||||
if (m_macro_receiving) {
|
||||
m_macro_receiving = false;
|
||||
memcpy(&m_macros, m_macro_buf, sizeof(m_macros));
|
||||
macro_config_save(m_macros);
|
||||
usb_serial_send(USB_EVT_MACRO_ACK, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
// ── Makro-Dump anfordern ─────────────────────────────────────────
|
||||
case USB_CMD_MACRO_READ:
|
||||
{
|
||||
const uint8_t* raw = reinterpret_cast<const uint8_t*>(&m_macros);
|
||||
const uint16_t sz = sizeof(SMacroTable); // 256
|
||||
const uint8_t payload = 6;
|
||||
uint8_t chunks = (uint8_t)((sz + payload - 1) / payload); // 43
|
||||
|
||||
usb_serial_send(USB_EVT_MACRO_BEGIN, chunks);
|
||||
|
||||
for (uint8_t i = 0; i < chunks; i++) {
|
||||
uint8_t p[SERIAL_PKT_SIZE] = {};
|
||||
p[0] = USB_EVT_MACRO_DATA;
|
||||
p[1] = i;
|
||||
uint16_t offset = (uint16_t)i * payload;
|
||||
for (uint8_t b = 0; b < payload; b++) {
|
||||
if (offset + b < sz) p[2 + b] = raw[offset + b];
|
||||
}
|
||||
if (SerialUSB) SerialUSB.write(p, SERIAL_PKT_SIZE);
|
||||
}
|
||||
|
||||
usb_serial_send(USB_EVT_MACRO_END, chunks);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -328,6 +387,23 @@ void CMainController::execute_action(SAction action)
|
|||
// Wird in processEvents() über Serial gesendet
|
||||
break;
|
||||
|
||||
case ActionType::MACRO:
|
||||
{
|
||||
// Makro-Slot aus dem RAM ausführen (bei setup() aus NVM geladen).
|
||||
// Steps mit keycode=0 werden übersprungen; erstes leeres Step stoppt.
|
||||
uint8_t slot = static_cast<uint8_t>(action.data);
|
||||
if (slot >= MACRO_SLOTS) break;
|
||||
for (uint8_t i = 0; i < MACRO_MAX_STEPS; i++) {
|
||||
const SMacroStep& s = m_macros.steps[slot][i];
|
||||
if (s.keycode == 0) break;
|
||||
usb_hid_send_key(s.keycode, s.modifier);
|
||||
delay(10);
|
||||
usb_hid_release_key();
|
||||
delay(20); // Kurze Pause zwischen Steps damit der Host mitkommt
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ActionType::NONE:
|
||||
default:
|
||||
break;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue