MeshCore/variants/radiomaster_900_bandit/target.cpp
gjelsoe 6c0da535b8 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.
2026-02-11 19:45:59 +01:00

160 lines
4.8 KiB
C++

#include "target.h"
#include <Arduino.h>
#include <helpers/ui/UIScreen.h>
BanditBoard board;
#if defined(P_LORA_SCLK)
static SPIClass spi;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
#else
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1);
#endif
WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
SensorManager sensors;
#ifndef PIN_USER_BTN
#define PIN_USER_BTN (-1)
#endif
#ifdef DISPLAY_CLASS
DISPLAY_CLASS display;
// MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
#if defined(PIN_USER_JOYSTICK)
static AnalogJoystick::JoyADCMapping joystick_mappings[] = {
{ 0, KEY_DOWN }, { 1290, KEY_SELECT }, { 1961, KEY_LEFT },
{ 2668, KEY_RIGHT }, { 3227, KEY_UP }, { 4095, 0 } // IDLE
};
AnalogJoystick analog_joystick(PIN_USER_JOYSTICK, joystick_mappings, 6, KEY_SELECT);
#endif
#endif
bool radio_init() {
#ifdef PA_FAN_EN
pinMode(PA_FAN_EN, OUTPUT);
digitalWrite(PA_FAN_EN, 1);
#endif
fallback_clock.begin();
rtc_clock.begin(Wire);
#if defined(P_LORA_SCLK)
return radio.std_init(&spi);
#else
return radio.std_init();
#endif
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
/**
* Linear interpolation helper for integers
*/
int16_t lerp_int(uint8_t x, uint8_t x0, uint8_t x1, int16_t y0, int16_t y1) {
if (x1 == x0) return y0;
return y0 + ((int16_t)(x - x0) * (y1 - y0)) / (x1 - x0);
}
/**
* Set PA output power with automatic SX1276 and DAC calculation
*
* @param target_output_dbm Desired PA output power in dBm (20-30 dBm for 100-1000mW)
* @param clamp_to_range If true, clamp out-of-range values to min/max instead of failing
* @return true if successful, false if out of range and clamp_to_range is false
*/
bool setPAOutputPower(uint8_t target_output_dbm, bool clamp_to_range = true) {
// Validate and clamp range
if (target_output_dbm < MIN_OUTPUT_DBM) {
if (clamp_to_range) {
MESH_DEBUG_PRINT("Warning: Target %u dBm too low, clamping to min %u dBm\n", target_output_dbm,
MIN_OUTPUT_DBM);
target_output_dbm = MIN_OUTPUT_DBM;
} else {
MESH_DEBUG_PRINT("Error: Target %u dBm below minimum %u dBm\n", target_output_dbm, MIN_OUTPUT_DBM);
return false;
}
}
if (target_output_dbm > MAX_OUTPUT_DBM) {
if (clamp_to_range) {
MESH_DEBUG_PRINT("Warning: Target %u dBm too high, clamping to max %u dBm\n", target_output_dbm,
MAX_OUTPUT_DBM);
target_output_dbm = MAX_OUTPUT_DBM;
} else {
MESH_DEBUG_PRINT("Error: Target %u dBm above maximum %u dBm\n", target_output_dbm, MAX_OUTPUT_DBM);
return false;
}
}
// Find the calibration points to interpolate between
int lower_idx = 0;
int upper_idx = NUM_CAL_POINTS - 1;
for (int i = 0; i < NUM_CAL_POINTS - 1; i++) {
if (target_output_dbm >= calibration[i].output_dbm &&
target_output_dbm <= calibration[i + 1].output_dbm) {
lower_idx = i;
upper_idx = i + 1;
break;
}
}
// Handle exact matches
if (target_output_dbm == calibration[lower_idx].output_dbm) {
int8_t sx1278_power = calibration[lower_idx].sx1278_dbm;
uint8_t dac_value = calibration[lower_idx].dac_value;
radio.setOutputPower(sx1278_power, true); // RFO output
dacWrite(DAC_PA_PIN, dac_value);
MESH_DEBUG_PRINT("Set power: %u dBm (SX1278: %d dBm, DAC: %u)\n", target_output_dbm, sx1278_power,
dac_value);
return true;
}
// Linear interpolation between calibration points
uint8_t lower_out = calibration[lower_idx].output_dbm;
uint8_t upper_out = calibration[upper_idx].output_dbm;
// Interpolate SX1278 power (maintaining 18dB gain relationship)
int16_t sx1278_power = lerp_int(target_output_dbm, lower_out, upper_out, calibration[lower_idx].sx1278_dbm,
calibration[upper_idx].sx1278_dbm);
// Interpolate DAC value
int16_t dac_value = lerp_int(target_output_dbm, lower_out, upper_out, calibration[lower_idx].dac_value,
calibration[upper_idx].dac_value);
// Set the calculated values
radio.setOutputPower((int8_t)sx1278_power, true); // RFO output
dacWrite(DAC_PA_PIN, (uint8_t)dac_value);
MESH_DEBUG_PRINT("Set power: %u dBm (SX1278: %d dBm, DAC: %u)\n", target_output_dbm, sx1278_power,
dac_value);
return true;
}
void radio_set_tx_power(uint8_t dbm) {
setPAOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}