Merge branch 'master' into apollo

This commit is contained in:
Thomas Göttgens
2023-12-01 18:28:00 +01:00
committed by GitHub
77 changed files with 837 additions and 528 deletions

View File

@@ -36,6 +36,9 @@ class ButtonThread : public concurrency::OSThread
#endif
#ifdef BUTTON_PIN_TOUCH
OneButton userButtonTouch;
#endif
#if defined(ARCH_RASPBERRY_PI)
OneButton userButton;
#endif
static bool shutdown_on_long_stop;
@@ -45,8 +48,14 @@ class ButtonThread : public concurrency::OSThread
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
ButtonThread() : OSThread("Button")
{
#ifdef BUTTON_PIN
#if defined(ARCH_RASPBERRY_PI) || defined(BUTTON_PIN)
#if defined(ARCH_RASPBERRY_PI)
if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC)
userButton = OneButton(settingsMap[user], true, true);
#elif defined(BUTTON_PIN)
userButton = OneButton(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, true, true);
#endif
#ifdef INPUT_PULLUP_SENSE
// Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
pinMode(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, INPUT_PULLUP_SENSE);
@@ -58,8 +67,13 @@ class ButtonThread : public concurrency::OSThread
userButton.attachMultiClick(userButtonMultiPressed);
userButton.attachLongPressStart(userButtonPressedLongStart);
userButton.attachLongPressStop(userButtonPressedLongStop);
#if defined(ARCH_RASPBERRY_PI)
if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC)
wakeOnIrq(settingsMap[user], FALLING);
#else
wakeOnIrq(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, FALLING);
#endif
#endif
#ifdef BUTTON_PIN_ALT
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
#ifdef INPUT_PULLUP_SENSE
@@ -87,9 +101,14 @@ class ButtonThread : public concurrency::OSThread
{
canSleep = true; // Assume we should not keep the board awake
#ifdef BUTTON_PIN
#if defined(BUTTON_PIN)
userButton.tick();
canSleep &= userButton.isIdle();
#elif defined(ARCH_RASPBERRY_PI)
if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) {
userButton.tick();
canSleep &= userButton.isIdle();
}
#endif
#ifdef BUTTON_PIN_ALT
userButtonAlt.tick();
@@ -121,6 +140,13 @@ class ButtonThread : public concurrency::OSThread
!moduleConfig.canned_message.enabled) {
powerFSM.trigger(EVENT_PRESS);
}
#endif
#if defined(ARCH_RASPBERRY_PI)
if ((settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) &&
(settingsMap[user] != moduleConfig.canned_message.inputbroker_pin_press) ||
!moduleConfig.canned_message.enabled) {
powerFSM.trigger(EVENT_PRESS);
}
#endif
}
static void userButtonPressedLong()
@@ -199,4 +225,4 @@ class ButtonThread : public concurrency::OSThread
}
};
} // namespace concurrency
} // namespace concurrency

View File

@@ -19,6 +19,11 @@
#include "meshUtils.h"
#include "sleep.h"
// Working USB detection for powered/charging states on the RAK platform
#ifdef NRF_APM
#include "nrfx_power.h"
#endif
#ifdef DEBUG_HEAP_MQTT
#include "mqtt/MQTT.h"
#include "target_specific.h"
@@ -52,6 +57,7 @@ static const adc_atten_t atten = ADC_ATTENUATION;
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
INA260Sensor ina260Sensor;
INA219Sensor ina219Sensor;
INA3221Sensor ina3221Sensor;
#endif
#ifdef HAS_PMU
@@ -286,6 +292,9 @@ class AnalogBatteryLevel : public HasBatteryLevel
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first ==
config.power.device_battery_ina_address) {
return ina260Sensor.getBusVoltageMv();
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first ==
config.power.device_battery_ina_address) {
return ina3221Sensor.getBusVoltageMv();
}
return 0;
}
@@ -456,10 +465,25 @@ void Power::readPowerStatus()
}
}
OptionalBool NRF_USB = OptFalse;
#ifdef NRF_APM // Section of code detects USB power on the RAK4631 and updates the power states. Takes 20 seconds or so to detect
// changes.
nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get();
if (nrf_usb_state == NRFX_POWER_USB_STATE_DISCONNECTED) {
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
NRF_USB = OptFalse;
} else {
powerFSM.trigger(EVENT_POWER_CONNECTED);
NRF_USB = OptTrue;
}
#endif
// Notify any status instances that are observing us
const PowerStatus powerStatus2 =
PowerStatus(hasBattery ? OptTrue : OptFalse, batteryLevel->isVbusIn() ? OptTrue : OptFalse,
batteryLevel->isCharging() ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent);
const PowerStatus powerStatus2 = PowerStatus(
hasBattery ? OptTrue : OptFalse, batteryLevel->isVbusIn() || NRF_USB == OptTrue ? OptTrue : OptFalse,
batteryLevel->isCharging() || NRF_USB == OptTrue ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent);
LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus2.getHasUSB(),
powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
newStatus.notifyObservers(&powerStatus2);

