Skip to content

Commit

Permalink
Reactive LEDs (#1104)
Browse files Browse the repository at this point in the history
* Implementation of basic reactive LEDs (non-addressable)

* Fixed DPad button masks.
  • Loading branch information
mikepparks authored Aug 14, 2024
1 parent 50f9a76 commit 362e23e
Show file tree
Hide file tree
Showing 14 changed files with 483 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ src/addons/display.cpp
src/addons/neopicoleds.cpp
src/addons/playernum.cpp
src/addons/playerleds.cpp
src/addons/reactiveleds.cpp
src/addons/rotaryencoder.cpp
src/addons/reverse.cpp
src/addons/drv8833_rumble.cpp
Expand Down
56 changes: 56 additions & 0 deletions headers/addons/reactiveleds.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#ifndef REACTIVELEDS_H_
#define REACTIVELEDS_H_

#include "gpaddon.h"

#ifndef REACTIVE_LED_ENABLED
#define REACTIVE_LED_ENABLED 0
#endif

#ifndef REACTIVE_LED_COUNT
#define REACTIVE_LED_COUNT 8
#endif

#ifndef REACTIVE_LED_DELAY
#define REACTIVE_LED_DELAY 1
#endif

#ifndef REACTIVE_LED_MAX_BRIGHTNESS
#define REACTIVE_LED_MAX_BRIGHTNESS 255
#endif

#ifndef REACTIVE_LED_FADE_INC
#define REACTIVE_LED_FADE_INC 1
#endif

// Reactive LED Module
#define ReactiveLEDName "ReactiveLED"

// Reactive LED
class ReactiveLEDAddon : public GPAddon
{
public:
virtual bool available();
virtual void setup();
virtual void preprocess() {}
virtual void process();
virtual std::string name() { return ReactiveLEDName; }
private:
struct ReactiveLEDPinState {
uint16_t pinNumber = -1;
ReactiveLEDMode modeDown = ReactiveLEDMode::REACTIVE_LED_STATIC_ON;
ReactiveLEDMode modeUp = ReactiveLEDMode::REACTIVE_LED_STATIC_OFF;
GpioAction action = GpioAction::NONE;
uint8_t value = 0;
bool currState = false;
bool prevState = false;
uint32_t lastUpdate;
uint32_t currUpdate;
};

ReactiveLEDPinState ledPins[REACTIVE_LED_COUNT];

void setLEDByMode(ReactiveLEDPinState &ledState, bool pressed);
};

#endif
15 changes: 15 additions & 0 deletions proto/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,20 @@ message DRV8833RumbleOptions
optional float dutyMax = 7;
}

message ReactiveLEDInfo
{
optional int32 pin = 1;
optional GpioAction action = 2;
optional ReactiveLEDMode modeDown = 3;
optional ReactiveLEDMode modeUp = 4;
}

message ReactiveLEDOptions
{
optional bool enabled = 1;
repeated ReactiveLEDInfo leds = 2 [(nanopb).max_count = 8];
}

message AddonOptions
{
optional BootselButtonOptions bootselButtonOptions = 1;
Expand Down Expand Up @@ -778,6 +792,7 @@ message AddonOptions
optional RotaryOptions rotaryOptions = 24;
optional PCF8575Options pcf8575Options = 25;
optional DRV8833RumbleOptions drv8833RumbleOptions = 26;
optional ReactiveLEDOptions reactiveLEDOptions = 27;
}

message MigrationHistory
Expand Down
10 changes: 10 additions & 0 deletions proto/enums.proto
Original file line number Diff line number Diff line change
Expand Up @@ -404,3 +404,13 @@ enum RotaryEncoderPinMode
ENCODER_MODE_DPAD_X = 7;
ENCODER_MODE_DPAD_Y = 8;
};

enum ReactiveLEDMode
{
option (nanopb_enumopt).long_names = false;

REACTIVE_LED_STATIC_OFF = 0;
REACTIVE_LED_STATIC_ON = 1;
REACTIVE_LED_FADE_IN = 2;
REACTIVE_LED_FADE_OUT = 3;
};
114 changes: 114 additions & 0 deletions src/addons/reactiveleds.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#include "hardware/pwm.h"
#include "addons/reactiveleds.h"
#include "storagemanager.h"
#include "usbdriver.h"
#include "helper.h"
#include "config.pb.h"

