Daniel Knox / RN2483

Dependents:   rn2483-TestProgram

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers RN2483.cpp Source File

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