View File

@@ -57,8 +57,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define REQUIRE_RADIO true // If true, we will fail to start if the radio is not found
/// Convert a preprocessor name into a quoted string
#define xstr(s) str(s)
#define str(s) #s
#define xstr(s) ystr(s)
#define ystr(s) #s
/// Convert a preprocessor name into a quoted string and if that string is empty use "unset"
#define optstr(s) (xstr(s)[0] ? xstr(s) : "unset")
@@ -209,4 +209,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef HW_VENDOR
#error HW_VENDOR must be defined
#endif
#endif

View File

@@ -2,7 +2,9 @@
#include "concurrency/LockGuard.h"
#include "configuration.h"
#if defined(ARCH_RASPBERRY_PI)
#include "linux/LinuxHardwareI2C.h"
#endif
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
#include "main.h" // atecc
#endif
@@ -162,7 +164,14 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
for (addr.address = 1; addr.address < 127; addr.address++) {
i2cBus->beginTransmission(addr.address);
#ifdef ARCH_PORTDUINO
if (i2cBus->read() != -1)
err = 0;
else
err = 2;
#else
err = i2cBus->endTransmission();
#endif
type = NONE;
if (err == 0) {
LOG_DEBUG("I2C device found at address 0x%x\n", addr.address);

View File

@@ -7,6 +7,7 @@
#include "ubx.h"
#ifdef ARCH_PORTDUINO
#include "PortduinoGlue.h"
#include "meshUtils.h"
#include <ctime>
#endif
@@ -15,7 +16,7 @@
#define GPS_RESET_MODE HIGH
#endif
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32)
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_RASPBERRY_PI)
HardwareSerial *GPS::_serial_gps = &Serial1;
#else
HardwareSerial *GPS::_serial_gps = NULL;
@@ -261,6 +262,20 @@ bool GPS::setup()
isProblematicGPS = true;
}
#endif
#if defined(RAK4630) && defined(PIN_3V3_EN)
// If we are using the RAK4630 and we have no other peripherals on the I2C bus or module interest in 3V3_S,
// then we can safely set en_gpio turn off power to 3V3 (IO2) to hard sleep the GPS
if (rtc_found.port == ScanI2C::DeviceType::NONE && rgb_found.type == ScanI2C::DeviceType::NONE &&
accelerometer_found.port == ScanI2C::DeviceType::NONE && !moduleConfig.detection_sensor.enabled &&
!moduleConfig.telemetry.air_quality_enabled && !moduleConfig.telemetry.environment_measurement_enabled &&
config.power.device_battery_ina_address == 0 && en_gpio == 0) {
LOG_DEBUG("Since no problematic peripherals or interested modules were found, setting power save GPS_EN to pin %i\n",
PIN_3V3_EN);
en_gpio = PIN_3V3_EN;
}
#endif
if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) {
LOG_DEBUG("Probing for GPS at %d \n", serialSpeeds[speedSelect]);
gnssModel = probe(serialSpeeds[speedSelect]);
@@ -433,6 +448,7 @@ bool GPS::setup()
notifyDeepSleepObserver.observe(&notifyDeepSleep);
notifyGPSSleepObserver.observe(&notifyGPSSleep);
return true;
}
@@ -889,6 +905,10 @@ GPS *GPS::createGps()
#if defined(PIN_GPS_EN)
if (!_en_gpio)
_en_gpio = PIN_GPS_EN;
#endif
#ifdef ARCH_RASPBERRY_PI
if (!settingsMap[has_gps])
return nullptr;
#endif
if (!_rx_gpio || !_serial_gps) // Configured to have no GPS at all
return nullptr;

View File

@@ -1564,7 +1564,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
// Jm
void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
#if HAS_WIFI
#if HAS_WIFI && !defined(ARCH_RASPBERRY_PI)
const char *wifiName = config.network.wifi_ssid;
display->setFont(FONT_SMALL);

View File

