diff --git a/boards/wiscore_rak4631.json b/boards/wiscore_rak4631.json
index 6dec3f7cb4..c783f33a69 100644
--- a/boards/wiscore_rak4631.json
+++ b/boards/wiscore_rak4631.json
@@ -35,7 +35,7 @@
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52840-mdk-rs"
},
- "frameworks": ["arduino"],
+ "frameworks": ["arduino", "freertos"],
"name": "WisCore RAK4631 Board",
"upload": {
"maximum_ram_size": 248832,
diff --git a/pyocd.yaml b/pyocd.yaml
new file mode 100644
index 0000000000..84bd9336b9
--- /dev/null
+++ b/pyocd.yaml
@@ -0,0 +1,7 @@
+# This is a config file to control pyocd ICE debugger probe options (only used for NRF52 targets with hardware debugging connections)
+# for more info see FIXMEURL
+
+# console or telnet
+semihost_console_type: telnet
+enable_semihosting: True
+telnet_port: 4444
diff --git a/src/DebugConfiguration.h b/src/DebugConfiguration.h
index cd8301274c..ebe9da8d44 100644
--- a/src/DebugConfiguration.h
+++ b/src/DebugConfiguration.h
@@ -26,6 +26,14 @@
#include "SerialConsole.h"
+// If defined we will include support for ARM ICE "semihosting" for a virtual
+// console over the JTAG port (to replace the normal serial port)
+// Note: Normally this flag is passed into the gcc commandline by platformio.ini.
+// for an example see env:rak4631_dap.
+// #ifndef USE_SEMIHOSTING
+// #define USE_SEMIHOSTING
+// #endif
+
#define DEBUG_PORT (*console) // Serial debug port
#ifdef USE_SEGGER
diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp
index 60168cffcf..eb92d824e7 100644
--- a/src/graphics/Screen.cpp
+++ b/src/graphics/Screen.cpp
@@ -43,6 +43,7 @@ along with this program. If not, see .
#include "meshUtils.h"
#include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h"
+#include "modules/WaypointModule.h"
#include "sleep.h"
#include "target_specific.h"
@@ -59,6 +60,9 @@ along with this program. If not, see .
#include "platform/portduino/PortduinoGlue.h"
#endif
+/// Convert an integer GPS coords to a floating point
+#define DegD(i) (i * 1e-7)
+
using namespace meshtastic; /** @todo remove */
namespace graphics
@@ -446,6 +450,37 @@ static bool shouldDrawMessage(const meshtastic_MeshPacket *packet)
return packet->from != 0 && !moduleConfig.store_forward.enabled;
}
+// Determine whether the waypoint frame should be drawn (waypoint deleted? expired?)
+static bool shouldDrawWaypoint(const meshtastic_MeshPacket *packet)
+{
+#if !MESHTASTIC_EXCLUDE_WAYPOINT
+ // If no waypoint to show
+ if (!devicestate.has_rx_waypoint)
+ return false;
+
+ // Decode the message, to find the expiration time (is waypoint still valid)
+ // This handles "deletion" as well as expiration
+ meshtastic_Waypoint wp;
+ memset(&wp, 0, sizeof(wp));
+ if (pb_decode_from_bytes(packet->decoded.payload.bytes, packet->decoded.payload.size, &meshtastic_Waypoint_msg, &wp)) {
+ // Valid waypoint
+ if (wp.expire > getTime())
+ return devicestate.has_rx_waypoint = true;
+
+ // Expired, or deleted
+ else
+ return devicestate.has_rx_waypoint = false;
+ }
+
+ // If decoding failed
+ LOG_ERROR("Failed to decode waypoint\n");
+ devicestate.has_rx_waypoint = false;
+ return false;
+#else
+ return false;
+#endif
+}
+
// Draw power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage.
static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus)
{
@@ -1091,43 +1126,6 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
#endif
}
-/// Draw the last waypoint we received
-static void drawWaypointFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- static char tempBuf[237];
-
- meshtastic_MeshPacket &mp = devicestate.rx_waypoint;
- meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
-
- display->setTextAlignment(TEXT_ALIGN_LEFT);
- display->setFont(FONT_SMALL);
- if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
- display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
- display->setColor(BLACK);
- }
-
- uint32_t seconds = sinceReceived(&mp);
- uint32_t minutes = seconds / 60;
- uint32_t hours = minutes / 60;
- uint32_t days = hours / 24;
-
- if (config.display.heading_bold) {
- display->drawStringf(1 + x, 0 + y, tempBuf, "%s ago from %s",
- screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
- (node && node->has_user) ? node->user.short_name : "???");
- }
- display->drawStringf(0 + x, 0 + y, tempBuf, "%s ago from %s", screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
- (node && node->has_user) ? node->user.short_name : "???");
-
- display->setColor(WHITE);
- meshtastic_Waypoint scratch;
- memset(&scratch, 0, sizeof(scratch));
- if (pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) {
- snprintf(tempBuf, sizeof(tempBuf), "Received waypoint: %s", scratch.name);
- display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf);
- }
-}
-
/// Draw a series of fields in a column, wrapping to multiple columns if needed
static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
{
@@ -1453,8 +1451,35 @@ static void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t com
drawLine(display, N1, N4);
}
-/// Convert an integer GPS coords to a floating point
-#define DegD(i) (i * 1e-7)
+// Get a string representation of the time passed since something happened
+static void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
+{
+ // Use an absolute timestamp in some cases.
+ // Particularly useful with E-Ink displays. Static UI, fewer refreshes.
+ uint8_t timestampHours, timestampMinutes;
+ int32_t daysAgo;
+ bool useTimestamp = deltaToTimestamp(agoSecs, ×tampHours, ×tampMinutes, &daysAgo);
+
+ if (agoSecs < 120) // last 2 mins?
+ snprintf(timeStr, maxLength, "%u seconds ago", agoSecs);
+ // -- if suitable for timestamp --
+ else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes
+ snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / SECONDS_IN_MINUTE);
+ else if (useTimestamp && daysAgo == 0) // Today
+ snprintf(timeStr, maxLength, "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes);
+ else if (useTimestamp && daysAgo == 1) // Yesterday
+ snprintf(timeStr, maxLength, "Seen yesterday");
+ else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method)
+ snprintf(timeStr, maxLength, "%li days ago", (long)daysAgo);
+ // -- if using time delta instead --
+ else if (agoSecs < 120 * 60) // last 2 hrs
+ snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / 60);
+ // Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data.
+ else if ((agoSecs / 60 / 60) < (hours_in_month * 6))
+ snprintf(timeStr, maxLength, "%u hours ago", agoSecs / 60 / 60);
+ else
+ snprintf(timeStr, maxLength, "unknown age");
+}
static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
@@ -1494,34 +1519,8 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100));
}
- uint32_t agoSecs = sinceLastSeen(node);
static char lastStr[20];
-
- // Use an absolute timestamp in some cases.
- // Particularly useful with E-Ink displays. Static UI, fewer refreshes.
- uint8_t timestampHours, timestampMinutes;
- int32_t daysAgo;
- bool useTimestamp = deltaToTimestamp(agoSecs, ×tampHours, ×tampMinutes, &daysAgo);
-
- if (agoSecs < 120) // last 2 mins?
- snprintf(lastStr, sizeof(lastStr), "%u seconds ago", agoSecs);
- // -- if suitable for timestamp --
- else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes
- snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / SECONDS_IN_MINUTE);
- else if (useTimestamp && daysAgo == 0) // Today
- snprintf(lastStr, sizeof(lastStr), "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes);
- else if (useTimestamp && daysAgo == 1) // Yesterday
- snprintf(lastStr, sizeof(lastStr), "Seen yesterday");
- else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method)
- snprintf(lastStr, sizeof(lastStr), "%li days ago", (long)daysAgo);
- // -- if using time delta instead --
- else if (agoSecs < 120 * 60) // last 2 hrs
- snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / 60);
- // Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data.
- else if ((agoSecs / 60 / 60) < (hours_in_month * 6))
- snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
- else
- snprintf(lastStr, sizeof(lastStr), "unknown age");
+ getTimeAgoStr(sinceLastSeen(node), lastStr, sizeof(lastStr));
static char distStr[20];
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
@@ -1596,6 +1595,112 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
drawColumns(display, x, y, fields);
}
+/// Draw the last waypoint we received
+static void drawWaypointFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ // Prepare to draw
+ display->setFont(FONT_SMALL);
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+
+ // Handle inverted display
+ // Unsure of expected behavior: for now, copy drawNodeInfo
+ if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED)
+ display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
+
+ // Decode the waypoint
+ meshtastic_MeshPacket &mp = devicestate.rx_waypoint;
+ meshtastic_Waypoint wp;
+ memset(&wp, 0, sizeof(wp));
+ if (!pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, &meshtastic_Waypoint_msg, &wp)) {
+ // This *should* be caught by shouldDrawWaypoint, but we'll short-circuit here just in case
+ display->drawStringMaxWidth(0 + x, 0 + y, x + display->getWidth(), "Couldn't decode waypoint");
+ devicestate.has_rx_waypoint = false;
+ return;
+ }
+
+ // Get timestamp info. Will pass as a field to drawColumns
+ static char lastStr[20];
+ getTimeAgoStr(sinceReceived(&mp), lastStr, sizeof(lastStr));
+
+ // Will contain distance information, passed as a field to drawColumns
+ static char distStr[20];
+
+ // Get our node, to use our own position
+ meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
+
+ // Text fields to draw (left of compass)
+ // Last element must be NULL. This signals the end of the char*[] to drawColumns
+ const char *fields[] = {"Waypoint", lastStr, wp.name, distStr, NULL};
+
+ // Co-ordinates for the center of the compass/circle
+ int16_t compassX = 0, compassY = 0;
+ if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
+ compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
+ compassY = y + SCREEN_HEIGHT / 2;
+ } else {
+ compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
+ compassY = y + FONT_HEIGHT_SMALL + (SCREEN_HEIGHT - FONT_HEIGHT_SMALL) / 2;
+ }
+
+ // If our node has a position:
+ if (ourNode && (hasValidPosition(ourNode) || screen->hasHeading())) {
+ const meshtastic_PositionLite &op = ourNode->position;
+ float myHeading;
+ if (screen->hasHeading())
+ myHeading = (screen->getHeading()) * PI / 180; // gotta convert compass degrees to Radians
+ else
+ myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
+ drawCompassNorth(display, compassX, compassY, myHeading);
+
+ // Distance to Waypoint
+ float d = GeoCoord::latLongToMeter(DegD(wp.latitude_i), DegD(wp.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
+ if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
+ if (d < (2 * MILES_TO_FEET))
+ snprintf(distStr, sizeof(distStr), "%.0f ft", d * METERS_TO_FEET);
+ else
+ snprintf(distStr, sizeof(distStr), "%.1f mi", d * METERS_TO_FEET / MILES_TO_FEET);
+ } else {
+ if (d < 2000)
+ snprintf(distStr, sizeof(distStr), "%.0f m", d);
+ else
+ snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
+ }
+
+ // Compass bearing to waypoint
+ float bearingToOther =
+ GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(wp.latitude_i), DegD(wp.longitude_i));
+ // If the top of the compass is a static north then bearingToOther can be drawn on the compass directly
+ // If the top of the compass is not a static north we need adjust bearingToOther based on heading
+ if (!config.display.compass_north_top)
+ bearingToOther -= myHeading;
+ drawNodeHeading(display, compassX, compassY, bearingToOther);
+ }
+
+ // If our node doesn't have position
+ else {
+ // ? in the compass
+ display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?");
+
+ // ? in the distance field
+ if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL)
+ strncpy(distStr, "? mi", sizeof(distStr));
+ else
+ strncpy(distStr, "? km", sizeof(distStr));
+ }
+
+ // Undo color-inversion, if set prior to drawing header
+ // Unsure of expected behavior? For now: copy drawNodeInfo
+ if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
+ display->setColor(BLACK);
+ }
+
+ // Draw compass circle
+ display->drawCircle(compassX, compassY, getCompassDiam(display) / 2);
+
+ // Must be after distStr is populated
+ drawColumns(display, x, y, fields);
+}
+
Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_OledType screenType, OLEDDISPLAY_GEOMETRY geometry)
: concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32)
{
@@ -1806,6 +1911,8 @@ void Screen::setup()
textMessageObserver.observe(textMessageModule);
if (inputBroker)
inputObserver.observe(inputBroker);
+ if (waypointModule)
+ waypointObserver.observe(waypointModule);
// Modules can notify screen about refresh
MeshModule::observeUIEvents(&uiFrameEventObserver);
@@ -2133,8 +2240,9 @@ void Screen::setFrames()
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
normalFrames[numframes++] = drawTextMessageFrame;
}
- // If we have a waypoint - show it next, unless it's a phone message and we aren't using any special modules
- if (devicestate.has_rx_waypoint && shouldDrawMessage(&devicestate.rx_waypoint)) {
+
+ // If we have a waypoint (not expired, not deleted)
+ if (devicestate.has_rx_waypoint && shouldDrawWaypoint(&devicestate.rx_waypoint)) {
normalFrames[numframes++] = drawWaypointFrame;
}
@@ -2736,6 +2844,13 @@ int Screen::handleInputEvent(const InputEvent *event)
return 0;
}
+int Screen::handleWaypoint(const meshtastic_MeshPacket *arg)
+{
+ // TODO: move to appropriate frame when redrawing
+ setFrames();
+ return 0;
+}
+
} // namespace graphics
#else
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h
index f4d7197152..b1bbffc3b2 100644
--- a/src/graphics/Screen.h
+++ b/src/graphics/Screen.h
@@ -126,6 +126,8 @@ class Screen : public concurrency::OSThread
CallbackObserver(this, &Screen::handleStatusUpdate);
CallbackObserver textMessageObserver =
CallbackObserver(this, &Screen::handleTextMessage);
+ CallbackObserver waypointObserver =
+ CallbackObserver(this, &Screen::handleWaypoint);
CallbackObserver uiFrameEventObserver =
CallbackObserver(this, &Screen::handleUIFrameEvent);
CallbackObserver inputObserver =
@@ -336,6 +338,7 @@ class Screen : public concurrency::OSThread
int handleTextMessage(const meshtastic_MeshPacket *arg);
int handleUIFrameEvent(const UIFrameEvent *arg);
int handleInputEvent(const InputEvent *arg);
+ int handleWaypoint(const meshtastic_MeshPacket *arg);
/// Used to force (super slow) eink displays to draw critical frames
void forceDisplay(bool forceUiUpdate = false);
diff --git a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
index ea2cb4ea8c..edd29682e0 100644
--- a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
@@ -16,8 +16,7 @@ int32_t INA3221Sensor::runOnce()
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
if (!status) {
- ina3221.setAddr(INA3221_ADDR42_SDA); // i2c address 0x42
- ina3221.begin();
+ ina3221.begin(nodeTelemetrySensorsMap[sensorType].second);
ina3221.setShuntRes(100, 100, 100); // 0.1 Ohm shunt resistors
status = true;
} else {
diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp
index b70420c8a0..48f945b0a8 100644
--- a/src/nimble/NimbleBluetooth.cpp
+++ b/src/nimble/NimbleBluetooth.cpp
@@ -180,7 +180,8 @@ void NimbleBluetooth::setupService()
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE);
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ);
fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
- logRadioCharacteristic = bleService->createCharacteristic(LOGRADIO_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
+ logRadioCharacteristic =
+ bleService->createCharacteristic(LOGRADIO_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ, 512U);
} else {
ToRadioCharacteristic = bleService->createCharacteristic(
TORADIO_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_AUTHEN | NIMBLE_PROPERTY::WRITE_ENC);
@@ -189,9 +190,10 @@ void NimbleBluetooth::setupService()
fromNumCharacteristic =
bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
- logRadioCharacteristic =
- bleService->createCharacteristic(LOGRADIO_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ |
- NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
+ logRadioCharacteristic = bleService->createCharacteristic(
+ LOGRADIO_UUID,
+ NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC, 512U);
+ logRadioCharacteristic->setValue("Init");
}
bluetoothPhoneAPI = new BluetoothPhoneAPI();
@@ -245,7 +247,7 @@ void NimbleBluetooth::sendLog(const char *logMessage)
if (!bleServer || !isConnected() || strlen(logMessage) > 512) {
return;
}
- logRadioCharacteristic->notify(reinterpret_cast(logMessage), strlen(logMessage));
+ logRadioCharacteristic->notify(reinterpret_cast(logMessage), strlen(logMessage), true);
}
void clearNVS()
diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp
index 1f2c6867d5..86575bda6f 100644
--- a/src/platform/nrf52/main-nrf52.cpp
+++ b/src/platform/nrf52/main-nrf52.cpp
@@ -149,13 +149,43 @@ void nrf52Loop()
checkSDEvents();
}
+#ifdef USE_SEMIHOSTING
+#include
+
+/**
+ * Note: this variable is in BSS and therfore false by default. But the gdbinit
+ * file will be installing a temporary breakpoint that changes wantSemihost to true.
+ */
+bool wantSemihost;
+
+/**
+ * Turn on semihosting if the ICE debugger wants it.
+ */
+void nrf52InitSemiHosting()
+{
+ if (wantSemihost) {
+ static SemihostingStream semiStream;
+ // We must dynamically alloc because the constructor does semihost operations which
+ // would crash any load not talking to a debugger
+ semiStream.open();
+ semiStream.println("Semihosting starts!");
+ // Redirect our serial output to instead go via the ICE port
+ console->setDestination(&semiStream);
+ }
+}
+#endif
+
void nrf52Setup()
{
- auto why = NRF_POWER->RESETREAS;
+ uint32_t why = NRF_POWER->RESETREAS;
// per
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fpower.html
LOG_DEBUG("Reset reason: 0x%x\n", why);
+#ifdef USE_SEMIHOSTING
+ nrf52InitSemiHosting();
+#endif
+
// Per
// https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/monitor-mode-debugging-with-j-link-and-gdbeclipse
// This is the recommended setting for Monitor Mode Debugging
diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini
index ef3e5a6458..beffa7d3df 100644
--- a/variants/rak4631/platformio.ini
+++ b/variants/rak4631/platformio.ini
@@ -20,6 +20,7 @@ lib_deps =
debug_tool = jlink
+
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds
;upload_protocol = jlink
@@ -27,26 +28,92 @@ debug_tool = jlink
; Allows programming and debug via the RAK NanoDAP as the default debugger tool for the RAK4631 (it is only $10!)
; programming time is about the same as the bootloader version.
; For information on this see the meshtastic developers documentation for "Development on the NRF52"
-[env:rak4631_dap]
+[env:rak4631_dbg]
extends = env:rak4631
board_level = extra
-; pyocd pack --i nrf52840
+
+; if the builtin version of openocd has a buggy version of semihosting, so use the external version
+; platform_packages = platformio/tool-openocd@^3.1200.0
+
+build_flags =
+ ${env:rak4631.build_flags}
+ -D USE_SEMIHOSTING
+
+lib_deps =
+ ${env:rak4631.lib_deps}
+ https://github.com/geeksville/Armduino-Semihosting.git#35b538fdf208c3530c1434cd099a08e486672ee4
+
+; NOTE: the pyocd support for semihosting is buggy. So I switched to using the builtin platformio support for the stlink adapter which worked much better.
+; However the built in openocd version in platformio has buggy support for TCP to semihosting.
+;
+; So I'm now trying the external openocd - but the openocd scripts for nrf52.cfg assume you are using a DAP adapter not an STLINK adapter.
+; In theory I could change those scripts. But for now I'm trying going back to a DAP adapter but with the external openocd.
+
+upload_protocol = stlink
; eventually use platformio/tool-pyocd@^2.3600.0 instad
-upload_protocol = custom
-upload_command = pyocd flash -t nrf52840 $UPLOADERFLAGS $SOURCE
+;upload_protocol = custom
+;upload_command = pyocd flash -t nrf52840 $UPLOADERFLAGS $SOURCE
+
+; We want the initial breakpoint at setup() instead of main(). Also we want to enable semihosting at that point so instead of
+; debug_init_break = tbreak setup
+; we just turn off the platformio tbreak and do it in .gdbinit (where we have more flexibility for scripting)
+; also we use a permanent breakpoint so it gets reused each time we restart the debugging session?
+debug_init_break = tbreak setup
+
+; Note: add "monitor arm semihosting_redirect tcp 4444 all" if you want the stdout from the device to go to that port number instead
+; (for use by meshtastic command line)
+; monitor arm semihosting disable
+; monitor debug_level 3
+;
+; IMPORTANT: fileio must be disabled before using port 5555 - openocd ver 0.12 has a bug where if enabled it never properly parses the special :tt name
+; for stdio access.
+; monitor arm semihosting_redirect tcp 5555 stdio
+
+; Also note: it is _impossible_ to do non blocking reads on the semihost console port (an oversight when ARM specified the semihost API).
+; So we'll neve be able to general purpose bi-directional communication with the device over semihosting.
+debug_extra_cmds =
+ echo Running .gdbinit script
+ monitor arm semihosting enable
+ monitor arm semihosting_fileio enable
+ monitor arm semihosting_redirect disable
+ commands 1
+ echo Breakpoint at setup() has semihosting console, connect to it with "telnet localhost 5555"
+ set wantSemihost = true
+ end
+
; Only reprogram the board if the code has changed
debug_load_mode = modified
;debug_load_mode = manual
-debug_tool = custom
+debug_tool = stlink
+;debug_tool = custom
+; debug_server =
+; openocd
+; -f
+; /usr/local/share/openocd/scripts/interface/stlink.cfg
+; -f
+; /usr/local/share/openocd/scripts/target/nrf52.cfg
+; $PLATFORMIO_CORE_DIR/packages/tool-openocd/openocd/scripts/interface/cmsis-dap.cfg
+
+; Allows programming and debug via the RAK NanoDAP as the default debugger tool for the RAK4631 (it is only $10!)
+; programming time is about the same as the bootloader version.
+; For information on this see the meshtastic developers documentation for "Development on the NRF52"
; We manually pass in the elf file so that pyocd can reverse engineer FreeRTOS data (running threads, etc...)
-debug_server =
- pyocd
- gdbserver
- -t
- nrf52840
- --elf
- ${platformio.build_dir}/${this.__env__}/firmware.elf
+;debug_server =
+; pyocd
+; gdbserver
+; -j
+; ${platformio.workspace_dir}/..
+; -t
+; nrf52840
+; --semihosting
+; --elf
+; ${platformio.build_dir}/${this.__env__}/firmware.elf
+
+; If you want to debug the semihosting support you can turn on extra logging in pyocd with
+; -L
+; pyocd.debug.semihost.trace=debug
+
; The following is not needed because it automatically tries do this
;debug_server_ready_pattern = -.*GDB server started on port \d+.*
;debug_port = localhost:3333
\ No newline at end of file
diff --git a/version.properties b/version.properties
index 268987418b..1cb93ac2bf 100644
--- a/version.properties
+++ b/version.properties
@@ -1,4 +1,4 @@
[VERSION]
major = 2
minor = 3
-build = 14
+build = 15