Orefatoi / afLib_1_3
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers afLib.cpp Source File

afLib.cpp

00001 /**
00002  * Copyright 2015 Afero, Inc.
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include "mbed.h"
00018 #define Stream Serial
00019 #include "afLib.h"
00020 #include "af_queue.h"
00021 
00022 /**
00023  * Define this to debug your selected transport (ie SPI or UART).
00024  * This will cause a println each time an interrupt or ready byte is received from the ASR.
00025  * You will also get states printed whenever a SYNC transaction is performed by the afLib.
00026  */
00027 #define DEBUG_TRANSPORT     0
00028 
00029 /**
00030  * These are required to be able to recognize the MCU trying to reboot the ASR by setting the command
00031  * attribute. We used local defines with the aflib prefix to make sure they are always defined and don't
00032  * clash with anything the app is using.
00033  */
00034 #define AFLIB_SYSTEM_COMMAND_ATTR_ID    (65012)
00035 #define AFLIB_SYSTEM_COMMAND_REBOOT     (1)
00036 
00037 /**
00038  * Prevent the MCU from spamming us with too many setAttribute requests.
00039  * We do this by waiting a small amount of time in between transactions.
00040  * This prevents sync retries and allows the module to get it's work done.
00041  */
00042 #define MIN_TIME_BETWEEN_UPDATES_MILLIS (50)
00043 
00044 #define IS_MCU_ATTR(x) (x >= 0 && x < 1024)
00045 
00046 static iafLib *_iaflib = NULL;
00047 
00048 #define MAX_SYNC_RETRIES    10
00049 static long lastSync = 0;
00050 static int syncRetries = 0;
00051 static long lastComplete = 0;
00052 
00053 AF_QUEUE_DECLARE(s_request_queue, sizeof(request_t), REQUEST_QUEUE_SIZE);
00054 
00055 /**
00056 * Required for the Linux version of afLib.
00057 */
00058 #ifndef ARDUINO
00059 #if 0
00060 #include <sys/time.h>
00061 
00062 struct timeval start;
00063 
00064 long millis() {
00065     gettimeofday(&start, NULL);
00066     return start.tv_sec;
00067 }
00068 #endif /* for 0 */
00069 #endif /* ARDUINO */
00070 
00071 /**
00072 * These methods are required for the Arduino version of afLib.
00073 * They are no-ops on linux.
00074 */
00075 void noInterrupts() 
00076 {
00077     __disable_irq() ;
00078 }
00079 
00080 void interrupts() 
00081 {
00082     __enable_irq() ;
00083 }
00084 
00085 /* for mbed implementation */
00086 Timer *aflib_timer = 0 ;
00087 
00088 long millis(void)
00089 {
00090     if (aflib_timer == 0) {
00091         aflib_timer = new Timer() ;
00092         aflib_timer->start() ;
00093     }
00094     return(aflib_timer->read_ms()) ;
00095 }
00096 
00097 /**
00098  * getRequestId
00099  * by Motoo Tanaka on 20-Mar-2018
00100  */
00101 int afLib::getRequestId(void)
00102 {
00103     return( _requestId ) ;
00104 }
00105 
00106 /**
00107  * create
00108  *
00109  * The public constructor for the afLib. This allows us to create the afLib object once and hold a reference to it.
00110  */
00111 iafLib *iafLib::create(PinName mcuInterrupt, isr isrWrapper,
00112                        AttrSetHandler attrSet, AttrNotifyHandler attrNotify, Stream *theLog , afTransport *theTransport)
00113 {
00114     if (_iaflib == NULL) {
00115         _iaflib = new afLib( mcuInterrupt, isrWrapper, attrSet, attrNotify, theLog, theTransport);
00116     }
00117 
00118     return _iaflib;
00119 }
00120 
00121 /**
00122  * create
00123  *
00124  * The public constructor for the afLib. This allows us to create the afLib object once and hold a reference to it.
00125  */
00126 iafLib *iafLib::create(AttrSetHandler attrSet, AttrNotifyHandler attrNotify, Stream *theLog, afTransport *theTransport)
00127 {
00128     if (_iaflib == NULL) {
00129         _iaflib = new afLib(PinName(-1), NULL, attrSet, attrNotify, theLog, theTransport);
00130     }
00131 
00132     return _iaflib;
00133 }
00134 
00135 /**
00136  * afLib
00137  *
00138  * The private constructor for the afLib. This one actually initializes the afLib and prepares it for use.
00139  */
00140 afLib::afLib(PinName mcuInterrupt, isr isrWrapper,
00141              AttrSetHandler attrSet, AttrNotifyHandler attrNotify, Stream *theLog, afTransport *theTransport)
00142 {
00143     queueInit();
00144     _theLog= theLog;
00145     _theTransport= theTransport;
00146     _request.p_value = NULL;
00147 
00148     //_spiSettings = SPISettings(1000000, LSBFIRST, SPI_MODE0);
00149     _interrupts_pending = 0;
00150     _state = STATE_IDLE;
00151 
00152     _writeCmd = NULL;
00153     _writeCmdOffset = 0;
00154 
00155     _outstandingSetGetAttrId = 0;
00156 
00157     _readCmd = NULL;
00158     _readCmdOffset = 0;
00159     _readBufferLen = 0;
00160 
00161     _txStatus = new StatusCommand(_theLog);
00162     _rxStatus = new StatusCommand(_theLog);
00163 
00164     _attrSetHandler = attrSet;
00165     _attrNotifyHandler = attrNotify;
00166 
00167 
00168 #ifdef ARDUINO
00169     if (mcuInterrupt != -1) {
00170         pinMode(mcuInterrupt, INPUT);
00171         attachInterrupt(mcuInterrupt, isrWrapper, FALLING);
00172     }
00173 #endif
00174 /* 20-Mar-2018 by Motoo Tanaka for mbed implementation */
00175     if (isrWrapper != 0) {
00176         fco = new InterruptIn(mcuInterrupt) ; 
00177         fco->fall(isrWrapper) ;
00178     }
00179 
00180     _interrupts_pending = 0;
00181 }
00182 
00183 /**
00184  * loop
00185  *
00186  * This is how the afLib gets time to run its state machine. This method should be called periodically from the
00187  * loop() function of the Arduino sketch.
00188  * This function pulls pending attribute operations from the queue. It takes approximately 4 calls to loop() to
00189  * complete one attribute operation.
00190  */
00191 void afLib::loop(void) {
00192     // For UART, we need to look for a magic character on the line as our interrupt.
00193     // We call this method to handle that. For other interfaces, the interrupt pin is used and this method does nothing.
00194     _theTransport->checkForInterrupt(&_interrupts_pending, isIdle());
00195 
00196     if (isIdle() && (queueGet(&_request.messageType, &_request.requestId, &_request.attrId, &_request.valueLen,
00197                               &_request.p_value, &_request.status, &_request.reason) == afSUCCESS)) {
00198         switch (_request.messageType) {
00199             case MSG_TYPE_GET:
00200                 doGetAttribute(_request.requestId, _request.attrId);
00201                 break;
00202 
00203             case MSG_TYPE_SET:
00204                 doSetAttribute(_request.requestId, _request.attrId, _request.valueLen, _request.p_value);
00205                 break;
00206 
00207             case MSG_TYPE_UPDATE:
00208                 doUpdateAttribute(_request.requestId, _request.attrId, _request.valueLen, _request.p_value, _request.status, _request.reason);
00209                 break;
00210 
00211             default:
00212                 _theLog->printf("loop: request type!\n");
00213         }
00214     }
00215 
00216     if (_request.p_value != NULL) {
00217         delete (_request.p_value);
00218         _request.p_value = NULL;
00219     }
00220     runStateMachine();
00221 }
00222 
00223 /**
00224  * updateIntsPending
00225  *
00226  * Interrupt-safe method for updating the interrupt count. This is called to increment and decrement the interrupt count
00227  * as interrupts are received and handled.
00228  */
00229 void afLib::updateIntsPending(int amount) {
00230     noInterrupts();
00231     _interrupts_pending += amount;
00232     interrupts();
00233 }
00234 
00235 /**
00236  * sendCommand
00237  *
00238  * This increments the interrupt count to kick off the state machine in the next call to loop().
00239  */
00240 void afLib::sendCommand(void) {
00241     noInterrupts();
00242     if (_interrupts_pending == 0 && _state == STATE_IDLE) {
00243         updateIntsPending(1);
00244     }
00245     interrupts();
00246 }
00247 
00248 /**
00249  * getAttribute
00250  *
00251  * The public getAttribute method. This method queues the operation and returns immediately. Applications must call
00252  * loop() for the operation to complete.
00253  */
00254 int afLib::getAttribute(const uint16_t attrId) {
00255     _requestId++;
00256     uint8_t dummy; // This value isn't actually used.
00257     return queuePut(MSG_TYPE_GET, _requestId, attrId, 0, &dummy, 0, 0);
00258 }
00259 
00260 /**
00261  * The many moods of setAttribute
00262  *
00263  * These are the public versions of the setAttribute method.
00264  * These methods queue the operation and return immediately. Applications must call loop() for the operation to complete.
00265  */
00266 int afLib::setAttributeBool(const uint16_t attrId, const bool value) {
00267     _requestId++;
00268     uint8_t val = value ? 1 : 0;
00269     return queuePut(IS_MCU_ATTR(attrId) ? MSG_TYPE_UPDATE : MSG_TYPE_SET, _requestId, attrId, sizeof(val),
00270                     (uint8_t *)&val, UPDATE_STATE_UPDATED, UPDATE_REASON_LOCAL_OR_MCU_UPDATE);
00271 }
00272 
00273 int afLib::setAttribute8(const uint16_t attrId, const int8_t value) {
00274     _requestId++;
00275     return queuePut(IS_MCU_ATTR(attrId) ? MSG_TYPE_UPDATE : MSG_TYPE_SET, _requestId, attrId, sizeof(value),
00276                     (uint8_t *)&value, UPDATE_STATE_UPDATED, UPDATE_REASON_LOCAL_OR_MCU_UPDATE);
00277 }
00278 
00279 int afLib::setAttribute16(const uint16_t attrId, const int16_t value) {
00280     _requestId++;
00281     return queuePut(IS_MCU_ATTR(attrId) ? MSG_TYPE_UPDATE : MSG_TYPE_SET, _requestId, attrId, sizeof(value),
00282                     (uint8_t *) &value, UPDATE_STATE_UPDATED, UPDATE_REASON_LOCAL_OR_MCU_UPDATE);
00283 }
00284 
00285 int afLib::setAttribute32(const uint16_t attrId, const int32_t value) {
00286     _requestId++;
00287     return queuePut(IS_MCU_ATTR(attrId) ? MSG_TYPE_UPDATE : MSG_TYPE_SET, _requestId, attrId, sizeof(value),
00288                     (uint8_t *) &value, UPDATE_STATE_UPDATED, UPDATE_REASON_LOCAL_OR_MCU_UPDATE);
00289 }
00290 
00291 int afLib::setAttribute64(const uint16_t attrId, const int64_t value) {
00292     _requestId++;
00293     return queuePut(IS_MCU_ATTR(attrId) ? MSG_TYPE_UPDATE : MSG_TYPE_SET, _requestId, attrId, sizeof(value),
00294                     (uint8_t *) &value, UPDATE_STATE_UPDATED, UPDATE_REASON_LOCAL_OR_MCU_UPDATE);
00295 }
00296 
00297 int afLib::setAttributeStr(const uint16_t attrId, const char *value) {
00298     _requestId++;
00299     return queuePut(IS_MCU_ATTR(attrId) ? MSG_TYPE_UPDATE : MSG_TYPE_SET, _requestId, attrId, strlen(value),
00300                     (uint8_t *) value, UPDATE_STATE_UPDATED, UPDATE_REASON_LOCAL_OR_MCU_UPDATE);
00301 }
00302 
00303 int afLib::setAttributeCStr(const uint16_t attrId, const uint16_t valueLen, const char *value) {
00304     if (valueLen > MAX_ATTRIBUTE_SIZE) {
00305         return afERROR_INVALID_PARAM;
00306     }
00307 
00308     if (value == NULL) {
00309         return afERROR_INVALID_PARAM;
00310     }
00311 
00312     _requestId++;
00313     return queuePut(IS_MCU_ATTR(attrId) ? MSG_TYPE_UPDATE : MSG_TYPE_SET, _requestId, attrId, valueLen,
00314                     (const uint8_t *) value, UPDATE_STATE_UPDATED, UPDATE_REASON_LOCAL_OR_MCU_UPDATE);
00315 }
00316 
00317 int afLib::setAttributeBytes(const uint16_t attrId, const uint16_t valueLen, const uint8_t *value) {
00318     if (valueLen > MAX_ATTRIBUTE_SIZE) {
00319         return afERROR_INVALID_PARAM;
00320     }
00321 
00322     if (value == NULL) {
00323         return afERROR_INVALID_PARAM;
00324     }
00325 
00326     _requestId++;
00327     return queuePut(IS_MCU_ATTR(attrId) ? MSG_TYPE_UPDATE : MSG_TYPE_SET, _requestId, attrId, valueLen, value, UPDATE_STATE_UPDATED, UPDATE_REASON_LOCAL_OR_MCU_UPDATE);
00328 }
00329 
00330 int afLib::setAttributeComplete(uint8_t requestId, const uint16_t attrId, const uint16_t valueLen, const uint8_t *value, uint8_t status, uint8_t reason) {
00331     if (valueLen > MAX_ATTRIBUTE_SIZE) {
00332         return afERROR_INVALID_PARAM;
00333     }
00334 
00335     if (value == NULL) {
00336         return afERROR_INVALID_PARAM;
00337     }
00338 
00339     return queuePut(MSG_TYPE_UPDATE, requestId, attrId, valueLen, value, status, reason);
00340 }
00341 
00342 /**
00343  * doGetAttribute
00344  *
00345  * The private version of getAttribute. This version actually calls sendCommand() to kick off the state machine and
00346  * execute the operation.
00347  */
00348 int afLib::doGetAttribute(uint8_t requestId, uint16_t attrId) {
00349     if (_interrupts_pending > 0 || _writeCmd != NULL) {
00350         return afERROR_BUSY;
00351     }
00352 
00353     _writeCmd = new Command(_theLog,requestId, MSG_TYPE_GET, attrId);
00354     if (!_writeCmd->isValid()) {
00355         _theLog->printf("getAttribute invalid command:");
00356         _writeCmd->dumpBytes();
00357         _writeCmd->dump();
00358         delete (_writeCmd);
00359         _writeCmd = NULL;
00360         return afERROR_INVALID_COMMAND;
00361     }
00362 
00363     _outstandingSetGetAttrId = attrId;
00364 
00365     // Start the transmission.
00366     sendCommand();
00367 
00368     return afSUCCESS;
00369 }
00370 
00371 /**
00372  * doSetAttribute
00373  *
00374  * The private version of setAttribute. This version actually calls sendCommand() to kick off the state machine and
00375  * execute the operation.
00376  */
00377 int afLib::doSetAttribute(uint8_t requestId, uint16_t attrId, uint16_t valueLen, uint8_t *value) {
00378     if (_interrupts_pending > 0 || _writeCmd != NULL) {
00379         return afERROR_BUSY;
00380     }
00381 
00382     _writeCmd = new Command(_theLog,requestId, MSG_TYPE_SET, attrId, valueLen, value);
00383     if (!_writeCmd->isValid()) {
00384         _theLog->printf("setAttributeComplete invalid command:");
00385         _writeCmd->dumpBytes();
00386         _writeCmd->dump();
00387         delete (_writeCmd);
00388         _writeCmd = NULL;
00389         return afERROR_INVALID_COMMAND;
00390     }
00391 
00392     /**
00393      * Recognize when the MCU is trying to reboot the ASR. When this is the case, the ASR will reboot before
00394      * the SPI transaction completes and the _outstandingSetGetAttrId will be left set. Instead, just don't
00395      * set it for this case.
00396      */
00397     if (attrId != AFLIB_SYSTEM_COMMAND_ATTR_ID || *value != AFLIB_SYSTEM_COMMAND_REBOOT) {
00398         _outstandingSetGetAttrId = attrId;
00399     }
00400 
00401     // Start the transmission.
00402     sendCommand();
00403 
00404     return afSUCCESS;
00405 }
00406 
00407 /**
00408  * doUpdateAttribute
00409  *
00410  * setAttribute calls on MCU attributes turn into updateAttribute calls. See documentation on the SPI protocol for
00411  * more information. This method calls sendCommand() to kick off the state machine and execute the operation.
00412  */
00413 int afLib::doUpdateAttribute(uint8_t requestId, uint16_t attrId, uint16_t valueLen, uint8_t *value, uint8_t status, uint8_t reason) {
00414     if (_interrupts_pending > 0 || _writeCmd != NULL) {
00415         return afERROR_BUSY;
00416     }
00417 
00418     _writeCmd = new Command(_theLog, requestId, MSG_TYPE_UPDATE, attrId, status, reason, valueLen, value);
00419     if (!_writeCmd->isValid()) {
00420         _theLog->printf("updateAttribute invalid command:");
00421         _writeCmd->dumpBytes();
00422         _writeCmd->dump();
00423         delete (_writeCmd);
00424         return afERROR_INVALID_COMMAND;
00425     }
00426 
00427     // Start the transmission.
00428     sendCommand();
00429 
00430     return afSUCCESS;
00431 }
00432 
00433 /**
00434  * parseCommand
00435  *
00436  * A debug method for parsing a string into a command. This is not required for library operation and is only supplied
00437  * as an example of how to execute attribute operations from a command line interface.
00438  */
00439 #ifdef ATTRIBUTE_CLI
00440 int afLib::parseCommand(const char *cmd) {
00441     if (_interrupts_pending > 0 || _writeCmd != NULL) {
00442         _theLog->print("Busy: ");
00443         _theLog->print(_interrupts_pending);
00444         _theLog->print(", ");
00445         _theLog->println(_writeCmd != NULL);
00446         return afERROR_BUSY;
00447     }
00448 
00449     int reqId = _requestId++;
00450     _writeCmd = new Command(_theLog,reqId, cmd);
00451     if (!_writeCmd->isValid()) {
00452         _theLog->print("BAD: ");
00453         _theLog->println(cmd);
00454         _writeCmd->dumpBytes();
00455         _writeCmd->dump();
00456         delete (_writeCmd);
00457         _writeCmd = NULL;
00458         return afERROR_INVALID_COMMAND;
00459     }
00460 
00461     // Start the transmission.
00462     sendCommand();
00463 
00464     return afSUCCESS;
00465 }
00466 #endif
00467 
00468 /**
00469  * runStateMachine
00470  *
00471  * The state machine for afLib. This state machine is responsible for implementing the KSP SPI protocol and executing
00472  * attribute operations.
00473  * This method is run:
00474  *      1. In response to receiving an interrupt from the ASR-1.
00475  *      2. When an attribute operation is pulled out of the queue and executed.
00476  */
00477 void afLib::runStateMachine(void) {
00478     if (_interrupts_pending > 0) {
00479 //        _theLog->printf("_interrupts_pending: %d\n",_interrupts_pending ); 
00480 
00481         switch (_state) {
00482             case STATE_IDLE:
00483                 onStateIdle();
00484                 return;
00485 
00486             case STATE_STATUS_SYNC:
00487                 onStateSync();
00488                 break;
00489 
00490             case STATE_STATUS_ACK:
00491                 onStateAck();
00492                 break;
00493 
00494             case STATE_SEND_BYTES:
00495                 onStateSendBytes();
00496                 break;
00497 
00498             case STATE_RECV_BYTES:
00499                 onStateRecvBytes();
00500                 break;
00501 
00502             case STATE_CMD_COMPLETE:
00503                 onStateCmdComplete();
00504                 break;
00505         }
00506 
00507         updateIntsPending(-1);
00508     } else {
00509         if (syncRetries > 0 && syncRetries < MAX_SYNC_RETRIES && millis() - lastSync > 1000) {
00510             _theLog->printf("Sync Retry\n");
00511             updateIntsPending(1);
00512         } else if (syncRetries >= MAX_SYNC_RETRIES) {
00513             _theLog->printf("No response from ASR - does profile have MCU enabled?\n");
00514             syncRetries = 0;
00515             _state = STATE_IDLE;
00516         }
00517     }
00518 }
00519 
00520 /**
00521  * onStateIdle
00522  *
00523  * If there is a command to be written, update the bytes to send. Otherwise we're sending a zero-sync message.
00524  * Either way advance the state to send a sync message.
00525  */
00526 void afLib::onStateIdle(void) {
00527     if (_writeCmd != NULL) {
00528         // Include 2 bytes for length
00529         _bytesToSend = _writeCmd->getSize() + 2;
00530     } else {
00531         _bytesToSend = 0;
00532     }
00533     _state = STATE_STATUS_SYNC;
00534     printState(_state);
00535 }
00536 
00537 /**
00538  * onStateSync
00539  *
00540  * Write a sync message over SPI to let the ASR-1 know that we want to send some data.
00541  * Check for a "collision" which occurs if the ASR-1 is trying to send us data at the same time.
00542  */
00543 void afLib::onStateSync(void) {
00544     int result;
00545 
00546     _txStatus->setAck(false);
00547     _txStatus->setBytesToSend(_bytesToSend);
00548     _txStatus->setBytesToRecv(0);
00549 
00550     result = _theTransport->exchangeStatus(_txStatus, _rxStatus);
00551         
00552     if (result == afSUCCESS && _rxStatus->isValid() && inSync(_txStatus, _rxStatus)) {
00553         syncRetries = 0;   // Flag that sync completed.
00554         _state = STATE_STATUS_ACK;
00555         if (_txStatus->getBytesToSend() == 0 && _rxStatus->getBytesToRecv() > 0) {
00556             _bytesToRecv = _rxStatus->getBytesToRecv();
00557         }
00558     } else {
00559         // Try resending the preamble
00560         _state = STATE_STATUS_SYNC;
00561         lastSync = millis();
00562         syncRetries++;
00563         // _txStatus->dumpBytes();
00564         // _rxStatus->dumpBytes(); 
00565     }
00566     printState(_state);
00567 }
00568 
00569 /**
00570  * onStateAck
00571  *
00572  * Acknowledge the previous sync message and advance the state.
00573  * If there are bytes to send, advance to send bytes state.
00574  * If there are bytes to receive, advance to receive bytes state.
00575  * Otherwise it was a zero-sync so advance to command complete.
00576  */
00577 void afLib::onStateAck(void) {
00578     int result;
00579 
00580     _txStatus->setAck(true);
00581     _txStatus->setBytesToRecv(_rxStatus->getBytesToRecv());
00582     _bytesToRecv = _rxStatus->getBytesToRecv();
00583     result = _theTransport->writeStatus(_txStatus);
00584     if (result != afSUCCESS) {
00585         _state = STATE_STATUS_SYNC;
00586         printState(_state);
00587         return;
00588     }
00589     if (_bytesToSend > 0) {
00590         _writeBufferLen = (uint16_t) _writeCmd->getSize();
00591         _writeBuffer = new uint8_t[_bytesToSend];
00592         memcpy(_writeBuffer, &_writeBufferLen, 2);
00593         _writeCmd->getBytes(&_writeBuffer[2]);
00594         _state = STATE_SEND_BYTES;
00595     } else if (_bytesToRecv > 0) {
00596         _state = STATE_RECV_BYTES;
00597     } else {
00598         _state = STATE_CMD_COMPLETE;
00599     }
00600     printState(_state);
00601 }
00602 
00603 /**
00604  * onStateSendBytes
00605  *
00606  * Send the required number of bytes to the ASR-1 and then advance to command complete.
00607  */
00608 void afLib::onStateSendBytes(void) {
00609     //_theLog->printf("send bytes: %d\n", _bytesToSend); 
00610     _theTransport->sendBytesOffset((char *)_writeBuffer, &_bytesToSend, &_writeCmdOffset);
00611 
00612     if (_bytesToSend == 0) {
00613         _writeBufferLen = 0;
00614         delete (_writeBuffer);
00615         _writeBuffer = NULL;
00616         _state = STATE_CMD_COMPLETE;
00617         printState(_state);
00618     }
00619 }
00620 
00621 /**
00622  * onStateRecvBytes
00623  *
00624  * Receive the required number of bytes from the ASR-1 and then advance to command complete.
00625  */
00626 void afLib::onStateRecvBytes(void) {
00627     _theTransport->recvBytesOffset((char **)&_readBuffer, &_readBufferLen, &_bytesToRecv, &_readCmdOffset);
00628     if (_bytesToRecv == 0) {
00629         _state = STATE_CMD_COMPLETE;
00630         printState(_state);
00631         _readCmd = new Command(_theLog, _readBufferLen, &_readBuffer[2]);
00632         //_readCmd->dumpBytes();
00633         delete (_readBuffer);
00634         _readBuffer = NULL;
00635     }
00636 }
00637 
00638 /**
00639  * onStateCmdComplete
00640  *
00641  * Call the appropriate sketch callback to report the result of the command.
00642  * Clear the command object and go back to waiting for the next interrupt or command.
00643  */
00644 void afLib::onStateCmdComplete(void) {
00645     int result;
00646 
00647     _state = STATE_IDLE;
00648     printState(_state);
00649     if (_readCmd != NULL) {
00650         uint8_t *val = new uint8_t[_readCmd->getValueLen()];
00651         _readCmd->getValue(val);
00652 
00653         uint8_t state;
00654         uint8_t reason;
00655 
00656         switch (_readCmd->getCommand()) {
00657             case MSG_TYPE_SET:
00658                 if (_attrSetHandler(_readCmd->getReqId(), _readCmd->getAttrId(), _readCmd->getValueLen(), val)) {
00659                     state = UPDATE_STATE_UPDATED;
00660                     reason = UPDATE_REASON_SERVICE_SET;
00661                 } else {
00662                     state = UPDATE_STATE_FAILED;
00663                     reason = UPDATE_REASON_INTERNAL_SET_FAIL;
00664                 }
00665                 result = setAttributeComplete(_readCmd->getReqId(), _readCmd->getAttrId(), _readCmd->getValueLen(), val, state, reason);
00666 
00667                 if (result != afSUCCESS) {
00668                     _theLog->printf("Can't reply to SET! This is FATAL!\n");
00669                 }
00670 
00671                 break;
00672 
00673             case MSG_TYPE_UPDATE:
00674                 // If the attr update is a "fake" update, don't send it to the MCU
00675                 if (_readCmd->getReason() != UPDATE_REASON_FAKE_UPDATE) {
00676                     if (_readCmd->getAttrId() == _outstandingSetGetAttrId) {
00677                         _outstandingSetGetAttrId = 0;
00678                     }
00679                     static bool inNotifyHandler;
00680                     if (!inNotifyHandler) {
00681                         inNotifyHandler = true;
00682                         _attrNotifyHandler(_readCmd->getReqId(), _readCmd->getAttrId(), _readCmd->getValueLen(), val);
00683                         inNotifyHandler = false;
00684                     }
00685                     lastComplete = millis();
00686                 }
00687                 break;
00688 
00689             default:
00690                 break;
00691         }
00692         delete (val);
00693         delete (_readCmd);
00694         _readCmdOffset = 0;
00695         _readCmd = NULL;
00696     }
00697 
00698     if (_writeCmd != NULL) {
00699         // Fake a callback here for MCU attributes as we don't get one from the module.
00700         if (_writeCmd->getCommand() == MSG_TYPE_UPDATE && IS_MCU_ATTR(_writeCmd->getAttrId())) {
00701             _attrNotifyHandler(_writeCmd->getReqId(), _writeCmd->getAttrId(), _writeCmd->getValueLen(), _writeCmd->getValueP());
00702             lastComplete = millis();
00703         }
00704         delete (_writeCmd);
00705         _writeCmdOffset = 0;
00706         _writeCmd = NULL;
00707     }
00708 }
00709 
00710 /**
00711  * inSync
00712  *
00713  * Check to make sure the Arduino and the ASR-1 aren't trying to send data at the same time.
00714  * Return true only if there is no collision.
00715  */
00716 bool afLib::inSync(StatusCommand *tx, StatusCommand *rx) {
00717     return (tx->getBytesToSend() == 0 && rx->getBytesToRecv() == 0) ||
00718            (tx->getBytesToSend() > 0 && rx->getBytesToRecv() == 0) ||
00719            (tx->getBytesToSend() == 0 && rx->getBytesToRecv() > 0);
00720 }
00721 
00722 /**
00723  * isIdle
00724  *
00725  * Provide a way for the sketch to know if we're idle. Returns true if there are no attribute operations in progress.
00726  */
00727 bool afLib::isIdle() {
00728     if (lastComplete != 0 && (millis() - lastComplete) < MIN_TIME_BETWEEN_UPDATES_MILLIS) {
00729         return false;
00730     }
00731     lastComplete = 0;
00732     return _interrupts_pending == 0 && _state == STATE_IDLE && _outstandingSetGetAttrId == 0;
00733 }
00734 
00735 /**
00736  * These methods are required to disable/enable interrupts for the Linux version of afLib.
00737  * They are no-ops on Arduino.
00738  */
00739 #ifndef ARDUINO
00740 void disableInterrupts(){}
00741 void enableInterrupts(){}
00742 #endif
00743 
00744 void afLib::mcuISR() {
00745 #if (defined(DEBUG_TRANSPORT) && DEBUG_TRANSPORT > 0)
00746     _theLog->printf("mcuISR\n");
00747 #endif
00748     updateIntsPending(1);
00749 }
00750 
00751 /****************************************************************************
00752  *                              Queue Methods                               *
00753  ****************************************************************************/
00754 
00755 static uint8_t af_queue_preemption_disable(void) {
00756     return 0;
00757 }
00758 
00759 static void af_queue_preemption_enable(uint8_t is_nested) {
00760 }
00761 
00762 /**
00763  * queueInit
00764  *
00765  * Create a small queue to prevent flooding the ASR-1 with attribute operations.
00766  * The initial size is small to allow running on small boards like UNO.
00767  * Size can be increased on larger boards.
00768  */
00769 void afLib::queueInit() {
00770     af_queue_init_system(af_queue_preemption_disable, af_queue_preemption_enable, _theLog);
00771     AF_QUEUE_INIT(s_request_queue, sizeof(request_t), REQUEST_QUEUE_SIZE);
00772 }
00773 
00774 /**
00775  * queuePut
00776  *
00777  * Add an item to the end of the queue. Return an error if we're out of space in the queue.
00778  */
00779 int afLib::queuePut(uint8_t messageType, uint8_t requestId, const uint16_t attributeId, uint16_t valueLen,
00780                     const uint8_t *value, const uint8_t status, const uint8_t reason) {
00781 
00782     queue_t volatile *p_q = &s_request_queue;
00783     request_t *p_event = (request_t *)AF_QUEUE_ELEM_ALLOC_FROM_INTERRUPT(p_q);
00784     if (p_event != NULL) {
00785         p_event->messageType = messageType;
00786         p_event->attrId = attributeId;
00787         p_event->requestId = requestId;
00788         p_event->valueLen = valueLen;
00789         p_event->p_value = new uint8_t[valueLen];
00790         memcpy(p_event->p_value, value, valueLen);
00791         p_event->status = status;
00792         p_event->reason = reason;
00793 
00794         AF_QUEUE_PUT_FROM_INTERRUPT(p_q, p_event);
00795         return afSUCCESS;
00796     }
00797 
00798     return afERROR_QUEUE_OVERFLOW;
00799 }
00800 
00801 /**
00802  * queueGet
00803  *
00804  * Pull and return the oldest item from the queue. Return an error if the queue is empty.
00805  */
00806 int afLib::queueGet(uint8_t *messageType, uint8_t *requestId, uint16_t *attributeId, uint16_t *valueLen,
00807                     uint8_t **value, uint8_t *status, uint8_t *reason) {
00808 
00809     if (AF_QUEUE_PEEK_FROM_INTERRUPT(&s_request_queue)) {
00810         request_t *p_event = (request_t *)AF_QUEUE_GET_FROM_INTERRUPT(&s_request_queue);
00811         *messageType = p_event->messageType;
00812         *attributeId = p_event->attrId;
00813         *requestId = p_event->requestId;
00814         *valueLen = p_event->valueLen;
00815         *value = new uint8_t[*valueLen];
00816         memcpy(*value, p_event->p_value, *valueLen);
00817         delete (p_event->p_value);
00818         p_event->p_value = NULL;
00819         *status = p_event->status;
00820         *reason = p_event->reason;
00821 
00822         AF_QUEUE_ELEM_FREE_FROM_INTERRUPT(&s_request_queue, p_event);
00823         return afSUCCESS;
00824     }
00825 
00826     return afERROR_QUEUE_UNDERFLOW;
00827 }
00828 
00829 /****************************************************************************
00830  *                              Debug Methods                               *
00831  ****************************************************************************/
00832 /**
00833  * dumpBytes
00834  *
00835  * Dump a byte buffer to the debug log.
00836  */
00837 void afLib::dumpBytes(char *label, int len, uint8_t *bytes) {
00838     _theLog->printf("%s\n", label); 
00839     for (int i = 0; i < len; i++) {
00840         if (i > 0) {
00841             _theLog->printf(", ");
00842         }
00843         uint8_t b = bytes[i] & 0xff;
00844 
00845         _theLog->printf("0x%02X", b) ;
00846 #if 0
00847         if (b < 0x10) {
00848             _theLog->print("0x0");
00849             _theLog->print(b, HEX);
00850         } else {
00851             _theLog->print("0x");
00852             _theLog->print(b, HEX);
00853         }
00854 #endif
00855     }
00856     _theLog->printf("\n");
00857 }
00858 
00859 /**
00860  * printState
00861  *
00862  * Print the current state of the afLib state machine.
00863  */
00864 void afLib::printState(int state) {
00865 #if (defined(DEBUG_TRANSPORT) && DEBUG_TRANSPORT > 0)
00866     switch (state) {
00867         case STATE_IDLE:
00868             _theLog->printf("STATE_IDLE\n");
00869             break;
00870         case STATE_STATUS_SYNC:
00871             _theLog->printf("STATE_STATUS_SYNC\n");
00872             break;
00873         case STATE_STATUS_ACK:
00874             _theLog->printf("STATE_STATUS_ACK\n");
00875             break;
00876         case STATE_SEND_BYTES:
00877             _theLog->printf("STATE_SEND_BYTES\n");
00878             break;
00879         case STATE_RECV_BYTES:
00880             _theLog->printf("STATE_RECV_BYTES\n");
00881             break;
00882         case STATE_CMD_COMPLETE:
00883             _theLog->printf("STATE_CMD_COMPLETE\n");
00884             break;
00885         default:
00886             _theLog->printf("Unknown State!\n");
00887             break;
00888     }
00889 #endif
00890 }