OOKwiz
on/off-keying for ESP32 and a variety of supported radio modules
Radio.cpp
Go to the documentation of this file.
1 #include "Radio.h"
2 #include "serial_output.h"
3 #include "tools.h"
4 #include "radio_plugins/RADIO_INDEX"
5 
6 
7 // static members
8 decltype(Radio::store) Radio::store;
9 int Radio::len = 0;
10 Radio* Radio::current = nullptr;
11 int Radio::pin_rx;
12 int Radio::pin_tx;
13 
14 /// @brief Registers an instance of Radio, i.e. a radio plugin in the static `store`
15 /// @param name (char*) Name of plugin, maximum MAX_RADIO_NAME_LEN characters
16 /// @param pointer Pointer to the plugin instance
17 /// @return `false` if store already holds info on MAX_RADIOS plugins
18 bool Radio::add(const char* name, Radio *pointer) {
19  // Uses char*, and does not DEBUG or INFO because this is ran pre-main by the
20  // constructor of the AutoRegister trick: String and Serial are not available yet.
21  if (len == MAX_RADIOS) {
22  return false;
23  }
24  strncpy(store[len].name, name, MAX_RADIO_NAME_LEN);
25  store[len].name[MAX_RADIO_NAME_LEN] = 0; // just in case
26  store[len].pointer = pointer;
27  len++;
28  return true;
29 }
30 
31 /// @brief Shows loaded plugins, selects the radio in setting `radio` and inits it
32 /// @return `false` if no radio could be selected, whatever `radio_init()` returned oterwise
33 bool Radio::setup() {
34  MANDATORY(radio);
35  INFO("Radio plugins loaded: %s\n", list().c_str());
36  if (select(Settings::getString("radio"))) {
37  return radio_init();
38  }
39  return false;
40 }
41 
42 /// @brief Selects radio given a name by storing its pointer in static `current`
43 /// @param name Name of plugin t be selected
44 /// @return `true` if that radio exists.
45 bool Radio::select(const String &name) {
46  for (int n = 0; n < len; n++) {
47  if (strcmp(store[n].name, name.c_str()) == 0) {
48  current = store[n].pointer;
49  INFO("Radio %s selected.\n", name);
50  return true;
51  }
52  }
53  ERROR("No such radio: '%s'.\n", name.c_str());
54  return false;
55 }
56 
57 /// @brief Returns a String with alist of registered radio plugins
58 /// @param separator Between the names, e.g. ", "
59 /// @return The list
60 String Radio::list(String separator) {
61  String ret;
62  for (int n = 0; n < len; n++) {
63  ret += store[n].name;
64  if (n < len - 1) {
65  ret += separator;
66  }
67  }
68  return ret;
69 }
70 
71 /// @brief Returns the name of the plugin as a String
72 /// @return Name of plugin
73 String Radio::name() {
74  for (int n = 0; n < len; n++) {
75  if (store[n].pointer == this) {
76  return String(store[n].name);
77  }
78  }
79 }
80 
81 /// @brief Static, called as `Radio::radio_init()`, will call overridden `init()` in plugin
82 /// @return whatever plugin's `init()` returns, or `false` if no radio is selected
83 bool Radio::radio_init() {
85  INFO("Initializing radio.\n");
86  pin_rx = Settings::getInt("pin_rx");
87  pin_tx = Settings::getInt("pin_tx");
88  return current->init();
89 }
90 
91 /// @brief Static, called as `Radio::radio_rx()`, will call overridden `rx()` in plugin
92 /// @return whatever plugin's `rx()` returns, or `false` if no radio is selected or no `pin_rx` set
93 bool Radio::radio_rx() {
95  DEBUG("Configuring radio for receiving.\n");
96  if (pin_rx < 0) {
97  ERROR("ERROR: pin_rx needs to be set receive.\n");
98  return false;
99  }
100  PIN_MODE(pin_rx, INPUT);
101  return current->rx();
102 }
103 
104 /// @brief Static, called as `Radio::radio_tx()`, will call overridden `tx()` in plugin
105 /// @return whatever plugin's `tx()` returns, or `false` if no radio is selected or no `pin_tx` set
106 bool Radio::radio_tx() {
108  DEBUG("Configuring radio for transmission.\n");
109  if (pin_tx < 0) {
110  ERROR("ERROR: pin_tx needs to be set for transmit.\n");
111  return false;
112  }
113  PIN_MODE(pin_tx, OUTPUT);
114  PIN_WRITE(pin_tx, !Settings::isSet("tx_active_high"));
115  return current->tx();
116 }
117 
118 /// @brief Static, called as `Radio::radio_standby()`, will call overridden `standby()` in plugin
119 /// @return whatever plugin's `standby()` returns, or `false` if no radio is selected.
122  DEBUG("Radio entering standby mode.\n");
123  return current->standby();
124 }
125 
126 /// @brief virtual, to be overridden by each plugin
127 /// @return `false` if not overridden
128 bool Radio::init() {
129  return false;
130 }
131 
132 /// @brief virtual, to be overridden by each plugin
133 /// @return `false` if not overridden
134 bool Radio::rx() {
135  return false;
136 }
137 
138 /// @brief virtual, to be overridden by each plugin
139 /// @return `false` if not overridden
140 bool Radio::tx() {
141  return false;
142 }
143 
144 /// @brief virtual, to be overridden by each plugin
145 /// @return `false` if not overridden
146 bool Radio::standby() {
147  return false;
148 }
149 
150 // RadioLib-specific
151 
152 /// @brief Sets up the SPI port, inits a RadioLib `Module`, outputs diagnostics wrt RadioLib parameters like freq and bandwidth
154  int pin_sck;
155  SETTING_WITH_DEFAULT(pin_sck, -1);
156  int pin_miso;
157  SETTING_WITH_DEFAULT(pin_miso, -1);
158  int pin_mosi;
159  SETTING_WITH_DEFAULT(pin_mosi, -1);
160  int pin_reset;
161  SETTING_WITH_DEFAULT(pin_reset, -1);
162  String spi_port;
163  SETTING_WITH_DEFAULT(spi_port, "HSPI");
164  int spi_port_int = -1;
165  if (spi_port == "HSPI") {
166  spi_port_int = HSPI;
167  } else if (spi_port == "FSPI") {
168  spi_port_int = FSPI;
169  }
170  #ifdef VSPI
171  else if (spi_port == "VSPI") {
172  spi_port_int = VSPI;
173  }
174  #endif
175  else {
176  ERROR("SPI port '%s' unknown, trying default SPI.\n", spi_port.c_str());
177  }
178  if (spi_port_int != -1 && pin_miso != -1 && pin_mosi != -1 && pin_sck != -1) {
179  INFO("Radio %s: SPI port %s, SCK %i, MISO %i, MOSI %i, CS %i, RESET %i, RX %i, TX %i\n", name().c_str(), spi_port.c_str(), pin_sck, pin_miso, pin_mosi, pin_cs, pin_reset, pin_rx, pin_tx);
180  spi = new SPIClass(spi_port_int);
181  spi->begin(pin_sck, pin_miso, pin_mosi, pin_cs);
182  radioLibModule = new Module(pin_cs, -1, pin_reset, -1, *spi);
183  } else {
184  INFO("Radio %s: default SPI, SCK %i, MISO %i, MOSI %i, CS %i, RESET %i, RX %i, TX %i\n", name().c_str(), SCK, MISO, MOSI, pin_cs, pin_reset, pin_rx, pin_tx);
185  radioLibModule = new Module(pin_cs, -1, pin_reset, -1);
186  }
187  INFO("%s: Frequency: %.2f Mhz, bandwidth %.1f kHz, bitrate %.3f kbps\n", name().c_str(), frequency, bandwidth, bitrate);
188 }
189 
190 /// @brief Serial output of the command as a string and the RadioLib result value
191 /// @param result the numeric value from RadioLib
192 /// @param action The command that was executed as a char*
193 void Radio::showRadiolibResult(const int result, const char* action) {
194  switch (result) {
195  case 0:
196  DEBUG("%s: %s returned 0 (OK)\n", name().c_str(), action);
197  break;
198  case -2:
199  ERROR("%s ERROR: %s returned -2 (CHIP NOT FOUND)\n", name().c_str(), action);
200  break;
201  default:
202  ERROR("%s ERROR: %s returned %i\n%s\n", name().c_str(), action, result,
203  "(See https://github.com/jgromes/RadioLib/blob/master/src/TypeDef.h for Meaning of RadioLib error codes.)");
204  break;
205  }
206 }
207 
208 /// @brief RadioLib-specific picking of one of three constants based on `threshold_type` setting
209 /// @param fixed constant to mean fixed threshold
210 /// @param average constant to mean average threshold
211 /// @param peak constant to mean peak threshold
212 /// @return one of these constants, based on what's in `threshold_type` setting
213 int Radio::thresholdSetup(const int fixed, const int average, const int peak) {
214  String threshold_type;
215  SETTING_WITH_DEFAULT(threshold_type, "peak");
217  INFO("%s: Threshold type %s, level %i\n", name().c_str(), threshold_type.c_str(), threshold_level);
218  if (threshold_type == "fixed") {
219  return fixed;
220  } else if (threshold_type == "average") {
221  return average;
222  } else {
223  return peak;
224  }
225 }