mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-10 03:47:39 +00:00
Compare commits
10 Commits
bme680
...
v2.7.15.56
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d18f3f7a65 | ||
|
|
6c09cf9d3d | ||
|
|
79a91578b7 | ||
|
|
17cd83085b | ||
|
|
b7bdcbe43e | ||
|
|
df063f40ff | ||
|
|
84bb1e33a6 | ||
|
|
955347bf50 | ||
|
|
4284fc2aec | ||
|
|
034aaa376a |
@@ -33,8 +33,6 @@ lib_deps =
|
||||
adafruit/Adafruit seesaw Library@1.7.9
|
||||
# renovate: datasource=git-refs depName=RAK12034-BMX160 packageName=https://github.com/RAKWireless/RAK12034-BMX160 gitBranch=main
|
||||
https://github.com/RAKWireless/RAK12034-BMX160/archive/dcead07ffa267d3c906e9ca4a1330ab989e957e2.zip
|
||||
# renovate: datasource=custom.pio depName=adafruit/Adafruit BME680 Library packageName=adafruit/library/Adafruit BME680
|
||||
adafruit/Adafruit BME680 Library@^2.0.5
|
||||
|
||||
build_flags =
|
||||
${arduino_base.build_flags}
|
||||
|
||||
@@ -87,6 +87,9 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<release version="2.7.15" date="2025-11-13">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.15</url>
|
||||
</release>
|
||||
<release version="2.7.14" date="2025-11-03">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.14</url>
|
||||
</release>
|
||||
|
||||
6
debian/changelog
vendored
6
debian/changelog
vendored
@@ -1,3 +1,9 @@
|
||||
meshtasticd (2.7.15.0) unstable; urgency=medium
|
||||
|
||||
* Version 2.7.15
|
||||
|
||||
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Thu, 13 Nov 2025 12:31:57 +0000
|
||||
|
||||
meshtasticd (2.7.14.0) unstable; urgency=medium
|
||||
|
||||
* Version 2.7.14
|
||||
|
||||
@@ -194,7 +194,7 @@ static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level se
|
||||
|
||||
#ifdef BATTERY_PIN
|
||||
|
||||
static void adcEnable()
|
||||
void battery_adcEnable()
|
||||
{
|
||||
#ifdef ADC_CTRL // enable adc voltage divider when we need to read
|
||||
#ifdef ADC_USE_PULLUP
|
||||
@@ -214,7 +214,7 @@ static void adcEnable()
|
||||
#endif
|
||||
}
|
||||
|
||||
static void adcDisable()
|
||||
static void battery_adcDisable()
|
||||
{
|
||||
#ifdef ADC_CTRL // disable adc voltage divider when we need to read
|
||||
#ifdef ADC_USE_PULLUP
|
||||
@@ -320,7 +320,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
uint32_t raw = 0;
|
||||
float scaled = 0;
|
||||
|
||||
adcEnable();
|
||||
battery_adcEnable();
|
||||
#ifdef ARCH_ESP32 // ADC block for espressif platforms
|
||||
raw = espAdcRead();
|
||||
scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs);
|
||||
@@ -332,7 +332,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
raw = raw / BATTERY_SENSE_SAMPLES;
|
||||
scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw;
|
||||
#endif
|
||||
adcDisable();
|
||||
battery_adcDisable();
|
||||
|
||||
if (!initial_read_done) {
|
||||
// Flush the smoothing filter with an ADC reading, if the reading is plausibly correct
|
||||
@@ -906,13 +906,8 @@ void Power::readPowerStatus()
|
||||
low_voltage_counter++;
|
||||
LOG_DEBUG("Low voltage counter: %d/10", low_voltage_counter);
|
||||
if (low_voltage_counter > 10) {
|
||||
#ifdef ARCH_NRF52
|
||||
// We can't trigger deep sleep on NRF52, it's freezing the board
|
||||
LOG_DEBUG("Low voltage detected, but not trigger deep sleep");
|
||||
#else
|
||||
LOG_INFO("Low voltage detected, trigger deep sleep");
|
||||
powerFSM.trigger(EVENT_LOW_BATTERY);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
low_voltage_counter = 0;
|
||||
@@ -1552,4 +1547,4 @@ bool Power::meshSolarInit()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -410,18 +410,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// BME680 BSEC2 support detection
|
||||
#if !defined(MESHTASTIC_BME680_BSEC2_SUPPORTED)
|
||||
#if defined(RAK_4631) || defined(TBEAM_V10)
|
||||
|
||||
#define MESHTASTIC_BME680_BSEC2_SUPPORTED 1
|
||||
#define MESHTASTIC_BME680_HEADER <bsec2.h>
|
||||
#else
|
||||
#define MESHTASTIC_BME680_BSEC2_SUPPORTED 0
|
||||
#define MESHTASTIC_BME680_HEADER <Adafruit_BME680.h>
|
||||
#endif // defined(RAK_4631)
|
||||
#endif // !defined(MESHTASTIC_BME680_BSEC2_SUPPORTED)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Global switches to turn off features for a minimized build
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -240,6 +240,9 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
|
||||
buffer[bytesRead] = b;
|
||||
bytesRead++;
|
||||
if ((bytesRead == 767) || (b == '\r')) {
|
||||
#ifdef GPS_DEBUG
|
||||
LOG_DEBUG(debugmsg.c_str());
|
||||
#endif
|
||||
if (strnstr((char *)buffer, message, bytesRead) != nullptr) {
|
||||
#ifdef GPS_DEBUG
|
||||
LOG_DEBUG("Found: %s", message); // Log the found message
|
||||
@@ -247,9 +250,6 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
|
||||
return GNSS_RESPONSE_OK;
|
||||
} else {
|
||||
bytesRead = 0;
|
||||
#ifdef GPS_DEBUG
|
||||
LOG_DEBUG(debugmsg.c_str());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1275,6 +1275,24 @@ GnssModel_t GPS::probe(int serialSpeed)
|
||||
memset(&ublox_info, 0, sizeof(ublox_info));
|
||||
delay(100);
|
||||
|
||||
#if defined(PIN_GPS_RESET) && PIN_GPS_RESET != -1
|
||||
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
|
||||
delay(10);
|
||||
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
|
||||
|
||||
// attempt to detect the chip based on boot messages
|
||||
std::vector<ChipInfo> passive_detect = {
|
||||
{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
|
||||
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
|
||||
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352},
|
||||
{"UC6580", "UC6580", GNSS_MODEL_UC6580},
|
||||
// as L76K is sort of a last ditch effort, we won't attempt to detect it by startup messages for now.
|
||||
/*{"L76K", "SW=URANUS", GNSS_MODEL_MTK}*/};
|
||||
GnssModel_t detectedDriver = getProbeResponse(500, passive_detect, serialSpeed);
|
||||
if (detectedDriver != GNSS_MODEL_UNKNOWN) {
|
||||
return detectedDriver;
|
||||
}
|
||||
#endif
|
||||
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
|
||||
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
|
||||
delay(20);
|
||||
@@ -1473,12 +1491,12 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipI
|
||||
}
|
||||
|
||||
if (c == ',' || (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n')) {
|
||||
#ifdef GPS_DEBUG
|
||||
LOG_DEBUG(response);
|
||||
#endif
|
||||
// check if we can see our chips
|
||||
for (const auto &chipInfo : responseMap) {
|
||||
if (strstr(response, chipInfo.detectionString.c_str()) != nullptr) {
|
||||
#ifdef GPS_DEBUG
|
||||
LOG_DEBUG(response);
|
||||
#endif
|
||||
LOG_INFO("%s detected", chipInfo.chipName.c_str());
|
||||
delete[] response; // Cleanup before return
|
||||
return chipInfo.driver;
|
||||
@@ -1486,6 +1504,9 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipI
|
||||
}
|
||||
}
|
||||
if (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n') {
|
||||
#ifdef GPS_DEBUG
|
||||
LOG_DEBUG(response);
|
||||
#endif
|
||||
// Reset the response buffer for the next potential message
|
||||
responseLen = 0;
|
||||
response[0] = '\0';
|
||||
@@ -1572,8 +1593,6 @@ GPS *GPS::createGps()
|
||||
|
||||
#ifdef PIN_GPS_RESET
|
||||
pinMode(PIN_GPS_RESET, OUTPUT);
|
||||
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
|
||||
delay(10);
|
||||
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -310,7 +310,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
|
||||
#ifdef BUILD_EPOCH
|
||||
if (tv.tv_sec < BUILD_EPOCH) {
|
||||
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||
LOG_WARN("Ignore time (%lu) before build epoch (%lu)!", printableEpoch, BUILD_EPOCH);
|
||||
lastTimeValidationWarning = millis();
|
||||
}
|
||||
return RTCSetResultInvalidTime;
|
||||
@@ -319,7 +319,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
|
||||
// Calculate max allowed time safely to avoid overflow in logging
|
||||
uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS;
|
||||
uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime;
|
||||
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch,
|
||||
LOG_WARN("Ignore time (%lu) too far in the future (build epoch: %lu, max allowed: %lu)!", printableEpoch,
|
||||
(uint32_t)BUILD_EPOCH, maxAllowedPrintable);
|
||||
lastTimeValidationWarning = millis();
|
||||
}
|
||||
|
||||
@@ -194,17 +194,12 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
||||
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
|
||||
int line = 0;
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
|
||||
graphics::ClockRenderer::drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
|
||||
char timeString[16];
|
||||
int hour = 0;
|
||||
int minute = 0;
|
||||
int second = 0;
|
||||
|
||||
if (rtc_sec > 0) {
|
||||
long hms = rtc_sec % SEC_PER_DAY;
|
||||
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
||||
@@ -215,11 +210,11 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
||||
}
|
||||
|
||||
bool isPM = hour >= 12;
|
||||
// hour = hour > 12 ? hour - 12 : hour;
|
||||
if (config.display.use_12h_clock) {
|
||||
hour %= 12;
|
||||
if (hour == 0)
|
||||
if (hour == 0) {
|
||||
hour = 12;
|
||||
}
|
||||
snprintf(timeString, sizeof(timeString), "%d:%02d", hour, minute);
|
||||
} else {
|
||||
snprintf(timeString, sizeof(timeString), "%02d:%02d", hour, minute);
|
||||
@@ -229,24 +224,56 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
||||
char secondString[8];
|
||||
snprintf(secondString, sizeof(secondString), "%02d", second);
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
float scale = 1.5;
|
||||
#elif defined(CHATTER_2)
|
||||
float scale = 1.1;
|
||||
#else
|
||||
float scale = 0.75;
|
||||
if (isHighResolution) {
|
||||
scale = 1.5;
|
||||
}
|
||||
#endif
|
||||
static bool scaleInitialized = false;
|
||||
static float scale = 0.75f;
|
||||
static float segmentWidth = SEGMENT_WIDTH * 0.75f;
|
||||
static float segmentHeight = SEGMENT_HEIGHT * 0.75f;
|
||||
|
||||
uint16_t segmentWidth = SEGMENT_WIDTH * scale;
|
||||
uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
|
||||
if (!scaleInitialized) {
|
||||
float screenwidth_target_ratio = 0.80f; // Target 80% of display width (adjustable)
|
||||
float max_scale = 3.5f; // Safety limit to avoid runaway scaling
|
||||
float step = 0.05f; // Step increment per iteration
|
||||
|
||||
float target_width = display->getWidth() * screenwidth_target_ratio;
|
||||
float target_height =
|
||||
display->getHeight() -
|
||||
(isHighResolution
|
||||
? 46
|
||||
: 33); // Be careful adjusting this number, we have to account for header and the text under the time
|
||||
|
||||
float calculated_width_size = 0.0f;
|
||||
float calculated_height_size = 0.0f;
|
||||
|
||||
while (true) {
|
||||
segmentWidth = SEGMENT_WIDTH * scale;
|
||||
segmentHeight = SEGMENT_HEIGHT * scale;
|
||||
|
||||
calculated_width_size = segmentHeight + ((segmentWidth + (segmentHeight * 2) + 4) * 4);
|
||||
calculated_height_size = segmentHeight + ((segmentHeight + (segmentHeight * 2) + 4) * 2);
|
||||
|
||||
if (calculated_width_size >= target_width || calculated_height_size >= target_height || scale >= max_scale) {
|
||||
break;
|
||||
}
|
||||
|
||||
scale += step;
|
||||
}
|
||||
|
||||
// If we overshot width, back off one step and recompute segment sizes
|
||||
if (calculated_width_size > target_width || calculated_height_size > target_height) {
|
||||
scale -= step;
|
||||
segmentWidth = SEGMENT_WIDTH * scale;
|
||||
segmentHeight = SEGMENT_HEIGHT * scale;
|
||||
}
|
||||
|
||||
scaleInitialized = true;
|
||||
}
|
||||
|
||||
size_t len = strlen(timeString);
|
||||
|
||||
// calculate hours:minutes string width
|
||||
uint16_t timeStringWidth = strlen(timeString) * 5;
|
||||
uint16_t timeStringWidth = len * 5; // base spacing between characters
|
||||
|
||||
for (uint8_t i = 0; i < strlen(timeString); i++) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
char character = timeString[i];
|
||||
|
||||
if (character == ':') {
|
||||
@@ -257,19 +284,21 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
||||
}
|
||||
|
||||
uint16_t hourMinuteTextX = (display->getWidth() / 2) - (timeStringWidth / 2);
|
||||
|
||||
uint16_t startingHourMinuteTextX = hourMinuteTextX;
|
||||
|
||||
uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2);
|
||||
uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2) + 2;
|
||||
|
||||
// iterate over characters in hours:minutes string and draw segmented characters
|
||||
for (uint8_t i = 0; i < strlen(timeString); i++) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
char character = timeString[i];
|
||||
|
||||
if (character == ':') {
|
||||
drawSegmentedDisplayColon(display, hourMinuteTextX, hourMinuteTextY, scale);
|
||||
|
||||
hourMinuteTextX += segmentHeight + 6;
|
||||
if (scale >= 2.0f) {
|
||||
hourMinuteTextX += (uint16_t)(4.5f * scale);
|
||||
}
|
||||
} else {
|
||||
drawSegmentedDisplayCharacter(display, hourMinuteTextX, hourMinuteTextY, character - '0', scale);
|
||||
|
||||
@@ -279,38 +308,29 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
||||
hourMinuteTextX += 5;
|
||||
}
|
||||
|
||||
// draw seconds string
|
||||
// draw seconds string + AM/PM
|
||||
display->setFont(FONT_SMALL);
|
||||
int xOffset = (isHighResolution) ? 0 : -1;
|
||||
if (hour >= 10) {
|
||||
xOffset += (isHighResolution) ? 32 : 18;
|
||||
}
|
||||
int yOffset = (isHighResolution) ? 3 : 1;
|
||||
#ifdef SENSECAP_INDICATOR
|
||||
yOffset -= 3;
|
||||
#endif
|
||||
#ifdef T_DECK
|
||||
yOffset -= 5;
|
||||
#endif
|
||||
|
||||
if (config.display.use_12h_clock) {
|
||||
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2,
|
||||
isPM ? "pm" : "am");
|
||||
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - 1, isPM ? "pm" : "am");
|
||||
}
|
||||
|
||||
#ifndef USE_EINK
|
||||
xOffset = (isHighResolution) ? 18 : 10;
|
||||
display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - yOffset,
|
||||
if (scale >= 2.0f) {
|
||||
xOffset -= (int)(4.5f * scale);
|
||||
}
|
||||
display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - 1,
|
||||
secondString);
|
||||
#endif
|
||||
|
||||
graphics::drawCommonFooter(display, x, y);
|
||||
}
|
||||
|
||||
void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y)
|
||||
{
|
||||
display->drawFastImage(x, y, 18, 14, bluetoothConnectedIcon);
|
||||
}
|
||||
|
||||
// Draw an analog clock
|
||||
void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
@@ -321,11 +341,6 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
|
||||
int line = 0;
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
|
||||
drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
|
||||
}
|
||||
#endif
|
||||
// clock face center coordinates
|
||||
int16_t centerX = display->getWidth() / 2;
|
||||
int16_t centerY = display->getHeight() / 2;
|
||||
|
||||
@@ -24,7 +24,6 @@ void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int heig
|
||||
|
||||
// UI elements for clock displays
|
||||
// void drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode = true, float scale = 1);
|
||||
void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y);
|
||||
|
||||
} // namespace ClockRenderer
|
||||
|
||||
|
||||
@@ -581,11 +581,8 @@ void menuHandler::systemBaseMenu()
|
||||
|
||||
optionsArray[options] = "Notifications";
|
||||
optionsEnumArray[options++] = Notifications;
|
||||
#if defined(ST7789_CS) || defined(ST7796_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || \
|
||||
defined(USE_SH1107) || defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
|
||||
optionsArray[options] = "Display Options";
|
||||
optionsEnumArray[options++] = ScreenOptions;
|
||||
#endif
|
||||
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
optionsArray[options] = "Bluetooth";
|
||||
@@ -785,17 +782,24 @@ void menuHandler::nodeNameLengthMenu()
|
||||
|
||||
void menuHandler::resetNodeDBMenu()
|
||||
{
|
||||
static const char *optionsArray[] = {"Back", "Confirm"};
|
||||
static const char *optionsArray[] = {"Back", "Reset All", "Preserve Favorites"};
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Confirm Reset NodeDB";
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = 2;
|
||||
bannerOptions.optionsCount = 3;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == 1) {
|
||||
if (selected == 1 || selected == 2) {
|
||||
disableBluetooth();
|
||||
screen->setFrames(Screen::FOCUS_DEFAULT);
|
||||
}
|
||||
if (selected == 1) {
|
||||
LOG_INFO("Initiate node-db reset");
|
||||
nodeDB->resetNodes();
|
||||
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
||||
} else if (selected == 2) {
|
||||
LOG_INFO("Initiate node-db reset but keeping favorites");
|
||||
nodeDB->resetNodes(1);
|
||||
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
||||
}
|
||||
};
|
||||
screen->showOverlayBanner(bannerOptions);
|
||||
|
||||
@@ -34,7 +34,8 @@ void NeighborInfoModule::printNodeDBNeighbors()
|
||||
}
|
||||
}
|
||||
|
||||
/* Send our initial owner announcement 35 seconds after we start (to give network time to setup) */
|
||||
/* Send our initial owner announcement 35 seconds after we start (to give
|
||||
* network time to setup) */
|
||||
NeighborInfoModule::NeighborInfoModule()
|
||||
: ProtobufModule("neighborinfo", meshtastic_PortNum_NEIGHBORINFO_APP, &meshtastic_NeighborInfo_msg),
|
||||
concurrency::OSThread("NeighborInfo")
|
||||
@@ -53,8 +54,8 @@ NeighborInfoModule::NeighborInfoModule()
|
||||
}
|
||||
|
||||
/*
|
||||
Collect neighbor info from the nodeDB's history, capping at a maximum number of entries and max time
|
||||
Assumes that the neighborInfo packet has been allocated
|
||||
Collect neighbor info from the nodeDB's history, capping at a maximum number of
|
||||
entries and max time Assumes that the neighborInfo packet has been allocated
|
||||
@returns the number of entries collected
|
||||
*/
|
||||
uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighborInfo)
|
||||
@@ -71,8 +72,8 @@ uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighb
|
||||
if ((neighborInfo->neighbors_count < MAX_NUM_NEIGHBORS) && (nbr.node_id != my_node_id)) {
|
||||
neighborInfo->neighbors[neighborInfo->neighbors_count].node_id = nbr.node_id;
|
||||
neighborInfo->neighbors[neighborInfo->neighbors_count].snr = nbr.snr;
|
||||
// Note: we don't set the last_rx_time and node_broadcast_intervals_secs here, because we don't want to send this over
|
||||
// the mesh
|
||||
// Note: we don't set the last_rx_time and node_broadcast_intervals_secs
|
||||
// here, because we don't want to send this over the mesh
|
||||
neighborInfo->neighbors_count++;
|
||||
}
|
||||
}
|
||||
@@ -88,8 +89,9 @@ void NeighborInfoModule::cleanUpNeighbors()
|
||||
uint32_t now = getTime();
|
||||
NodeNum my_node_id = nodeDB->getNodeNum();
|
||||
for (auto it = neighbors.rbegin(); it != neighbors.rend();) {
|
||||
// We will remove a neighbor if we haven't heard from them in twice the broadcast interval
|
||||
// cannot use isWithinTimespanMs() as it->last_rx_time is seconds since 1970
|
||||
// We will remove a neighbor if we haven't heard from them in twice the
|
||||
// broadcast interval cannot use isWithinTimespanMs() as it->last_rx_time is
|
||||
// seconds since 1970
|
||||
if ((now - it->last_rx_time > it->node_broadcast_interval_secs * 2) && (it->node_id != my_node_id)) {
|
||||
LOG_DEBUG("Remove neighbor with node ID 0x%x", it->node_id);
|
||||
it = std::vector<meshtastic_Neighbor>::reverse_iterator(
|
||||
@@ -132,25 +134,55 @@ int32_t NeighborInfoModule::runOnce()
|
||||
return Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_neighbor_info_broadcast_secs);
|
||||
}
|
||||
|
||||
meshtastic_MeshPacket *NeighborInfoModule::allocReply()
|
||||
{
|
||||
LOG_INFO("NeighborInfoRequested.");
|
||||
if (lastSentReply && Throttle::isWithinTimespanMs(lastSentReply, 3 * 60 * 1000)) {
|
||||
LOG_DEBUG("Skip Neighbors reply since we sent a reply <3min ago");
|
||||
ignoreRequest = true; // Mark it as ignored for MeshModule
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
meshtastic_NeighborInfo neighborInfo = meshtastic_NeighborInfo_init_zero;
|
||||
collectNeighborInfo(&neighborInfo);
|
||||
|
||||
meshtastic_MeshPacket *reply = allocDataProtobuf(neighborInfo);
|
||||
|
||||
if (reply) {
|
||||
lastSentReply = millis(); // Track when we sent this reply
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
/*
|
||||
Collect a received neighbor info packet from another node
|
||||
Pass it to an upper client; do not persist this data on the mesh
|
||||
*/
|
||||
bool NeighborInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_NeighborInfo *np)
|
||||
{
|
||||
LOG_DEBUG("NeighborInfo: handleReceivedProtobuf");
|
||||
if (np) {
|
||||
printNeighborInfo("RECEIVED", np);
|
||||
updateNeighbors(mp, np);
|
||||
// Ignore dummy/interceptable packets: single neighbor with nodeId 0 and snr 0
|
||||
if (np->neighbors_count != 1 || np->neighbors[0].node_id != 0 || np->neighbors[0].snr != 0.0f) {
|
||||
LOG_DEBUG(" Updating neighbours");
|
||||
updateNeighbors(mp, np);
|
||||
} else {
|
||||
LOG_DEBUG(" Ignoring dummy neighbor info packet (single neighbor with nodeId 0, snr 0)");
|
||||
}
|
||||
} else if (mp.hop_start != 0 && mp.hop_start == mp.hop_limit) {
|
||||
LOG_DEBUG("Get or create neighbor: %u with snr %f", mp.from, mp.rx_snr);
|
||||
// If the hopLimit is the same as hopStart, then it is a neighbor
|
||||
getOrCreateNeighbor(mp.from, mp.from, 0, mp.rx_snr); // Set the broadcast interval to 0, as we don't know it
|
||||
getOrCreateNeighbor(mp.from, mp.from, 0,
|
||||
mp.rx_snr); // Set the broadcast interval to 0, as we don't know it
|
||||
}
|
||||
// Allow others to handle this packet
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
Copy the content of a current NeighborInfo packet into a new one and update the last_sent_by_id to our NodeNum
|
||||
Copy the content of a current NeighborInfo packet into a new one and update the
|
||||
last_sent_by_id to our NodeNum
|
||||
*/
|
||||
void NeighborInfoModule::alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtastic_NeighborInfo *n)
|
||||
{
|
||||
@@ -168,8 +200,10 @@ void NeighborInfoModule::resetNeighbors()
|
||||
|
||||
void NeighborInfoModule::updateNeighbors(const meshtastic_MeshPacket &mp, const meshtastic_NeighborInfo *np)
|
||||
{
|
||||
// The last sent ID will be 0 if the packet is from the phone, which we don't count as
|
||||
// an edge. So we assume that if it's zero, then this packet is from our node.
|
||||
LOG_DEBUG("updateNeighbors");
|
||||
// The last sent ID will be 0 if the packet is from the phone, which we don't
|
||||
// count as an edge. So we assume that if it's zero, then this packet is from
|
||||
// our node.
|
||||
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) {
|
||||
getOrCreateNeighbor(mp.from, np->last_sent_by_id, np->node_broadcast_interval_secs, mp.rx_snr);
|
||||
}
|
||||
@@ -188,7 +222,8 @@ meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSen
|
||||
// if found, update it
|
||||
neighbors[i].snr = snr;
|
||||
neighbors[i].last_rx_time = getTime();
|
||||
// Only if this is the original sender, the broadcast interval corresponds to it
|
||||
// Only if this is the original sender, the broadcast interval corresponds
|
||||
// to it
|
||||
if (originalSender == n && node_broadcast_interval_secs != 0)
|
||||
neighbors[i].node_broadcast_interval_secs = node_broadcast_interval_secs;
|
||||
return &neighbors[i];
|
||||
@@ -200,10 +235,12 @@ meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSen
|
||||
new_nbr.node_id = n;
|
||||
new_nbr.snr = snr;
|
||||
new_nbr.last_rx_time = getTime();
|
||||
// Only if this is the original sender, the broadcast interval corresponds to it
|
||||
// Only if this is the original sender, the broadcast interval corresponds to
|
||||
// it
|
||||
if (originalSender == n && node_broadcast_interval_secs != 0)
|
||||
new_nbr.node_broadcast_interval_secs = node_broadcast_interval_secs;
|
||||
else // Assume the same broadcast interval as us for the neighbor if we don't know it
|
||||
else // Assume the same broadcast interval as us for the neighbor if we don't
|
||||
// know it
|
||||
new_nbr.node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval;
|
||||
|
||||
if (neighbors.size() < MAX_NUM_NEIGHBORS) {
|
||||
|
||||
@@ -28,6 +28,10 @@ class NeighborInfoModule : public ProtobufModule<meshtastic_NeighborInfo>, priva
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_NeighborInfo *nb) override;
|
||||
|
||||
/* Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
||||
* so that subclasses can (optionally) send a response back to the original sender. */
|
||||
virtual meshtastic_MeshPacket *allocReply() override;
|
||||
|
||||
/*
|
||||
* Collect neighbor info from the nodeDB's history, capping at a maximum number of entries and max time
|
||||
* @return the number of entries collected
|
||||
@@ -66,5 +70,8 @@ class NeighborInfoModule : public ProtobufModule<meshtastic_NeighborInfo>, priva
|
||||
/* These are for debugging only */
|
||||
void printNeighborInfo(const char *header, const meshtastic_NeighborInfo *np);
|
||||
void printNodeDBNeighbors();
|
||||
|
||||
private:
|
||||
uint32_t lastSentReply = 0; // Last time we sent a position reply (used for reply throttling only)
|
||||
};
|
||||
extern NeighborInfoModule *neighborInfoModule;
|
||||
@@ -53,7 +53,7 @@ extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const c
|
||||
#include "Sensor/LTR390UVSensor.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(MESHTASTIC_BME680_HEADER)
|
||||
#if __has_include(<bsec2.h>)
|
||||
#include "Sensor/BME680Sensor.h"
|
||||
#endif
|
||||
|
||||
@@ -214,7 +214,7 @@ void EnvironmentTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner)
|
||||
#if __has_include(<Adafruit_LTR390.h>)
|
||||
addSensor<LTR390UVSensor>(i2cScanner, ScanI2C::DeviceType::LTR390UV);
|
||||
#endif
|
||||
#if __has_include(MESHTASTIC_BME680_HEADER)
|
||||
#if __has_include(<bsec2.h>)
|
||||
addSensor<BME680Sensor>(i2cScanner, ScanI2C::DeviceType::BME_680);
|
||||
#endif
|
||||
#if __has_include(<Adafruit_BMP280.h>)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(MESHTASTIC_BME680_HEADER)
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<bsec2.h>)
|
||||
|
||||
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "BME680Sensor.h"
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
BME680Sensor::BME680Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME680, "BME680") {}
|
||||
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
int32_t BME680Sensor::runOnce()
|
||||
{
|
||||
if (!bme680.run()) {
|
||||
@@ -18,13 +17,10 @@ int32_t BME680Sensor::runOnce()
|
||||
}
|
||||
return 35;
|
||||
}
|
||||
#endif // defined(MESHTASTIC_BME680_BSEC2_SUPPORTED)
|
||||
|
||||
bool BME680Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
|
||||
{
|
||||
status = 0;
|
||||
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
if (!bme680.begin(dev->address.address, *bus))
|
||||
checkStatus("begin");
|
||||
|
||||
@@ -46,25 +42,12 @@ bool BME680Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
|
||||
if (status == 0)
|
||||
LOG_DEBUG("BME680Sensor::runOnce: bme680.status %d", bme680.status);
|
||||
|
||||
#else
|
||||
bme680 = makeBME680(bus);
|
||||
|
||||
if (!bme680->begin(dev->address.address)) {
|
||||
LOG_ERROR("Init sensor: %s failed at begin()", sensorName);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = 1;
|
||||
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
|
||||
initI2CSensor();
|
||||
return status;
|
||||
}
|
||||
|
||||
bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||
{
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
if (bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal == 0)
|
||||
return false;
|
||||
|
||||
@@ -82,27 +65,9 @@ bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||
// Check if we need to save state to filesystem (every STATE_SAVE_PERIOD ms)
|
||||
measurement->variant.environment_metrics.iaq = bme680.getData(BSEC_OUTPUT_IAQ).signal;
|
||||
updateState();
|
||||
#else
|
||||
if (!bme680->performReading()) {
|
||||
LOG_ERROR("BME680Sensor::getMetrics: performReading failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
measurement->variant.environment_metrics.has_temperature = true;
|
||||
measurement->variant.environment_metrics.has_relative_humidity = true;
|
||||
measurement->variant.environment_metrics.has_barometric_pressure = true;
|
||||
measurement->variant.environment_metrics.has_gas_resistance = true;
|
||||
|
||||
measurement->variant.environment_metrics.temperature = bme680->readTemperature();
|
||||
measurement->variant.environment_metrics.relative_humidity = bme680->readHumidity();
|
||||
measurement->variant.environment_metrics.barometric_pressure = bme680->readPressure() / 100.0F;
|
||||
measurement->variant.environment_metrics.gas_resistance = bme680->readGas() / 1000.0;
|
||||
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
void BME680Sensor::loadState()
|
||||
{
|
||||
#ifdef FSCom
|
||||
@@ -179,6 +144,5 @@ void BME680Sensor::checkStatus(const char *functionName)
|
||||
else if (bme680.sensor.status > BME68X_OK)
|
||||
LOG_WARN("%s BME68X code: %d", functionName, bme680.sensor.status);
|
||||
}
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,40 +1,23 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(MESHTASTIC_BME680_HEADER)
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<bsec2.h>)
|
||||
|
||||
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "TelemetrySensor.h"
|
||||
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
#include <bme68xLibrary.h>
|
||||
#include <bsec2.h>
|
||||
#else
|
||||
#include <Adafruit_BME680.h>
|
||||
#include <memory>
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
|
||||
#define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // That's 6 hours worth of millis()
|
||||
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
const uint8_t bsec_config[] = {
|
||||
#include "config/bme680/bme680_iaq_33v_3s_4d/bsec_iaq.txt"
|
||||
};
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
|
||||
class BME680Sensor : public TelemetrySensor
|
||||
{
|
||||
private:
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
Bsec2 bme680;
|
||||
#else
|
||||
using BME680Ptr = std::unique_ptr<Adafruit_BME680>;
|
||||
|
||||
static BME680Ptr makeBME680(TwoWire *bus) { return std::make_unique<Adafruit_BME680>(bus); }
|
||||
|
||||
BME680Ptr bme680;
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
|
||||
protected:
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
const char *bsecConfigFileName = "/prefs/bsec.dat";
|
||||
uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0};
|
||||
uint8_t accuracy = 0;
|
||||
@@ -51,13 +34,10 @@ class BME680Sensor : public TelemetrySensor
|
||||
void loadState();
|
||||
void updateState();
|
||||
void checkStatus(const char *functionName);
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
|
||||
public:
|
||||
BME680Sensor();
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
virtual int32_t runOnce() override;
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
|
||||
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
|
||||
};
|
||||
|
||||
@@ -51,6 +51,7 @@ constexpr int reconnectMax = 5;
|
||||
static uint8_t bytes[meshtastic_MqttClientProxyMessage_size + 30]; // 12 for channel name and 16 for nodeid
|
||||
|
||||
static bool isMqttServerAddressPrivate = false;
|
||||
static bool isConnected = false;
|
||||
|
||||
inline void onReceiveProto(char *topic, byte *payload, size_t length)
|
||||
{
|
||||
@@ -320,8 +321,10 @@ bool connectPubSub(const PubSubConfig &config, PubSubClient &pubSub, Client &cli
|
||||
std::string nodeId = nodeDB->getNodeId();
|
||||
const bool connected = pubSub.connect(nodeId.c_str(), config.mqttUsername, config.mqttPassword);
|
||||
if (connected) {
|
||||
isConnected = true;
|
||||
LOG_INFO("MQTT connected");
|
||||
} else {
|
||||
isConnected = false;
|
||||
LOG_WARN("Failed to connect to MQTT server");
|
||||
}
|
||||
return connected;
|
||||
@@ -507,6 +510,7 @@ bool MQTT::publish(const char *topic, const uint8_t *payload, size_t length, boo
|
||||
|
||||
void MQTT::reconnect()
|
||||
{
|
||||
isConnected = false;
|
||||
if (wantsLink()) {
|
||||
if (moduleConfig.mqtt.proxy_to_client_enabled) {
|
||||
LOG_INFO("MQTT connect via client proxy instead");
|
||||
@@ -534,7 +538,7 @@ void MQTT::reconnect()
|
||||
runASAP = true;
|
||||
reconnectCount = 0;
|
||||
isMqttServerAddressPrivate = isPrivateIpAddress(clientConnection->remoteIP());
|
||||
|
||||
isConnected = true;
|
||||
publishNodeInfo();
|
||||
sendSubscriptions();
|
||||
} else {
|
||||
@@ -688,7 +692,7 @@ void MQTT::publishNodeInfo()
|
||||
}
|
||||
void MQTT::publishQueuedMessages()
|
||||
{
|
||||
if (mqttQueue.isEmpty())
|
||||
if (mqttQueue.isEmpty() || !isConnected)
|
||||
return;
|
||||
|
||||
LOG_DEBUG("Publish enqueued MQTT message");
|
||||
@@ -895,4 +899,4 @@ void MQTT::perhapsReportToMap()
|
||||
|
||||
// Update the last report time
|
||||
last_report_to_map = millis();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
#include "error.h"
|
||||
#include "main.h"
|
||||
#include "meshUtils.h"
|
||||
#include "power.h"
|
||||
|
||||
#include <hal/nrf_lpcomp.h>
|
||||
|
||||
#ifdef BQ25703A_ADDR
|
||||
#include "BQ25713.h"
|
||||
@@ -389,6 +392,23 @@ void cpuDeepSleep(uint32_t msecToWake)
|
||||
nrf_gpio_cfg_sense_set(BUTTON_PIN, sense); // Apply SENSE to wake up the device from the deep sleep
|
||||
#endif
|
||||
|
||||
#ifdef BATTERY_LPCOMP_INPUT
|
||||
// Wake up if power rises again
|
||||
nrf_lpcomp_config_t c;
|
||||
c.reference = BATTERY_LPCOMP_THRESHOLD;
|
||||
c.detection = NRF_LPCOMP_DETECT_UP;
|
||||
c.hyst = NRF_LPCOMP_HYST_NOHYST;
|
||||
nrf_lpcomp_configure(NRF_LPCOMP, &c);
|
||||
nrf_lpcomp_input_select(NRF_LPCOMP, BATTERY_LPCOMP_INPUT);
|
||||
nrf_lpcomp_enable(NRF_LPCOMP);
|
||||
|
||||
battery_adcEnable();
|
||||
|
||||
nrf_lpcomp_task_trigger(NRF_LPCOMP, NRF_LPCOMP_TASK_START);
|
||||
while (!nrf_lpcomp_event_check(NRF_LPCOMP, NRF_LPCOMP_EVENT_READY))
|
||||
;
|
||||
#endif
|
||||
|
||||
auto ok = sd_power_system_off();
|
||||
if (ok != NRF_SUCCESS) {
|
||||
LOG_ERROR("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!");
|
||||
@@ -420,4 +440,4 @@ void enterDfuMode()
|
||||
#else
|
||||
enterUf2Dfu();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,20 @@ void getMacAddr(uint8_t *dmac)
|
||||
}
|
||||
}
|
||||
|
||||
std::string cleanupNameForAutoconf(std::string name)
|
||||
{
|
||||
// Convert spaces -> dashes, lowercase
|
||||
|
||||
std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) {
|
||||
if (c == ' ') {
|
||||
return '-';
|
||||
}
|
||||
return (char)std::tolower(c);
|
||||
});
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/** apps run under portduino can optionally define a portduinoSetup() to
|
||||
* use portduino specific init code (such as gpioBind) to setup portduino on their host machine,
|
||||
* before running 'arduino' code.
|
||||
@@ -218,6 +232,11 @@ void portduinoSetup()
|
||||
// If LoRa `Module: auto` (default in config.yaml),
|
||||
// attempt to auto config based on Product Strings
|
||||
if (portduino_config.lora_module == use_autoconf) {
|
||||
bool found_hat = false;
|
||||
bool found_rak_eeprom = false;
|
||||
bool found_ch341 = false;
|
||||
|
||||
char hat_vendor[96] = {0};
|
||||
char autoconf_product[96] = {0};
|
||||
// Try CH341
|
||||
try {
|
||||
@@ -227,21 +246,32 @@ void portduinoSetup()
|
||||
ch341Hal->getProductString(autoconf_product, 95);
|
||||
delete ch341Hal;
|
||||
std::cout << "autoconf: Found CH341 device " << autoconf_product << std::endl;
|
||||
|
||||
found_ch341 = true;
|
||||
} catch (...) {
|
||||
std::cout << "autoconf: Could not locate CH341 device" << std::endl;
|
||||
}
|
||||
// Try Pi HAT+
|
||||
if (strlen(autoconf_product) < 6) {
|
||||
std::cout << "autoconf: Looking for Pi HAT+..." << std::endl;
|
||||
if (access("/proc/device-tree/hat/vendor", R_OK) == 0) {
|
||||
std::ifstream hatVendorFile("/proc/device-tree/hat/vendor");
|
||||
if (hatVendorFile.is_open()) {
|
||||
hatVendorFile.read(hat_vendor, 95);
|
||||
hatVendorFile.close();
|
||||
}
|
||||
}
|
||||
if (access("/proc/device-tree/hat/product", R_OK) == 0) {
|
||||
std::ifstream hatProductFile("/proc/device-tree/hat/product");
|
||||
if (hatProductFile.is_open()) {
|
||||
hatProductFile.read(autoconf_product, 95);
|
||||
hatProductFile.close();
|
||||
}
|
||||
std::cout << "autoconf: Found Pi HAT+ " << autoconf_product << " at /proc/device-tree/hat/product" << std::endl;
|
||||
std::cout << "autoconf: Found Pi HAT+ " << hat_vendor << " " << autoconf_product << " at /proc/device-tree/hat"
|
||||
<< std::endl;
|
||||
found_hat = true;
|
||||
} else {
|
||||
std::cout << "autoconf: Could not locate Pi HAT+ at /proc/device-tree/hat/product" << std::endl;
|
||||
std::cout << "autoconf: Could not locate Pi HAT+ at /proc/device-tree/hat" << std::endl;
|
||||
}
|
||||
}
|
||||
// attempt to load autoconf data from an EEPROM on 0x50
|
||||
@@ -297,6 +327,7 @@ void portduinoSetup()
|
||||
autoconf_product[0] = 0x0;
|
||||
} else {
|
||||
std::cout << "autoconf: Found eeprom data " << autoconf_raw << std::endl;
|
||||
found_rak_eeprom = true;
|
||||
if (mac_start != nullptr) {
|
||||
std::cout << "autoconf: Found mac data " << mac_start << std::endl;
|
||||
if (strlen(mac_start) == 12)
|
||||
@@ -325,12 +356,29 @@ void portduinoSetup()
|
||||
if (strlen(autoconf_product) > 0) {
|
||||
// From configProducts map in PortduinoGlue.h
|
||||
std::string product_config = "";
|
||||
try {
|
||||
|
||||
if (configProducts.find(autoconf_product) != configProducts.end()) {
|
||||
product_config = configProducts.at(autoconf_product);
|
||||
} catch (std::out_of_range &e) {
|
||||
std::cerr << "autoconf: Unable to find config for " << autoconf_product << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
if (found_hat) {
|
||||
product_config =
|
||||
cleanupNameForAutoconf("lora-hat-" + std::string(hat_vendor) + "-" + autoconf_product + ".yaml");
|
||||
} else if (found_ch341) {
|
||||
product_config = cleanupNameForAutoconf("lora-usb-" + std::string(autoconf_product) + ".yaml");
|
||||
}
|
||||
|
||||
// Don't try to automatically find config for a device with RAK eeprom.
|
||||
if (found_rak_eeprom) {
|
||||
std::cerr << "autoconf: Found unknown RAK product " << autoconf_product << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (access((portduino_config.available_directory + product_config).c_str(), R_OK) != 0) {
|
||||
std::cerr << "autoconf: Unable to find config for " << autoconf_product << "(tried " << product_config << ")"
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (loadConfig((portduino_config.available_directory + product_config).c_str())) {
|
||||
std::cout << "autoconf: Using " << product_config << " as config file for " << autoconf_product << std::endl;
|
||||
} else {
|
||||
|
||||
@@ -144,4 +144,6 @@ class Power : private concurrency::OSThread
|
||||
#endif
|
||||
};
|
||||
|
||||
void battery_adcEnable();
|
||||
|
||||
extern Power *power;
|
||||
|
||||
@@ -210,6 +210,16 @@ No longer populated on PCB
|
||||
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
|
||||
#define ADC_MULTIPLIER (4.916F)
|
||||
|
||||
// rf52840 AIN2 = Pin 4
|
||||
#define BATTERY_LPCOMP_INPUT NRF_LPCOMP_INPUT_2
|
||||
|
||||
// We have AIN2 with a VBAT divider so AIN2 = VBAT * (100/490)
|
||||
// We have the device going deep sleep under 3.1V, which is AIN2 = 0.63V
|
||||
// So we can wake up when VBAT>=VDD is restored to 3.3V, where AIN2 = 0.67V
|
||||
// Ratio 0.67/3.3 = 0.20, so we can pick a bit higher, 2/8 VDD, which means
|
||||
// VBAT=4.04V
|
||||
#define BATTERY_LPCOMP_THRESHOLD NRF_LPCOMP_REF_SUPPLY_2_8
|
||||
|
||||
#define HAS_RTC 0
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -267,6 +267,20 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
|
||||
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
|
||||
#define ADC_MULTIPLIER 1.73
|
||||
|
||||
// RAK4630 AIN0 = nrf52840 AIN3 = Pin 5
|
||||
#define BATTERY_LPCOMP_INPUT NRF_LPCOMP_INPUT_3
|
||||
|
||||
// We have AIN3 with a VBAT divider so AIN3 = VBAT * (1.5/2.5)
|
||||
// We have the device going deep sleep under 3.1V, which is AIN3 = 1.86V
|
||||
// So we can wake up when VBAT>=VDD is restored to 3.3V, where AIN3 = 1.98V
|
||||
// 1.98/3.3 = 6/10, but that's close to the VBAT divider, so we
|
||||
// pick 6/8VDD, which means VBAT=4.1V.
|
||||
// Reference:
|
||||
// VDD=3.3V AIN3=5/8*VDD=2.06V VBAT=1.66*AIN3=3.41V
|
||||
// VDD=3.3V AIN3=11/16*VDD=2.26V VBAT=1.66*AIN3=3.76V
|
||||
// VDD=3.3V AIN3=6/8*VDD=2.47V VBAT=1.66*AIN3=4.1V
|
||||
#define BATTERY_LPCOMP_THRESHOLD NRF_LPCOMP_REF_SUPPLY_11_16
|
||||
|
||||
#define HAS_RTC 1
|
||||
|
||||
#define HAS_ETHERNET 1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[VERSION]
|
||||
major = 2
|
||||
minor = 7
|
||||
build = 14
|
||||
build = 15
|
||||
|
||||
Reference in New Issue
Block a user