Scanner.ino¶

 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/**
 * Channel scanner and Continuous Carrier Wave Output
 *
 * Example to detect interference on the various channels available.
 * This is a good diagnostic tool to check whether you're picking a
 * good channel for your application.
 *
 * Run this sketch on two devices. On one device, start emitting a constant carrier wave
 * by sending a channel number in the Serial Monitor. The other device scanning should
 * detect the constant carrier wave from the sending device on the given channel.
 * Send a negative number in the Serial Monitor to stop emitting a constant carrier wave
 * and resume scanning.
 *
 * Inspired by cpixip.
 * See https://forum.arduino.cc/t/poor-mans-2-4-ghz-scanner/54846
 *
 * See documentation at https://nRF24.github.io/RF24
 */

/*
 * How to read the output:
 * - The header is a list of supported channels in decimal written vertically.
 * - Each column corresponding to the vertical header is a hexadecimal count of
 *   detected signals (max is 15 or 'f').
 *
 * The following example
 *    000
 *    111
 *    789
 *    ~~~   <- just a divider between the channel's vertical labels and signal counts
 *    1-2
 * can be interpreted as
 * - 1 signal detected on channel 17
 * - 0 signals (denoted as '-') detected on channel 18
 * - 2 signals detected on channel 19
 *
 * Each line of signal counts represent 100 passes of the supported spectrum.
 */

#include "RF24.h"
#include "printf.h"

//
// Hardware configuration
//

#define CE_PIN 7
#define CSN_PIN 8
// instantiate an object for the nRF24L01 transceiver
RF24 radio(CE_PIN, CSN_PIN);

//
// Channel info
//

const uint8_t num_channels = 126;  // 0-125 are supported
uint8_t values[num_channels];      // the array to store summary of signal counts per channel

// To detect noise, we'll use the worst addresses possible (a reverse engineering tactic).
// These addresses are designed to confuse the radio into thinking
// that the RF signal's preamble is part of the packet/payload.
const uint8_t noiseAddress[][2] = { { 0x55, 0x55 }, { 0xAA, 0xAA }, { 0xA0, 0xAA }, { 0xAB, 0xAA }, { 0xAC, 0xAA }, { 0xAD, 0xAA } };

const int num_reps = 100;   // number of passes for each scan of the entire spectrum
bool constCarrierMode = 0;  // this flag controls example behavior (scan mode is default)

void printHeader();  // prototype function for printing the channels' header


void setup(void) {

  // Print preamble
  Serial.begin(115200);
  while (!Serial) {
    // some boards need this to wait for Serial connection
  }
  Serial.println(F("RF24/examples/scanner/"));

  // Setup and configure rf radio
  if (!radio.begin()) {
    Serial.println(F("radio hardware not responding!"));
    while (true) {
      // hold in an infinite loop
    }
  }
  radio.stopConstCarrier();  // in case MCU was reset while radio was emitting carrier wave
  radio.setAutoAck(false);   // Don't acknowledge arbitrary signals
  radio.disableCRC();        // Accept any signal we find
  radio.setAddressWidth(2);  // A reverse engineering tactic (not typically recommended)
  for (uint8_t i = 0; i < 6; ++i) {
    radio.openReadingPipe(i, noiseAddress[i]);
  }

  // set the data rate
  Serial.print(F("Select your Data Rate. "));
  Serial.print(F("Enter '1' for 1 Mbps, '2' for 2 Mbps, '3' for 250 kbps. "));
  Serial.println(F("Defaults to 1Mbps."));
  while (!Serial.available()) {
    // wait for user input
  }
  uint8_t dataRate = Serial.parseInt();
  if (dataRate == 50) {
    Serial.println(F("Using 2 Mbps."));
    radio.setDataRate(RF24_2MBPS);
  } else if (dataRate == 51) {
    Serial.println(F("Using 250 kbps."));
    radio.setDataRate(RF24_250KBPS);
  } else {
    Serial.println(F("Using 1 Mbps."));
    radio.setDataRate(RF24_1MBPS);
  }
  Serial.println(F("***Enter a channel number to emit a constant carrier wave."));
  Serial.println(F("***Enter a negative number to switch back to scanner mode."));

  // Get into standby mode
  radio.startListening();
  radio.stopListening();
  radio.flush_rx();

  // printf_begin();
  // radio.printPrettyDetails();
  // delay(1000);

  // Print out vertical header
  printHeader();
}

void loop(void) {
  /****************************************/
  // Send a number over Serial to begin Constant Carrier Wave output
  // Configure the power amplitude level below
  if (Serial.available()) {
    int8_t c = Serial.parseInt();
    if (c >= 0) {
      c = min(125, max(0, c));  // clamp channel to supported range
      constCarrierMode = 1;
      radio.stopListening();
      delay(2);
      Serial.print("\nStarting Carrier Wave Output on channel ");
      Serial.println(c);
      // for non-plus models, startConstCarrier() changes address on pipe 0 and sets address width to 5
      radio.startConstCarrier(RF24_PA_LOW, c);
    } else {
      constCarrierMode = 0;
      radio.stopConstCarrier();
      radio.setAddressWidth(2);                   // reset address width
      radio.openReadingPipe(0, noiseAddress[0]);  // ensure address is looking for noise
      Serial.println("\nStopping Carrier Wave Output");
      printHeader();
    }

    // discard any CR and LF sent
    while (Serial.peek() != -1) {
      if (Serial.peek() == '\r' || Serial.peek() == '\n') {
        Serial.read();
      } else {  // got a charater that isn't a line feed
        break;  // handle it on next loop() iteration
      }
    }
  }

  /****************************************/

  if (constCarrierMode == 0) {
    // Clear measurement values
    memset(values, 0, sizeof(values));

    // Scan all channels num_reps times
    int rep_counter = num_reps;
    while (rep_counter--) {
      int i = num_channels;
      while (i--) {
        // Select this channel
        radio.setChannel(i);

        // Listen for a little
        radio.startListening();
        delayMicroseconds(128);
        bool foundSignal = radio.testRPD();
        radio.stopListening();

        // Did we get a signal?
        if (foundSignal || radio.testRPD() || radio.available()) {
          ++values[i];
          radio.flush_rx();  // discard packets of noise
        }
      }
    }

    // Print out channel measurements, clamped to a single hex digit
    for (int i = 0; i < num_channels; ++i) {
      if (values[i])
        Serial.print(min(0xf, values[i]), HEX);
      else
        Serial.print(F("-"));
    }
    Serial.println();

  }  // if constCarrierMode == 0
  else {
    // show some output to prove that the program isn't bricked
    Serial.print(F("."));
    delay(1000);  // delay a second to keep output readable
  }
}  // end loop()

void printHeader() {
  // Print the hundreds digits
  for (uint8_t i = 0; i < num_channels; ++i)
    Serial.print(i / 100);
  Serial.println();

  // Print the tens digits
  for (uint8_t i = 0; i < num_channels; ++i)
    Serial.print((i % 100) / 10);
  Serial.println();

  // Print the singles digits
  for (uint8_t i = 0; i < num_channels; ++i)
    Serial.print(i % 10);
  Serial.println();

  // Print the header's divider
  for (uint8_t i = 0; i < num_channels; ++i)
    Serial.print(F("~"));
  Serial.println();
}