@@ -68,13 +68,14 @@ NRF52Bluetooth *nrf52Bluetooth;
#endif
#ifdef ARCH_RASPBERRY_PI
#include "platform/portduino/PiHal.h"
#include "linux/LinuxHardwareI2C.h"
#include "platform/portduino/PortduinoGlue.h"
#include <fstream>
#include <iostream>
#include <string>
#endif
#if HAS_BUTTON
#if HAS_BUTTON || defined(ARCH_RASPBERRY_PI)
#include "ButtonThread.h"
#endif
#include "PowerFSMThread.h"
@@ -205,13 +206,13 @@ static int32_t ledBlinker()
uint32_t timeLastPowered = 0;
#if HAS_BUTTON
#if HAS_BUTTON || defined(ARCH_RASPBERRY_PI)
bool ButtonThread::shutdown_on_long_stop = false;
#endif
static Periodic *ledPeriodic;
static OSThread *powerFSMthread;
#if HAS_BUTTON
#if HAS_BUTTON || defined(ARCH_RASPBERRY_PI)
static OSThread *buttonThread;
uint32_t ButtonThread::longPressTime = 0;
#endif
@@ -582,7 +583,7 @@ void setup()
else
router = new ReliableRouter();
#if HAS_BUTTON
#if HAS_BUTTON || defined(ARCH_RASPBERRY_PI)
// Buttons. Moved here cause we need NodeDB to be initialized
buttonThread = new ButtonThread();
#endif
@@ -627,16 +628,16 @@ void setup()
initSPI();
#ifdef ARCH_RP2040
#ifdef HW_SPI1_DEVICE
SPI1.setSCK(RF95_SCK);
SPI1.setTX(RF95_MOSI);
SPI1.setRX(RF95_MISO);
pinMode(RF95_NSS, OUTPUT);
digitalWrite(RF95_NSS, HIGH);
SPI1.setSCK(LORA_SCK);
SPI1.setTX(LORA_MOSI);
SPI1.setRX(LORA_MISO);
pinMode(LORA_CS, OUTPUT);
digitalWrite(LORA_CS, HIGH);
SPI1.begin(false);
#else // HW_SPI1_DEVICE
SPI.setSCK(RF95_SCK);
SPI.setTX(RF95_MOSI);
SPI.setRX(RF95_MISO);
#else // HW_SPI1_DEVICE
SPI.setSCK(LORA_SCK);
SPI.setTX(LORA_MOSI);
SPI.setRX(LORA_MISO);
SPI.begin(false);
#endif // HW_SPI1_DEVICE
#elif defined(ARCH_APOLLO3)
@@ -646,8 +647,8 @@ void setup()
SPI.begin();
#else
// ESP32
SPI.begin(RF95_SCK, RF95_MISO, RF95_MOSI, RF95_NSS);
LOG_WARN("SPI.begin(SCK=%d, MISO=%d, MOSI=%d, NSS=%d)\n", RF95_SCK, RF95_MISO, RF95_MOSI, RF95_NSS);
SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
LOG_WARN("SPI.begin(SCK=%d, MISO=%d, MOSI=%d, NSS=%d)\n", LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
SPI.setFrequency(4000000);
#endif
@@ -693,15 +694,32 @@ void setup()
#endif
#ifdef ARCH_RASPBERRY_PI
PiHal *RadioLibHAL = new PiHal(1);
if (!rIf) {
rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, 21, 16, 18, 20);
if (!rIf->init()) {
LOG_WARN("Failed to find SX1262 radio\n");
delete rIf;
rIf = NULL;
} else {
LOG_INFO("SX1262 Radio init succeeded, using SX1262 radio\n");
if (settingsMap[use_sx1262]) {
if (!rIf) {
LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
settingsMap[busy]);
if (!rIf->init()) {
LOG_ERROR("Failed to find SX1262 radio\n");
delete rIf;
exit(EXIT_FAILURE);
} else {
LOG_INFO("SX1262 Radio init succeeded, using SX1262 radio\n");
}
}
} else if (settingsMap[use_rf95]) {
if (!rIf) {
LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
rIf = new RF95Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
settingsMap[busy]);
if (!rIf->init()) {
LOG_ERROR("Failed to find RF95 radio\n");
delete rIf;
rIf = NULL;
exit(EXIT_FAILURE);
} else {
LOG_INFO("RF95 Radio init succeeded, using RF95 radio\n");
}
}
}
@@ -740,7 +758,7 @@ void setup()
#if defined(RF95_IRQ)
if (!rIf) {
rIf = new RF95Interface(RadioLibHAL, RF95_NSS, RF95_IRQ, RF95_RESET, RF95_DIO1);
rIf = new RF95Interface(RadioLibHAL, LORA_CS, RF95_IRQ, RF95_RESET, RF95_DIO1);
if (!rIf->init()) {
LOG_WARN("Failed to find RF95 radio\n");
delete rIf;

View File

@@ -200,7 +200,7 @@ void NodeDB::installDefaultConfig()
config.position.position_flags =
(meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL |
meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING |
meshtastic_Config_PositionConfig_PositionFlags_DOP);
meshtastic_Config_PositionConfig_PositionFlags_DOP | meshtastic_Config_PositionConfig_PositionFlags_SATINVIEW);
#ifdef T_WATCH_S3
config.display.screen_on_secs = 30;
@@ -316,8 +316,8 @@ void NodeDB::installDefaultChannels()
void NodeDB::resetNodes()
{
devicestate.node_db_lite_count = 0;
memset(devicestate.node_db_lite, 0, sizeof(devicestate.node_db_lite));
devicestate.node_db_lite_count = 1;
std::fill(&devicestate.node_db_lite[1], &devicestate.node_db_lite[MAX_NUM_NODES - 1], meshtastic_NodeInfoLite());
saveDeviceStateToDisk();
if (neighborInfoModule && moduleConfig.neighbor_info.enabled)
neighborInfoModule->resetNeighbors();
@@ -370,7 +370,6 @@ void NodeDB::installDefaultDeviceState()
pickNewNodeNum(); // based on macaddr now
snprintf(owner.long_name, sizeof(owner.long_name), "Meshtastic %02x%02x", ourMacAddr[4], ourMacAddr[5]);
snprintf(owner.short_name, sizeof(owner.short_name), "%02x%02x", ourMacAddr[4], ourMacAddr[5]);
snprintf(owner.id, sizeof(owner.id), "!%08x", getNodeNum()); // Default node ID now based on nodenum
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
}
@@ -395,6 +394,8 @@ void NodeDB::init()
// Set our board type so we can share it with others
owner.hw_model = HW_VENDOR;
// Ensure user (nodeinfo) role is set to whatever we're configured to
owner.role = config.device.role;
// Include our owner in the node db under our nodenum
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());

