Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: rn2483-TestProgram
RN2483.cpp
00001 #include "RN2483.h" 00002 #include "StringLiterals.h" 00003 #include "Utils.h" 00004 #include "errno.h" 00005 #include "limits.h" 00006 00007 // Structure for mapping error response strings and error codes. 00008 typedef struct StringEnumPair { 00009 const char* stringValue; 00010 uint8_t enumValue; 00011 } StringEnumPair_t; 00012 00013 /** 00014 * @brief Create a new instance of the RN2483. 00015 * @param Serial TX pin name. 00016 * @param Serial RX pin name. 00017 */ 00018 RN2483::RN2483(PinName tx, PinName rx) : 00019 _RN2483(tx, rx, getDefaultBaudRate()), 00020 inputBufferSize(DEFAULT_INPUT_BUFFER_SIZE), 00021 receivedPayloadBufferSize(DEFAULT_RECEIVED_PAYLOAD_BUFFER_SIZE), 00022 packetReceived(false), 00023 isRN2903(false) 00024 { 00025 #ifdef USE_DYNAMIC_BUFFER 00026 this->isBufferInitialized = false; 00027 #endif 00028 } 00029 00030 /** 00031 * @brief Takes care of the init tasks common to both initOTA() and initABP. 00032 */ 00033 void RN2483::init() 00034 { 00035 #ifdef USE_DYNAMIC_BUFFER 00036 // make sure the buffers are only initialized once 00037 if (!isBufferInitialized) { 00038 this->inputBuffer = static_cast<char*>(malloc(this->inputBufferSize)); 00039 this->receivedPayloadBuffer = static_cast<char*>(malloc(this->receivedPayloadBufferSize)); 00040 isBufferInitialized = true; 00041 } 00042 #endif 00043 // make sure the module's state is synced and woken up 00044 sleep(259200000); 00045 wait_ms(10); 00046 wakeUp(); 00047 } 00048 00049 /** 00050 * @brief Initialise settings and connect to network using Over The Air activation. 00051 * @param devEUI provided by LoRaWAN Network server registration. 00052 * @param appEUI provided by LoRaWAN Network server registration. 00053 * @param appKey provided by LoRaWAN Network server registration. 00054 * @return Returns true if network confirmation and able to save settings. 00055 */ 00056 bool RN2483::initOTA(const uint8_t devEUI[8], const uint8_t appEUI[8], const uint8_t appKey[16], bool adr) 00057 { 00058 init(); 00059 if(resetDevice() && setMacParam(STR_DEV_EUI, devEUI, 8) && setMacParam(STR_APP_EUI, appEUI, 8) && 00060 setMacParam(STR_APP_KEY, appKey, 16) && setMacParam(STR_ADR, BOOL_TO_ONOFF(adr)) && joinOTTA()) { 00061 if(saveConfiguration()) { 00062 return true; 00063 } 00064 } 00065 return false; 00066 } 00067 00068 /** 00069 * @brief Initializes the device and connects to the network using Activation By Personalization. 00070 * @param devADDR provided by LoRaWAN Network server registration. 00071 * @param appSKey provided by LoRaWAN Network server registration. 00072 * @param nwkSKey provided by LoRaWAN Network server registration. 00073 * @return Returns true if the parameters were valid and able to save settings. 00074 */ 00075 bool RN2483::initABP(const uint8_t devAddr[4], const uint8_t appSKey[16], const uint8_t nwkSKey[16], bool adr) 00076 { 00077 init(); 00078 if(resetDevice() && setMacParam(STR_DEV_ADDR, devAddr, 4) && setMacParam(STR_APP_SESSION_KEY, appSKey, 16) && 00079 setMacParam(STR_NETWORK_SESSION_KEY, nwkSKey, 16) && setMacParam(STR_ADR, BOOL_TO_ONOFF(adr)) && 00080 joinABP()) { 00081 if(saveConfiguration()) { 00082 return true; 00083 } 00084 } 00085 return false; 00086 } 00087 00088 /** 00089 * @brief Attempts to connect to the network using Over The Air Activation. 00090 * @return Returns true if able to join network. 00091 */ 00092 bool RN2483::joinOTTA() 00093 { 00094 return joinNetwork(STR_OTAA); 00095 } 00096 00097 /** 00098 * @brief Attempts to connect to the network using Activation By Personalization. 00099 * @return Returns true if able to join network. 00100 */ 00101 bool RN2483::joinABP() 00102 { 00103 return joinNetwork(STR_ABP); 00104 } 00105 00106 /** 00107 * @brief Sends the given payload without acknowledgement. 00108 * @param Port to use for transmission. 00109 * @param Payload buffer 00110 * @param Payload buffer size 00111 * @return Returns 0 (NoError) when data was sucessfully fowarded to radio, otherwise returns MacTransmitErrorCode. 00112 */ 00113 uint8_t RN2483::send(uint8_t port, const uint8_t* payload, uint8_t size) 00114 { 00115 return macTransmit(STR_UNCONFIRMED, port, payload, size); 00116 } 00117 00118 /** 00119 * @brief Sends the given payload with acknowledgement. 00120 * @param Port to use for transmission. 00121 * @param Payload buffer 00122 * @param Payload buffer size 00123 * @param Number of transmission retries in event of network transmission failure. 00124 * @return Returns 0 (NoError) when network acks transmission, otherwise returns MacTransmitErrorCode. 00125 */ 00126 uint8_t RN2483::sendReqAck(uint8_t port, const uint8_t* payload, uint8_t size, uint8_t maxRetries) 00127 { 00128 // Need to implement retries! mac set retx 00129 return macTransmit(STR_CONFIRMED, port, payload, size); 00130 } 00131 00132 /** 00133 * @brief Copies the latest received packet (optionally starting from the "payloadStartPosition" of the payload). 00134 * @param Buffer to read into. 00135 * @param Buffer size. 00136 * @return Returns the number of bytes written or 0 if no packet is received since last transmission. 00137 */ 00138 uint16_t RN2483::receive(uint8_t* buffer, uint16_t size, 00139 uint16_t payloadStartPosition) 00140 { 00141 00142 if (!this->packetReceived) { 00143 return 0; 00144 } 00145 00146 uint16_t inputIndex = payloadStartPosition * 2; // payloadStartPosition is in bytes, not hex char pairs 00147 uint16_t outputIndex = 0; 00148 00149 // check that the asked starting position is within bounds 00150 if (inputIndex >= this->receivedPayloadBufferSize) { 00151 return 0; 00152 } 00153 00154 // stop at the first string termination char, or if output buffer is over, or if payload buffer is over 00155 while (outputIndex < size 00156 && inputIndex + 1 < this->receivedPayloadBufferSize 00157 && this->receivedPayloadBuffer[inputIndex] != 0 00158 && this->receivedPayloadBuffer[inputIndex + 1] != 0) { 00159 buffer[outputIndex] = HEX_PAIR_TO_BYTE( 00160 this->receivedPayloadBuffer[inputIndex], 00161 this->receivedPayloadBuffer[inputIndex + 1]); 00162 00163 inputIndex += 2; 00164 outputIndex++; 00165 } 00166 00167 // Note: if the payload has an odd length, the last char is discarded 00168 00169 buffer[outputIndex] = 0; // terminate the string 00170 00171 return outputIndex; 00172 } 00173 00174 /** 00175 * @brief Gets the preprogrammed EUI node address from the module in HEX. 00176 * @param Buffer to read into. 00177 * @param Buffer size. 00178 * @return Returns the number of bytes written or 0 in case of error.. 00179 */ 00180 uint8_t RN2483::getHWEUI(uint8_t* buffer, uint8_t size) 00181 { 00182 _RN2483.printf(STR_CMD_GET_HWEUI); 00183 _RN2483.printf(CRLF); 00184 00185 // TODO move to general "read hex" method 00186 uint8_t inputIndex = 0; 00187 uint8_t outputIndex = 0; 00188 Timer t; 00189 t.start(); 00190 00191 int start = t.read_ms (); 00192 while (t.read_ms () < start + DEFAULT_TIMEOUT) { 00193 if (readLn() > 0) { 00194 while (outputIndex < size 00195 && inputIndex + 1 < this->inputBufferSize 00196 && this->inputBuffer[inputIndex] != 0 00197 && this->inputBuffer[inputIndex + 1] != 0) { 00198 buffer[outputIndex] = HEX_PAIR_TO_BYTE( 00199 this->inputBuffer[inputIndex], 00200 this->inputBuffer[inputIndex + 1]); 00201 inputIndex += 2; 00202 outputIndex++; 00203 } 00204 t.stop(); 00205 return outputIndex; 00206 } 00207 } 00208 t.stop(); 00209 return 0; 00210 } 00211 00212 /** 00213 * @brief Informs the RN2483 to do an ADC conversion on the VDD. 00214 * @param Pass pointer to long for conversion to read into. 00215 * @return Returns if a value was sucessfully read into the long. 00216 */ 00217 bool RN2483::getVDD(long *vdd) 00218 { 00219 _RN2483.printf(STR_CMD_GET_VDD); 00220 _RN2483.printf(CRLF); 00221 Timer t; 00222 t.start(); 00223 int timeout = t.read_ms() + RECEIVE_TIMEOUT; // hard timeouts 00224 while (t.read_ms() < timeout) { 00225 if (readLn() > 0) { 00226 char *temp; 00227 bool rc = true; 00228 errno = 0; 00229 *vdd = strtol(this->inputBuffer, &temp, 10); 00230 if (temp == this->inputBuffer || *temp != '\0' || ((*vdd == LONG_MIN || 00231 *vdd == LONG_MAX) && errno == ERANGE)){ 00232 rc = false; 00233 } 00234 t.stop(); 00235 return rc; 00236 } 00237 } 00238 t.stop(); 00239 return false; 00240 } 00241 00242 #ifdef ENABLE_SLEEP 00243 /** 00244 * @brief Sends a serial line break to wake up the RN2483 00245 */ 00246 void RN2483::wakeUp() 00247 { 00248 // "emulate" break condition 00249 _RN2483.send_break(); 00250 // set baudrate 00251 _RN2483.baud(getDefaultBaudRate()); 00252 _RN2483.putc((uint8_t)0x55); 00253 } 00254 00255 /** 00256 * @brief Sends the RN2483 to sleep for a finite length of time. 00257 * @param Milliseconds to sleep for. 00258 */ 00259 void RN2483::sleep(uint32_t sleepLength) 00260 { 00261 if(sleepLength > 100) { 00262 _RN2483.printf("%s%u",STR_CMD_SLEEP,sleepLength); 00263 _RN2483.printf(CRLF); 00264 } 00265 } 00266 00267 /** 00268 * @brief Sends the RN2483 to sleep for a finite length of time. 00269 * Roughly three days. 00270 */ 00271 void RN2483::sleep() 00272 { 00273 sleep(4294967295); 00274 } 00275 00276 #endif 00277 00278 /** 00279 * @brief Reads a line from the device serial stream. 00280 * @param Buffer to read into. 00281 * @param Size of buffer. 00282 * @param Position to start from. 00283 * @return Number of bytes read. 00284 */ 00285 uint16_t RN2483::readLn(char* buffer, uint16_t size, uint16_t start) 00286 { 00287 int len = readBytesUntil('\n', buffer + start, size); 00288 if (len > 0) { 00289 this->inputBuffer[start + len - 1] = 0; // bytes until \n always end with \r, so get rid of it (-1) 00290 } 00291 00292 return len; 00293 } 00294 00295 /** 00296 * @brief Waits for the given string. 00297 * @param String to look for. 00298 * @param Timeout Period 00299 * @param Position to start from. 00300 * @return Returns true if the string is received before a timeout. 00301 * Returns false if a timeout occurs or if another string is received. 00302 */ 00303 bool RN2483::expectString(const char* str, uint16_t timeout) 00304 { 00305 Timer t; 00306 t.start(); 00307 int start = t.read_ms(); 00308 while (t.read_ms() < start + timeout) { 00309 if (readLn() > 0) { 00310 if (strstr(this->inputBuffer, str) != NULL) { 00311 t.stop(); 00312 return true; 00313 } 00314 t.stop(); 00315 return false; 00316 } 00317 } 00318 t.stop(); 00319 return false; 00320 } 00321 00322 /** 00323 * @brief Looks for an 'OK' response from the RN2483 00324 * @param Timeout Period 00325 * @return Returns true if the string is received before a timeout. 00326 * Returns false if a timeout occurs or if another string is received. 00327 */ 00328 bool RN2483::expectOK(uint16_t timeout) 00329 { 00330 return expectString(STR_RESULT_OK, timeout); 00331 } 00332 00333 /** 00334 * @brief Sends a reset command to the module 00335 * Also sets-up some initial parameters like power index, SF and FSB channels. 00336 * @return Waits for sucess reponse or timeout. 00337 */ 00338 bool RN2483::resetDevice() 00339 { 00340 _RN2483.printf(STR_CMD_RESET); 00341 _RN2483.printf(CRLF); 00342 if (expectString(STR_DEVICE_TYPE_RN)) { 00343 if (strstr(this->inputBuffer, STR_DEVICE_TYPE_RN2483) != NULL) { 00344 isRN2903 = false; 00345 return setPowerIndex(DEFAULT_PWR_IDX_868) && 00346 setSpreadingFactor(DEFAULT_SF_868); 00347 } else if (strstr(this->inputBuffer, STR_DEVICE_TYPE_RN2903) != NULL) { 00348 // TODO move into init once it is decided how to handle RN2903-specific operations 00349 isRN2903 = true; 00350 00351 return setFsbChannels(DEFAULT_FSB) && 00352 setPowerIndex(DEFAULT_PWR_IDX_915) && 00353 setSpreadingFactor(DEFAULT_SF_915); 00354 } else { 00355 return false; 00356 } 00357 } 00358 return false; 00359 } 00360 00361 /** 00362 * @brief Enables all the channels that belong to the given Frequency Sub-Band (FSB) 00363 * disables the rest. 00364 * @param FSB is [1, 8] or 0 to enable all channels. 00365 * @return Returns true if all channels were set successfully. 00366 */ 00367 bool RN2483::setFsbChannels(uint8_t fsb) 00368 { 00369 uint8_t first125kHzChannel = fsb > 0 ? (fsb - 1) * 8 : 0; 00370 uint8_t last125kHzChannel = fsb > 0 ? first125kHzChannel + 7 : 71; 00371 uint8_t fsb500kHzChannel = fsb + 63; 00372 00373 bool allOk = true; 00374 for (uint8_t i = 0; i < 72; i++) { 00375 _RN2483.printf(STR_CMD_SET_CHANNEL_STATUS); 00376 _RN2483.printf("%u",i); 00377 _RN2483.printf(" "); 00378 _RN2483.printf(BOOL_TO_ONOFF(((i == fsb500kHzChannel) || (i >= first125kHzChannel && i <= last125kHzChannel)))); 00379 _RN2483.printf(CRLF); 00380 00381 allOk &= expectOK(); 00382 } 00383 00384 return allOk; 00385 } 00386 00387 /** 00388 * @brief Sets the spreading factor. 00389 * @param Spreading factor parameter. 00390 * @return Returns true if was set successfully. 00391 */ 00392 bool RN2483::setSpreadingFactor(uint8_t spreadingFactor) 00393 { 00394 int8_t datarate; 00395 if (!isRN2903) { 00396 // RN2483 SF(DR) = 7(5), 8(4), 9(3), 10(2), 11(1), 12(0) 00397 datarate = 12 - spreadingFactor; 00398 } else { 00399 // RN2903 SF(DR) = 7(3), 8(2), 9(1), 10(0) 00400 datarate = 10 - spreadingFactor; 00401 } 00402 00403 if (datarate > -1) { 00404 return setMacParam(STR_DATARATE, datarate); 00405 } 00406 00407 return false; 00408 } 00409 00410 /** 00411 * @brief Sets the power index 00412 * @param 868MHz: 1 to 5 / 915MHz: 5, 7, 8, 9 or 10. 00413 * @return Returns true if succesful. 00414 */ 00415 bool RN2483::setPowerIndex(uint8_t powerIndex) 00416 { 00417 return setMacParam(STR_PWR_IDX, powerIndex); 00418 } 00419 00420 /** 00421 * @brief Sets the time interval for the link check process. When the time expires, the next application 00422 * packet will include a link check command to the server. 00423 * @param Decimal number that sets the time interval in seconds, from 0 to 65535. 0 disables link check process. 00424 * @return Returns true if parameter is valid or false if time interval is not valid. 00425 */ 00426 bool RN2483::setLinkCheckInterval(uint8_t linkCheckInterval) 00427 { 00428 return setMacParam(STR_LNK_CHK, linkCheckInterval); 00429 } 00430 00431 /** 00432 * @brief Sets the battery level required for the Device Status Answer frame in LoRaWAN Class A Protocol. 00433 * @param temperature Decimal number between 0-255 representing battery level. 0 means external power, 1 means 00434 * low level, 254 means high level, 255 means the device was unable to measure battery level. 00435 * @return Returns true if battery level is valid or false if value not valid. 00436 */ 00437 bool RN2483::setBattery(uint8_t batLvl) 00438 { 00439 return setMacParam(STR_BAT, batLvl); 00440 } 00441 00442 /** 00443 * @brief Sets the module operation frequency on a given channel ID. 00444 * @param Channel ID from 3 - 15. 00445 * @param Decimal number representing the frequency. 00446 * 863000000 to 870000000 or 433050000 to 434790000 in Hz 00447 * @return Returns true if parameters are valid or false if not. 00448 */ 00449 bool RN2483::setChannelFreq(uint8_t channelID, uint32_t frequency) 00450 { 00451 if((channelID <= 15 && channelID >= 3)) { 00452 if((frequency <=870000000 && frequency >= 863000000)||(frequency <=434790000 && frequency >= 433050000)) { 00453 char buffer [15]; 00454 int bytesWritten = sprintf(buffer, "%d %lu", channelID, frequency); 00455 // Check to make sure sprintf did not return an error before sending. 00456 if(bytesWritten > 0) { 00457 return setMacParam(STR_CH_FREQ, buffer); 00458 } 00459 } 00460 } 00461 return false; 00462 } 00463 00464 /** 00465 * @brief Sets the duty cycle allowed on the given channel ID. 00466 * @param Channel ID to set duty cycle (0-15), 00467 * @param Duty cycle is 0 - 100% as a float. 00468 * @return Returns true if parameters are valid or false if not. 00469 */ 00470 bool RN2483::setDutyCycle(uint8_t channelID, float dutyCycle) 00471 { 00472 // Convert duty cycle into the required value using equation (100 / X) - 1 00473 if((dutyCycle <= (float)100 && dutyCycle >=(float)0) && (channelID > 15)) { 00474 uint8_t dutyCycleSetting = ((float)100 / dutyCycle) - 1; 00475 // Create the string for the settings 00476 char buffer [15]; 00477 int bytesWritten = sprintf(buffer, "%d %d", channelID, dutyCycleSetting); 00478 // Check to make sure sprintf did not return an error before sending. 00479 if(bytesWritten > 0) { 00480 return setMacParam(STR_CH_DCYCLE, buffer); 00481 } 00482 } 00483 return false; 00484 } 00485 00486 /** 00487 * @brief Sets the data rate for a given channel ID. 00488 * Please refer to the LoRaWAN spec for the actual values. 00489 * @param Channel ID from 0 - 15. 00490 * @param Number representing the minimum data rate range from 0 to 7. 00491 * @param Number representing the maximum data rate range from 0 to 7 00492 * @return Returns true if parameters are valid or false if not. 00493 */ 00494 bool RN2483::setDrRange(uint8_t channelID, uint8_t minRange, uint8_t maxRange) 00495 { 00496 if((channelID <= 15)&&(minRange<=7)&&(maxRange<=7)) { 00497 char buffer [15]; 00498 int bytesWritten = sprintf(buffer, "%d %d %d", channelID, minRange, maxRange); 00499 // Check to make sure sprintf did not return an error before sending. 00500 if(bytesWritten > 0) { 00501 return setMacParam(STR_CH_DRRANGE, buffer); 00502 } 00503 } 00504 return false; 00505 } 00506 00507 /** 00508 * @brief Sets a given channel ID to be enabled or disabled. 00509 * @param Channel ID from 0 - 15. 00510 * @param Flag representing if channel is enabled or disabled. 00511 * Warning: duty cycle, frequency and data range must be set for a channel 00512 * before enabling! 00513 * @return Returns true if parameters are valid or false if not. 00514 */ 00515 bool RN2483::setStatus(uint8_t channelID, bool status) 00516 { 00517 if((channelID <= 15)) { 00518 int bytesWritten = 0; 00519 char buffer [15]; 00520 if(status) 00521 bytesWritten = sprintf(buffer, "%d %s", channelID, "on"); 00522 else { 00523 bytesWritten = sprintf(buffer, "%d %s", channelID, "off"); 00524 } 00525 // Check to make sure sprintf did not return an error before sending. 00526 if(bytesWritten > 0) { 00527 return sendCommand(STR_CMD_SET_CHANNEL_STATUS, buffer); 00528 } 00529 } 00530 return false; 00531 } 00532 00533 /** 00534 * @brief The network can issue a command to silence the RN2483. This restores the module. 00535 * @return Returns true if parameters are valid or false if not. 00536 */ 00537 bool RN2483::forceEnable() 00538 { 00539 return sendCommand(STR_MAC_FORCEENABLE); 00540 } 00541 00542 /** 00543 * @brief Saves configurable parameters to eeprom. 00544 * @return Returns true if parameters are valid or false if not. 00545 */ 00546 bool RN2483::saveConfiguration() 00547 { 00548 // Forced to return true currently. 00549 // Currently broken due to the long length of time it takes save to return. 00550 //_RN2483.printf(STR_CMD_SAVE); 00551 //_RN2483.printf(CRLF); 00552 return true; 00553 } 00554 00555 /** 00556 * @brief Sends the command together with the given, paramValue (optional) 00557 * @param Command should include a trailing space if paramValue is set. Refer to RN2483 command ref 00558 * @param Command Parameter to send 00559 * @param Size of param buffer 00560 * @return Returns true on success or false if invalid. 00561 */ 00562 bool RN2483::sendCommand(const char* command, const uint8_t* paramValue, uint16_t size) 00563 { 00564 _RN2483.printf(command); 00565 00566 for (uint16_t i = 0; i < size; ++i) { 00567 _RN2483.putc(static_cast<char>(NIBBLE_TO_HEX_CHAR(HIGH_NIBBLE(paramValue[i])))); 00568 _RN2483.putc(static_cast<char>(NIBBLE_TO_HEX_CHAR(LOW_NIBBLE(paramValue[i])))); 00569 } 00570 00571 _RN2483.printf(CRLF); 00572 00573 return expectOK(); 00574 } 00575 00576 /** 00577 * @brief Sends the command together with the given, paramValue (optional) 00578 * @param Command should include a trailing space if paramValue is set. Refer to RN2483 command ref 00579 * @param Command Parameter to send 00580 * @return Returns true on success or false if invalid. 00581 */ 00582 bool RN2483::sendCommand(const char* command, uint8_t paramValue) 00583 { 00584 _RN2483.printf(command); 00585 _RN2483.printf("%u",paramValue); 00586 _RN2483.printf(CRLF); 00587 00588 return expectOK(); 00589 } 00590 00591 /** 00592 * @brief Sends the command together with the given, paramValue (optional) 00593 * @param Command should include a trailing space if paramValue is set. Refer to RN2483 command ref 00594 * @param Command Parameter to send 00595 * @return Returns true on success or false if invalid. 00596 */ 00597 bool RN2483::sendCommand(const char* command, const char* paramValue) 00598 { 00599 _RN2483.printf(command); 00600 if (paramValue != NULL) { 00601 _RN2483.printf(paramValue); 00602 } 00603 _RN2483.printf(CRLF); 00604 return expectOK(); 00605 } 00606 00607 /** 00608 * @brief Sends a join command to the network 00609 * @param Type of join, OTAA or ABP 00610 * @return Returns true on success or false if fail. 00611 */ 00612 bool RN2483::joinNetwork(const char* type) 00613 { 00614 _RN2483.printf(STR_CMD_JOIN); 00615 _RN2483.printf(type); 00616 _RN2483.printf(CRLF); 00617 00618 return expectOK() && expectString(STR_ACCEPTED, 30000); 00619 } 00620 00621 /** 00622 * @brief Sends the command together with the given paramValue (optional) 00623 * @param MAC param should include a trailing space if paramValue is set. Refer to RN2483 command ref. 00624 * @param Param value to send 00625 * @param Size of Param buffer 00626 * @return Returns true on success or false if invalid. 00627 */ 00628 bool RN2483::setMacParam(const char* paramName, const uint8_t* paramValue, uint16_t size) 00629 { 00630 _RN2483.printf(STR_CMD_SET); 00631 _RN2483.printf(paramName); 00632 00633 for (uint16_t i = 0; i < size; ++i) { 00634 _RN2483.putc(static_cast<char>(NIBBLE_TO_HEX_CHAR(HIGH_NIBBLE(paramValue[i])))); 00635 _RN2483.putc(static_cast<char>(NIBBLE_TO_HEX_CHAR(LOW_NIBBLE(paramValue[i])))); 00636 } 00637 00638 _RN2483.printf(CRLF); 00639 00640 return expectOK(); 00641 } 00642 00643 /** 00644 * @brief Sends the command together with the given paramValue (optional) 00645 * @param MAC param should include a trailing space if paramValue is set. Refer to RN2483 command ref. 00646 * @param Param value to send 00647 * @return Returns true on success or false if invalid. 00648 */ 00649 bool RN2483::setMacParam(const char* paramName, uint8_t paramValue) 00650 { 00651 _RN2483.printf(STR_CMD_SET); 00652 _RN2483.printf(paramName); 00653 _RN2483.printf("%u",paramValue); 00654 _RN2483.printf(CRLF); 00655 00656 return expectOK(); 00657 } 00658 00659 /** 00660 * @brief Sends the command together with the given paramValue (optional) 00661 * @param MAC param should include a trailing space if paramValue is set. Refer to RN2483 command ref. 00662 * @param Param value to send 00663 * @return Returns true on success or false if invalid. 00664 */ 00665 bool RN2483::setMacParam(const char* paramName, const char* paramValue) 00666 { 00667 _RN2483.printf(STR_CMD_SET); 00668 _RN2483.printf(paramName); 00669 _RN2483.printf(paramValue); 00670 _RN2483.printf(CRLF); 00671 00672 return expectOK(); 00673 } 00674 00675 /** 00676 * @brief Returns the enum that is mapped to the given "error" message 00677 * @param Error to lookup. 00678 * @return Returns the enum. 00679 */ 00680 uint8_t RN2483::lookupMacTransmitError(const char* error) 00681 { 00682 if (error[0] == 0) { 00683 return NoResponse; 00684 } 00685 00686 StringEnumPair_t errorTable[] = { 00687 { STR_RESULT_INVALID_PARAM, InternalError }, 00688 { STR_RESULT_NOT_JOINED, NotConnected }, 00689 { STR_RESULT_NO_FREE_CHANNEL, Busy }, 00690 { STR_RESULT_SILENT, Silent }, 00691 { STR_RESULT_FRAME_COUNTER_ERROR, NetworkFatalError }, 00692 { STR_RESULT_BUSY, Busy }, 00693 { STR_RESULT_MAC_PAUSED, InternalError }, 00694 { STR_RESULT_INVALID_DATA_LEN, PayloadSizeError }, 00695 { STR_RESULT_MAC_ERROR, NoAcknowledgment }, 00696 }; 00697 00698 for (StringEnumPair_t * p = errorTable; p->stringValue != NULL; ++p) { 00699 if (strcmp(p->stringValue, error) == 0) { 00700 return p->enumValue; 00701 } 00702 } 00703 00704 return NoResponse; 00705 } 00706 00707 /** 00708 * @brief Sends a a payload and blocks until there is a response back, 00709 * or the receive windows have closed or the hard timeout has passed. 00710 * @param Transmit type 00711 * @param Port to use for transmit 00712 * @param Payload buffer 00713 * @param Size of payload buffer 00714 * @return Returns if sucessfull or if a MAC transmit error. 00715 */ 00716 uint8_t RN2483::macTransmit(const char* type, uint8_t port, const uint8_t* payload, uint8_t size) 00717 { 00718 _RN2483.printf(STR_CMD_MAC_TX); 00719 _RN2483.printf(type); 00720 _RN2483.printf("%u",port); 00721 _RN2483.printf(" "); 00722 00723 for (int i = 0; i < size; ++i) { 00724 _RN2483.putc(static_cast<char>(NIBBLE_TO_HEX_CHAR(HIGH_NIBBLE(payload[i])))); 00725 _RN2483.putc(static_cast<char>(NIBBLE_TO_HEX_CHAR(LOW_NIBBLE(payload[i])))); 00726 } 00727 00728 _RN2483.printf(CRLF); 00729 00730 if (!expectOK()) { 00731 return lookupMacTransmitError(this->inputBuffer); // inputBuffer still has the last line read 00732 } 00733 00734 this->packetReceived = false; // prepare for receiving a new packet 00735 Timer t; 00736 t.start(); 00737 int timeout = t.read_ms() + RECEIVE_TIMEOUT; // hard timeouts 00738 while (t.read_ms() < timeout) { 00739 if (readLn() > 0) { 00740 if (strstr(this->inputBuffer, " ") != NULL) { // to avoid double delimiter search 00741 // there is a splittable line -only case known is mac_rx 00742 t.stop(); 00743 return onMacRX(); 00744 } else if (strstr(this->inputBuffer, STR_RESULT_MAC_TX_OK)) { 00745 // done 00746 t.stop(); 00747 return NoError; 00748 } else { 00749 // lookup the error message 00750 t.stop(); 00751 return lookupMacTransmitError(this->inputBuffer); 00752 } 00753 } 00754 } 00755 t.stop(); 00756 return Timedout; 00757 } 00758 00759 /** 00760 * @brief Parses the input buffer and copies the received payload into 00761 * the "received payload" buffer when a "mac rx" message has been received. 00762 * @return Returns 0 (NoError) or otherwise one of the MacTransmitErrorCodes. 00763 */ 00764 uint8_t RN2483::onMacRX() 00765 { 00766 // parse inputbuffer, put payload into packet buffer 00767 char* token = strtok(this->inputBuffer, " "); 00768 00769 // sanity check 00770 if (strcmp(token, STR_RESULT_MAC_RX) != 0) { 00771 return InternalError; 00772 } 00773 00774 // port 00775 token = strtok(NULL, " "); 00776 00777 // payload 00778 token = strtok(NULL, " "); // until end of string 00779 00780 uint16_t len = strlen(token) + 1; // include termination char 00781 memcpy(this->receivedPayloadBuffer, token, len <= this->receivedPayloadBufferSize ? len : this->receivedPayloadBufferSize); 00782 00783 this->packetReceived = true; // enable receive() again 00784 return NoError; 00785 } 00786 00787 /** 00788 * @brief Private method to read serial port with timeout 00789 * @param The time to wait for in milliseconds. 00790 * @return Returns character or -1 on timeout 00791 */ 00792 int RN2483::timedRead(int _timeout) 00793 { 00794 int c; 00795 Timer t; 00796 t.start(); 00797 long _startMillis = t.read_ms(); // get milliseconds 00798 do { 00799 if(_RN2483.readable()){ 00800 c = _RN2483.getc(); 00801 if (c >= 0){ 00802 t.stop(); 00803 return c; 00804 } 00805 } 00806 } while(t.read_ms() - _startMillis <_timeout); 00807 t.stop(); 00808 return -1; // -1 indicates timeout 00809 } 00810 00811 /** 00812 * @brief Read characters into buffer. 00813 * Terminates if length characters have been read, timeout, or 00814 * if the terminator character has been detected 00815 * @param The terminator character to look for 00816 * @param The buffer to read into. 00817 * @param The number of bytes to read. 00818 * @return The number of bytes read. 0 means no valid data found. 00819 */ 00820 size_t RN2483::readBytesUntil(char terminator, char *buffer, size_t bytesToRead) 00821 { 00822 if (bytesToRead < 1) return 0; 00823 size_t index = 0; 00824 while (index < (bytesToRead - 1 )) { 00825 int c = timedRead(1000); 00826 if (c < 0 || c == terminator) break; 00827 *buffer++ = (char)c; 00828 index++; 00829 } 00830 return index; // return number of characters, not including null terminator 00831 } 00832 00833 #ifdef DEBUG 00834 int freeRam() 00835 { 00836 extern int __heap_start; 00837 extern int *__brkval; 00838 int v; 00839 return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval); 00840 } 00841 #endif
Generated on Fri Jul 22 2022 10:14:24 by
1.7.2