/* * 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-2026 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, uint16_t length) { for (uint16_t i = 0; i < length; ++i) { PRINT(PSTR("%02" PRIX8 " "), data[i]); if ( ((i + 1u) % 16u == 0u) || (i + 1u == length) ) { 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, reinterpret_cast(EEPROM_NODE_ID_ADDRESS), SIZE_NODE_ID); PrintHex8(buffer, SIZE_NODE_ID); MY_SERIALDEVICE.print(F("> PAR_ID=")); hwReadConfigBlock(buffer, reinterpret_cast(EEPROM_PARENT_NODE_ID_ADDRESS), SIZE_PARENT_NODE_ID); PrintHex8(buffer, SIZE_PARENT_NODE_ID); MY_SERIALDEVICE.print(F("> D_GW=")); hwReadConfigBlock(buffer, reinterpret_cast(EEPROM_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_cast(EEPROM_CONTROLLER_CONFIG_ADDRESS), SIZE_CONTROLLER_CONFIG); PrintHex8(buffer, SIZE_CONTROLLER_CONFIG); MY_SERIALDEVICE.print(F("> PERS_CRC=")); hwReadConfigBlock(buffer, reinterpret_cast(EEPROM_PERSONALIZATION_CHECKSUM_ADDRESS), SIZE_PERSONALIZATION_CHECKSUM); PrintHex8(buffer, SIZE_PERSONALIZATION_CHECKSUM); MY_SERIALDEVICE.print(F("> FW_TYPE=")); hwReadConfigBlock(buffer, reinterpret_cast(EEPROM_FIRMWARE_TYPE_ADDRESS), SIZE_PERSONALIZATION_CHECKSUM); PrintHex8(buffer, SIZE_PERSONALIZATION_CHECKSUM); MY_SERIALDEVICE.print(F("> FW_VERS=")); hwReadConfigBlock(buffer, reinterpret_cast(EEPROM_FIRMWARE_VERSION_ADDRESS), SIZE_FIRMWARE_VERSION); PrintHex8(buffer, SIZE_FIRMWARE_VERSION); MY_SERIALDEVICE.print(F("> FW_BLOCKS=")); hwReadConfigBlock(buffer, reinterpret_cast(EEPROM_FIRMWARE_BLOCKS_ADDRESS), SIZE_FIRMWARE_BLOCKS); PrintHex8(buffer, SIZE_FIRMWARE_BLOCKS); MY_SERIALDEVICE.print(F("> FW_CRC=")); hwReadConfigBlock(buffer, reinterpret_cast(EEPROM_FIRMWARE_CRC_ADDRESS), SIZE_FIRMWARE_CRC); PrintHex8(buffer, SIZE_FIRMWARE_CRC); MY_SERIALDEVICE.println(F("> SGN_REQ_TABLE:")); hwReadConfigBlock(buffer, reinterpret_cast(EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS), SIZE_SIGNING_REQUIREMENT_TABLE); PrintHex8(buffer, SIZE_SIGNING_REQUIREMENT_TABLE); MY_SERIALDEVICE.println(F("> WL_REQ_TABLE:")); hwReadConfigBlock(buffer, reinterpret_cast(EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS), SIZE_WHITELIST_REQUIREMENT_TABLE); PrintHex8(buffer, SIZE_WHITELIST_REQUIREMENT_TABLE); MY_SERIALDEVICE.println(F("> SGN_SOFT_KEY:")); hwReadConfigBlock(buffer, reinterpret_cast(EEPROM_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_cast(EEPROM_SIGNING_SOFT_SERIAL_ADDRESS), SIZE_SIGNING_SOFT_SERIAL); PrintHex8(buffer, SIZE_SIGNING_SOFT_SERIAL); MY_SERIALDEVICE.println(F("> AES_KEY:")); hwReadConfigBlock(buffer, reinterpret_cast(EEPROM_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_cast(EEPROM_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(temp_iv, aes_iv, sizeof(aes_iv)); (void)memcpy(temp_data, 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(test_data, 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(dest, 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(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_STM32) MY_SERIALDEVICE.println(F("ARCH: STM32")); #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 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(); }