View File

@@ -1,9 +1,6 @@
#include "RadioLibRF95.h"
#include "configuration.h"
#define RF95_CHIP_VERSION 0x12
#define RF95_ALT_VERSION 0x11 // Supposedly some versions of the chip have id 0x11
// From datasheet but radiolib doesn't know anything about this
#define SX127X_REG_TCXO 0x4B
@@ -13,9 +10,8 @@ int16_t RadioLibRF95::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_
uint8_t gain)
{
// execute common part
int16_t state = SX127x::begin(RF95_CHIP_VERSION, syncWord, preambleLength);
if (state != RADIOLIB_ERR_NONE)
state = SX127x::begin(RF95_ALT_VERSION, syncWord, preambleLength);
uint8_t rf95versions[2] = {0x12, 0x11};
int16_t state = SX127x::begin(rf95versions, sizeof(rf95versions), syncWord, preambleLength);
RADIOLIB_ASSERT(state);
// current limit was removed from module' ctor
@@ -80,4 +76,4 @@ bool RadioLibRF95::isReceiving()
uint8_t RadioLibRF95::readReg(uint8_t addr)
{
return mod->SPIreadRegister(addr);
}
}

View File

@@ -2,6 +2,9 @@
#include "configuration.h"
#include "error.h"
#include "mesh/NodeDB.h"
#ifdef ARCH_RASPBERRY_PI
#include "PortduinoGlue.h"
#endif
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not
// specified (may be dangerous if using external PA and SX126x power config forgotten)
@@ -74,6 +77,12 @@ template <typename T> bool SX126xInterface<T>::init()
#ifdef SX126X_DIO2_AS_RF_SWITCH
LOG_DEBUG("Setting DIO2 as RF switch\n");
bool dio2AsRfSwitch = true;
#elif defined(ARCH_RASPBERRY_PI)
bool dio2AsRfSwitch = false;
if (settingsMap[dio2_as_rf_switch]) {
LOG_DEBUG("Setting DIO2 as RF switch\n");
dio2AsRfSwitch = true;
}
#else
LOG_DEBUG("Setting DIO2 as not RF switch\n");
bool dio2AsRfSwitch = false;
@@ -241,7 +250,7 @@ template <typename T> void SX126xInterface<T>::startReceive()
// We use a 16 bit preamble so this should save some power by letting radio sit in standby mostly.
// Furthermore, we need the PREAMBLE_DETECTED and HEADER_VALID IRQ flag to detect whether we are actively receiving
int err = lora.startReceiveDutyCycleAuto(preambleLength, 8,
RADIOLIB_SX126X_IRQ_RX_DEFAULT | RADIOLIB_SX126X_IRQ_RADIOLIB_PREAMBLE_DETECTED |
RADIOLIB_SX126X_IRQ_RX_DEFAULT | RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED |
RADIOLIB_SX126X_IRQ_HEADER_VALID);
assert(err == RADIOLIB_ERR_NONE);
@@ -275,7 +284,7 @@ template <typename T> bool SX126xInterface<T>::isActivelyReceiving()
// received and handled the interrupt for reading the packet/handling errors.
uint16_t irq = lora.getIrqStatus();
bool detected = (irq & (RADIOLIB_SX126X_IRQ_HEADER_VALID | RADIOLIB_SX126X_IRQ_RADIOLIB_PREAMBLE_DETECTED));
bool detected = (irq & (RADIOLIB_SX126X_IRQ_HEADER_VALID | RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED));
// Handle false detections
if (detected) {
uint32_t now = millis();
@@ -318,4 +327,4 @@ template <typename T> bool SX126xInterface<T>::sleep()
#endif
return true;
}
}

View File