bool ReactiveLEDAddon::available() {
bool pinsEnabled = false;
const ReactiveLEDOptions& options = Storage::getInstance().getAddonOptions().reactiveLEDOptions;
for (uint8_t led = 0; led < sizeof(REACTIVE_LED_COUNT); led++) {
if (isValidPin(options.leds[led].pin)) {
pinsEnabled = true;
break;
}
}
return options.enabled && pinsEnabled;
}

void ReactiveLEDAddon::setup() {
const ReactiveLEDOptions& options = Storage::getInstance().getAddonOptions().reactiveLEDOptions;

for (uint8_t led = 0; led < sizeof(REACTIVE_LED_COUNT); led++) {
ReactiveLEDInfo ledInfo = options.leds[led];

ledPins[led].pinNumber = ledInfo.pin;
ledPins[led].action = ledInfo.action;
ledPins[led].modeDown = ledInfo.modeDown;
ledPins[led].modeUp = ledInfo.modeUp;

if (isValidPin(ledPins[led].pinNumber)) {
gpio_init(ledPins[led].pinNumber);
gpio_set_dir(ledPins[led].pinNumber, GPIO_OUT);
gpio_set_function(ledPins[led].pinNumber, GPIO_FUNC_PWM);

pwm_set_wrap(pwm_gpio_to_slice_num(ledPins[led].pinNumber), REACTIVE_LED_MAX_BRIGHTNESS);
pwm_set_enabled(pwm_gpio_to_slice_num(ledPins[led].pinNumber), true);

ledPins[led].lastUpdate = to_ms_since_boot(get_absolute_time());

setLEDByMode(ledPins[led], false);
}
}
}

void ReactiveLEDAddon::process() {
Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad();

uint32_t currUpdate = to_ms_since_boot(get_absolute_time());

for (uint8_t led = 0; led < sizeof(REACTIVE_LED_COUNT); led++) {
if (isValidPin(ledPins[led].pinNumber) && ledPins[led].action != GpioAction::NONE) {
ledPins[led].currUpdate = currUpdate;
switch (ledPins[led].action) {
case BUTTON_PRESS_UP: setLEDByMode(ledPins[led], gamepad->pressedDpad(GAMEPAD_MASK_UP)); break;
case BUTTON_PRESS_DOWN: setLEDByMode(ledPins[led], gamepad->pressedDpad(GAMEPAD_MASK_DOWN)); break;
case BUTTON_PRESS_LEFT: setLEDByMode(ledPins[led], gamepad->pressedDpad(GAMEPAD_MASK_LEFT)); break;
case BUTTON_PRESS_RIGHT: setLEDByMode(ledPins[led], gamepad->pressedDpad(GAMEPAD_MASK_RIGHT)); break;
case BUTTON_PRESS_B1: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_B1)); break;
case BUTTON_PRESS_B2: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_B2)); break;
case BUTTON_PRESS_B3: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_B3)); break;
case BUTTON_PRESS_B4: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_B4)); break;
case BUTTON_PRESS_L1: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_L1)); break;
case BUTTON_PRESS_R1: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_R1)); break;
case BUTTON_PRESS_L2: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_L2)); break;
case BUTTON_PRESS_R2: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_R2)); break;
case BUTTON_PRESS_S1: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_S1)); break;
case BUTTON_PRESS_S2: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_S2)); break;
case BUTTON_PRESS_A1: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_A1)); break;
case BUTTON_PRESS_A2: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_A2)); break;
case BUTTON_PRESS_L3: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_L3)); break;
case BUTTON_PRESS_R3: setLEDByMode(ledPins[led], gamepad->pressedButton(GAMEPAD_MASK_R3)); break;
default: break;
}
}
}
}

