An example project for the Heltec Turtle LoRa board (STM32L4 and SX1276 chips). The projects is only supported for the Nucleo-L432KC board platform in the mbed online and offline compiler environment. Visit www.radioshuttle.de (choose Turtle board) for instructions. Note that most source files and libraries are open source, however some files especially the RadioShuttle core protocol is copyrighted work. Check header for details.
Dependencies: mbed BufferedSerial SX1276GenericLib OLED_SSD1306 HELIOS_Si7021 NVProperty RadioShuttle-STM32L4 USBDeviceHT
Revision 37:77fa81e4ad79, committed 2019-02-13
- Comitter:
- Helmut Tschemernjak
- Date:
- Wed Feb 13 14:10:17 2019 +0100
- Parent:
- 36:138234ddcad2
- Child:
- 38:1f3792d6f9ec
- Commit message:
- Added a local RadioTestSample.cpp
Moved commands into RunCommands function
Added RunStartup function to provide proper startup messages
Changed in this revision
--- a/PinMap.h Wed Feb 13 14:08:15 2019 +0100 +++ b/PinMap.h Wed Feb 13 14:10:17 2019 +0100 @@ -6,10 +6,13 @@ #define RS_MAJOR 3 #define RS_MINOR 1 +#define MAJOR_VERSION 1 +#define MINOR_VERSION 1 #ifdef TARGET_STM32L432KC #define HELTECL432_REV1 #define FEATURE_LORA + #define FEATURE_RADIOTESTSAMPLE #define FEATURE_LORA_PING_PONG #define FEATURE_USBSERIAL #define FEATURE_NVPROPERTY
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RadioTestSample.cpp Wed Feb 13 14:10:17 2019 +0100
@@ -0,0 +1,303 @@
+/*
+ * The file is Licensed under the Apache License, Version 2.0
+ * (c) 2017 Helmut Tschemernjak
+ * 30826 Garbsen (Hannover) Germany
+ */
+
+#include "mbed.h"
+#include "PinMap.h"
+
+#if defined(FEATURE_LORA) && defined(FEATURE_RADIOTESTSAMPLE)
+
+#include "sx1276-mbed-hal.h"
+#include "RadioShuttle.h"
+#include "RadioStatus.h"
+#include "RadioSecurity.h"
+#include "main.h"
+#ifdef FEATURE_NVPROPERTY
+#include <NVPropertyProviderInterface.h>
+#include "NVProperty.h"
+#endif
+
+
+
+#define CHECK_ERROR_RET(func, err) { \
+ if (err) { \
+ dprintf("Error in %s: %s", func, rs->StrError(err)); \
+ return err; \
+ } \
+}
+
+// #define RADIO_SERVER 1
+
+
+bool usePassword = false; // password the can used indepenend of AES
+bool server; // automatically being set if radioTypeMode RadioShuttle::RS_Station_Basic
+bool useAES = false; // AES needs the usePassword option on
+const char *appPassword; // the AES password
+
+static const int myTempSensorApp = 0x0001; // Must be unique world wide.
+#ifdef RADIO_SERVER
+int myDeviceID = 1;
+int remoteDeviceID = 14;
+uint32_t myCode = 0;
+RadioShuttle::RadioType radioTypeMode = RadioShuttle::RS_Station_Basic; // 1 = RS_Node_Offline, 3 = RS_Node_Online, 4 = RS_Station_Basic
+#else
+int myDeviceID = 14;
+int remoteDeviceID = 1;
+uint32_t myCode = 0;
+RadioShuttle::RadioType radioTypeMode = RadioShuttle::RS_Node_Offline; // 1 = RS_Node_Offline, 3 = RS_Node_Online, 4 = RS_Station_Basic
+#endif
+
+
+/*
+ * For details review: SX1276GenericLib/sx1276/sx1276.h
+ * Supported spreading factors SF 7,8, 9, 10, 11, (12 does not work well)
+ * Working frequencies using the 125000 bandwidth which leaves
+ * sufficient distance to the neighbour channel
+ * EU: 868.1, 868.3, 868.5 (Default LoRaWAN EU channels)
+ * EU: 865.1, 865.3, 865.5, 865.7, 865.9 (additional channels)
+ * EU: 866.1, 866.3, 866.5, 866.7, 866.9 (additional channels)
+ * EU: 867.1, 867.3, 867.5, 867.7, 867.9 (additional channels)
+ * Utilisation of these channels should not exceed 1% per hour per node
+ * Bandwidth changes other than 125k requires different channels distances
+ */
+RadioShuttle::RadioProfile myProfile[] = {
+ /*
+ * Our default profile
+ * frequency, bandwidth, TX power, spreading factor, frequency-offset
+ */
+ { 868100000, 125000, 14, 7, 0 },
+ { 0, 0, 0, 0, 0 },
+};
+
+
+struct sensor {
+ uint8_t version;
+ uint8_t padding;
+ uint16_t pm25;
+ uint16_t pm10;
+ uint16_t id;
+} PMAppData;
+
+
+void TempSensorRecvHandler(int AppID, RadioShuttle::devid_t stationID, int msgID, int status, void *buffer, int length)
+{
+ switch(status) {
+ case RadioShuttle::MS_SentCompleted: // A SendMsg has been sent.
+ dprintf("MSG_SentCompleted: id=%d %d bytes", msgID, length);
+ break;
+ case RadioShuttle::MS_SentCompletedConfirmed:// A SendMsg has been sent and confirmed
+ dprintf("MSG_SentCompletedConfirmed: id=%d %d bytes", msgID, length);
+ break;
+ case RadioShuttle::MS_SentTimeout: // A timeout occurred, number of retries exceeded
+ dprintf("MSG_SentTimeout ID: %d", msgID);
+ break;
+
+ case RadioShuttle::MS_RecvData: // a simple input message
+ dprintf("MSG_RecvData ID: %d, len=%d", msgID, length);
+ // dump("MSG_RecvData", buffer, length);
+ break;
+ case RadioShuttle::MS_RecvDataConfirmed: // received a confirmed message
+ dprintf("MSG_RecvDataConfirmed ID: %d, len=%d", msgID, length);
+ // dump("MSG_RecvDataConfirmed", buffer, length);
+ break;
+ case RadioShuttle::MS_NoStationFound:
+ dprintf("MSG_NoStationFound");
+ break;
+ case RadioShuttle::MS_NoStationSupportsApp:
+ dprintf("MSG_NoStationSupportsApp");
+ break;
+ case RadioShuttle::MS_AuthenicationRequired: // the password does not match.
+ dprintf("MSG_AuthenicationRequired");
+ break;
+
+ case RadioShuttle::MS_StationConnected: // a confirmation that the connection was accepted
+ dprintf("MSG_StationConnected");
+ break;
+ case RadioShuttle::MS_StationDisconnected: // a confirmation that the disconnect was accepted
+ dprintf("MSG_StationDisconnected");
+ break;
+ default:
+ break;
+ }
+}
+
+
+Radio *radio;
+RadioShuttle *rs;
+RadioStatusInterface *statusIntf;
+RadioSecurityInterface *securityIntf;
+
+int InitRadio()
+{
+ Radio *radio;
+ RSCode err;
+
+#ifdef FEATURE_NVPROPERTY
+ NVProperty prop;
+ int value;
+
+ myDeviceID = prop.GetProperty(prop.LORA_DEVICE_ID, 0);
+ myCode = prop.GetProperty(prop.LORA_CODE_ID, 0);
+ if ((value = prop.GetProperty(prop.LORA_RADIO_TYPE, 0)) != 0)
+ radioTypeMode = (RadioShuttle::RadioType)value;
+
+ if (myDeviceID == 0 || myCode == 0 || radioTypeMode == 0) {
+ dprintf("LORA_DEVICE_ID or LORA_CODE_ID or LORA_RADIO_TYPE not set, use PropertyEditor to set this!");
+ return -1;
+ }
+ /*
+ * Here are optional properties for custom settings
+ */
+ if ((value = prop.GetProperty(prop.LORA_REMOTE_ID, 0)) != 0)
+ remoteDeviceID = value;
+ if ((value = prop.GetProperty(prop.LORA_FREQUENCY, 0)) != 0)
+ myProfile[0].Frequency = value;
+ if ((value = prop.GetProperty(prop.LORA_BANDWIDTH, 0)) != 0)
+ myProfile[0].Bandwidth = value;
+ if ((value = prop.GetProperty(prop.LORA_SPREADING_FACTOR, 0)) != 0)
+ myProfile[0].SpreadingFaktor = value;
+ if ((value = prop.GetProperty(prop.LORA_TXPOWER, 0)) != 0)
+ myProfile[0].TXPower = value;
+ if ((value = prop.GetProperty(prop.LORA_FREQUENCY_OFFSET, 0)) != 0)
+ myProfile[0].FrequencyOffset = value;
+ appPassword = prop.GetProperty(prop.LORA_APP_PWD, (const char *)NULL);
+#endif
+
+ if (radioTypeMode >= RadioShuttle::RS_Station_Basic)
+ server = true;
+
+#ifdef TARGET_DISCO_L072CZ_LRWAN1
+ radio = new SX1276Generic(NULL, MURATA_SX1276,
+ LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET,
+ LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5,
+ LORA_ANT_RX, LORA_ANT_TX, LORA_ANT_BOOST, LORA_TCXO);
+#elif defined(HELTECL432_REV1)
+ radio = new SX1276Generic(NULL, HELTEC_L4_1276,
+ LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET,
+ LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5,
+ LORA_ANT_PWR);
+#else // RFM95
+ radio = new SX1276Generic(NULL, RFM95_SX1276,
+ LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET,
+ LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5);
+#endif
+
+
+ statusIntf = new MyRadioStatus();
+ securityIntf = new RadioSecurity();
+
+ rs = new RadioShuttle("MyRadioShuttle");
+
+ rs->EnablePacketTrace(RadioShuttle::DEV_ID_ANY, true, true);
+
+ err = rs->AddLicense(myDeviceID, myCode);
+ CHECK_ERROR_RET("AddLicense", err);
+
+ err = rs->AddRadio(radio, MODEM_LORA, myProfile);
+ CHECK_ERROR_RET("AddRadio", err);
+ dprintf("Radio: %.1f MHz, SF%d, %.f kHz", (float)myProfile[0].Frequency/1000000.0, myProfile[0].SpreadingFaktor, (float)myProfile[0].Bandwidth/1000.0);
+
+ rs->AddRadioStatus(statusIntf);
+ CHECK_ERROR_RET("AddRadioStatus", err);
+
+ rs->AddRadioSecurity(securityIntf);
+ CHECK_ERROR_RET("AddRadioSecurity", err);
+
+ /*
+ * The password parameter can be NULL if no password is required
+ */
+ err = rs->RegisterApplication(myTempSensorApp, &TempSensorRecvHandler, (void *)appPassword);
+ CHECK_ERROR_RET("RegisterApplication", err);
+
+ if (server) {
+ // usually RadioShuttle::RS_Station_Basic, set via properties
+ err = rs->Startup(radioTypeMode);
+ dprintf("Startup as a Server: %s ID=%d", rs->GetRadioName(rs->GetRadioType()), myDeviceID);
+ } else {
+ // usually RadioShuttle::RS_Node_Online or RadioShuttle, set via properties
+ err = rs->Startup(radioTypeMode);
+ dprintf("Startup as a Node: %s ID=%d", rs->GetRadioName(rs->GetRadioType()), myDeviceID);
+ if (!err && rs->AppRequiresAuthentication(myTempSensorApp) == RS_PasswordSet) {
+ err = rs->Connect(myTempSensorApp, remoteDeviceID);
+ }
+ }
+ CHECK_ERROR_RET("Startup", err);
+ return 0;
+}
+
+void DeInitRadio()
+{
+ if (securityIntf) {
+ delete securityIntf;
+ securityIntf = NULL;
+ }
+ if (statusIntf) {
+ delete statusIntf;
+ statusIntf = NULL;
+ }
+ if (rs) {
+ delete rs;
+ rs = NULL;
+ }
+ if (radio) {
+ delete radio;
+ radio = NULL;
+ }
+}
+
+
+int RadioUpdate(bool keyPressed)
+{
+ if (!rs)
+ return 0;
+
+ if (keyPressed) {
+ int flags = 0;
+ flags |= RadioShuttle::MF_NeedsConfirm; // optional
+ if (usePassword && useAES)
+ flags |= RadioShuttle::MF_Encrypted;
+ if (server) {
+ static char msg[] = "The server feels very good today";
+ rs->SendMsg(myTempSensorApp, msg, sizeof(msg), flags, remoteDeviceID);
+ } else {
+ static char msg[] = "Hello, the temperature is 26 celsius";
+ rs->SendMsg(myTempSensorApp, msg, sizeof(msg), flags, remoteDeviceID);
+ }
+ }
+ rs->RunShuttle();
+ return 0;
+}
+
+bool RadioISIdle()
+{
+ if (!rs)
+ return true;
+ return rs->Idle();
+}
+
+void InitLoRaChipWithShutdown()
+{
+#ifdef LORA_CS
+ if (LORA_CS == NC)
+ return;
+#ifdef HELTECL432_REV1
+ Radio *radio = new SX1276Generic(NULL, HELTEC_L4_1276,
+ LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET,
+ LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5, LORA_ANT_PWR);
+#else
+ Radio *radio = new SX1276Generic(NULL, RFM95_SX1276,
+ LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET,
+ LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5);
+#endif
+
+ RadioEvents_t radioEvents;
+ memset(&radioEvents, 0, sizeof(radioEvents));
+ if (radio->Init(&radioEvents)) {
+ radio->Sleep();
+ delete radio;
+ }
+#endif
+}
+#endif // FEATURE_LORA
--- a/main.cpp Wed Feb 13 14:08:15 2019 +0100
+++ b/main.cpp Wed Feb 13 14:10:17 2019 +0100
@@ -9,15 +9,8 @@
* Compiler Date/Time is not set correctly on startup
*/
#include "main.h"
-#include "RadioShuttle.h"
#include "RadioTest.h"
-#include "GenericPingPong.h"
-#ifdef FEATURE_NVPROPERTY
-#include <NVPropertyProviderInterface.h>
-#include "NVProperty.h"
-#endif
-void RunCommands(int timeout_ms);
DigitalOut statusLED(LED);
DigitalOut redLED(LED2);
@@ -28,7 +21,6 @@
InterrruptMSG(INT_BUTTON1);
}
-
void timerUpdate(void) {
static LowPowerTimeout timeout;
if (redLED == 0)
@@ -36,7 +28,6 @@
else
timeout.attach_us(&timerUpdate, 2000000); // setup to call timerUpdate after 2 seconds
- redLED = ! redLED;
InterrruptMSG(INT_TIMEOUT);
}
@@ -50,23 +41,21 @@
* It waits up to 30 seconds for a USB terminal connections
*/
InitSerial(30*1000, &statusLED);
+ RunStartup();
dprintf("Welcome to RadioShuttle v%d.%d", RS_MAJOR, RS_MINOR);
- dprintf("Voltage: %.2f (%s powered)", BatteryVoltage(), BatterySource());
- timerUpdate(); // start timer
+ timerUpdate(); // start timer for status blinked, can be disalbed to save energy
#if defined (USER_BUTTON_RISE) // attach switchInput function to the rising or falling edge
buttonIntr.rise(&switchInput);
#else
buttonIntr.fall(&switchInput);
#endif
- RunCommands(10000); // check for any commands
+ RunCommands(10000); // check 10 secs for any commands
#ifdef FEATURE_LORA
InitRadio();
#endif
- dprintf("InitDefaults Done");
-
/*
* Main event loop, process interrupts and go to sleep
* the green statusLED indicates CPU activity
@@ -95,75 +84,7 @@
#endif
}
if (pendirqs & INT_TIMEOUT) {
+ redLED = ! redLED;
}
}
}
-
-static const char *cmds = \
- "\r\nThe following commands are available:\r\n\r\n" \
- " p -- Property Editor\r\n" \
- " t -- LoRa PingPong Test\r\n" \
- " d -- Hexdump of memory address [offset count]\r\n"
- " r -- Reset\r\n" \
- " c -- Continue with RadioShuttle\r\n" \
- "\r\n" \
- "waiting 10 secs ...\r\n" \
- "\r\n";
-
-void RunCommands(int timeout_ms) {
- bool cmdLoop = true;
- while(cmdLoop) {
- char buf[32];
-
- rprintf(cmds);
- rprintf("Turtle$ ");
- if (ConsoleReadline(buf, sizeof(buf), true, timeout_ms) == NULL) {
- cmdLoop = false;
- break;
- }
- switch(buf[0]) {
- case 'p':
- case 'P':
-#ifdef FEATURE_NVPROPERTYEDITOR
- NVPropertyEditor();
-#endif
- break;
- case 't':
- case 'T':
-#ifdef FEATURE_LORA_PING_PONG
- SX1276PingPong(); // basic LoRa raw ping/pong without RadioShuttle
-#endif
- break;
- case 'r':
- case 'R':
- MCUReset();
- break;
- case 'd':
- case 'D':
- {
- char *addr = strchr(buf, ' ');
- if (addr) {
- *addr++ = 0;
- char *length = strchr(addr, ' ');
- if (length) {
- *length++ = 0;
- }
- unsigned long address = strtoll(addr, NULL, 0);
- unsigned long cnt = 32;
- if (length)
- cnt = strtoll(length, NULL, 0);
- dump("Hexdump", (void *)address, cnt);
- }
- }
- break;
- case 'c':
- case 'C':
- cmdLoop = false;
- break;
- default:
- break;
- }
- }
- rprintf("\r\n");
-
-}
--- a/main.h Wed Feb 13 14:08:15 2019 +0100 +++ b/main.h Wed Feb 13 14:10:17 2019 +0100 @@ -32,7 +32,10 @@ extern USBSerialBuffered *usb; #endif extern bool _useDprintf; -extern void InitSerial(int timeout, DigitalOut *led); +extern void InitSerial(int timeout, DigitalOut *led); +extern void RunStartup(void); +extern size_t MemoryAvailable(bool print); + extern void dump(const char *title, const void *data, int len, bool dwords = false); extern const char *BatterySource(void); @@ -42,4 +45,5 @@ extern char *ConsoleReadline(char *buf, int buflen, bool echo, int timeout_ms = 0); extern void NVPropertyEditor(void); extern void MCUReset(void); +extern void RunCommands(int timeout_ms);
--- a/utils.cpp Wed Feb 13 14:08:15 2019 +0100
+++ b/utils.cpp Wed Feb 13 14:10:17 2019 +0100
@@ -3,11 +3,17 @@
* 30826 Garbsen (Hannover) Germany
*/
#include "main.h"
+#include "GenericPingPong.h"
+#ifdef TOOLCHAIN_GCC
+#include <malloc.h>
+#endif
volatile uint32_t PendingInterrupts; // global interrupt mask of received interrupts
time_t cvt_date(char const *date, char const *time);
+static float GetBrownOutVolt(void);
+
BufferedSerial *ser;
#ifdef FEATURE_USBSERIAL
USBSerialBuffered *usb;
@@ -55,6 +61,36 @@
}
}
+void RunStartup(void)
+{
+ rprintf("\r\n");
+ int mbedversion = 9999;
+#ifdef MBED_LIBRARY_VERSION // not available in mbed head compiles
+ mbedversion = MBED_LIBRARY_VERSION;
+#endif
+ dprintf("Turtle: %d.%d (%s %s mbed: v%d)", MAJOR_VERSION, MINOR_VERSION, __DATE__, __TIME__, mbedversion);
+
+ dprintf("SysClock: %u Hz.", (unsigned int)SystemCoreClock);
+#ifdef __ARMCC_VERSION
+ dprintf("ARM Compiler Version: 0x%x", __ARMCC_VERSION);
+#elif __GNUC__
+ dprintf("GCC Compiler Version: %d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
+#endif
+
+ const char *errstr;
+ if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST) != RESET)
+ errstr = "RESET OCCURRED";
+ else
+ errstr = "initalized";
+
+ dprintf("Brown Out Reset %s (%1.1f V)", errstr, GetBrownOutVolt());
+ dprintf("Voltage: %.2f (%s powered)", BatteryVoltage(), BatterySource());
+
+ dprintf("InitDefaults Done");
+ MemoryAvailable(true);
+
+ __HAL_RCC_CLEAR_RESET_FLAGS();
+}
void printTimeStamp()
{
static LowPowerTimer *timer;
@@ -323,9 +359,142 @@
}
+float
+GetBrownOutVolt(void)
+{
+ float borVolt;
+ unsigned int *FlashOptionRegister = (unsigned int *)0x1FFF7800;
+
+ int val = *FlashOptionRegister >> 8 & 0x7; // masking out the BOR bits 9-11
+ switch(val) {
+ case 0:
+ return borVolt = 1.7;
+ case 1:
+ return borVolt = 2.0;
+ case 2:
+ return borVolt = 2.2;
+ case 3:
+ return borVolt = 2.5;
+ case 4:
+ return borVolt = 2.8;
+ default:
+ return borVolt = 999;
+ }
+}
+
void MCUReset(void)
{
#define AIRCR_VECTKEY_MASK 0x05FA0000
SCB->AIRCR = AIRCR_VECTKEY_MASK | 0x04; // NVIC_GenerateSystemReset();
}
+size_t
+MemoryAvailable(bool print)
+{
+ size_t counter;
+#ifdef TOOLCHAIN_GCC
+ struct mallinfo mi = mallinfo();
+ extern char end[];
+ extern char _estack[];
+ counter = (_estack - end) - mi.uordblks;
+ if (print)
+ dprintf("MemoryAvailable: %d kB (%d bytes)", counter/1024, counter);
+ return counter;
+#else
+ struct elem *head, *current, *nextone;
+ current = head = (struct elem*) malloc(sizeof(struct elem));
+ if (head == NULL)
+ return 0; /*No memory available.*/
+ counter = 0;
+ // __disable_irq();
+ do {
+ counter++;
+ current->next = (struct elem*) malloc(sizeof(struct elem));
+ current = current->next;
+ } while (current != NULL);
+ /* Now counter holds the number of type elem
+ structures we were able to allocate. We
+ must free them all before returning. */
+ current = head;
+ do {
+ nextone = current->next;
+ free(current);
+ current = nextone;
+ } while (nextone != NULL);
+ // __enable_irq();
+
+ if (print)
+ dprintf("MemoryAvailable: %d kB (%d bytes)", (counter*FREEMEM_CELL)/1024, counter*FREEMEM_CELL);
+ return counter*FREEMEM_CELL;
+#endif
+}
+
+
+static const char *cmds = \
+ "\r\nThe following commands are available:\r\n\r\n" \
+ " p -- Property Editor\r\n" \
+ " t -- LoRa PingPong Test\r\n" \
+ " d -- Hexdump of memory address [offset count]\r\n"
+ " r -- Reset\r\n" \
+ " c -- Continue with RadioShuttle\r\n" \
+ "\r\n" \
+ "waiting 10 secs ...\r\n" \
+ "\r\n";
+
+void RunCommands(int timeout_ms) {
+ bool cmdLoop = true;
+ while(cmdLoop) {
+ char buf[32];
+
+ rprintf(cmds);
+ rprintf("Turtle$ ");
+ if (ConsoleReadline(buf, sizeof(buf), true, timeout_ms) == NULL) {
+ cmdLoop = false;
+ break;
+ }
+ switch(buf[0]) {
+ case 'p':
+ case 'P':
+#ifdef FEATURE_NVPROPERTYEDITOR
+ NVPropertyEditor();
+#endif
+ break;
+ case 't':
+ case 'T':
+#ifdef FEATURE_LORA_PING_PONG
+ SX1276PingPong(); // basic LoRa raw ping/pong without RadioShuttle
+#endif
+ break;
+ case 'r':
+ case 'R':
+ MCUReset();
+ break;
+ case 'd':
+ case 'D':
+ {
+ char *addr = strchr(buf, ' ');
+ if (addr) {
+ *addr++ = 0;
+ char *length = strchr(addr, ' ');
+ if (length) {
+ *length++ = 0;
+ }
+ unsigned long address = strtoll(addr, NULL, 0);
+ unsigned long cnt = 32;
+ if (length)
+ cnt = strtoll(length, NULL, 0);
+ dump("Hexdump", (void *)address, cnt);
+ }
+ }
+ break;
+ case 'c':
+ case 'C':
+ cmdLoop = false;
+ break;
+ default:
+ break;
+ }
+ }
+ rprintf("\r\n");
+
+}
Helmut Tschemernjak