@@ -313,8 +313,8 @@ extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePin_msg;
/* Maximum encoded size of messages (where known) */
#define meshtastic_ChannelFile_size 638
#define meshtastic_DeviceState_size 16854
#define meshtastic_NodeInfoLite_size 151
#define meshtastic_DeviceState_size 17056
#define meshtastic_NodeInfoLite_size 153
#define meshtastic_NodeRemoteHardwarePin_size 29
#define meshtastic_OEMStore_size 3231
#define meshtastic_PositionLite_size 28

View File

@@ -402,6 +402,8 @@ typedef struct _meshtastic_User {
If this user is a licensed operator, set this flag.
Also, "long_name" should be their licence number. */
bool is_licensed;
/* Indicates that the user's role in the mesh */
meshtastic_Config_DeviceConfig_Role role;
} meshtastic_User;
/* A message used in our Dynamic Source Routing protocol (RFC 4728 based) */
@@ -826,6 +828,7 @@ extern "C" {
#define meshtastic_Position_altitude_source_ENUMTYPE meshtastic_Position_AltSource
#define meshtastic_User_hw_model_ENUMTYPE meshtastic_HardwareModel
#define meshtastic_User_role_ENUMTYPE meshtastic_Config_DeviceConfig_Role
#define meshtastic_Routing_variant_error_reason_ENUMTYPE meshtastic_Routing_Error
@@ -854,7 +857,7 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_Position_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0}
#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN}
#define meshtastic_RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}}
#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
@@ -872,7 +875,7 @@ extern "C" {
#define meshtastic_Neighbor_init_default {0, 0, 0, 0}
#define meshtastic_DeviceMetadata_init_default {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0}
#define meshtastic_Position_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0}
#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN}
#define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}}
#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
@@ -919,6 +922,7 @@ extern "C" {
#define meshtastic_User_macaddr_tag 4
#define meshtastic_User_hw_model_tag 5
#define meshtastic_User_is_licensed_tag 6
#define meshtastic_User_role_tag 7
#define meshtastic_RouteDiscovery_route_tag 1
#define meshtastic_Routing_route_request_tag 1
#define meshtastic_Routing_route_reply_tag 2
@@ -1047,7 +1051,8 @@ X(a, STATIC, SINGULAR, STRING, long_name, 2) \
X(a, STATIC, SINGULAR, STRING, short_name, 3) \
X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 4) \
X(a, STATIC, SINGULAR, UENUM, hw_model, 5) \
X(a, STATIC, SINGULAR, BOOL, is_licensed, 6)
X(a, STATIC, SINGULAR, BOOL, is_licensed, 6) \
X(a, STATIC, SINGULAR, UENUM, role, 7)
#define meshtastic_User_CALLBACK NULL
#define meshtastic_User_DEFAULT NULL
@@ -1280,13 +1285,13 @@ extern const pb_msgdesc_t meshtastic_DeviceMetadata_msg;
#define meshtastic_MyNodeInfo_size 18
#define meshtastic_NeighborInfo_size 258
#define meshtastic_Neighbor_size 22
#define meshtastic_NodeInfo_size 261
#define meshtastic_NodeInfo_size 263
#define meshtastic_Position_size 137
#define meshtastic_QueueStatus_size 23
#define meshtastic_RouteDiscovery_size 40
#define meshtastic_Routing_size 42
#define meshtastic_ToRadio_size 504
#define meshtastic_User_size 77
#define meshtastic_User_size 79
#define meshtastic_Waypoint_size 165
#ifdef __cplusplus

View File

@@ -11,11 +11,6 @@
#include "sleep.h"
#include "target_specific.h"
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
#include "Sensor/INA3221Sensor.h"
INA3221Sensor ina3221Sensor;
#endif
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true

View File

@@ -13,8 +13,9 @@ int32_t INA3221Sensor::runOnce()
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
if (!status) {
ina3221.setAddr(INA3221_ADDR42_SDA);
ina3221.setAddr(INA3221_ADDR42_SDA); // i2c address 0x42
ina3221.begin();
ina3221.setShuntRes(100, 100, 100); // 0.1 Ohm shunt resistors
status = true;
} else {
status = true;

View File

@@ -1,16 +1,19 @@
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include "VoltageSensor.h"
#include <INA3221.h>
class INA3221Sensor : public TelemetrySensor
class INA3221Sensor : public TelemetrySensor, VoltageSensor
{
private:
INA3221 ina3221 = INA3221(INA3221_ADDR42_SDA);
protected:
void setup() override;
public:
INA3221Sensor();
int32_t runOnce() override;
void setup() override;
bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual uint16_t getBusVoltageMv();
private:
INA3221 ina3221 = INA3221(INA3221_ADDR42_SDA);
virtual uint16_t getBusVoltageMv() override;
};

View File

@@ -9,6 +9,7 @@
#include <NimBLEDevice.h>
NimBLECharacteristic *fromNumCharacteristic;
NimBLECharacteristic *BatteryCharacteristic;
NimBLEServer *bleServer;
static bool passkeyShowing;
@@ -181,6 +182,18 @@ void NimbleBluetooth::setupService()
FromRadioCharacteristic->setCallbacks(fromRadioCallbacks);
bleService->start();
// Setup the battery service
NimBLEService *batteryService = bleServer->createService(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
BatteryCharacteristic = batteryService->createCharacteristic( // 0x2A19 is the Battery Level characteristic)
(uint16_t)0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
NimBLE2904 *batteryLevelDescriptor = (NimBLE2904 *)BatteryCharacteristic->createDescriptor((uint16_t)0x2904);
batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
batteryLevelDescriptor->setNamespace(1);
batteryLevelDescriptor->setUnit(0x27ad);
batteryService->start();
}
void NimbleBluetooth::startAdvertising()
@@ -188,13 +201,15 @@ void NimbleBluetooth::startAdvertising()
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->reset();
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
pAdvertising->addServiceUUID(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
pAdvertising->start(0);
}
/// Given a level between 0-100, update the BLE attribute
void updateBatteryLevel(uint8_t level)
{
// blebas.write(level);
BatteryCharacteristic->setValue(&level, 1);
BatteryCharacteristic->notify();
}
void NimbleBluetooth::clearBonds()

View File

@@ -127,12 +127,20 @@
// LoRa SPI
// -----------------------------------------------------------------------------
// NRF52 boards will define this in variant.h
#ifndef RF95_SCK
#define RF95_SCK 5
#define RF95_MISO 19
#define RF95_MOSI 27
#define RF95_NSS 18
// If an SPI-related pin used by the LoRa module isn't defined, use the conventional pin number for it.
// FIXME: these pins should really be defined in each variant.h file to prevent breakages if the defaults change, currently many
// ESP32 variants don't define these pins in their variant.h file.
#ifndef LORA_SCK
#define LORA_SCK 5
#endif
#ifndef LORA_MISO
#define LORA_MISO 19
#endif
#ifndef LORA_MOSI
#define LORA_MOSI 27
#endif
#ifndef LORA_CS
#define LORA_CS 18
#endif
#define SERIAL0_RX_GPIO 3 // Always GPIO3 on ESP32
#define SERIAL0_RX_GPIO 3 // Always GPIO3 on ESP32 // FIXME: may be different on ESP32-S3, etc.

View File

@@ -1,155 +0,0 @@
#ifndef PI_HAL_H
#define PI_HAL_H
// include RadioLib
#include <RadioLib.h>
// include the library for Raspberry GPIO pins
#include "pigpio.h"
// create a new Raspberry Pi hardware abstraction layer
// using the pigpio library
// the HAL must inherit from the base RadioLibHal class
// and implement all of its virtual methods
class PiHal : public RadioLibHal
{
public:
// default constructor - initializes the base HAL and any needed private members
PiHal(uint8_t spiChannel, uint32_t spiSpeed = 2000000)
: RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, RISING_EDGE, FALLING_EDGE), _spiChannel(spiChannel),
_spiSpeed(spiSpeed)
{
}
void init() override
{
// first initialise pigpio library
gpioInitialise();
// now the SPI
spiBegin();
// Waveshare LoRaWAN Hat also needs pin 18 to be pulled high to enable the radio
// gpioSetMode(18, PI_OUTPUT);
// gpioWrite(18, PI_HIGH);
}
void term() override
{
// stop the SPI
spiEnd();
// pull the enable pin low
// gpioSetMode(18, PI_OUTPUT);
// gpioWrite(18, PI_LOW);
// finally, stop the pigpio library
gpioTerminate();
}
// GPIO-related methods (pinMode, digitalWrite etc.) should check
// RADIOLIB_NC as an alias for non-connected pins
void pinMode(uint32_t pin, uint32_t mode) override
{
if (pin == RADIOLIB_NC) {
return;
}
gpioSetMode(pin, mode);
}
void digitalWrite(uint32_t pin, uint32_t value) override
{
if (pin == RADIOLIB_NC) {
return;
}
gpioWrite(pin, value);
}
uint32_t digitalRead(uint32_t pin) override
{
if (pin == RADIOLIB_NC) {
return (0);
}
return (gpioRead(pin));
}
void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override
{
if (interruptNum == RADIOLIB_NC) {
return;
}
if (gpioRead(interruptNum) == 1) {
interruptCb();
} else {
gpioSetAlertFunc(interruptNum, (gpioISRFunc_t)interruptCb);
}
}
void detachInterrupt(uint32_t interruptNum) override
{
if (interruptNum == RADIOLIB_NC) {
return;
}
gpioSetAlertFunc(interruptNum, NULL);
}
void delay(unsigned long ms) override { gpioDelay(ms * 1000); }
void delayMicroseconds(unsigned long us) override { gpioDelay(us); }
unsigned long millis() override { return (gpioTick() / 1000); }
unsigned long micros() override { return (gpioTick()); }
long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override
{
if (pin == RADIOLIB_NC) {
return (0);
}
this->pinMode(pin, PI_INPUT);
uint32_t start = this->micros();
uint32_t curtick = this->micros();
while (this->digitalRead(pin) == state) {
if ((this->micros() - curtick) > timeout) {
return (0);
}
}
return (this->micros() - start);
}
void spiBegin()
{
if (_spiHandle < 0) {
_spiHandle = spiOpen(_spiChannel, _spiSpeed, 0);
}
}
void spiBeginTransaction() {}
void spiTransfer(uint8_t *out, size_t len, uint8_t *in) { spiXfer(_spiHandle, (char *)out, (char *)in, len); }
void spiEndTransaction() {}
void spiEnd()
{
if (_spiHandle >= 0) {
spiClose(_spiHandle);
_spiHandle = -1;
}
}
private:
// the HAL can contain any additional private members
const unsigned int _spiSpeed;
const uint8_t _spiChannel;
int _spiHandle = -1;
};
#endif

View File

@@ -9,7 +9,15 @@
#include <assert.h>
#ifdef ARCH_RASPBERRY_PI
#include "PortduinoGlue.h"
#include "linux/gpio/LinuxGPIOPin.h"
#include "pigpio.h"
#include "yaml-cpp/yaml.h"
#include <iostream>
#include <map>
#include <unistd.h>
std::map<int, int> settingsMap;
#else
#include <linux/gpio/LinuxGPIOPin.h>
@@ -27,7 +35,7 @@ void cpuDeepSleep(uint32_t msecs)
}
void updateBatteryLevel(uint8_t level) NOT_IMPLEMENTED("updateBatteryLevel");
#ifndef ARCH_RASPBERRY_PI
/** a simulated pin for busted IRQ hardware
* Porduino helper class to do this i2c based polling:
*/
@@ -54,7 +62,7 @@ class PolledIrqPin : public GPIOPin
};
static GPIOPin *loraIrq;
#endif
int TCPPort = 4403;
static error_t parse_opt(int key, char *arg, struct argp_state *state)
@@ -94,6 +102,95 @@ void portduinoSetup()
printf("Setting up Meshtastic on Portduino...\n");
#ifdef ARCH_RASPBERRY_PI
gpioInit();
std::string gpioChipName = "gpiochip";
YAML::Node yamlConfig;
if (access("config.yaml", R_OK) == 0) {
try {
yamlConfig = YAML::LoadFile("config.yaml");
} catch (YAML::Exception e) {
std::cout << "*** Exception " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
} else if (access("/etc/meshtasticd/config.yaml", R_OK) == 0) {
try {
yamlConfig = YAML::LoadFile("/etc/meshtasticd/config.yaml");
} catch (YAML::Exception e) {
std::cout << "*** Exception " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
} else {
std::cout << "No 'config.yaml' found, exiting." << std::endl;
exit(EXIT_FAILURE);
}
try {
if (yamlConfig["Lora"]) {
settingsMap[use_sx1262] = false;
settingsMap[use_rf95] = false;
if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as<std::string>("") == "sx1262") {
settingsMap[use_sx1262] = true;
} else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as<std::string>("") == "RF95") {
settingsMap[use_rf95] = true;
}
settingsMap[dio2_as_rf_switch] = yamlConfig["Lora"]["DIO2_AS_RF_SWITCH"].as<bool>(false);
settingsMap[cs] = yamlConfig["Lora"]["CS"].as<int>(RADIOLIB_NC);
settingsMap[irq] = yamlConfig["Lora"]["IRQ"].as<int>(RADIOLIB_NC);
settingsMap[busy] = yamlConfig["Lora"]["Busy"].as<int>(RADIOLIB_NC);
settingsMap[reset] = yamlConfig["Lora"]["Reset"].as<int>(RADIOLIB_NC);
settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as<int>(0);
gpioChipName += std::to_string(settingsMap[gpiochip]);
}
if (yamlConfig["GPIO"]) {
settingsMap[user] = yamlConfig["GPIO"]["User"].as<int>(RADIOLIB_NC);
}
if (yamlConfig["GPS"]) {
std::string serialPath = yamlConfig["GPS"]["SerialPath"].as<std::string>("");
if (serialPath != "") {
Serial1.setPath(serialPath);
settingsMap[has_gps] = 1;
}
}
} catch (YAML::Exception e) {
std::cout << "*** Exception " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
if (access("/sys/kernel/debug/bluetooth/hci0/identity", R_OK) != 0) {
std::cout << "Cannot read Bluetooth MAC Address. Please run as root" << std::endl;
exit(EXIT_FAILURE);
}
// Need to bind all the configured GPIO pins so they're not simulated
if (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) {
settingsMap[cs] = RADIOLIB_NC;
}
}
if (settingsMap.count(irq) > 0 && settingsMap[irq] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[irq], gpioChipName) != ERRNO_OK) {
settingsMap[irq] = RADIOLIB_NC;
}
}
if (settingsMap.count(busy) > 0 && settingsMap[busy] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[busy], gpioChipName) != ERRNO_OK) {
settingsMap[busy] = RADIOLIB_NC;
}
}
if (settingsMap.count(reset) > 0 && settingsMap[reset] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[reset], gpioChipName) != ERRNO_OK) {
settingsMap[reset] = RADIOLIB_NC;
}
}
if (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[user], gpioChipName) != ERRNO_OK) {
settingsMap[user] = RADIOLIB_NC;
}
}
return;
#endif
@@ -121,7 +218,7 @@ void portduinoSetup()
gpioBind(loraCs);
} else
#endif
#ifndef ARCH_RASPBERRY_PI
{
// Set the random seed equal to TCPPort to have a different seed per instance
randomSeed(TCPPort);
@@ -139,5 +236,23 @@ void portduinoSetup()
gpioBind(new SimGPIOPin(LORA_DIO1, "fakeLoraIrq"));
}
// gpioBind((new SimGPIOPin(LORA_RESET, "LORA_RESET")));
// gpioBind((new SimGPIOPin(RF95_NSS, "RF95_NSS"))->setSilent());
}
// gpioBind((new SimGPIOPin(LORA_CS, "LORA_CS"))->setSilent());
#endif
}
#ifdef ARCH_RASPBERRY_PI
int initGPIOPin(int pinNum, std::string gpioChipName)
{
std::string gpio_name = "GPIO" + std::to_string(pinNum);
try {
GPIOPin *csPin;
csPin = new LinuxGPIOPin(pinNum, gpioChipName.c_str(), pinNum, gpio_name.c_str());
csPin->setSilent();
gpioBind(csPin);
return ERRNO_OK;
} catch (std::invalid_argument &e) {
std::cout << "Warning, cannot claim pin" << gpio_name << std::endl;
return ERRNO_DISABLED;
}
}
#endif

