Add Radiomaster Bandit/Bandit Nano support

Added support for 5-Way analog joystick.
Added Custom Sh1115 OLED driver.
Added NeoPixels support for Radiomaster Bandit.

Power output 20-30 dbm (100mW-1000mW).

Changed so Analog joystick can be used in UI.
Changed so NeoPixels is used for new Message. (Color can be defined).

Radiomaster Bandit Micro uses the same code as Nano.
This commit is contained in:
gjelsoe 2026-02-11 19:45:59 +01:00
parent e33d93dc7f
commit 6c0da535b8
17 changed files with 1951 additions and 2 deletions

View file

@ -19,11 +19,31 @@
#define LONG_PRESS_MILLIS 1200
#ifdef RADIOMASTER_900_BANDIT
// NeoPixel message notification settings
#define NEOPIXEL_MSG_UPDATE_MILLIS 30 // Update every 30ms for smooth breathing
#define NEOPIXEL_MIN_BRIGHTNESS 5 // Minimum brightness (dim)
#define NEOPIXEL_MAX_BRIGHTNESS 80 // Maximum brightness (bright blue)
#define NEOPIXEL_BRIGHTNESS_STEP 2 // Brightness change per update
// User-definable message notification color (RGB hex format)
// Examples: 0x0000FF (blue), 0x00FF00 (green), 0xFF0000 (red),
// 0xFF00FF (magenta), 0x00FFFF (cyan), 0xFFFF00 (yellow)
#ifndef NEW_MSG_LED
#define NEW_MSG_LED 0x0000FF // Default: Blue
#endif
// Extract RGB components from hex color
#define NEOPIXEL_MSG_RED ((NEW_MSG_LED >> 16) & 0xFF)
#define NEOPIXEL_MSG_GREEN ((NEW_MSG_LED >> 8) & 0xFF)
#define NEOPIXEL_MSG_BLUE (NEW_MSG_LED & 0xFF)
#endif
#ifndef UI_RECENT_LIST_SIZE
#define UI_RECENT_LIST_SIZE 4
#endif
#if UI_HAS_JOYSTICK
#if defined(UI_HAS_JOYSTICK) || defined(PIN_USER_JOYSTICK)
#define PRESS_LABEL "press Enter"
#else
#define PRESS_LABEL "long press"
@ -549,6 +569,9 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
#if defined(PIN_USER_BTN_ANA)
analog_btn.begin();
#endif
#if defined(PIN_USER_JOYSTICK)
analog_joystick.begin();
#endif
_node_prefs = node_prefs;
@ -666,6 +689,58 @@ void UITask::userLedHandler() {
#endif
}
#ifdef RADIOMASTER_900_BANDIT
void UITask::neopixelMsgHandler() {
unsigned long cur_time = millis();
if (_msgcount > 0) {
// We have unread messages - do breathing effect
if (cur_time >= next_neopixel_change) {
// Update brightness
if (neopixel_brightness_increasing) {
neopixel_brightness += NEOPIXEL_BRIGHTNESS_STEP;
if (neopixel_brightness >= NEOPIXEL_MAX_BRIGHTNESS) {
neopixel_brightness = NEOPIXEL_MAX_BRIGHTNESS;
neopixel_brightness_increasing = false;
}
} else {
if (neopixel_brightness <= NEOPIXEL_BRIGHTNESS_STEP) {
neopixel_brightness = NEOPIXEL_MIN_BRIGHTNESS;
neopixel_brightness_increasing = true;
} else {
neopixel_brightness -= NEOPIXEL_BRIGHTNESS_STEP;
}
}
// Set NeoPixels 2-5 to user-defined color with current brightness
// Leave 0-1 for button backlights
// Scale each RGB component by the brightness level
uint8_t r = (NEOPIXEL_MSG_RED * neopixel_brightness) / NEOPIXEL_MAX_BRIGHTNESS;
uint8_t g = (NEOPIXEL_MSG_GREEN * neopixel_brightness) / NEOPIXEL_MAX_BRIGHTNESS;
uint8_t b = (NEOPIXEL_MSG_BLUE * neopixel_brightness) / NEOPIXEL_MAX_BRIGHTNESS;
for (int i = 2; i <= 6; i++) {
pixels.setPixelColor(i, pixels.Color(r, g, b));
}
pixels.show();
next_neopixel_change = cur_time + NEOPIXEL_MSG_UPDATE_MILLIS;
}
} else {
// No messages - turn off message notification NeoPixels (2-5)
// Only turn them off if they were previously on
if (neopixel_brightness > 0) {
neopixel_brightness = 0;
neopixel_brightness_increasing = true;
for (int i = 2; i <= 6; i++) {
pixels.setPixelColor(i, pixels.Color(0, 0, 0));
}
pixels.show();
}
}
}
#endif
void UITask::setCurrScreen(UIScreen* c) {
curr = c;
_next_refresh = 100;
@ -701,6 +776,8 @@ void UITask::shutdown(bool restart){
bool UITask::isButtonPressed() const {
#ifdef PIN_USER_BTN
return user_btn.isPressed();
#elif defined(PIN_USER_JOYSTICK)
return analog_joystick.isPressed();
#else
return false;
#endif
@ -758,6 +835,33 @@ void UITask::loop() {
_analogue_pin_read_millis = millis();
}
#endif
#if defined(PIN_USER_JOYSTICK)
if ((millis() - _analogue_pin_read_millis) > 10) {
uint8_t key = analog_joystick.check();
if (key != 0) {
// Map joystick directions to key codes and check display
switch (key) {
case KEY_UP:
c = checkDisplayOn(KEY_UP);
break;
case KEY_DOWN:
c = checkDisplayOn(KEY_DOWN);
break;
case KEY_LEFT:
c = checkDisplayOn(KEY_PREV);
break;
case KEY_RIGHT:
c = checkDisplayOn(KEY_NEXT);
break;
case KEY_SELECT:
// For center button, maybe long press support?
c = checkDisplayOn(KEY_ENTER);
break;
}
}
_analogue_pin_read_millis = millis();
}
#endif
#if defined(BACKLIGHT_BTN)
if (millis() > next_backlight_btn_check) {
bool touch_state = digitalRead(PIN_BUTTON2);
@ -778,6 +882,10 @@ void UITask::loop() {
userLedHandler();
#ifdef RADIOMASTER_900_BANDIT
neopixelMsgHandler();
#endif
#ifdef PIN_BUZZER
if (buzzer.isPlaying()) buzzer.loop();
#endif
@ -806,6 +914,11 @@ void UITask::loop() {
#if AUTO_OFF_MILLIS > 0
if (millis() > _auto_off) {
_display->turnOff();
#ifdef RADIOMASTER_900_BANDIT
pixels.setPixelColor(0, pixels.Color(0, 0, 0));
pixels.setPixelColor(1, pixels.Color(0, 0, 0));
pixels.show();
#endif
}
#endif
}
@ -845,6 +958,12 @@ char UITask::checkDisplayOn(char c) {
if (!_display->isOn()) {
_display->turnOn(); // turn display on and consume event
c = 0;
#ifdef RADIOMASTER_900_BANDIT
// Restore backlight for buttons here.
// pixels.setPixelColor(0, pixels.Color(255, 0, 0));
// pixels.setPixelColor(1, pixels.Color(0, 255, 0));
// pixels.show();
#endif
}
_auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer
_next_refresh = 0; // trigger refresh

View file

@ -19,6 +19,12 @@
#include <helpers/ui/GenericVibration.h>
#endif
// Add NeoPixel support for Bandit Board
#ifdef RADIOMASTER_900_BANDIT
#include <Adafruit_NeoPixel.h>
extern Adafruit_NeoPixel pixels;
#endif
#include "../AbstractUITask.h"
#include "../NodePrefs.h"
@ -44,7 +50,15 @@ class UITask : public AbstractUITask {
int last_led_increment = 0;
#endif
#ifdef PIN_USER_BTN_ANA
#ifdef RADIOMASTER_900_BANDIT
// NeoPixel message notification support
int neopixel_state = 0;
unsigned long next_neopixel_change = 0;
uint8_t neopixel_brightness = 0;
bool neopixel_brightness_increasing = true;
#endif
#if defined(PIN_USER_JOYSTICK) || defined(PIN_USER_BTN_ANA)
unsigned long _analogue_pin_read_millis = millis();
#endif
@ -54,6 +68,9 @@ class UITask : public AbstractUITask {
UIScreen* curr;
void userLedHandler();
#ifdef RADIOMASTER_900_BANDIT
void neopixelMsgHandler();
#endif
// Button action handlers
char checkDisplayOn(char c);