From 13a0d69f4e6a2a2e3a250236acb878d860813d19 Mon Sep 17 00:00:00 2001 From: Olivier Date: Thu, 1 Aug 2019 21:14:32 +0200 Subject: [PATCH] MySensors Diagnostics --- MyConfig.h | 18 + MySensors.h | 16 +- core/MyDiagnostics.cpp | 958 ++++++++++++++++++ core/MyDiagnostics.h | 29 + core/MySensorsCore.cpp | 5 + hal/architecture/ESP32/MyHwESP32.cpp | 14 +- hal/architecture/ESP32/MyHwESP32.h | 3 + hal/transport/RF24/driver/RF24.cpp | 25 +- hal/transport/RF24/driver/RF24.h | 20 +- hal/transport/RFM69/driver/new/RFM69_new.cpp | 10 + hal/transport/RFM69/driver/new/RFM69_new.h | 5 + keywords.txt | 2 + .../diagnostics_rf24/diagnostics_rf24.ino | 28 + .../diagnostics_rfm69/diagnostics_rfm69.ino | 29 + .../diagnostics_rfm95/diagnostics_rfm95.ino | 28 + 15 files changed, 1180 insertions(+), 10 deletions(-) create mode 100644 core/MyDiagnostics.cpp create mode 100644 core/MyDiagnostics.h create mode 100644 tests/Arduino/sketches/diagnostics_rf24/diagnostics_rf24.ino create mode 100644 tests/Arduino/sketches/diagnostics_rfm69/diagnostics_rfm69.ino create mode 100644 tests/Arduino/sketches/diagnostics_rfm95/diagnostics_rfm95.ino diff --git a/MyConfig.h b/MyConfig.h index 7e37b9adb..8095a1a4f 100755 --- a/MyConfig.h +++ b/MyConfig.h @@ -133,6 +133,22 @@ */ //#define MY_SPECIAL_DEBUG +/** + * @def MY_DIAGNOSTICS + * @brief Define MY_DIAGNOSTICS to show a diagnostics serial user interface + * + */ +//#define MY_DIAGNOSTICS + +/** + * @def MY_DIAGNOSTICS_CRYPTO + * @brief Define MY_DIAGNOSTICS_CRYPTO to include crypto testing functions. + * + * This feature is disabled on AVR architectures due to limited flash/RAM space but can be enabled if needed. + * + */ +//#define MY_DIAGNOSTICS + /** * @def MY_DISABLED_SERIAL * @brief Define MY_DISABLED_SERIAL if you want to use the UART TX/RX pins as normal I/O pins. @@ -2400,6 +2416,8 @@ #define MY_SPECIAL_DEBUG #define MY_DISABLED_SERIAL #define MY_SPLASH_SCREEN_DISABLED +#define MY_DIAGNOSTICS +#define MY_DIAGNOSTICS_CRYPTO // linux #define MY_LINUX_SERIAL_PORT #define MY_LINUX_SERIAL_IS_PTY diff --git a/MySensors.h b/MySensors.h index e51f962d3..7b7eb8d5e 100644 --- a/MySensors.h +++ b/MySensors.h @@ -40,9 +40,19 @@ #endif #include +#if defined(MY_DIAGNOSTICS) +#if !defined(ARDUINO_ARCH_AVR) +// more flash available +#define MY_DIAGNOSTICS_CRYPTO +#endif +#define MY_DEBUG_VERBOSE_TRANSPORT +#define MY_DEBUG_VERBOSE_TRANSPORT_HAL +#define MY_SPECIAL_DEBUG +#include "core/MyDiagnostics.h" +#endif + #include "MyConfig.h" #include "core/MyHelperFunctions.cpp" - #include "core/MySplashScreen.h" #include "core/MySensorsCore.h" @@ -444,6 +454,10 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #include "core/MySplashScreen.cpp" #include "core/MySensorsCore.cpp" +#if defined(MY_DIAGNOSTICS) +#include "core/MyDiagnostics.cpp" +#endif + // HW mains #if defined(ARDUINO_ARCH_AVR) #include "hal/architecture/AVR/MyMainAVR.cpp" diff --git a/core/MyDiagnostics.cpp b/core/MyDiagnostics.cpp new file mode 100644 index 000000000..8222b15ff --- /dev/null +++ b/core/MyDiagnostics.cpp @@ -0,0 +1,958 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in RAM or EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2022 Sensnology AB + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + + +#include "MyDiagnostics.h" + +static char inputBuffer[15]; +static char inputCmd; +static uint8_t inputBufferPosition; +static String inputParameter; + +void diagnosticsFlushSerial(void) +{ + delay(100); + while (MY_SERIALDEVICE.available()) { + (void)MY_SERIALDEVICE.read(); + } +} + +void diagnosticsSerialInput(void) +{ + bool cmdReceived = false; + inputBufferPosition = 0; + while (!cmdReceived) { + if (MY_SERIALDEVICE.available()) { + const char inputChr = MY_SERIALDEVICE.read(); + if (inputChr == '\n' || inputBufferPosition == sizeof(inputBuffer) - 1) { + cmdReceived = true; + } else { + inputBuffer[inputBufferPosition++] = inputChr; + } + } + } + diagnosticsFlushSerial(); + // null termination + inputBuffer[inputBufferPosition] = 0; + inputParameter = String(&inputBuffer[1]); + inputCmd = toUpperCase(inputBuffer[0]); +} + +void PRINT(const char *fmt, ...) +{ + char fmtBuffer[MY_SERIAL_OUTPUT_SIZE]; + va_list args; + va_start(args, fmt); + vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args); + va_end(args); + MY_SERIALDEVICE.print(fmtBuffer); +} + +void PrintHex8(const uint8_t *data, const uint16_t length) +{ + for (uint16_t i = 0; i < length; i++) { + PRINT(PSTR("%02" PRIX8 " "), data[i]); + if ((((i + 1) % 16 == 0) && (i < length)) || (i == length - 1)) { + MY_SERIALDEVICE.println(); + } + } +} + +void diagnosticsPrintSeparationLine(void) +{ + for (uint8_t i = 0; i < 50; i++) { + MY_SERIALDEVICE.print("="); + } + MY_SERIALDEVICE.println(); +} + +void diagnosticsMySensorsEEPROMDump(void) +{ + uint8_t buffer[256]; + PRINT(PSTR("> MYS E2P START: 0x%04" PRIX16 "\n"), EEPROM_START); + + MY_SERIALDEVICE.print(F("> NODE_ID=")); + hwReadConfigBlock(buffer, EEPROM_NODE_ID_ADDRESS, SIZE_NODE_ID); + PrintHex8(buffer, SIZE_NODE_ID); + + MY_SERIALDEVICE.print(F("> PAR_ID=")); + hwReadConfigBlock(buffer, reinterpret_castEEPROM_PARENT_NODE_ID_ADDRESS, + SIZE_PARENT_NODE_ID); + PrintHex8(buffer, SIZE_PARENT_NODE_ID); + + MY_SERIALDEVICE.print(F("> D_GW=")); + hwReadConfigBlock(buffer, reinterpret_castEEPROM_DISTANCE_ADDRESS, SIZE_DISTANCE); + PrintHex8(buffer, SIZE_DISTANCE); + + MY_SERIALDEVICE.println(F("> RTE TABLE:")); + hwReadConfigBlock(buffer, reinterpret_cast EEPROM_ROUTES_ADDRESS, SIZE_ROUTES); + PrintHex8(buffer, SIZE_ROUTES); + + MY_SERIALDEVICE.println(F("> CTRL_CFG:")); + hwReadConfigBlock(buffer, reinterpret_castEEPROM_CONTROLLER_CONFIG_ADDRESS, + SIZE_CONTROLLER_CONFIG); + PrintHex8(buffer, SIZE_CONTROLLER_CONFIG); + + MY_SERIALDEVICE.print(F("> PERS_CRC=")); + hwReadConfigBlock(buffer, reinterpret_castEEPROM_PERSONALIZATION_CHECKSUM_ADDRESS, + SIZE_PERSONALIZATION_CHECKSUM); + PrintHex8(buffer, SIZE_PERSONALIZATION_CHECKSUM); + + MY_SERIALDEVICE.print(F("> FW_TYPE=")); + hwReadConfigBlock(buffer, reinterpret_castEEPROM_FIRMWARE_TYPE_ADDRESS, + SIZE_PERSONALIZATION_CHECKSUM); + PrintHex8(buffer, SIZE_PERSONALIZATION_CHECKSUM); + + MY_SERIALDEVICE.print(F("> FW_VERS=")); + hwReadConfigBlock(buffer, reinterpret_castEEPROM_FIRMWARE_VERSION_ADDRESS, + SIZE_FIRMWARE_VERSION); + PrintHex8(buffer, SIZE_FIRMWARE_VERSION); + + MY_SERIALDEVICE.print(F("> FW_BLOCKS=")); + hwReadConfigBlock(buffer, reinterpret_castEEPROM_FIRMWARE_BLOCKS_ADDRESS, + SIZE_FIRMWARE_BLOCKS); + PrintHex8(buffer, SIZE_FIRMWARE_BLOCKS); + + MY_SERIALDEVICE.print(F("> FW_CRC=")); + hwReadConfigBlock(buffer, reinterpret_castEEPROM_FIRMWARE_CRC_ADDRESS, SIZE_FIRMWARE_CRC); + PrintHex8(buffer, SIZE_FIRMWARE_CRC); + + MY_SERIALDEVICE.println(F("> SGN_REQ_TABLE:")); + hwReadConfigBlock(buffer, reinterpret_castEEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, + SIZE_SIGNING_REQUIREMENT_TABLE); + PrintHex8(buffer, SIZE_SIGNING_REQUIREMENT_TABLE); + + MY_SERIALDEVICE.println(F("> WL_REQ_TABLE:")); + hwReadConfigBlock(buffer, reinterpret_castEEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS, + SIZE_WHITELIST_REQUIREMENT_TABLE); + PrintHex8(buffer, SIZE_WHITELIST_REQUIREMENT_TABLE); + + MY_SERIALDEVICE.println(F("> SGN_SOFT_KEY:")); + hwReadConfigBlock(buffer, reinterpret_castEEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, + SIZE_SIGNING_SOFT_HMAC_KEY); + PrintHex8(buffer, SIZE_SIGNING_SOFT_HMAC_KEY); + + MY_SERIALDEVICE.println(F("> SGN_SOFT_SER:")); + hwReadConfigBlock(buffer, reinterpret_castEEPROM_SIGNING_SOFT_SERIAL_ADDRESS, + SIZE_SIGNING_SOFT_SERIAL); + PrintHex8(buffer, SIZE_SIGNING_SOFT_SERIAL); + + MY_SERIALDEVICE.println(F("> AES_KEY:")); + hwReadConfigBlock(buffer, reinterpret_castEEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, + SIZE_RF_ENCRYPTION_AES_KEY); + PrintHex8(buffer, SIZE_RF_ENCRYPTION_AES_KEY); + + MY_SERIALDEVICE.print(F("> NL_CNT=")); + hwReadConfigBlock(buffer, reinterpret_castEEPROM_NODE_LOCK_COUNTER_ADDRESS, + SIZE_NODE_LOCK_COUNTER); + PrintHex8(buffer, SIZE_NODE_LOCK_COUNTER); + + PRINT(PSTR("> USER E2P >= 0x%04" PRIX16 "\n"), EEPROM_LOCAL_CONFIG_ADDRESS); +} + +void diagnosticsClearMySensorsEEPROMConfig(void) +{ + for (uint16_t i = EEPROM_START; i < EEPROM_START + EEPROM_LOCAL_CONFIG_ADDRESS; i++) { + hwWriteConfig(i, 0xFF); + if (hwReadConfig(i) != 0xFF) { + PRINT(PSTR("!ERR POS 0x02%" PRIX8 "\n"), i); + } + } + MY_SERIALDEVICE.println(F("> E2P CLR")); +} + +void diagnosticsClearMySensorsRoutingTable(void) +{ + for (uint16_t i = 0; i < SIZE_ROUTES; i++) { + hwWriteConfig(EEPROM_ROUTES_ADDRESS + i, 0xFF); + if (hwReadConfig(EEPROM_ROUTES_ADDRESS + i) != 0xFF) { + PRINT(PSTR("!ERR POS 0x02%" PRIu8 "\n"), i); + } + } + MY_SERIALDEVICE.println(F("> RTE TABLE CLR")); +} + +void diagnosticsClearMySensorsTransportSettings(void) +{ + hwWriteConfig(EEPROM_NODE_ID_ADDRESS, 0xFF); + hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, 0xFF); + hwWriteConfig(EEPROM_DISTANCE_ADDRESS, 0xFF); + MY_SERIALDEVICE.println(F("> TSP CFG CLR")); +} + +void diagnosticsEEPROMTest(void) +{ + MY_SERIALDEVICE.println(F("EEPROM test:")); + uint16_t success = 0; + for (uint16_t i = 0; i < (EEPROM_LOCAL_CONFIG_ADDRESS - EEPROM_START); i++) { + if (i % 80 == 0) { + MY_SERIALDEVICE.println(); + MY_SERIALDEVICE.print(i + EEPROM_START, HEX); + MY_SERIALDEVICE.print(": "); + } else { + MY_SERIALDEVICE.print("."); + } + const uint8_t originalContent = hwReadConfig(i + EEPROM_START); + hwWriteConfig(i + EEPROM_START, 0xAA); + if (hwReadConfig(i + EEPROM_START) == 0xAA) { + success++; + } else { + PRINT(PSTR("!ERR POS 0x02%" PRIu8 "\n"), i + EEPROM_START); + } + hwWriteConfig(i + EEPROM_START, 0x55); + if (hwReadConfig(i + EEPROM_START) == 0x55) { + success++; + } + // write back original byte + hwWriteConfig(i + EEPROM_START, originalContent); + if (hwReadConfig(i + EEPROM_START) == originalContent) { + success++; + } else { + PRINT(PSTR("!ERR POS 0x02%" PRIu8 "\n"), i + EEPROM_START); + } + } + MY_SERIALDEVICE.print(F("\n>E2P check: ")); + if (success == (EEPROM_LOCAL_CONFIG_ADDRESS - EEPROM_START) * 3) { + MY_SERIALDEVICE.println(F("pass")); + } else { + MY_SERIALDEVICE.println(F("failed!")); + } + +} + +void diagnosticsEEPROMMenu(void) +{ + while (true) { + diagnosticsPrintSeparationLine(); + MY_SERIALDEVICE.println(F("EEPROM:\n\n" + "[D] Dump\n" + "[T] Test\n" + "[C] CLR\n" + "[R] CLR TSP RTE\n" + "[S] CLR TSP CFG\n" + "[X] Exit" + )); + diagnosticsPrintSeparationLine(); + diagnosticsFlushSerial(); + diagnosticsSerialInput(); + if (inputCmd == 'D') { + diagnosticsMySensorsEEPROMDump(); + } else if (inputCmd == 'T') { + diagnosticsEEPROMTest(); + } else if (inputCmd == 'C') { + diagnosticsClearMySensorsEEPROMConfig(); + } else if (inputCmd== 'R') { + diagnosticsClearMySensorsRoutingTable(); + } else if (inputCmd == 'S') { + diagnosticsClearMySensorsTransportSettings(); + } else if (inputCmd == 'X') { + return; + } + } +} + +#if defined(MY_DIAGNOSTICS_CRYPTO) +bool diagnosticsCryptoMenu(void) +{ + MY_SERIALDEVICE.println(F("Testing:")); + const uint8_t test_data[64] = { 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, + 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, + 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, + 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 + }; + const uint8_t test_psk[16] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; + uint8_t aes_iv[16]; + for (uint8_t i = 0; i < sizeof(aes_iv); i++) { + aes_iv[i] = i; + } + + AES128CBCInit(test_psk); +#if defined(CRYPTO_OUTPUT) + MY_SERIALDEVICE.println(F("AES128CBC input:")); + PrintHex8(test_data, sizeof(test_data)); + MY_SERIALDEVICE.println(F("AES128CBC key:")); + PrintHex8(test_psk, sizeof(test_psk)); + MY_SERIALDEVICE.println(F("AES128CBC IV:")); + PrintHex8(aes_iv, sizeof(aes_iv)); +#endif + uint8_t temp_iv[16]; + uint8_t temp_data[64]; + (void)memcpy((void *)temp_iv, (const void *)aes_iv, sizeof(aes_iv)); + (void)memcpy((void *)temp_data, (const void *)test_data, sizeof(temp_data)); + AES128CBCEncrypt(temp_iv, temp_data, sizeof(test_data)); + MY_SERIALDEVICE.print(F("- AES128 CBC encryption: ")); + const uint8_t aes_ciphertext[64] = { + 0x46,0xE3,0x35,0xB8,0xEA,0x11,0xBC,0xC5,0xB4,0xEB,0x7F,0x49,0xD1,0x14,0xFF,0x43,0x28,0x22,0x15, + 0xAD,0x3A,0xCF,0xF1,0x6B,0xE1,0x9B,0x6F,0x71,0x1A,0xA1,0x3B,0x89,0x69,0xFD,0x9F,0xB7,0x98,0x2A, + 0x37,0x03,0xE8,0x16,0x14,0x3F,0x89,0x62,0x56,0x0F,0xDA,0x85,0xAD,0x94,0xD3,0x4E,0x54,0x18,0x2A, + 0x52,0x5C,0x2B,0x28,0xFA,0x0E,0xAB + }; + // result verified here: http://extranet.cryptomathic.com/aescalc/index + if (memcmp(temp_data, &aes_ciphertext, sizeof(aes_ciphertext)) == 0) { + MY_SERIALDEVICE.println(F("OK")); + } else { + MY_SERIALDEVICE.println(F("FAIL!")); + }; +#if defined(CRYPTO_OUTPUT) + PrintHex8(temp_data, sizeof(test_data)); +#endif + (void)memcpy(temp_iv, aes_iv, sizeof(aes_iv)); + AES128CBCDecrypt(temp_iv, temp_data, sizeof(temp_data)); + MY_SERIALDEVICE.print(F("- AES128 CBC decryption: ")); + if (memcmp((const void *)test_data, (const void *)temp_data, sizeof(temp_data)) == 0) { + MY_SERIALDEVICE.println(F("OK")); + } else { + MY_SERIALDEVICE.println(F("FAIL!")); + }; +#if defined(CRYPTO_OUTPUT) + PrintHex8(temp_data, sizeof(temp_data)); +#endif + MY_SERIALDEVICE.print(F("- SHA256: ")); + uint8_t dest[64]; + SHA256((uint8_t *)dest, (const uint8_t *)test_data, sizeof(test_data)); +#if defined(CRYPTO_OUTPUT) + MY_SERIALDEVICE.println(F("SHA256 input:")); + PrintHex8(test_data, sizeof(test_data)); + MY_SERIALDEVICE.println(F("SHA256 output:")); + PrintHex8(dest, 32); +#endif + // result verified here: http://extranet.cryptomathic.com/hashcalc/index + const uint8_t sha256result[32] = { 0x51,0x3f,0xa7,0x82,0x3d,0xc3,0x05,0x3d,0xc6,0x43,0xa4,0x4b,0x8f,0xb8,0xdd,0x62, + 0x36,0x0b,0x00,0x44,0xf1,0xab,0x69,0x65,0xf8,0x36,0x29,0xd2,0xb1,0x64,0xbf,0x14 + }; + //PRINT(PSTR("SHA256 test: ")); + if (memcmp(dest, &sha256result, sizeof(sha256result)) == 0) { + MY_SERIALDEVICE.println(F("OK")); + } else { + MY_SERIALDEVICE.println(F("FAIL!")); + }; + + MY_SERIALDEVICE.print(F("- HMAC SHA256: ")); +#if defined(CRYPTO_OUTPUT) + MY_SERIALDEVICE.println(F("HMAC input:")); + PrintHex8(test_data, sizeof(test_data)); + MY_SERIALDEVICE.println(F("HMAC key:")); + PrintHex8(test_psk, sizeof(test_psk)); +#endif + SHA256HMAC(dest, test_psk, sizeof(test_psk), test_data, sizeof(test_data)); +#if defined(CRYPTO_OUTPUT) + MY_SERIALDEVICE.println(F("HMAC output:")); + PrintHex8(dest, sizeof(dest)); +#endif + // result verified here: http://extranet.cryptomathic.com/hmaccalc/index + const uint8_t hmacresult[32] = { 0xcc,0xa7,0x5f,0x5d,0xd5,0xeb,0x50,0x34,0x02,0x53,0x12,0x17,0x40,0x72,0xaf,0x29, + 0xe6,0xc9,0xb5,0xb1,0x9b,0x26,0x8b,0x23,0x0f,0x5c,0xeb,0x50,0x24,0x63,0xc2,0x33 + }; + //PRINT(PSTR("HMAC test: ")); + if (memcmp(dest, &hmacresult, sizeof(sha256result)) == 0) { + MY_SERIALDEVICE.println(F("OK")); + } else { + MY_SERIALDEVICE.println(F("FAIL!")); + }; + MY_SERIALDEVICE.println(F("> MUL speed:")); + uint32_t startMS, stopMS; + uint32_t cnt; + MY_SERIALDEVICE.print(F("- 8bit MUL: ")); + startMS = hwMillis(); + uint8_t u8 = 1; + cnt = 0xFFFFF; + while (cnt--) { + u8 *= 3; + } + stopMS = hwMillis(); + if (u8 == 171) { + PRINT(PSTR("OK, %" PRIu32 " ms\n"), stopMS - startMS); + } else { + MY_SERIALDEVICE.println(F("FAIL!")); + } + + MY_SERIALDEVICE.print(F("- 16bit MUL: ")); + startMS = hwMillis(); + uint16_t u16 = 1; + cnt = 0xFFFFF; + while (cnt--) { + u16 *= 3; + } + stopMS = hwMillis(); + if (u16 == 43691) { + PRINT(PSTR("OK, %" PRIu32 " ms\n"), stopMS - startMS); + } else { + MY_SERIALDEVICE.println(F("FAIL!")); + } + + MY_SERIALDEVICE.print(F("- 32bit MUL: ")); + startMS = hwMillis(); + uint32_t u32 = 1; + cnt = 0xFFFFF; + while (cnt--) { + u32 *= 3; + } + stopMS = hwMillis(); + if (u32 == 3664423595) { + PRINT(PSTR("OK, %" PRIu32 " ms\n"), stopMS - startMS); + } else { + MY_SERIALDEVICE.println(F("FAIL!")); + } + return false; +} +#endif + +#if defined(ARDUINO_ARCH_AVR) +void diagnosticsWatchdogTest(void) +{ + MY_SERIALDEVICE.println(F("Set WDT to 4s\n")); + hwWatchdogReset(); + wdt_enable(WDTO_4S); + for (uint8_t timer = 0; timer < 10; timer++) { + MY_SERIALDEVICE.print(timer); + delay(1000); + } + MY_SERIALDEVICE.println(F("WDT failed!\n")); +} +#endif + +#if defined(MY_RADIO_RFM95) +void diagnosticsRFM95Menu(void) +{ + +} +#endif + +#if defined(MY_RADIO_RFM69) && defined(MY_RFM69_NEW_DRIVER) +void diagnosticsRFM69Menu(void) +{ + RFM69_initialise(RFM69_868MHZ); + while (true) { + diagnosticsFlushSerial(); + diagnosticsPrintSeparationLine(); + MY_SERIALDEVICE.println(F("RFM69:\n")); + PRINT(PSTR("SPI: MOSI=%" PRIu8 ", MISO=%" PRIu8 ", SCK=%" PRIu8 ", CS=%" PRIu8 ", IRQ=%" PRIu8 + "\n"), + MOSI, MISO, SCK, MY_RFM69_CS_PIN, MY_RFM69_IRQ_PIN); + PRINT(PSTR("RF: ID=%" PRIu8 ", FREQ=%" PRIu32 ", POW=%" PRIu8 "\n"), + RFM69_getAddress(), RFM69_getFrequency(), RFM69_getTxPowerLevel()); + + MY_SERIALDEVICE.println(F( + "[I] Init\n" + "[D] Dump REG\n" + "[Ax] ADDR=x\n" + "[Fx] FREQ=x\n" + "[Wx] POW=X\n" + "[L] SLP\n" + "[B] STDBY\n" + "[O] CAR on\n" + "[Q] CAR off\n" + "[R] RX\n" + "[Tx] TX to x\n" + "[P] Poll STAT\n" + "[X] Exit" + )); + diagnosticsPrintSeparationLine(); + diagnosticsSerialInput(); + if (inputCmd == 'I') { + RFM69_initialise(RFM69_868MHZ); + } else if (inputCmd == 'A') { + RFM69_setAddress(inputParameter.toInt()); + } else if (inputCmd == 'F') { + RFM69_setFrequency(inputParameter.toInt()); + } else if (inputCmd == 'W') { + RFM69_setTxPowerLevel(inputParameter.toInt()); + } else if (inputCmd == 'L') { + RFM69_sleep(); + } else if (inputCmd == 'B') { + RFM69_standBy(); + } else if (inputCmd == 'R') { + (void)RFM69_setRadioMode(RFM69_RADIO_MODE_RX); + } else if (inputCmd == 'T') { + uint8_t buffer[] = { 'T','E','S','T','R','F','M','6','9' }; + RFM69_sendWithRetry(inputParameter.toInt(), buffer, sizeof(buffer), true); + } else if (inputCmd == 'P') { + diagnosticsPrintSeparationLine(); + MY_SERIALDEVICE.println(F("Press any key to exit")); + diagnosticsPrintSeparationLine(); + diagnosticsFlushSerial(); + while (!MY_SERIALDEVICE.available()) { + PRINT(PSTR("IRQF1=0x%02" PRIX8 ", IRQF2=0x%02" PRIX8 ", IRQF=%" PRIu8 "\n"), + RFM69_readReg(RFM69_REG_IRQFLAGS1), RFM69_readReg(RFM69_REG_IRQFLAGS2), RFM69_irq); + delay(300); + } + MY_SERIALDEVICE.println(F("Exiting...")); + } else if (inputCmd == 'O') { + (void)RFM69_setRadioMode(RFM69_RADIO_MODE_TX); + } else if (inputCmd == 'Q') { + (void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); + } else if (inputCmd == 'D') { + uint8_t i = 0; + do { + PRINT(PSTR("Reg 0x%02" PRIX8 " = 0x%02" PRIX8 "\n"), i, RFM69_readReg(i)); + } while (i++ != 0xFF); + } else if (inputCmd == 'X') { + return; + } + } +} +#endif + +#if defined(MY_RADIO_RF24) +void diagnosticsRF24Menu(void) +{ + RF24_initialize(); + while (true) { + diagnosticsFlushSerial(); + diagnosticsPrintSeparationLine(); + MY_SERIALDEVICE.println(F("RF24:\n")); + PRINT(PSTR("SPI: MOSI=%" PRIu8 ", MISO=%" PRIu8 ", SCK=%" PRIu8 ", CS=%" PRIu8 ", CE=%" PRIu8 "\n"), + MOSI, MISO, SCK, MY_RF24_CS_PIN, MY_RF24_CE_PIN); + PRINT(PSTR("RF: ADDR=%" PRIu8 ", CH=%" PRIu8 ", POW=%" PRIu8 ", CFG=%" PRIu8 "\n"), + RF24_getNodeID(), + RF24_getChannel(), RF24_getRawTxPowerLevel(), RF24_getRFConfiguration()); + + MY_SERIALDEVICE.println(F("[I] Init\n" + "[D] Dump REG\n" + "[Ax] ADDR=x\n" + "[Cx] CH=x\n" + "[Wx] POW=X\n" + "[L] SLP\n" + "[B] STDBY\n" + "[O] CAR on\n" + "[Q] CAR off\n" + "[R] RX\n" + "[Tx] TX to x\n" + "[P] Poll STAT\n" + "[S] Scan CHs\n" + "[X] Exit" + )); + diagnosticsPrintSeparationLine(); + diagnosticsSerialInput(); + if (inputCmd == 'I') { + RF24_initialize(); + } else if (inputCmd == 'A') { + RF24_setNodeAddress(inputParameter.toInt()); + } else if (inputCmd == 'C') { + RF24_setChannel(inputParameter.toInt()); + } else if (inputCmd == 'W') { + RF24_setTxPowerLevel(inputParameter.toInt()); + } else if (inputCmd == 'L') { + RF24_sleep(); + } else if (inputCmd == 'B') { + RF24_standBy(); + } else if (inputCmd == 'R') { + RF24_startListening(); + } else if (inputCmd == 'T') { + uint8_t buffer[] = { 'T','E','S','T','R','F','2','4' }; + RF24_sendMessage(inputParameter.toInt(), buffer, sizeof(buffer), false); + } else if (inputCmd == 'P') { + diagnosticsPrintSeparationLine(); + MY_SERIALDEVICE.println(F("Press any key to exit")); + diagnosticsPrintSeparationLine(); + diagnosticsFlushSerial(); + while (!MY_SERIALDEVICE.available()) { + PRINT(PSTR("status=%02" PRIX8 "\n"), RF24_getStatus()); + delay(300); + } + MY_SERIALDEVICE.println(F("Exiting...")); + } else if (inputCmd == 'O') { + RF24_enableConstantCarrierWave(); + } else if (inputCmd == 'Q') { + RF24_disableConstantCarrierWave(); + } else if (inputCmd == 'D') { + //uint8_t buffer[16]; + for (uint8_t i = 0; i < 0x20; i++) { + PRINT(PSTR("Reg 0x%02" PRIX8 " = 0x%02" PRIX8 "\n"), i, RF24_readByteRegister(i)); + /* + (void)RF24_readMultiByteRegister(i, buffer, sizeof(buffer)); + PrintHex8(buffer, sizeof(buffer)); + for (uint8_t cnt = 0; cnt < sizeof(buffer); cnt++) { + buffer[cnt] = 0xFF; + } + (void)RF24_writeMultiByteRegister(i, buffer, sizeof(buffer)); + (void)RF24_readMultiByteRegister(i, buffer, sizeof(buffer)); + PrintHex8(buffer, sizeof(buffer)); + + for (uint8_t cnt = 0; cnt < sizeof(buffer); cnt++) { + buffer[cnt] = 0x00; + } + (void)RF24_writeMultiByteRegister(i, buffer, sizeof(buffer)); + (void)RF24_readMultiByteRegister(i, buffer, sizeof(buffer)); + PrintHex8(buffer, sizeof(buffer)); + */ + } + } else if (inputCmd == 'S') { + + MY_SERIALDEVICE.println(F("Press any key to exit")); + diagnosticsFlushSerial(); + + const uint8_t num_channels = 126; + + for(uint8_t i = 0; i < num_channels; i++) { + PRINT(PSTR("%" PRIX8), i >> 4); + } + MY_SERIALDEVICE.println(); + + for (uint8_t i = 0; i < num_channels; i++) { + PRINT(PSTR("%" PRIX8), i & 0xf); + } + + MY_SERIALDEVICE.println(); + + + while (!MY_SERIALDEVICE.available()) { + + uint8_t values[num_channels]; + // disable ACK on all pipes + RF24_setAutoACK(0); + // clear result array + (void)memset((void *)values, 0, sizeof(values)); + for (uint8_t rep_counter = 0; rep_counter < 100; rep_counter++) { + for (uint8_t channel = 0; channel < num_channels; channel++) { + RF24_setChannel(channel); + RF24_startListening(); + delayMicroseconds(130 + 40); + // Carrier detected? + if (RF24_getReceivedPowerDetector()) { + values[channel] = values[channel] + 1; + } + RF24_stopListening(); + } + } + + for (uint8_t i = 0; i < num_channels; i++) { + PRINT(PSTR("%" PRIX8), min(0xf, values[i])); + } + + MY_SERIALDEVICE.println(); + } + } else if (inputCmd == 'X') { + return; + } + } + +} +#endif + +#if defined(MY_SENSOR_NETWORK) +void diagnosticsTSMStatus(void) +{ + PRINT(PSTR("%" PRIu32 " TSM,%" PRIu8 ",%" PRIu8 ",%" PRIu8 ",%" PRIu32 ",%" PRIu32 ",%" PRIu8 ",%" + PRIu8 ",%" PRIu8 ",%" PRIu8 ",%" PRIu8 ",%" PRIu8 ",%" PRIu8 ",%" PRIu8 "\n"), + hwMillis(), + transportSanityCheck(), + getNodeId(), + getParentNodeId(), + _transportSM.stateEnter, + _transportSM.lastUplinkCheck, + _transportSM.findingParentNode, + _transportSM.uplinkOk, + _transportSM.pingActive, + _transportSM.transportActive, + _transportSM.stateRetries, + _transportSM.failedUplinkTransmissions, + _transportSM.failureCounter, + _transportSM.pingResponse); +} +#endif + +void diagnosticsTransportSM(void) +{ + while (true) { + diagnosticsPrintSeparationLine(); + MY_SERIALDEVICE.println(F("TSP SM:\n")); +#if defined(MY_SENSOR_NETWORK) + PRINT(PSTR("ADDR=%" PRIu8 ",PAR=%" PRIu8 ",DGW=%" PRIu8 ",TSP=%" PRIu8 "\n"), getNodeId(), + getDistanceGW(), getParentNodeId(), + isTransportReady()); + MY_SERIALDEVICE.println(F("[I] Init TSP\n" + "[S] Step TSM\n" + "[R] Run TSM" + )); +#endif + MY_SERIALDEVICE.println(F("[X] Exit")); + diagnosticsPrintSeparationLine(); + diagnosticsFlushSerial(); + diagnosticsSerialInput(); + if (inputCmd == 'I') { +#if defined(MY_SENSOR_NETWORK) + transportInitialise(); +#endif + } else if (inputCmd == 'S') { +#if defined(MY_SENSOR_NETWORK) + transportProcess(); + diagnosticsTSMStatus(); +#endif + } else if (inputCmd == 'R') { +#if defined(MY_SENSOR_NETWORK) + MY_SERIALDEVICE.println(F("[U] CKU\n" + "[F] FPAR\n" + "[E] TSP ERR\n" + "[I] INIT\n" + "[Cx] PNG x\n" + "[Nx] ID=x\n" + "[Px] PAR=x\n" + "[Tx] TX x\n" + "[Sx] Sleep x ms\n" + "[X] EXIT\n" + )); + uint32_t lastTimer = 0; + bool exitSignal = false; + while (!exitSignal) { + if (MY_SERIALDEVICE.available()) { + diagnosticsSerialInput(); + if (inputCmd == 'U') { + transportCheckUplink(); + } else if (inputCmd == 'F') { + transportSwitchSM(stParent); + } else if (inputCmd == 'E') { + transportSwitchSM(stFailure); + } else if (inputCmd == 'I') { + transportInitialise(); + } else if (inputCmd == 'C') { + _transportSM.pingActive = false; + transportPingNode(inputParameter.toInt()); + } else if (inputCmd == 'N') { + const uint8_t nodeID = inputParameter.toInt(); + _transportConfig.nodeId = nodeID; + transportHALSetAddress(nodeID); + // Write ID to EEPROM + hwWriteConfig(EEPROM_NODE_ID_ADDRESS, nodeID); + } else if (inputCmd == 'P') { + _transportConfig.parentNodeId = inputParameter.toInt(); + } else if (inputCmd == 'T') { + transportSendRoute(build(_msgTmp, inputParameter.toInt(), NODE_SENSOR_ID, C_SET, V_VAR1, + false).set((uint32_t)0xDEADBEAF)); + } else if (inputCmd == 'S') { + (void)sleep((uint32_t)inputParameter.toInt(), false); + } else if (inputCmd == 'X') { + exitSignal = true; + } + + } + transportProcess(); + if (hwMillis() - lastTimer > 1000ul) { + lastTimer = hwMillis(); + diagnosticsTSMStatus(); + } + } +#endif + } else if (inputCmd == 'X') { + return; + } else { + MY_SERIALDEVICE.println(F("!CMD")); + } + } +} + +void diagnosticsMCUMenu(void) +{ +#if defined(ARDUINO_ARCH_ESP8266) + MY_SERIALDEVICE.println(F("ARCH: ESP8266")); +#elif defined(ARDUINO_ARCH_ESP32) + MY_SERIALDEVICE.println(F("ARCH: ESP32")); +#elif defined(ARDUINO_ARCH_AVR) + MY_SERIALDEVICE.println(F("ARCH: AVR")); +#elif defined(ARDUINO_ARCH_SAMD) + MY_SERIALDEVICE.println(F("ARCH: SAMD")); +#elif defined(ARDUINO_ARCH_STM32F1) + MY_SERIALDEVICE.println(F("ARCH: STM32F1")); +#elif defined(ARDUINO_ARCH_NRF5) || defined(ARDUINO_ARCH_NRF52) + MY_SERIALDEVICE.println(F("ARCH: NRF5")); +#elif defined(__arm__) && defined(TEENSYDUINO) + MY_SERIALDEVICE.println(F("ARCH: Teensyduino")); +#elif defined(__linux__) + MY_SERIALDEVICE.println(F("ARCH: Linux")); +#else + MY_SERIALDEVICE.println(F("ARCH: Unknown")); +#endif +#if defined(ARDUINO_ARCH_AVR) + PRINT(PSTR("AVR fuses: L:%02" PRIX8 ",H:%02" PRIX8 ",E:%02" PRIX8 ",LK:%02" PRIX8 + "\n"), + boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS), + boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS), + boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS), + boot_lock_fuse_bits_get(GET_LOCK_BITS)); +#endif + PRINT(PSTR("T_CPU: %" PRIi8 " C\n"), hwCPUTemperature()); + PRINT(PSTR("V_CPU: %" PRIu16 " mV\n"), hwCPUVoltage()); + MY_SERIALDEVICE.print(F("F_CPU: ")); + MY_SERIALDEVICE.print(hwCPUFrequency() / 10.0); + MY_SERIALDEVICE.println(F(" MHz")); + MY_SERIALDEVICE.print(F("CPU ID: ")); + unique_id_t ID; + const bool result = hwUniqueID(&ID); + PrintHex8(ID, sizeof(ID)); + PRINT(PSTR("UID unique: %s\n"), result ? "true" : "false"); +#if defined(MY_HW_HAS_GETENTROPY) + MY_SERIALDEVICE.println(F("RNG: True")); +#else + MY_SERIALDEVICE.println(F("RNG: Pseudo")); +#endif +#if defined(ARDUINO_ARCH_ESP32) + PRINT(PSTR("Chip rev: %" PRIu8 "\n"), ESP.getChipRevision()); + PRINT(PSTR("Cycles: %" PRIu32 "\n"), ESP.getCycleCount()); + PRINT(PSTR("SDK: %s\n"), ESP.getSdkVersion()); + PRINT(PSTR("EFUSE: %16" PRIX64 "\n"), ESP.getEfuseMac()); + PRINT(PSTR("Total HEAP size: %" PRIu32 "\n"), ESP.getHeapSize()); + PRINT(PSTR("Free HEAP size: %" PRIu32 "\n"), ESP.getFreeHeap()); + PRINT(PSTR("Min HEAP level: %" PRIu32 "\n"), ESP.getMinFreeHeap()); + PRINT(PSTR("Max HEAP alloc: %" PRIu32 "\n"), ESP.getMaxAllocHeap()); + PRINT(PSTR("PSRAM size: %" PRIu32 "\n"), ESP.getPsramSize()); + PRINT(PSTR("Free PSRAM: %" PRIu32 "\n"), ESP.getFreePsram()); + PRINT(PSTR("Min PSRAM level: %" PRIu32 "\n"), ESP.getMinFreePsram()); + PRINT(PSTR("Max PSRAM alloc: %" PRIu32 "\n"), ESP.getMaxAllocPsram()); + PRINT(PSTR("Flash size: %" PRIu32 "\n"), ESP.getFlashChipSize()); + PRINT(PSTR("Flash speed: %" PRIu32 "\n"), ESP.getFlashChipSpeed()); + PRINT(PSTR("Sketch size: %" PRIu32 "\n"), ESP.getSketchSize()); + PRINT(PSTR("Free sketch space: %" PRIu32 "\n"), ESP.getFreeSketchSpace()); +#endif + +#if defined(ARDUINO_ARCH_ESP8266) + PRINT(PSTR("Chip id: %08" PRIX32 "\n"), ESP.getChipId()); + PRINT(PSTR("Cycles: %" PRIu32 "\n"), ESP.getCycleCount()); + PRINT(PSTR("SDK: %s\n"), ESP.getSdkVersion()); + PRINT(PSTR("Free HEAP size: %" PRIu32 "\n"), ESP.getFreeHeap()); + PRINT(PSTR("HEAP fragmentation: %" PRIu8 "\n"), ESP.getHeapFragmentation()); + PRINT(PSTR("Max block alloc: %" PRIu32 "\n"), ESP.getMaxFreeBlockSize()); + PRINT(PSTR("Flash id: %08" PRIX32 "\n"), ESP.getFlashChipId()); + PRINT(PSTR("Flash size: %" PRIu32 "\n"), ESP.getFlashChipSize()); + PRINT(PSTR("Flash speed: %" PRIu32 "\n"), ESP.getFlashChipSpeed()); + PRINT(PSTR("Sketch size: %" PRIu32 "\n"), ESP.getSketchSize()); + PRINT(PSTR("Free sketch space: %" PRIu32 "\n"), ESP.getFreeSketchSpace()); +#endif + while (true) { + + diagnosticsPrintSeparationLine(); + MY_SERIALDEVICE.println(F("MCU:\n\n" + "[Dx] Read PIN\n" + "[Sx] Set PIN\n" + "[Rx] Reset PIN\n" +#if defined(ARDUINO_ARCH_AVR) + "[W] WDT\n" +#endif + "[Px] Sleep x ms\n" + "[X] Exit\n" + )); + diagnosticsPrintSeparationLine(); + diagnosticsFlushSerial(); + diagnosticsSerialInput(); + if (inputCmd == 'D') { + hwPinMode(inputParameter.toInt(), INPUT); + PRINT(PSTR("PIN %" PRIu8 " = %" PRIu8 "\n"), inputParameter.toInt(), + hwDigitalRead(inputParameter.toInt())); + } else if (inputCmd == 'S') { + PRINT(PSTR("SET PIN %" PRIu8 "\n"), inputParameter.toInt()); + hwPinMode(inputParameter.toInt(), OUTPUT); + hwDigitalWrite(inputParameter.toInt(), HIGH); + } else if (inputCmd == 'R') { + PRINT(PSTR("CLR PIN %" PRIu8 "\n"), inputParameter.toInt()); + hwPinMode(inputParameter.toInt(), OUTPUT); + hwDigitalWrite(inputParameter.toInt(), LOW); + } else if (inputCmd == 'W') { +#if defined(ARDUINO_ARCH_AVR) + diagnosticsWatchdogTest(); +#endif + } else if (inputCmd == 'P') { + PRINT(PSTR("Sleeping %" PRIu32 "ms\n"), inputParameter.toInt()); + transportSleep(); + hwSleep((uint32_t)inputParameter.toInt()); + PRINT(PSTR("waking up\n")); + transportStandBy(); + } else if (inputCmd == 'X') { + return; + } else { + MY_SERIALDEVICE.println(F("!CMD")); + } + } +} + +void diagnosticsMainMenu(void) +{ + while (true) { + diagnosticsPrintSeparationLine(); + MY_SERIALDEVICE.println(F("Main:\n\n" + "[M] MCU\n" + "[E] EEPROM\n" + "[C] CRYPTO\n" + "[R] Reboot\n" + "[I] Info\n" + + "[T] TSP SM\n" +#if defined(MY_RADIO_RF24) + "[2] RF24\n" +#endif +#if defined(MY_RADIO_RFM69) && defined(MY_RFM69_NEW_DRIVER) + "[6] RFM69\n" +#endif +#if defined(MY_RADIO_RFM95) + "[9] RFM95\n" +#endif + )); + diagnosticsPrintSeparationLine(); + diagnosticsFlushSerial(); + diagnosticsSerialInput(); + if (inputCmd == 'T') { + diagnosticsTransportSM(); + } else if (inputCmd == 'E') { + diagnosticsEEPROMMenu(); + } else if (inputCmd == 'C') { +#if defined(MY_DIAGNOSTICS_CRYPTO) + diagnosticsCryptoMenu(); +#else + MY_SERIALDEVICE.println(F("> Define MY_DIAGNOSTICS_CRYPTO to enable")); +#endif + } else if (inputCmd == 'M') { + diagnosticsMCUMenu(); + } else if (inputCmd == '2') { +#if defined(MY_RADIO_RF24) + diagnosticsRF24Menu(); +#endif + } else if (inputCmd == '6') { +#if defined(MY_RADIO_RFM69) + diagnosticsRFM69Menu(); +#endif + } else if (inputCmd == '9') { +#if defined(MY_RADIO_RFM95) + diagnosticsRFM95Menu(); +#endif + } else if (inputCmd == 'R') { + hwReboot(); + } else if (inputCmd == 'I') { + MY_SERIALDEVICE.println(F("Press any key to exit\n")); + hwRandomNumberInit(); + while (!MY_SERIALDEVICE.available()) { + PRINT(PSTR("> T_CPU=%" PRIi8 ", V_CPU=%" PRIu16 ", RNG=%" PRIu8 "\n"), + hwCPUTemperature(), hwCPUVoltage(), random(256)); + doYield(); + delay(100); + } + } else { + MY_SERIALDEVICE.println(F("!CMD")); + } + } +} + + +void diagnosticsRun(void) +{ + MY_SERIALDEVICE.println(F("\nMySensors HW diagnostics v1.0")); + diagnosticsPrintSeparationLine(); + PRINT(PSTR("LIB: MySensors %s\n"), MYSENSORS_LIBRARY_VERSION); + PRINT(PSTR("REL: %" PRIu8 "\n"), MYSENSORS_LIBRARY_VERSION_PRERELEASE_NUMBER); + PRINT(PSTR("VER: %" PRIx32 "\n"), MYSENSORS_LIBRARY_VERSION_INT); + PRINT(PSTR("CAP: %s\n"), MY_CAPABILITIES); + diagnosticsMainMenu(); +} diff --git a/core/MyDiagnostics.h b/core/MyDiagnostics.h new file mode 100644 index 000000000..c57600f67 --- /dev/null +++ b/core/MyDiagnostics.h @@ -0,0 +1,29 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in RAM or EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2022 Sensnology AB + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef MyDiagnostics_h +#define MyDiagnostics_h + +/** + * @brief Show diagnostics serial user interface + * + */ +void diagnosticsRun(void); + +#endif diff --git a/core/MySensorsCore.cpp b/core/MySensorsCore.cpp index 90243fdfe..c688f381e 100644 --- a/core/MySensorsCore.cpp +++ b/core/MySensorsCore.cpp @@ -98,6 +98,11 @@ void _infiniteLoop(void) void _begin(void) { +#if defined(MY_DIAGNOSTICS) + (void)hwInit(); + diagnosticsRun(); + _infiniteLoop(); +#endif #if defined(MY_CORE_ONLY) // initialize HW and run setup if present (void)hwInit(); diff --git a/hal/architecture/ESP32/MyHwESP32.cpp b/hal/architecture/ESP32/MyHwESP32.cpp index 8f1b557d3..d756227de 100644 --- a/hal/architecture/ESP32/MyHwESP32.cpp +++ b/hal/architecture/ESP32/MyHwESP32.cpp @@ -18,7 +18,7 @@ * * Arduino core for ESP32: https://github.com/espressif/arduino-esp32 * - * MySensors ESP32 implementation, Copyright (C) 2017-2018 Olivier Mauti + * MySensors ESP32 implementation, Copyright (C) 2017-2019 Olivier Mauti * */ @@ -121,8 +121,16 @@ int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t inte uint16_t hwCPUVoltage(void) { - // in mV - return FUNCTION_NOT_SUPPORTED; + // experimental, not documented feature and inaccurate? + uint16_t internalBatReading; + if (WiFi.status() == 255) { + btStart(); + internalBatReading = rom_phy_get_vdd33(); + btStop(); + } else { + internalBatReading = rom_phy_get_vdd33(); + } + return internalBatReading; } uint16_t hwCPUFrequency(void) diff --git a/hal/architecture/ESP32/MyHwESP32.h b/hal/architecture/ESP32/MyHwESP32.h index 6c4200ede..33b7535ba 100644 --- a/hal/architecture/ESP32/MyHwESP32.h +++ b/hal/architecture/ESP32/MyHwESP32.h @@ -76,6 +76,9 @@ #define hwRandomNumberInit() randomSeed(esp_random()) #define hwGetSleepRemaining() (0ul) +// experimental, not documented feature +extern "C" int rom_phy_get_vdd33(); + bool hwInit(void); void hwReadConfigBlock(void *buf, void *addr, size_t length); void hwWriteConfigBlock(void *buf, void *addr, size_t length); diff --git a/hal/transport/RF24/driver/RF24.cpp b/hal/transport/RF24/driver/RF24.cpp index 3e42b4ecc..865213099 100644 --- a/hal/transport/RF24/driver/RF24.cpp +++ b/hal/transport/RF24/driver/RF24.cpp @@ -127,13 +127,15 @@ LOCAL uint8_t RF24_spiByteTransfer(const uint8_t cmd) LOCAL uint8_t RF24_RAW_readByteRegister(const uint8_t cmd) { const uint8_t value = RF24_spiMultiByteTransfer(cmd, NULL, 1, true); - RF24_DEBUG(PSTR("RF24:RBR:REG=%" PRIu8 ",VAL=%" PRIu8 "\n"), cmd & RF24_REGISTER_MASK, value); + RF24_DEBUG(PSTR("RF24:RBR:REG=0x%02" PRIX8 ",VAL=0x%02" PRIX8 "\n"), cmd & RF24_REGISTER_MASK, + value); return value; } LOCAL uint8_t RF24_RAW_writeByteRegister(const uint8_t cmd, uint8_t value) { - RF24_DEBUG(PSTR("RF24:WBR:REG=%" PRIu8 ",VAL=%" PRIu8 "\n"), cmd & RF24_REGISTER_MASK, value); + RF24_DEBUG(PSTR("RF24:WBR:REG=0x%02" PRIX8 ",VAL=0x%02" PRIX8 "\n"), cmd & RF24_REGISTER_MASK, + value); return RF24_spiMultiByteTransfer( cmd, &value, 1, false); } @@ -161,7 +163,12 @@ LOCAL uint8_t RF24_getFIFOStatus(void) LOCAL void RF24_setChannel(const uint8_t channel) { - RF24_writeByteRegister(RF24_REG_RF_CH,channel); + RF24_writeByteRegister(RF24_REG_RF_CH, channel); +} + +LOCAL uint8_t RF24_getChannel(void) +{ + return RF24_readByteRegister(RF24_REG_RF_CH); } LOCAL void RF24_setRetries(const uint8_t retransmitDelay, const uint8_t retransmitCount) @@ -211,6 +218,11 @@ LOCAL void RF24_setDynamicPayload(const uint8_t pipe) RF24_writeByteRegister(RF24_REG_DYNPD, pipe); } +LOCAL uint8_t RF24_getRFConfiguration(void) +{ + return RF24_readByteRegister(RF24_REG_NRF_CONFIG); +} + LOCAL void RF24_setRFConfiguration(const uint8_t configuration) { RF24_writeByteRegister(RF24_REG_NRF_CONFIG, configuration); @@ -400,10 +412,15 @@ LOCAL bool RF24_sanityCheck(void) return (RF24_readByteRegister(RF24_REG_RF_SETUP) == RF24_RF_SETUP) && (RF24_readByteRegister( RF24_REG_RF_CH) == MY_RF24_CHANNEL); } + +LOCAL uint8_t RF24_getRawTxPowerLevel(void) +{ + return (RF24_readByteRegister(RF24_REG_RF_SETUP) >> 1) & 3; +} LOCAL int16_t RF24_getTxPowerLevel(void) { // in dBm - return (int16_t)((-6) * (3-((RF24_readByteRegister(RF24_REG_RF_SETUP) >> 1) & 3))); + return (int16_t)((-6) * (3 - RF24_getRawTxPowerLevel())); } LOCAL uint8_t RF24_getTxPowerPercent(void) diff --git a/hal/transport/RF24/driver/RF24.h b/hal/transport/RF24/driver/RF24.h index b2ed76d9d..99e67210a 100644 --- a/hal/transport/RF24/driver/RF24.h +++ b/hal/transport/RF24/driver/RF24.h @@ -177,8 +177,9 @@ LOCAL uint8_t RF24_RAW_writeByteRegister(const uint8_t cmd, const uint8_t value) // helper macros #define RF24_readByteRegister(__reg) RF24_RAW_readByteRegister(RF24_CMD_READ_REGISTER | (RF24_REGISTER_MASK & (__reg))) //!< RF24_readByteRegister +#define RF24_readMultiByteRegister(__reg,__buf,__len) RF24_spiMultiByteTransfer(RF24_CMD_READ_REGISTER | (RF24_REGISTER_MASK & (__reg)), (uint8_t *)__buf, __len, true) //!< RF24_readMultiByteRegister #define RF24_writeByteRegister(__reg,__value) RF24_RAW_writeByteRegister(RF24_CMD_WRITE_REGISTER | (RF24_REGISTER_MASK & (__reg)), __value) //!< RF24_writeByteRegister -#define RF24_writeMultiByteRegister(__reg,__buf,__len) RF24_spiMultiByteTransfer(RF24_CMD_WRITE_REGISTER | (RF24_REGISTER_MASK & (__reg)),(uint8_t *)__buf, __len,false) //!< RF24_writeMultiByteRegister +#define RF24_writeMultiByteRegister(__reg,__buf,__len) RF24_spiMultiByteTransfer(RF24_CMD_WRITE_REGISTER | (RF24_REGISTER_MASK & (__reg)), (uint8_t *)__buf, __len, false) //!< RF24_writeMultiByteRegister /** * @brief RF24_flushRX @@ -278,6 +279,11 @@ LOCAL bool RF24_initialize(void); */ LOCAL void RF24_setChannel(const uint8_t channel); /** +* @brief RF24_getChannel +* @return channel +*/ +LOCAL uint8_t RF24_getChannel(void) __attribute__((unused)); +/** * @brief RF24_setRetries * @param retransmitDelay * @param retransmitCount @@ -319,6 +325,11 @@ LOCAL void RF24_setAutoACK(const uint8_t pipe); */ LOCAL void RF24_setDynamicPayload(const uint8_t pipe); /** +* @brief RF24_getRFConfiguration +* @return RF configuration +*/ +LOCAL uint8_t RF24_getRFConfiguration(void) __attribute__((unused)); +/** * @brief RF24_setRFConfiguration * @param configuration */ @@ -357,8 +368,13 @@ LOCAL void RF24_enableFeatures(void); */ LOCAL uint8_t RF24_getTxPowerPercent(void); /** +* @brief RF24_getRawTxPowerLevel +* @return power level +*/ +LOCAL uint8_t RF24_getRawTxPowerLevel(void); +/** * @brief RF24_getTxPowerLevel -* @return +* @return power level in pseudo-dBm */ LOCAL int16_t RF24_getTxPowerLevel(void); /** diff --git a/hal/transport/RFM69/driver/new/RFM69_new.cpp b/hal/transport/RFM69/driver/new/RFM69_new.cpp index ed1f48b42..82b09c613 100644 --- a/hal/transport/RFM69/driver/new/RFM69_new.cpp +++ b/hal/transport/RFM69/driver/new/RFM69_new.cpp @@ -424,6 +424,16 @@ LOCAL bool RFM69_send(const uint8_t recipient, uint8_t *data, const uint8_t len, return RFM69_sendFrame(&packet, increaseSequenceCounter); } +LOCAL uint32_t RFM69_getFrequency(void) +{ + uint32_t freqHz; + freqHz = (uint32_t)RFM69_readReg(RFM69_REG_FRFMSB) << 16; + freqHz |= RFM69_readReg(RFM69_REG_FRFMID) << 8; + freqHz |= RFM69_readReg(RFM69_REG_FRFLSB); + freqHz *= RFM69_FSTEP; + return freqHz; +} + LOCAL void RFM69_setFrequency(const uint32_t frequencyHz) { const uint32_t freqHz = (uint32_t)(frequencyHz / RFM69_FSTEP); diff --git a/hal/transport/RFM69/driver/new/RFM69_new.h b/hal/transport/RFM69/driver/new/RFM69_new.h index 9384b50c2..aea02a39d 100644 --- a/hal/transport/RFM69/driver/new/RFM69_new.h +++ b/hal/transport/RFM69/driver/new/RFM69_new.h @@ -405,6 +405,11 @@ LOCAL bool RFM69_sendFrame(rfm69_packet_t *packet, const bool increaseSequenceCo LOCAL bool RFM69_send(const uint8_t recipient, uint8_t *data, const uint8_t len, const rfm69_controlFlags_t flags, const bool increaseSequenceCounter = true); +/** +* @brief Gets the transmitter and receiver center frequency +* @return frequencyHz Frequency in Hz +*/ +LOCAL uint32_t RFM69_getFrequency(void) __attribute__((unused)); /** * @brief Sets the transmitter and receiver center frequency * @param frequencyHz Frequency in Hz diff --git a/keywords.txt b/keywords.txt index 0889219f1..bcb912db1 100755 --- a/keywords.txt +++ b/keywords.txt @@ -85,6 +85,8 @@ MY_DEBUG LITERAL1 MY_DEBUGDEVICE LITERAL1 MY_DEBUG_VERBOSE_GATEWAY LITERAL1 MY_SPECIAL_DEBUG LITERAL1 +MY_DIAGNOSTICS LITERAL1 +MY_DIAGNOSTICS_CRYPTO LITERAL1 # OTA MY_DEBUG_OTA LITERAL1 diff --git a/tests/Arduino/sketches/diagnostics_rf24/diagnostics_rf24.ino b/tests/Arduino/sketches/diagnostics_rf24/diagnostics_rf24.ino new file mode 100644 index 000000000..9c86a70c7 --- /dev/null +++ b/tests/Arduino/sketches/diagnostics_rf24/diagnostics_rf24.ino @@ -0,0 +1,28 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2022 Sensnology AB + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + ******************************* + */ +#define MY_DEBUG +#define MY_RADIO_RF24 +#define MY_DIAGNOSTICS + +#include + +void setup() {} +void loop() {} diff --git a/tests/Arduino/sketches/diagnostics_rfm69/diagnostics_rfm69.ino b/tests/Arduino/sketches/diagnostics_rfm69/diagnostics_rfm69.ino new file mode 100644 index 000000000..7aaff718a --- /dev/null +++ b/tests/Arduino/sketches/diagnostics_rfm69/diagnostics_rfm69.ino @@ -0,0 +1,29 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2022 Sensnology AB + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + ******************************* + */ +#define MY_DEBUG +#define MY_RADIO_RFM69 +#define MY_RFM69_NEW_DRIVER +#define MY_DIAGNOSTICS + +#include + +void setup() {} +void loop() {} diff --git a/tests/Arduino/sketches/diagnostics_rfm95/diagnostics_rfm95.ino b/tests/Arduino/sketches/diagnostics_rfm95/diagnostics_rfm95.ino new file mode 100644 index 000000000..5eb44427e --- /dev/null +++ b/tests/Arduino/sketches/diagnostics_rfm95/diagnostics_rfm95.ino @@ -0,0 +1,28 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2022 Sensnology AB + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + ******************************* + */ +#define MY_DEBUG +#define MY_RADIO_RFM95 +#define MY_DIAGNOSTICS + +#include + +void setup() {} +void loop() {}