View File

@@ -0,0 +1,10 @@
#pragma once
#ifdef ARCH_RASPBERRY_PI
#include <map>
extern std::map<int, int> settingsMap;
enum { use_sx1262, cs, irq, busy, reset, dio2_as_rf_switch, use_rf95, user, gpiochip, has_gps };
int initGPIOPin(int pinNum, std::string gpioChipname);
#endif

View File

@@ -16,4 +16,4 @@
#endif
#ifndef HAS_TELEMETRY
#define HAS_TELEMETRY 1
#endif
#endif

View File

@@ -1,33 +1,63 @@
#include "AES.h"
#include "CTR.h"
#include "CryptoEngine.h"
#include "aes.hpp"
#include "configuration.h"
class RP2040CryptoEngine : public CryptoEngine
{
CTRCommon *ctr = NULL;
public:
RP2040CryptoEngine() {}
~RP2040CryptoEngine() {}
virtual void setKey(const CryptoKey &k) override
{
CryptoEngine::setKey(k);
LOG_DEBUG("Installing AES%d key!\n", key.length * 8);
if (ctr) {
delete ctr;
ctr = NULL;
}
if (key.length != 0) {
if (key.length == 16)
ctr = new CTR<AES128>();
else
ctr = new CTR<AES256>();
ctr->setKey(key.bytes, key.length);
}
}
/**
* Encrypt a packet
*
* @param bytes is updated in place
*/
virtual void encrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes) override
virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
{
if (key.length > 0) {
AES_ctx ctx;
initNonce(fromNode, packetNum);
AES_init_ctx_iv(&ctx, key.bytes, nonce);
AES_CTR_xcrypt_buffer(&ctx, bytes, numBytes);
initNonce(fromNode, packetId);
if (numBytes <= MAX_BLOCKSIZE) {
static uint8_t scratch[MAX_BLOCKSIZE];
memcpy(scratch, bytes, numBytes);
memset(scratch + numBytes, 0,
sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it)
ctr->setIV(nonce, sizeof(nonce));
ctr->setCounterSize(4);
ctr->encrypt(bytes, scratch, numBytes);
} else {
LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes);
}
}
}
virtual void decrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes) override
virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
{
// For CTR, the implementation is the same
encrypt(fromNode, packetNum, numBytes, bytes);
encrypt(fromNode, packetId, numBytes, bytes);
}
private:

View File

@@ -25,8 +25,10 @@ extern RTC_NOINIT_ATTR uint64_t RTC_reg_b;
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
#include "modules/Telemetry/Sensor/INA219Sensor.h"
#include "modules/Telemetry/Sensor/INA260Sensor.h"
#include "modules/Telemetry/Sensor/INA3221Sensor.h"
extern INA260Sensor ina260Sensor;
extern INA219Sensor ina219Sensor;
extern INA3221Sensor ina3221Sensor;
#endif
class Power : private concurrency::OSThread