void ReactiveLEDAddon::setLEDByMode(ReactiveLEDPinState &ledState, bool pressed) {
ledState.currState = pressed;

switch (pressed ? ledState.modeDown : ledState.modeUp) {
case ReactiveLEDMode::REACTIVE_LED_STATIC_OFF:
ledState.value = 0;
break;
case ReactiveLEDMode::REACTIVE_LED_STATIC_ON:
ledState.value = 255;
break;
case ReactiveLEDMode::REACTIVE_LED_FADE_IN:
if (ledState.currUpdate - ledState.lastUpdate >= REACTIVE_LED_DELAY) {
if (ledState.prevState != pressed) ledState.value = 0;
if (ledState.value < REACTIVE_LED_MAX_BRIGHTNESS) {
ledState.value+=REACTIVE_LED_FADE_INC;
}

ledState.lastUpdate = ledState.currUpdate;
}
break;
case ReactiveLEDMode::REACTIVE_LED_FADE_OUT:
if (ledState.currUpdate - ledState.lastUpdate >= REACTIVE_LED_DELAY) {
if (ledState.prevState != pressed) ledState.value = REACTIVE_LED_MAX_BRIGHTNESS;
if (ledState.value > 0) {
ledState.value-=REACTIVE_LED_FADE_INC;
}

ledState.lastUpdate = ledState.currUpdate;
}
break;
}

pwm_set_gpio_level(ledState.pinNumber, ledState.value);

ledState.prevState = pressed;
}
12 changes: 12 additions & 0 deletions src/config_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "addons/neopicoleds.h"
#include "addons/playernum.h"
#include "addons/pleds.h"
#include "addons/reactiveleds.h"
#include "addons/reverse.h"
#include "addons/slider_socd.h"
#include "addons/spi_analog_ads1256.h"
Expand Down Expand Up @@ -724,6 +725,17 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config)
INIT_UNSET_PROPERTY(config.addonOptions.rotaryOptions.encoderTwo, allowWrapAround, ENCODER_TWO_WRAP);
INIT_UNSET_PROPERTY(config.addonOptions.rotaryOptions.encoderTwo, multiplier, ENCODER_TWO_MULTIPLIER);

// addonOptions.reactiveLEDOptions
INIT_UNSET_PROPERTY(config.addonOptions.reactiveLEDOptions, enabled, !!REACTIVE_LED_ENABLED);
for (uint16_t led = 0; led < REACTIVE_LED_COUNT; led++) {
INIT_UNSET_PROPERTY(config.addonOptions.reactiveLEDOptions.leds[led], pin, -1);
INIT_UNSET_PROPERTY(config.addonOptions.reactiveLEDOptions.leds[led], action, GpioAction::NONE);
INIT_UNSET_PROPERTY(config.addonOptions.reactiveLEDOptions.leds[led], modeDown, REACTIVE_LED_STATIC_ON);
INIT_UNSET_PROPERTY(config.addonOptions.reactiveLEDOptions.leds[led], modeUp, REACTIVE_LED_STATIC_OFF);
}
// reminder that this must be set or else nanopb won't retain anything
config.addonOptions.reactiveLEDOptions.leds_count = REACTIVE_LED_COUNT;

// keyboardMapping
INIT_UNSET_PROPERTY(config.addonOptions.keyboardHostOptions, enabled, KEYBOARD_HOST_ENABLED);
INIT_UNSET_PROPERTY(config.addonOptions.keyboardHostOptions, deprecatedPinDplus, KEYBOARD_HOST_PIN_DPLUS);
Expand Down
42 changes: 42 additions & 0 deletions src/configs/webconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1343,6 +1343,40 @@ std::string setExpansionPins()
return serialize_json(doc);
}

std::string getReactiveLEDs()
{
DynamicJsonDocument doc(LWIP_HTTPD_POST_MAX_PAYLOAD_LEN);
ReactiveLEDInfo* ledInfo = Storage::getInstance().getAddonOptions().reactiveLEDOptions.leds;

for (uint16_t led = 0; led < 8; led++) {
writeDoc(doc, "leds", led, "pin", ledInfo[led].pin);
writeDoc(doc, "leds", led, "action", ledInfo[led].action);
writeDoc(doc, "leds", led, "modeDown", ledInfo[led].modeDown);
writeDoc(doc, "leds", led, "modeUp", ledInfo[led].modeUp);
}

return serialize_json(doc);
}

std::string setReactiveLEDs()
{
DynamicJsonDocument doc = get_post_data();

ReactiveLEDInfo* ledInfo = Storage::getInstance().getAddonOptions().reactiveLEDOptions.leds;

for (uint16_t led = 0; led < 8; led++) {
ledInfo[led].pin = doc["leds"][led]["pin"];
ledInfo[led].action = doc["leds"][led]["action"];
ledInfo[led].modeDown = doc["leds"][led]["modeDown"];
ledInfo[led].modeUp = doc["leds"][led]["modeUp"];
}
Storage::getInstance().getAddonOptions().reactiveLEDOptions.leds_count = 8;

Storage::getInstance().save();

return serialize_json(doc);
}

