Add Muzi Base board support (Duo + Uno + SuperIO)

One nRF52840 variant shared across both radios, picked per env: Duo runs
the LR1121, Uno the SX1262. Same PCB, only the radio and its DIO1 pin
change.

SuperIO adds the SH1107 OLED, GPS, buzzer and joystick. GPS is driven by
the 3-position mode switch (Mode 2 = on), polled live the way thinknode_m1
does it.

Five quick user-button presses power the unit off; a press wakes it back
on (arms the button as an nRF52 GPIO SENSE source before SYSTEMOFF).

Consolidates #2054 and andyshinn's shared-base/uno work, rebased on dev.

Co-authored-by: lbibass <ewdries02@gmail.com>
Co-authored-by: Andy Shinn <andys@andyshinn.as>
This commit is contained in:
authored and Audit Bot committed 2026-09-12 05:41:26 -07:00
1 parent b599bc5117
commit 21cdd7b319
12 files changed
+1230

No files matched your search

+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <RadioLib.h>
#include "MeshCore.h"
class CustomLR1121 : public LR1121 {
bool _rx_boosted = false;
public:
CustomLR1121(Module *mod) : LR1121(mod) { }
size_t getPacketLength(bool update) override {
size_t len = LR1121::getPacketLength(update);
if (len == 0 && getIrqStatus() & RADIOLIB_LR11X0_IRQ_HEADER_ERR) {
// we've just received a corrupted packet
// this may have triggered a bug causing subsequent packets to be shifted
// call standby() to return radio to known-good state
// recvRaw will call startReceive() to restart rx
MESH_DEBUG_PRINTLN("LR1121: got header err, calling standby()");
standby();
}
return len;
}
float getFreqMHz() const { return freqMHz; }
int16_t setRxBoostedGainMode(bool en) {
_rx_boosted = en;
return LR1121::setRxBoostedGainMode(en);
}
bool getRxBoostedGainMode() const { return _rx_boosted; }
bool isReceiving() {
uint16_t irq = getIrqStatus();
bool detected = ((irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID) || (irq & RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED));
return detected;
}
uint8_t getSpreadingFactor() const { return spreadingFactor; }
};
@@ -0,0 +1,50 @@
#pragma once
#include "CustomLR1121.h"
#include "RadioLibWrappers.h"
#include "LR11x0Reset.h"
class CustomLR1121Wrapper : public RadioLibWrapper {
public:
CustomLR1121Wrapper(CustomLR1121& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { }
void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override {
((CustomLR1121 *)_radio)->setFrequency(freq);
((CustomLR1121 *)_radio)->setSpreadingFactor(sf);
((CustomLR1121 *)_radio)->setBandwidth(bw);
((CustomLR1121 *)_radio)->setCodingRate(cr);
updatePreamble(sf);
}
void doResetAGC() override { lr11x0ResetAGC((LR11x0 *)_radio, ((CustomLR1121 *)_radio)->getFreqMHz(), getRxBoostedGainMode()); }
bool isReceivingPacket() override {
return ((CustomLR1121 *)_radio)->isReceiving();
}
float getCurrentRSSI() override {
float rssi = -110;
((CustomLR1121 *)_radio)->getRssiInst(&rssi);
return rssi;
}
void onSendFinished() override {
RadioLibWrapper::onSendFinished();
_radio->setPreambleLength(preambleLengthForSF(getSpreadingFactor())); // overcomes weird issues with small and big pkts
}
uint32_t getEstAirtimeFor(int len_bytes) override {
auto airtime = RadioLibWrapper::getEstAirtimeFor(len_bytes);
return airtime < 200 ? 200 : airtime; // at least 200 millis
}
float getLastRSSI() const override { return ((CustomLR1121 *)_radio)->getRSSI(); }
float getLastSNR() const override { return ((CustomLR1121 *)_radio)->getSNR(); }
uint8_t getSpreadingFactor() const override { return ((CustomLR1121 *)_radio)->getSpreadingFactor(); }
bool setRxBoostedGainMode(bool en) override {
return ((CustomLR1121 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE;
}
bool getRxBoostedGainMode() const override {
return ((CustomLR1121 *)_radio)->getRxBoostedGainMode();
}
};
+113
View File
@@ -0,0 +1,113 @@
#include "SH1107Display.h"
#include <Adafruit_GrayOLED.h>
#include "Adafruit_SH110X.h"
#ifndef DISPLAY_ROTATION
#define DISPLAY_ROTATION 0
#endif
ColorVal UIColor::window_bkg = SH110X_BLACK;
ColorVal UIColor::title_bkg = SH110X_BLACK;
ColorVal UIColor::title_txt = SH110X_WHITE;
ColorVal UIColor::primary_txt = SH110X_WHITE;
ColorVal UIColor::secondary_txt = SH110X_WHITE;
ColorVal UIColor::warning_txt = SH110X_WHITE;
ColorVal UIColor::popup_bkg = SH110X_BLACK;
ColorVal UIColor::popup_txt = SH110X_WHITE;
ColorVal UIColor::corp_blue = SH110X_WHITE;
bool SH1107Display::i2c_probe(TwoWire &wire, uint8_t addr)
{
wire.beginTransmission(addr);
uint8_t error = wire.endTransmission();
return (error == 0);
}
bool SH1107Display::begin()
{
bool result = display.begin(DISPLAY_ADDRESS, true) && i2c_probe(Wire, DISPLAY_ADDRESS);
if (result) {
display.setRotation(DISPLAY_ROTATION);
}
return result;
}
void SH1107Display::turnOn()
{
display.oled_command(SH110X_DISPLAYON);
uint8_t cmd[] = {0xD5, 0xF0};
display.oled_commandList(cmd, 2);
_isOn = true;
}
void SH1107Display::turnOff()
{
display.oled_command(SH110X_DISPLAYOFF);
_isOn = false;
}
void SH1107Display::clear()
{
display.clearDisplay();
display.display();
}
void SH1107Display::startFrame(ColorVal bkg)
{
display.clearDisplay(); // TODO: apply 'bkg'
display.setContrast(120); // 0-127. default setting was causing some flickering.
// display.SH110X_SETPRECHARGE(255);
_color = SH110X_WHITE;
display.setTextColor(_color);
display.setTextSize(1);
display.cp437(true); // Use full 256 char 'Code Page 437' font
}
void SH1107Display::setTextSize(int sz)
{
display.setTextSize(sz);
}
void SH1107Display::setColor(ColorVal c)
{
_color = (c != 0) ? SH110X_WHITE : SH110X_BLACK;
display.setTextColor(_color);
}
void SH1107Display::setCursor(int x, int y)
{
display.setCursor(x, y);
}
void SH1107Display::print(const char *str)
{
display.print(str);
}
void SH1107Display::fillRect(int x, int y, int w, int h)
{
display.fillRect(x, y, w, h, _color);
}
void SH1107Display::drawRect(int x, int y, int w, int h)
{
display.drawRect(x, y, w, h, _color);
}
void SH1107Display::drawXbm(int x, int y, const uint8_t *bits, int w, int h)
{
display.drawBitmap(x, y, bits, w, h, SH110X_WHITE);
}
uint16_t SH1107Display::getTextWidth(const char *str)
{
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h);
return w;
}
void SH1107Display::endFrame()
{
display.display();
}
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include "DisplayDriver.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#define SH110X_NO_SPLASH
#include <Adafruit_SH110X.h>
#ifndef PIN_OLED_RESET
#define PIN_OLED_RESET -1
#endif
#ifndef DISPLAY_ADDRESS
#define DISPLAY_ADDRESS 0x3c
#endif
class SH1107Display : public DisplayDriver
{
Adafruit_SH1107 display;
bool _isOn;
uint8_t _color;
bool i2c_probe(TwoWire &wire, uint8_t addr);
public:
SH1107Display() : DisplayDriver(128, 128), display(128, 128, &Wire, PIN_OLED_RESET) { _isOn = false; }
bool begin();
bool isOn() override { return _isOn; }
void turnOn() override;
void turnOff() override;
void clear() override;
void startFrame(ColorVal bkg = UIColor::window_bkg) override;
void setTextSize(int sz) override;
void setColor(ColorVal c) override;
void setCursor(int x, int y) override;
void print(const char *str) override;
void fillRect(int x, int y, int w, int h) override;
void drawRect(int x, int y, int w, int h) override;
void drawXbm(int x, int y, const uint8_t *bits, int w, int h) override;
uint16_t getTextWidth(const char *str) override;
void endFrame() override;
};