diff --git a/tools/rpi/discover/CMakeLists.txt b/tools/rpi/discover/CMakeLists.txt index 761276a1..33d51ddd 100644 --- a/tools/rpi/discover/CMakeLists.txt +++ b/tools/rpi/discover/CMakeLists.txt @@ -6,7 +6,7 @@ add_compile_options(-Ofast -Wall) # passing the compiler a `-pthread` flag doesn find_library(RF24 rf24 REQUIRED) message(STATUS "using RF24 library: ${RF24}") -add_executable(discover discover.cpp) +add_executable(discover discover.cpp common.cpp) target_link_libraries(discover PUBLIC ${RF24} pthread) add_executable(pretender pretender.cpp common.cpp) diff --git a/tools/rpi/discover/discover.cpp b/tools/rpi/discover/discover.cpp index 05149c68..60d013f6 100644 --- a/tools/rpi/discover/discover.cpp +++ b/tools/rpi/discover/discover.cpp @@ -18,65 +18,16 @@ #include // CLOCK_MONOTONIC_RAW, timespec, clock_gettime() #include // RF24, RF24_PA_LOW, delay() -using namespace std; - -// Generic: -RF24 radio(22, 0); -// See http://nRF24.github.io/RF24/pages.html for more information on usage - -// For this example, we'll be using a payload containing -// a single float number that will be incremented -// on every successful transmission -static union { -float payload = 0.0; -uint8_t b[4]; -}; - -void setRole(); // prototype to set the node's role -void master(); // prototype of the TX node's behavior -void slave(); // prototype of the RX node's behavior +#include +#include -// custom defined timer for evaluating transmission time in microseconds -struct timespec startTimer, endTimer; -uint32_t getMicros(); // prototype to get ellapsed time in microseconds - - -/** Convert given 5-byte address to human readable hex string */ -string prettyPrintAddr(string &a) -{ - ostringstream o; - o << hex << setw(2) - << setfill('0') << setw(2) << int(a[0]) - << ":" << setw(2) << int(a[1]) - << ":" << setw(2) << int(a[2]) - << ":" << setw(2) << int(a[3]) - << ":" << setw(2) << int(a[4]) << dec; - return o.str(); -} +#include "common.hpp" +using namespace std; -/** Convert a Hoymiles inverter/DTU serial number into its - * corresponding NRF24 address byte sequence (5 bytes). - * - * The inverters use a BCD representation of the last 8 - * digits of their serial number, in reverse byte order, - * followed by \x01. - */ -string serno2shockburstaddrbytes(uint64_t n) -{ - char b[5]; - b[3] = (((n/10)%10) << 4) | ((n/1)%10); - b[2] = (((n/1000)%10) << 4) | ((n/100)%10); - b[1] = (((n/100000)%10) << 4) | ((n/10000)%10); - b[0] = (((n/10000000)%10) << 4) | ((n/1000000)%10); - b[4] = 0x01; - - string s = string(b, sizeof(b)); - - cout << dec << "ser# " << n << " --> addr " - << prettyPrintAddr(s) << endl; - return s; -} +// connection to our radio board +RF24 radio(22, 0, 1000000); +// See http://nRF24.github.io/RF24/pages.html for more information on usage /** Ping the given address. @@ -84,31 +35,33 @@ string serno2shockburstaddrbytes(uint64_t n) */ bool doPing(int ch, string src, string dst) { -// radio.setPayloadSize(sizeof(payload)); // float datatype occupies 4 bytes -// radio.setPayloadSize(4); // float datatype occupies 4 bytes + const int payloadsize = 12; + char payload[20]; radio.enableDynamicPayloads(); radio.setChannel(ch); - radio.setPALevel(RF24_PA_MIN); // RF24_PA_MAX is default. + radio.setPALevel(RF24_PA_MAX); // RF24_PA_MAX is default. radio.setDataRate(RF24_250KBPS); - // set the TX address of the RX node into the TX pipe - radio.openWritingPipe((const uint8_t *)dst.c_str()); - // set the RX address of the TX node into a RX pipe radio.openReadingPipe(1, (const uint8_t *)src.c_str()); // ...not that this matters for simple ping/ack radio.stopListening(); // put radio in TX mode - clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer); // start the timer - // bool report = radio.write(&payload, sizeof(float)); // transmit & save the report - bool report = radio.write("P", 1); // transmit & save the report - uint32_t timerEllapsed = getMicros(); // end the timer + // set the TX address of the RX node into the TX pipe + radio.openWritingPipe((const uint8_t *)dst.c_str()); + + // We need to modify the payload every time otherwise recipients + // will detect packets as 'duplicates' and silently ignore them + // (although they will still auto-ack them). + unsigned int r = rand(); + snprintf(payload, sizeof(payload), "ping%08ud", r); + + bool report = radio.write(payload, payloadsize); if (report) { // payload was delivered - payload += 0.01; // increment float payload return true; } return false; // no reply received @@ -117,6 +70,8 @@ bool doPing(int ch, string src, string dst) int main(int argc, char** argv) { + srand(time(NULL)); // Initialization, should only be called once. + if (!radio.begin()) { cout << "radio hardware is not responding!!" << endl; return 0; // quit now @@ -136,13 +91,11 @@ int main(int argc, char** argv) // TODO // we probably want - // - 8-bit crc // - dynamic payloads (check in rf logs) - // - what's the "primary mode"? // - do we need/want "custom ack payloads"? // - use isAckPayloadAvailable() once we've actually contacted an inverter successfully! - radio.printPrettyDetails(); + //radio.printPrettyDetails(); // well-known valid DTU serial number // just in case the inverter only responds to addresses @@ -174,154 +127,12 @@ int main(int argc, char** argv) cout << " - "; } cout << " " << flush; - delay(20); + delay(10); } cout << endl; } - radio.setChannel(76); - - // to use different addresses on a pair of radios, we need a variable to - // uniquely identify which address this radio will use to transmit - bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit - - // print example's name - cout << argv[0] << endl; - - // Let these addresses be used for the pair - uint8_t address[2][6] = {"1Node", "2Node"}; - // It is very helpful to think of an address as a path instead of as - // an identifying device destination - - // Set the radioNumber via the terminal on startup - cout << "Which radio is this? Enter '0' or '1'. Defaults to '0' "; - string input; - getline(cin, input); - radioNumber = input.length() > 0 && (uint8_t)input[0] == 49; - - // save on transmission time by setting the radio to only transmit the - // number of bytes we need to transmit a float - radio.setPayloadSize(sizeof(payload)); // float datatype occupies 4 bytes - - // Set the PA Level low to try preventing power supply related problems - // because these examples are likely run with nodes in close proximity to - // each other. - radio.setPALevel(RF24_PA_MIN); // RF24_PA_MAX is default. - radio.setDataRate(RF24_250KBPS); - -radio.printPrettyDetails(); - - // set the TX address of the RX node into the TX pipe - radio.openWritingPipe(address[radioNumber]); // always uses pipe 0 - - // set the RX address of the TX node into a RX pipe - radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1 - - // For debugging info - // radio.printDetails(); // (smaller) function that prints raw register values - // radio.printPrettyDetails(); // (larger) function that prints human readable data - - // ready to execute program now - setRole(); // calls master() or slave() based on user input + //radio.printPrettyDetails(); return 0; } - -/** - * set this node's role from stdin stream. - * this only considers the first char as input. - */ -void setRole() { - string input = ""; - while (!input.length()) { - cout << "*** PRESS 'T' to begin transmitting to the other node\n"; - cout << "*** PRESS 'R' to begin receiving from the other node\n"; - cout << "*** PRESS 'Q' to exit" << endl; - getline(cin, input); - if (input.length() >= 1) { - if (input[0] == 'T' || input[0] == 't') - master(); - else if (input[0] == 'R' || input[0] == 'r') - slave(); - else if (input[0] == 'Q' || input[0] == 'q') - break; - else - cout << input[0] << " is an invalid input. Please try again." << endl; - } - input = ""; // stay in the while loop - } // while -} // setRole() - - -/** - * make this node act as the transmitter - */ -void master() { - radio.stopListening(); // put radio in TX mode - - unsigned int failure = 0; // keep track of failures - while (failure < 60) { - clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer); // start the timer - bool report = radio.write(&payload, sizeof(float)); // transmit & save the report - uint32_t timerEllapsed = getMicros(); // end the timer - - if (report) { - // payload was delivered - cout << "Transmission successful! Time to transmit = "; - cout << timerEllapsed; // print the timer result - cout << " us. Sent: " << payload; // print payload sent - cout << " hex: " << hex << (unsigned int)b[0] << " " << (unsigned int)b[1] << " " - << (unsigned int)b[2] << " " << (unsigned int)b[3] << " " <