std::string setAddonOptions()
{
DynamicJsonDocument doc = get_post_data();
Expand Down Expand Up @@ -1515,6 +1549,9 @@ std::string setAddonOptions()
PCF8575Options& pcf8575Options = Storage::getInstance().getAddonOptions().pcf8575Options;
docToValue(pcf8575Options.enabled, doc, "PCF8575AddonEnabled");

ReactiveLEDOptions& reactiveLEDOptions = Storage::getInstance().getAddonOptions().reactiveLEDOptions;
docToValue(reactiveLEDOptions.enabled, doc, "ReactiveLEDAddonEnabled");

DRV8833RumbleOptions& drv8833RumbleOptions = Storage::getInstance().getAddonOptions().drv8833RumbleOptions;
docToValue(drv8833RumbleOptions.enabled, doc, "DRV8833RumbleAddonEnabled");
docToPin(drv8833RumbleOptions.leftMotorPin, doc, "drv8833RumbleLeftMotorPin");
Expand Down Expand Up @@ -1944,6 +1981,9 @@ std::string getAddonOptions()
PCF8575Options& pcf8575Options = Storage::getInstance().getAddonOptions().pcf8575Options;
writeDoc(doc, "PCF8575AddonEnabled", pcf8575Options.enabled);

ReactiveLEDOptions& reactiveLEDOptions = Storage::getInstance().getAddonOptions().reactiveLEDOptions;
writeDoc(doc, "ReactiveLEDAddonEnabled", reactiveLEDOptions.enabled);

const DRV8833RumbleOptions& drv8833RumbleOptions = Storage::getInstance().getAddonOptions().drv8833RumbleOptions;
writeDoc(doc, "DRV8833RumbleAddonEnabled", drv8833RumbleOptions.enabled);
writeDoc(doc, "drv8833RumbleLeftMotorPin", cleanPin(drv8833RumbleOptions.leftMotorPin));
Expand Down Expand Up @@ -2213,6 +2253,8 @@ static const std::pair<const char*, HandlerFuncPtr> handlerFuncs[] =
{ "/api/getI2CPeripheralMap", getI2CPeripheralMap },
{ "/api/setExpansionPins", setExpansionPins },
{ "/api/getExpansionPins", getExpansionPins },
{ "/api/setReactiveLEDs", setReactiveLEDs },
{ "/api/getReactiveLEDs", getReactiveLEDs },
{ "/api/setKeyMappings", setKeyMappings },
{ "/api/setAddonsOptions", setAddonOptions },
{ "/api/setMacroAddonOptions", setMacroAddonOptions },
Expand Down
2 changes: 2 additions & 0 deletions src/gp2040aux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "addons/display.h"
#include "addons/pleds.h"
#include "addons/neopicoleds.h"
#include "addons/reactiveleds.h"
#include "addons/drv8833_rumble.h"

#include <iterator>
Expand All @@ -35,6 +36,7 @@ void GP2040Aux::setup() {
addons.LoadAddon(new BoardLedAddon(), CORE1_LOOP);
addons.LoadAddon(new BuzzerSpeakerAddon(), CORE1_LOOP);
addons.LoadAddon(new DRV8833RumbleAddon(), CORE1_LOOP);
addons.LoadAddon(new ReactiveLEDAddon(), CORE1_LOOP);

// Initialize our input driver's auxilliary functions
inputDriver = DriverManager::getInstance().getDriver();
Expand Down
16 changes: 16 additions & 0 deletions www/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ app.get('/api/getAddonsOptions', (req, res) => {
RotaryAddonEnabled: 1,
PCF8575AddonEnabled: 1,
DRV8833RumbleAddonEnabled: 1,
ReactiveLEDAddonEnabled: 1,
usedPins: Object.values(picoController),
});
});
Expand Down Expand Up @@ -731,6 +732,21 @@ app.get('/api/getButtonLayoutDefs', (req, res) => {
});
});

app.get('/api/getReactiveLEDs', (req, res) => {
return res.send({
leds: [
{ pin: -1, action: -10, modeDown: 0, modeUp: 1 },
{ pin: -1, action: -10, modeDown: 1, modeUp: 0 },
{ pin: -1, action: -10, modeDown: 1, modeUp: 0 },
{ pin: -1, action: -10, modeDown: 1, modeUp: 0 },
{ pin: -1, action: -10, modeDown: 1, modeUp: 0 },
{ pin: -1, action: -10, modeDown: 1, modeUp: 0 },
{ pin: -1, action: -10, modeDown: 1, modeUp: 0 },
{ pin: -1, action: -10, modeDown: 1, modeUp: 0 },
],
});
});

app.get('/api/reboot', (req, res) => {
return res.send({});
});
Expand Down
Loading

0 comments on commit 362e23e

Please sign in to comment.