| 1 | /* |
| 2 | * Copyright (C) 2011 J. Coliz <maniacbug@ymail.com> |
| 3 | * Updated 2020 TMRh20 |
| 4 | * |
| 5 | * This program is free software; you can redistribute it and/or |
| 6 | * modify it under the terms of the GNU General Public License |
| 7 | * version 2 as published by the Free Software Foundation. |
| 8 | */ |
| 9 | |
| 10 | /** |
| 11 | * Channel scanner and Continuous Carrier Wave Output |
| 12 | * |
| 13 | * Example to detect interference on the various channels available. |
| 14 | * This is a good diagnostic tool to check whether you're picking a |
| 15 | * good channel for your application. |
| 16 | * |
| 17 | * Run this sketch on two devices. On one device, start emitting a constant carrier wave |
| 18 | * by sending a channel number in the Serial Monitor. The other device scanning should |
| 19 | * detect the constant carrier wave from the sending device on the given channel. |
| 20 | * Send a negative number in the Serial Monitor to stop emitting a constant carrier wave |
| 21 | * and resume scanning. |
| 22 | * |
| 23 | * Inspired by cpixip. |
| 24 | * See https://forum.arduino.cc/t/poor-mans-2-4-ghz-scanner/54846 |
| 25 | * |
| 26 | * See documentation at https://nRF24.github.io/RF24 |
| 27 | */ |
| 28 | |
| 29 | /* |
| 30 | * How to read the output: |
| 31 | * - The header is a list of supported channels in decimal written vertically. |
| 32 | * - Each column corresponding to the vertical header is a hexadecimal count of |
| 33 | * detected signals (max is 15 or 'f'). |
| 34 | * |
| 35 | * The following example |
| 36 | * 000 |
| 37 | * 111 |
| 38 | * 789 |
| 39 | * ~~~ <- just a divider between the channel's vertical labels and signal counts |
| 40 | * 1-2 |
| 41 | * can be interpreted as |
| 42 | * - 1 signal detected on channel 17 |
| 43 | * - 0 signals (denoted as '-') detected on channel 18 |
| 44 | * - 2 signals detected on channel 19 |
| 45 | * |
| 46 | * Each line of signal counts represent 100 passes of the supported spectrum. |
| 47 | */ |
| 48 | |
| 49 | #include "RF24.h" |
| 50 | #include "printf.h" |
| 51 | |
| 52 | // |
| 53 | // Hardware configuration |
| 54 | // |
| 55 | |
| 56 | #define CE_PIN 7 |
| 57 | #define CSN_PIN 8 |
| 58 | // instantiate an object for the nRF24L01 transceiver |
| 59 | RF24 radio(CE_PIN, CSN_PIN); |
| 60 | |
| 61 | // |
| 62 | // Channel info |
| 63 | // |
| 64 | |
| 65 | const uint8_t num_channels = 126; // 0-125 are supported |
| 66 | uint8_t values[num_channels]; // the array to store summary of signal counts per channel |
| 67 | |
| 68 | // To detect noise, we'll use the worst addresses possible (a reverse engineering tactic). |
| 69 | // These addresses are designed to confuse the radio into thinking |
| 70 | // that the RF signal's preamble is part of the packet/payload. |
| 71 | const uint8_t noiseAddress[][2] = { { 0x55, 0x55 }, { 0xAA, 0xAA }, { 0xA0, 0xAA }, { 0xAB, 0xAA }, { 0xAC, 0xAA }, { 0xAD, 0xAA } }; |
| 72 | |
| 73 | const int num_reps = 100; // number of passes for each scan of the entire spectrum |
| 74 | bool constCarrierMode = 0; // this flag controls example behavior (scan mode is default) |
| 75 | |
| 76 | void printHeader(); // prototype function for printing the channels' header |
| 77 | |
| 78 | |
| 79 | void setup(void) { |
| 80 | |
| 81 | // Print preamble |
| 82 | Serial.begin(115200); |
| 83 | while (!Serial) { |
| 84 | // some boards need this to wait for Serial connection |
| 85 | } |
| 86 | Serial.println(F("RF24/examples/scanner/")); |
| 87 | |
| 88 | // Setup and configure rf radio |
| 89 | if (!radio.begin()) { |
| 90 | Serial.println(F("radio hardware not responding!")); |
| 91 | while (true) { |
| 92 | // hold in an infinite loop |
| 93 | } |
| 94 | } |
| 95 | radio.stopConstCarrier(); // in case MCU was reset while radio was emitting carrier wave |
| 96 | radio.setAutoAck(false); // Don't acknowledge arbitrary signals |
| 97 | radio.disableCRC(); // Accept any signal we find |
| 98 | radio.setAddressWidth(2); // A reverse engineering tactic (not typically recommended) |
| 99 | for (uint8_t i = 0; i < 6; ++i) { |
| 100 | radio.openReadingPipe(i, noiseAddress[i]); |
| 101 | } |
| 102 | |
| 103 | // set the data rate |
| 104 | Serial.print(F("Select your Data Rate. ")); |
| 105 | Serial.print(F("Enter '1' for 1 Mbps, '2' for 2 Mbps, '3' for 250 kbps. ")); |
| 106 | Serial.println(F("Defaults to 1Mbps.")); |
| 107 | while (!Serial.available()) { |
| 108 | // wait for user input |
| 109 | } |
| 110 | uint8_t dataRate = Serial.parseInt(); |
| 111 | if (dataRate == 50) { |
| 112 | Serial.println(F("Using 2 Mbps.")); |
| 113 | radio.setDataRate(RF24_2MBPS); |
| 114 | } else if (dataRate == 51) { |
| 115 | Serial.println(F("Using 250 kbps.")); |
| 116 | radio.setDataRate(RF24_250KBPS); |
| 117 | } else { |
| 118 | Serial.println(F("Using 1 Mbps.")); |
| 119 | radio.setDataRate(RF24_1MBPS); |
| 120 | } |
| 121 | Serial.println(F("***Enter a channel number to emit a constant carrier wave.")); |
| 122 | Serial.println(F("***Enter a negative number to switch back to scanner mode.")); |
| 123 | |
| 124 | // Get into standby mode |
| 125 | radio.startListening(); |
| 126 | radio.stopListening(); |
| 127 | radio.flush_rx(); |
| 128 | |
| 129 | // printf_begin(); |
| 130 | // radio.printPrettyDetails(); |
| 131 | // delay(1000); |
| 132 | |
| 133 | // Print out vertical header |
| 134 | printHeader(); |
| 135 | } |
| 136 | |
| 137 | void loop(void) { |
| 138 | /****************************************/ |
| 139 | // Send a number over Serial to begin Constant Carrier Wave output |
| 140 | // Configure the power amplitude level below |
| 141 | if (Serial.available()) { |
| 142 | int8_t c = Serial.parseInt(); |
| 143 | if (c >= 0) { |
| 144 | c = min((int8_t)125, c); // clamp channel to supported range |
| 145 | constCarrierMode = 1; |
| 146 | radio.stopListening(); |
| 147 | delay(2); |
| 148 | Serial.print("\nStarting Carrier Wave Output on channel "); |
| 149 | Serial.println(c); |
| 150 | // for non-plus models, startConstCarrier() changes address on pipe 0 and sets address width to 5 |
| 151 | radio.startConstCarrier(RF24_PA_LOW, c); |
| 152 | } else { |
| 153 | constCarrierMode = 0; |
| 154 | radio.stopConstCarrier(); |
| 155 | radio.setAddressWidth(2); // reset address width |
| 156 | radio.openReadingPipe(0, noiseAddress[0]); // ensure address is looking for noise |
| 157 | Serial.println("\nStopping Carrier Wave Output"); |
| 158 | printHeader(); |
| 159 | } |
| 160 | |
| 161 | // discard any CR and LF sent |
| 162 | while (Serial.peek() != -1) { |
| 163 | if (Serial.peek() == '\r' || Serial.peek() == '\n') { |
| 164 | Serial.read(); |
| 165 | } else { // got a charater that isn't a line feed |
| 166 | break; // handle it on next loop() iteration |
| 167 | } |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | /****************************************/ |
| 172 | |
| 173 | if (constCarrierMode == 0) { |
| 174 | // Clear measurement values |
| 175 | memset(values, 0, sizeof(values)); |
| 176 | |
| 177 | // Scan all channels num_reps times |
| 178 | int rep_counter = num_reps; |
| 179 | while (rep_counter--) { |
| 180 | int i = num_channels; |
| 181 | while (i--) { |
| 182 | // Select this channel |
| 183 | radio.setChannel(i); |
| 184 | |
| 185 | // Listen for a little |
| 186 | radio.startListening(); |
| 187 | delayMicroseconds(128); |
| 188 | bool foundSignal = radio.testRPD(); |
| 189 | radio.stopListening(); |
| 190 | |
| 191 | // Did we get a signal? |
| 192 | if (foundSignal || radio.testRPD() || radio.available()) { |
| 193 | ++values[i]; |
| 194 | radio.flush_rx(); // discard packets of noise |
| 195 | } |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | // Print out channel measurements, clamped to a single hex digit |
| 200 | for (int i = 0; i < num_channels; ++i) { |
| 201 | if (values[i]) |
| 202 | Serial.print(min((uint8_t)0xf, values[i]), HEX); |
| 203 | else |
| 204 | Serial.print(F("-")); |
| 205 | } |
| 206 | Serial.println(); |
| 207 | |
| 208 | } // if constCarrierMode == 0 |
| 209 | else { |
| 210 | // show some output to prove that the program isn't bricked |
| 211 | Serial.print(F(".")); |
| 212 | delay(1000); // delay a second to keep output readable |
| 213 | } |
| 214 | } // end loop() |
| 215 | |
| 216 | void printHeader() { |
| 217 | // Print the hundreds digits |
| 218 | for (uint8_t i = 0; i < num_channels; ++i) |
| 219 | Serial.print(i / 100); |
| 220 | Serial.println(); |
| 221 | |
| 222 | // Print the tens digits |
| 223 | for (uint8_t i = 0; i < num_channels; ++i) |
| 224 | Serial.print((i % 100) / 10); |
| 225 | Serial.println(); |
| 226 | |
| 227 | // Print the singles digits |
| 228 | for (uint8_t i = 0; i < num_channels; ++i) |
| 229 | Serial.print(i % 10); |
| 230 | Serial.println(); |
| 231 | |
| 232 | // Print the header's divider |
| 233 | for (uint8_t i = 0; i < num_channels; ++i) |
| 234 | Serial.print(F("~")); |
| 235 | Serial.println(); |
| 236 | } |