library for C++ CANOpen implementation. mbed independant, but is easy to attach into with mbed.

Dependents:   ppCANOpen_Example DISCO-F746NG_rtos_test

Example:

Import programppCANOpen_Example

I am no longer actively working on the ppCANOpen library, however, I want to publish this project so that anyone who wants to pick up any of the pieces can have a good example. This is a a project I was working on using the ppCANOpen library. It has a pretty in deep use of the object dictionary structure. And a number of functions to control high voltage pinball drivers, if you're into that sort of thing.

Files at this revision

API Documentation at this revision

Comitter:
ptpaterson
Date:
Sat Feb 13 20:22:59 2016 +0000
Parent:
4:2034b04c86d2
Commit message:
PDO receive complete

Changed in this revision

drivers/mbed-Nucleo-F091RC/canopen_api.cpp Show annotated file Show diff for this revision Revisions of this file
include/CanOpenMessage.h Show annotated file Show diff for this revision Revisions of this file
include/Node.h Show annotated file Show diff for this revision Revisions of this file
include/ObjectDictionary.h Show annotated file Show diff for this revision Revisions of this file
include/ServiceProvider.h Show annotated file Show diff for this revision Revisions of this file
include/canopen_api.h Show annotated file Show diff for this revision Revisions of this file
source/Node.cpp Show annotated file Show diff for this revision Revisions of this file
source/ServiceProvider.cpp Show annotated file Show diff for this revision Revisions of this file
diff -r 2034b04c86d2 -r 22a337cdc0e3 drivers/mbed-Nucleo-F091RC/canopen_api.cpp
--- a/drivers/mbed-Nucleo-F091RC/canopen_api.cpp	Sat Jan 09 17:15:29 2016 +0000
+++ b/drivers/mbed-Nucleo-F091RC/canopen_api.cpp	Sat Feb 13 20:22:59 2016 +0000
@@ -28,44 +28,78 @@
 
 #include "canopen_api.h"
 #include "canopen_config.h"
-#include "CanOpenHandle.h"
 
-#include "mbed.h"
-
-#include "CanOpenHandle.h"
 #include "CanOpenMessage.h"
 
-int CanOpenApiInit (CanOpenHandle *hCanOpen)
+#include "mbed.h" 
+#include "CAN.h"  /* use with CANNucleo library */
+
+#include "stdio.h"
+
+
+void *pServiceObject = 0;
+
+/* Can declarations ---------------------------------------------------------*/
+CAN * can; //(CANOPEN_PIN_RX, CANOPEN_PIN_TX);
+ServiceProviderRxInterruptCallback  serviceRxCallback = 0; 
+void CanReadInterruptCallback (void) {
+    if (serviceRxCallback && pServiceObject) {
+        serviceRxCallback(pServiceObject);   
+    }
+}
+
+/* Ticker declarations ---------------------------------------------------------*/
+Ticker updateTicker;
+ServiceProviderTickCallback serviceTickCallback = 0;
+void UpdateTickerCallback(void) {
+    if (serviceTickCallback && pServiceObject) {
+        serviceTickCallback(pServiceObject);   
+    }   
+}
+
+int CanOpenApiInit  (void *object, ServiceProviderRxInterruptCallback rxCallback, ServiceProviderTickCallback tickCallback)
 {
     printf ("----------- CANOPEN API: API INIT\r\n");
     
-    /** CAN instance to do all of the communication
-      *
-      * @note For some reason, I cannot use "static CAN can".  It will compile,
-      * but code get's stuck during initialization, and will not even enter
-      * main(). Works great like this though! Only one ServiceProvider, though,
-      * so mem leaks should not be an issue if we don't delete it???
-      */
+    /* CAN instance to do all of the communication
+     *
+     * For some reason, I cannot use "static CAN can".  It will compile,
+     * but code get's stuck during initialization, and will not even enter
+     * main(). Works great like this though! Only one ServiceProvider, though,
+     * so mem leaks should not be an issue if we don't delete it???
+     */
     //static CAN can(CANOPEN_PIN_RX, CANOPEN_PIN_TX);
-    CAN * can = new CAN(CANOPEN_PIN_RX, CANOPEN_PIN_TX);
+    //can = new CAN(CANOPEN_PIN_RX, CANOPEN_PIN_TX); 
+    can = new CAN(CANOPEN_PIN_RX, CANOPEN_PIN_TX);
+    //can.reset();
     
-    hCanOpen->can = can;
+    /* init the callback system ---------------------------------------------*/
+    pServiceObject = object;
+    
+    serviceRxCallback = rxCallback;
+    serviceTickCallback = tickCallback;
+    
+    can->attach(CanReadInterruptCallback);
+    updateTicker.attach(UpdateTickerCallback, .0005);
     
     return 1;
 }
 
-int CanOpenApiRead (CanOpenHandle *hCanOpen, CanOpenMessage *canOpenMsg)
+int CanOpenApiRead (CanOpenMessage *canOpenMsg)
 {
     int result = 0;
     
-    printf("api: can_read: Anything to read?\r\n");
+    /* CAUTON ********************************
+     * Could be used in interrupt!!!
+     * ***************************************
+     */
+    //printf("canopen_api: CanOpenApiRead()\r\n");
     
-    /*
-    if (hCanOpen->can) {
+    if (can) {
         
         CANMessage canMsg;
         
-        result = hCanOpen->can->read (canMsg);
+        result = can->read (canMsg);
         if (result) {
             canOpenMsg->id          = canMsg.id;
             canOpenMsg->dataCount   = canMsg.len, 
@@ -82,42 +116,53 @@
             canOpenMsg->data[5] = canMsg.data[5];
             canOpenMsg->data[6] = canMsg.data[6];
             canOpenMsg->data[7] = canMsg.data[7];
+            
+            // DEBUG
+            //printf("   ID:      %d\r\n", (int)canOpenMsg->id);
+            //printf("   Count:   %d\r\n", (int)canOpenMsg->dataCount);
+            //printf("   Type:    %d\r\n", (int)canOpenMsg->type);
+            //printf("   Format:  %d\r\n", (int)canOpenMsg->format);
+            //printf("   Data[0]: %d\r\n", (int)canOpenMsg->data[0]);
         }
         
     }
-    */
     return result;
 }
 
-int CanOpenApiWrite (CanOpenHandle *hCanOpen, CanOpenMessage *canOpenMsg)
+int CanOpenApiWrite (CanOpenMessage *canOpenMsg)
 {
     int result = 0;
     
-    if (hCanOpen->can) {    
+    CANMessage canMsg;
+    canMsg.id       = canOpenMsg->id;
+    canMsg.len      = canOpenMsg->dataCount, 
+    canMsg.type     = canOpenMsg->type   == CANOPEN_TYPE_DATA       ? CANData     : CANRemote;
+    canMsg.format   = canOpenMsg->format == CANOPEN_FORMAT_STANDARD ? CANStandard : CANExtended;
     
-        CANMessage canMsg;
-        canMsg.id       = canOpenMsg->id;
-        canMsg.len      = canOpenMsg->dataCount, 
-        canMsg.type     = canOpenMsg->type   == CANOPEN_TYPE_DATA       ? CANData     : CANRemote;
-        canMsg.format   = canOpenMsg->format == CANOPEN_FORMAT_STANDARD ? CANStandard : CANExtended;
-        
-        // NOTE: memcpy is freezing execution
-        //memcpy(canMsg.data, canOpenMsg->data, canOpenMsg->dataCount); 
-        canMsg.data[0] = canOpenMsg->data[0];
-        canMsg.data[1] = canOpenMsg->data[1];
-        canMsg.data[2] = canOpenMsg->data[2];
-        canMsg.data[3] = canOpenMsg->data[3];
-        canMsg.data[4] = canOpenMsg->data[4];
-        canMsg.data[5] = canOpenMsg->data[5];
-        canMsg.data[6] = canOpenMsg->data[6];
-        canMsg.data[7] = canOpenMsg->data[7];
-                        
-        result = hCanOpen->can->write (canMsg);
-    }
+    // NOTE: memcpy is freezing execution
+    //memcpy(canMsg.data, canOpenMsg->data, canOpenMsg->dataCount); 
+    canMsg.data[0] = canOpenMsg->data[0];
+    canMsg.data[1] = canOpenMsg->data[1];
+    canMsg.data[2] = canOpenMsg->data[2];
+    canMsg.data[3] = canOpenMsg->data[3];
+    canMsg.data[4] = canOpenMsg->data[4];
+    canMsg.data[5] = canOpenMsg->data[5];
+    canMsg.data[6] = canOpenMsg->data[6];
+    canMsg.data[7] = canOpenMsg->data[7];
+                    
+    result = can->write (canMsg);
+    
     
     return result;
 }
 
+static int timeSyncOffset = 0;
+
+uint32_t CanOpenApiGetHardwareTime (void)
+{
+    return HAL_GetTick();
+}
+
 
 
 
@@ -127,4 +172,3 @@
 
 
 
-
diff -r 2034b04c86d2 -r 22a337cdc0e3 include/CanOpenMessage.h
--- a/include/CanOpenMessage.h	Sat Jan 09 17:15:29 2016 +0000
+++ b/include/CanOpenMessage.h	Sat Feb 13 20:22:59 2016 +0000
@@ -29,6 +29,8 @@
 #ifndef PPCAN_CANOPEN_MESSAGE_H
 #define PPCAN_CANOPEN_MESSAGE_H
 
+#include "stdint.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -54,13 +56,91 @@
 
 /** CANOpen Message */
 typedef struct CanOpenMessage {
-    unsigned int    id;
-    char   data[8];
+    uint32_t        id;
+    uint8_t         data[8];
     CanOpenFormat   format;
     CanOpenType     type;
-    unsigned char   dataCount;
+    uint8_t         dataCount;
 } CanOpenMessage;
 
+
+/*=========================================================================
+ * CANOpen Function Codes
+ *=========================================================================
+ */
+ 
+/** CANOpen Function Codes */
+typedef enum {
+    CANOPEN_FUNCTION_CODE_NMT        = 0x00,
+    CANOPEN_FUNCTION_CODE_SYNC       = 0x01,
+    CANOPEN_FUNCTION_CODE_TIME       = 0x02,
+    CANOPEN_FUNCTION_CODE_PDO1T      = 0x03,
+    CANOPEN_FUNCTION_CODE_PDO1R      = 0x04,
+    CANOPEN_FUNCTION_CODE_PDO2T      = 0x05,
+    CANOPEN_FUNCTION_CODE_PDO2R      = 0x06,
+    CANOPEN_FUNCTION_CODE_PDO3T      = 0x07,
+    CANOPEN_FUNCTION_CODE_PDO3R      = 0x08,
+    CANOPEN_FUNCTION_CODE_PDO4T      = 0x09,
+    CANOPEN_FUNCTION_CODE_PDO4R      = 0x0A,
+    CANOPEN_FUNCTION_CODE_SDOT       = 0x0B,
+    CANOPEN_FUNCTION_CODE_SDOR       = 0x0C,
+    CANOPEN_FUNCTION_CODE_NODE_GUARD = 0x0E,
+    CANOPEN_FUNCTION_CODE_LSS        = 0x0F
+} CanOpenFunctionCodes;
+
+/* Message Constants */
+#define MESSAGE_NODEID_BITS     0b00001111111
+#define MESSAGE_COMMAND_BITS    0b11110000000
+
+/* Message Macros -----------------------------------------------------------*/ 
+#define MESSAGE_GET_NODEID(cobId)   (cobId & MESSAGE_NODEID_BITS)
+#define MESSAGE_GET_COMMAND(cobId)  ((cobId & MESSAGE_COMMAND_BITS) >> 7)
+
+/*=========================================================================
+ * SDO MESSAGE PARAMETERS
+ *=========================================================================
+ */
+
+/** SDO initiate protocol command specifiers */
+typedef enum {
+     SDO_CCS_DOWNLOAD_SEGMENT_REQUEST   = 0x00, 
+     SDO_CCS_INITIATE_DOWNLOAD_REQUEST  = 0x01,
+     SDO_CCS_INITIATE_UPLOAD_REQUEST    = 0x02,
+} SdoClientCommandSpecifier;
+
+/** SDO segment protocol command specifiers */
+typedef enum {
+     SDO_SCS_DOWNLOAD_SEGMENT_RESPONSE  = 0x01,
+     SDO_SCS_INITIATE_UPLOAD_RESPONSE   = 0x02,
+     SDO_SCS_INITIATE_DOWNLOAD_RESPONSE = 0x03,
+} SdoServerCommandSpecifier;
+
+/* SDO constants --------------------------------------------------------*/
+#define SDO_SIZE_INDICATOR_BIT  0b00000001
+#define SDO_TRANSFER_TYPE_BIT   0b00000010
+#define SDO_DATA_COUNT_BITS     0b00001100
+#define SDO_TOGGLE_BIT          0b00010000
+#define SDO_CS_BITS             0b11100000
+
+/* SDO macros -----------------------------------------------------------*/ 
+#define SDO_GET_CS(data0)           ((data0 & SDO_CS_BITS) >> 5)
+#define SDO_GET_DATA_COUNT(data0)   ((data0 & SDO_DATA_COUNT_BITS) >> 2)
+
+
+/*=========================================================================
+ * NMT MESSAGE PARAMETERS
+ *=========================================================================
+ */
+
+/** NMT node control protocol command specifiers */
+typedef enum {
+     NMT_CS_START       = 0x01,
+     NMT_CS_STOP        = 0x02,
+     NMT_CS_ENTER_PREOP = 0x80,
+     NMT_CS_RESET_NODE  = 0x81,
+     NMT_CS_RESET_COM   = 0x82
+} NmtCommandSpecifier;
+
 #ifdef __cplusplus
 };
 #endif
diff -r 2034b04c86d2 -r 22a337cdc0e3 include/Node.h
--- a/include/Node.h	Sat Jan 09 17:15:29 2016 +0000
+++ b/include/Node.h	Sat Feb 13 20:22:59 2016 +0000
@@ -29,12 +29,14 @@
 #ifndef PPCAN_NODE_H
 #define PPCAN_NODE_H
 
+#include "ObjectDictionary.h"
 
 /*=========================================================================
  * Forward declarations
  *=========================================================================
  */
 struct CanOpenMessage;
+//struct ObjectData;
 
 /* NMT Constants --------------------------------------------------------*/
 #define NMT_STATE_TOGGLE_BIT      0x80
@@ -47,8 +49,6 @@
 
 /* Avoid circular reference */
 class ServiceProvider;
-class ObjectDictionary;
-
 
 
 /** Node Class to implement feature of a CANOpen NMT node
@@ -57,6 +57,11 @@
 {
 public:
 
+    /* ========================================================================
+     * Internal structures and constants
+     * ========================================================================
+     */
+     
     /** Node network management state */
     struct State {
         static const int INITIALIZED     = 0x00;
@@ -80,45 +85,60 @@
         int bLifeGuardToggle;
     };
 
+    /* ========================================================================
+     * Construction
+     * ========================================================================
+     */
+     
     /** Maintain the multitude of states for a node */
-
-    Node (ServiceProvider * provider);    
+    Node (int id, ServiceProvider * provider, int bLoop = 0);    
 
     /* ========================================================================
-     * Methods to handle operation of node device
+     * Public Methods
      * ========================================================================
      */
     
-    // TODO: pass elapsed time to Update method
-    virtual void Update (void);
+    /** Call from ServiceProvider on an interrupt */
+    void FixedUpdate (uint32_t time);
+    
+    /** Call from ServiceProvider every loop of Run() */
+    void Update (void);
     
+    /** Handle message given by the ServiceProvider*/
+     int DispatchMessage(CanOpenMessage *canOpenMsg);
+     
     /* ========================================================================
-     * Message indication
-     * Called by a ServiceProvider when a message is received.
+     * Public Member Variables
      * ========================================================================
      */
      
-    /**
-      * @note
+    /* Common Node properties -----------------------------------------------*/
+    /** Network id for the node
+      * @note Ref is held in object dictionary and upon setting, may need to
+      * update some other objects.
       */
-     int DispatchMessage(CanOpenMessage *canOpenMsg);
+    int nodeId;
 
+    /** Loopback Mode.  Default off (0), if on (1) then will hear it's own 
+      * messages
+      */
+    int bLoopbackOn;
 
 protected:
 
     /* ========================================================================
-     * Methods to handle various messages
-     * Called by a ServiceProvider when a message is received.
+     * Protected? Methods to handle various messages
+     * Used to deal with everything from inside of Dispatch Message
      * ========================================================================
      */
     
     /* PDO (7.2.2), MPDO (7.2.3) --------------------------------------------*/
     
-    int ConsumePdo (int pdoNum, char * dataIn);
-    int HandlePdoReadRequest (int pdoNum);
+    int HandlePdo (CanOpenMessage *canOpenMsg);
     
     
     /* SDO (7.2.4) ----------------------------------------------------------*/
+    int HandleSdo (CanOpenMessage *canOpenMsg);
     
     int HandleExpeditedDownload (int sdoNum, int index, int subIndex, int dataCount, char * data);
     int HandleInitiateDownloadRequest (int sdoNum, int index, int subIndex, int dataCount, char * data);
@@ -129,12 +149,10 @@
     // TODO: express and not express
     
     /* SYNC object (7.2.5) --------------------------------------------------*/
-    
-    // TODO: SYNC consumer 
+    int HandleSync (CanOpenMessage *canOpenMsg);
     
     /* Time Stamp object (7.2.6) --------------------------------------------*/
-    
-    // TODO: time consumer
+    /* Handled all inside of the ServiceProvider */
     
     /* Emergency object (7.2.7) ---------------------------------------------*/
     
@@ -143,7 +161,7 @@
     
     /* Network Management (7.2.8) -------------------------------------------*/
     /* ---- Node Control (7.2.8.2.1) ----------------------------------------*/
-    int HandleNodeControl (int commandSpecifier);
+    int HandleNodeControl (CanOpenMessage *canOpenMsg);
 
 
     /* ---- Error Control (7.2.8.2.2) ---------------------------------------*/
@@ -151,17 +169,34 @@
     int HandleNodeGuardRequest (int masterId);
     int ConsumeHeartbeat (int producerId);
     
-    
+protected:
+
     /* ========================================================================
-     * Methods to implement node control in derived classes
+     * Protected Methods to implement node application in derived classes
      * ========================================================================
      */
      
-     /** Perform actions every cycle
+     /** Perform actions every cycle (when in operational mode)
+       * @note Override to implement user application
+       * @note The OnTick function should avoid modifying data in the object
+       * dictionary unless necessary, because there will almost certainly be 
+       * a race condition.
+       */
+     virtual void OnFixedUpdate(void) = 0;
+     
+     
+     
+     virtual void OnUpdate(void) = 0;
+    
+    
+    /* SYNC -----------------------------------------------------------------*/
+    /** Perform actions when state changed to stop
       * @note Override to implement user application
       */
-     virtual void OnUpdate(void) = 0;
+    virtual void OnSync (uint8_t counter) = 0;
     
+    
+    /* NMT Node Control -----------------------------------------------------*/
     /** Perform actions when node reset
       * @note Override to implement user application
       */
@@ -182,36 +217,61 @@
       */
     virtual void OnStopped (void) = 0;
     
+    /* Object Dictionary Handling -------------------------------------------*/
+    /** Abstract method to give access to the object entries of derived
+      * classes
+      */
+    virtual ObjectData * ScanIndex(IndexSize index) = 0;
+
+
     /* ========================================================================
-     * Protected Members
+     * Protected Methods to Post Messages
      * ========================================================================
      */
+     
+    int PostTPDO (int cobId);
 
-    /** object dictionary */
-    ObjectDictionary * pMyDictionary;
 
-private:
+    /* ========================================================================
+     * Protected Member Variables
+     * ========================================================================
+     */
+     
+    /* Object Dictionary Handling -------------------------------------------*/
+    /** Array of ObjectData to contain all of the object dictionary data */
+    ObjectData *dictionary;
+    
+    /* Elapsed Time ---------------------------------------------------------*/
+    /** Millisecond time stamp at this OnTick() */
+    uint32_t timeCurrentTick;
+    
+    /** Millisecond difference between current OnTick() and the previous */
+    uint32_t timeSinceLastTick;
+
+// should be private
+// TODO: implement functions in Node class to post all types of messages so
+//       derived classes do not have to consider message structure.
+protected:
 
     /* ========================================================================
      * Private members
      * ========================================================================
      */
      
-     
-    /** Reference to Service Provider that this node is attached to.
-      * @note May need to be a pointer, because node id is held in object dictionary
-      */
+    /* ServiceProvider ------------------------------------------------------*/
+    /** Reference to Service Provider that this node is attached to */
     ServiceProvider * pMyProvider;
-     
-    /** Network id for the node
-      * @note May need to be a pointer, because node id is held in object dictionary
-      */
-    int nodeId;
+
+private:  
+    /* Common Node Methods --------------------------------------------------*/
+    /** Change state of the node */
+    void ChangeState(int newState);
     
-    /** Network and communication state of the node
-      * @note
-      */
-    State state;     
+    /* Common Node properties -----------------------------------------------*/    
+    /** Network and communication state of the node */
+    State state;
+    
+    
 };
 
 } /* namespace ppCANOpen */
diff -r 2034b04c86d2 -r 22a337cdc0e3 include/ObjectDictionary.h
--- a/include/ObjectDictionary.h	Sat Jan 09 17:15:29 2016 +0000
+++ b/include/ObjectDictionary.h	Sat Feb 13 20:22:59 2016 +0000
@@ -34,27 +34,21 @@
 namespace ppCANOpen
 {
 
-/** Abstract Object Dictionary class to provide constants and virtual scan
-  * function.
-  */
-class ObjectDictionary
+/** Data Type alias to provide context when defining the object library */
+typedef uint8_t DataType;
+
+/** Data Property alias to provide context when defining the object library */
+typedef uint8_t DataProperty;
+
+/** defines the data in a single entry of data (subindex)
+ */
+struct EntryData
 {
-    
-public:
-
-    /*=========================================================================
-     * Constants wrapped into the Object Dictionary to use with construction
-     *=========================================================================
-     */
-     
     /* Data value type constants ----------------------------------------------
      * Taken from the CANOpen Standard.  they are part of the object dictionary
      * at indices 0x0001 to 0x0023.  Given to every Sub Index.
      */
      
-    /** Data Type alias to provide context when defining the object library */
-    typedef uint8_t DataType;
-     
     static const DataType TYPE_BOOLEAN                     = 0x01;
     static const DataType TYPE_INT8                        = 0x02;
     static const DataType TYPE_INT16                       = 0x03;
@@ -94,69 +88,46 @@
      * Can be strung together into a mask.
      */
      
-    /** Data Property alias to provide context when defining the object library */
-    typedef uint8_t DataProperty;
-    
     static const DataProperty PROPERTY_READABLE         = 0x01;
     static const DataProperty PROPERTY_WRITEABLE        = 0x02;
     static const DataProperty PROPERTY_READ_WRITEABLE   = 0x03;
     /* static const uint8_t PROPERTY_STATIC                = 0x04 *//* possible to save setting after powerdown */
     
-    
-    /* Index Constants --------------------------------------------------------
+    /* EntryData data --------------------------------------------------------
      */
-     
-    /** Index alias to provide context when defining the object library */
-    typedef uint8_t Index;
+    
+    void           *pData;
+    uint16_t        size;
+    DataType        type;
+    DataProperty    properties;
+    
+    EntryData(){}
+    
+    EntryData(void *d, uint16_t s, DataType t, DataProperty p)
+    : pData(d), size(s), type(t), properties(p)
+    {}
+};
 
 
-    /*=========================================================================
-     * Internal structures
-     *=========================================================================
-     */
-     
-    /** defines the data in a single entry of data (subindex)
-     */
-    struct EntryData
-    {           
-        void           *pData;
-        uint16_t        size;
-        DataType        type;
-        DataProperty    properties;
-        
-        EntryData(){}
-        
-        EntryData(void *d, uint16_t s, DataType t, DataProperty p)
-        : pData(d), size(s), type(t), properties(p)
-        {}
-    };
+/** Index alias to provide context when defining the object library */
+typedef uint16_t IndexSize;
+
+/** Sub-Index alias to provide context when defining the object library */
+typedef uint8_t SubIndexSize;
+
+/** defines the data for a single index, including an array of subindices
+ */
+struct ObjectData
+{                
+    EntryData       *entries;
+    IndexSize        index;
+    SubIndexSize     entryCount;
     
-    /** defines the data for a single index, including an array of subindices
-     */
-    struct ObjectData
-    {        
-        EntryData  *entries;
-        uint16_t    index;
-        uint16_t    entryCount;
-        
-        ObjectData(){}
-        
-        ObjectData(EntryData *e, uint16_t i, uint16_t c)
-        : entries(e), index(i), entryCount(c)
-        {}
-    };
-
-
-    /*=========================================================================
-     * Public Methods
-     *=========================================================================
-     */
-     
-    /** Abstract method to give access to the object entries of derived
-      * classes
-      */
-    virtual ObjectData * ScanIndex(int index) = 0;
+    ObjectData(){}
     
+    ObjectData(EntryData *e, IndexSize i, SubIndexSize c)
+    : entries(e), index(i), entryCount(c)
+    {}
 };
 
 } /* namespace ppCANOpen */
diff -r 2034b04c86d2 -r 22a337cdc0e3 include/ServiceProvider.h
--- a/include/ServiceProvider.h	Sat Jan 09 17:15:29 2016 +0000
+++ b/include/ServiceProvider.h	Sat Feb 13 20:22:59 2016 +0000
@@ -51,65 +51,117 @@
 {
 public:
 
-    
-
     ServiceProvider (void);
     ~ServiceProvider (void);
     
+    /* ========================================================================
+     * Public Methods
+     * ========================================================================
+     */
+    
+    /* Main application -----------------------------------------------------*/
     /** Main loop */
     void Run (void);
     
-    /** Register a node to get messages and get update calls
-      * @note
-      * @param
-      * @retval
-      */
+    /** Iterate through Nodes and update */
+    void UpdateNodes(void);
+    
+    /** C helper function to pass to API */
+    static void UpdateNodes_CWrapper(void *pServiceObject);
+    
+    /* Node Management ------------------------------------------------------*/
+    /** Register a node to get messages and get update calls */
     int AddNode (Node * node);
     
-    /** Register a node to get messages and get update calls
-      * @note
-      * @param
-      * @retval
-      */
+    // TODO: remove node (swap pointers to fill in nicely
+    
       
-    /** Add a message to the message queue outbox
-      * @note
-      * @param
+    /* CanMessage Transmission ----------------------------------------------*/
+    /** Add a message to the message queue outbox */
+    void PostMessage (int nodeId, CanOpenMessage * msg);
+    
+    /** quick way to create and send an NMT Control message
+      * @note Make posting a message easier! 
       */
-    void PostMessage (CanOpenMessage * msg);
+    void PostNmtControl (char targetNodeId, NmtCommandSpecifier cs);
+    
+    /* Elapsed Time ---------------------------------------------------------*/
+    /** A way for the the Nodes to get elapsed time when they want it */
+    uint32_t GetTime(void);
     
-    // TODO: remove node (swap pointers to fill in nicely
 private:
+    /* ========================================================================
+     * Private Constants
+     * ========================================================================
+     */
     
-    /** define size of arrays in one place */
+    /** Define size of arrays in one place */
     static const int SERVICE_MAX_NODES = 8;    
 
     /* ========================================================================
-     * Private Members
+     * Private Methods
      * ========================================================================
      */
 
-    /** array of messages in a queue
-      * @note twice number of max nodes is a bit arbitrary.  Will need at
-      * least max nodes, but not sure how much more to be safe.
+    /* CanMessage Reading ---------------------------------------------------*/
+    /** Function to be called on every CAN read interrupt */
+    void ReadIT(void);
+    
+    /** C helper function used to pass ReadIT() to API */
+    static void ReadIT_CWrapper(void *pServiceObject);
+    
+    /** Helper function that utilizes backup message inbox when the main one
+      * is locked by the application.
       */
-    std::queue<CanOpenMessage> outbox;
-    //CanOpenMessage outbox[SERVICE_MAX_NODES * 2];
-    //int messageCount;
+    void ReadIT_clearBuffer(void);
 
-    /** array of nodes to cycle through.
-     *  @note
+    /* Elapsed Time ---------------------------------------------------------*/
+    /** Synchronize elapsed time*/
+    void HandleTimeMessage(CanOpenMessage *canOpenMsg); 
+    
+    
+    /* ========================================================================
+     * Private Member Variables
+     * ========================================================================
      */
+     
+    /* Node Management -------------------------------------------------------*/
+    /** array of nodes to cycle through. */
     Node * nodes[SERVICE_MAX_NODES];
     
-    /** Keeps count of current number of nodes.
-      * @note
+    /** Keeps count of current number of nodes. */
+    int nodeCount;    
+    
+    /* CanMessage Reading ---------------------------------------------------*/
+    /** array of messages in a queue */
+    std::queue<CanOpenMessage> inbox;
+    
+    /** Extra buffer only handled by ISR. Used if inbox is locked */
+    std::queue<CanOpenMessage> inboxBuffer; 
+    
+    /** boolean to lock and unlock inbox  */
+    int bInboxUnlocked;
+    
+    /** Data to keep track of internal messages.  Node ID allows SP to avoid
+      * dispatching message to the one who sent it
       */
-    int nodeCount;
+    struct InternalMessage {
+        int             nodeId;
+        CanOpenMessage  message;
+        
+        InternalMessage(uint8_t n, CanOpenMessage m):
+            nodeId(n),
+            message(m)
+        {}
+    };
+
+    /** array of messages in a queue */
+    std::queue<InternalMessage> outbox; 
     
-    /** stores handle for can open api */
-    CanOpenHandle * hCanOpen;
-
+    /* Elapsed Time ---------------------------------------------------------*/   
+    /** Elapsed time offset used for synchronization*/
+    int32_t elapsedTimeOffset; 
+    
 };    
     
 } /* namspace ppCANOpen */
diff -r 2034b04c86d2 -r 22a337cdc0e3 include/canopen_api.h
--- a/include/canopen_api.h	Sat Jan 09 17:15:29 2016 +0000
+++ b/include/canopen_api.h	Sat Feb 13 20:22:59 2016 +0000
@@ -29,6 +29,8 @@
 #ifndef PPCAN_CANOPEN_API_H
 #define PPCAN_CANOPEN_API_H
 
+#include "stdint.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -36,8 +38,7 @@
     /*=========================================================================
      * Forward declarations
      *=========================================================================
-     */  
-    struct CanOpenHandle;
+     */
     struct CanOpenMessage;
 
     /*=========================================================================
@@ -45,10 +46,15 @@
      *=========================================================================
      */    
     
-    int CanOpenApiInit  (CanOpenHandle * hCanOpen);
+    typedef void (* ServiceProviderRxInterruptCallback)(void *pServiceObject);
+    typedef void (* ServiceProviderTickCallback)       (void *pServiceObject);
+    
+    int CanOpenApiInit  (void *object, ServiceProviderRxInterruptCallback rxCallback, ServiceProviderTickCallback tickCallback);
     
-    int CanOpenApiRead  (CanOpenHandle * hCanOpen, CanOpenMessage * canOpenMsg);
-    int CanOpenApiWrite (CanOpenHandle * hCanOpen, CanOpenMessage * canOpenMsg);
+    int CanOpenApiRead  (CanOpenMessage * canOpenMsg);
+    int CanOpenApiWrite (CanOpenMessage * canOpenMsg);
+    
+    uint32_t CanOpenApiGetHardwareTime  (void);
 
 #ifdef __cplusplus
 };
diff -r 2034b04c86d2 -r 22a337cdc0e3 source/Node.cpp
--- a/source/Node.cpp	Sat Jan 09 17:15:29 2016 +0000
+++ b/source/Node.cpp	Sat Feb 13 20:22:59 2016 +0000
@@ -36,84 +36,17 @@
 
 namespace ppCANOpen
 {
-    
 
-    /** CANOpen Function Codes */
-    typedef enum {
-        CANOPEN_FUNCTION_CODE_NMT        = 0x00,
-        CANOPEN_FUNCTION_CODE_SYNC       = 0x01,
-        CANOPEN_FUNCTION_CODE_TIME       = 0x02,
-        CANOPEN_FUNCTION_CODE_PDO1T      = 0x03,
-        CANOPEN_FUNCTION_CODE_PD01R      = 0x04,
-        CANOPEN_FUNCTION_CODE_PD02T      = 0x05,
-        CANOPEN_FUNCTION_CODE_PD02R      = 0x06,
-        CANOPEN_FUNCTION_CODE_PD03T      = 0x07,
-        CANOPEN_FUNCTION_CODE_PD03R      = 0x08,
-        CANOPEN_FUNCTION_CODE_PD04T      = 0x09,
-        CANOPEN_FUNCTION_CODE_PD04R      = 0x0A,
-        CANOPEN_FUNCTION_CODE_SD0T       = 0x0B,
-        CANOPEN_FUNCTION_CODE_SD0R       = 0x0C,
-        CANOPEN_FUNCTION_CODE_NODE_GUARD = 0x0E,
-        CANOPEN_FUNCTION_CODE_LSS        = 0x0F
-    } CanOpenFunctionCodes;
-
-    /* Message Constants */
-    #define MESSAGE_NODEID_BITS     0b00001111111
-    #define MESSAGE_COMMAND_BITS    0b11110000000
-
-    /* Message Macros -----------------------------------------------------------*/ 
-    #define MESSAGE_GET_NODEID(cobId)   (cobId & MESSAGE_NODEID_BITS)
-    #define MESSAGE_GET_COMMAND(cobId)  ((cobId & MESSAGE_COMMAND_BITS) >> 7)
+Node::Node (int id, ServiceProvider * pProvider, int bLoop)
+{
+    nodeId = id;
 
-    /*=========================================================================
-     * SDO MESSAGE PARAMETERS
-     *=========================================================================
-     */
-    
-    /** SDO initiate protocol command specifiers */
-    typedef enum {
-         SDO_CCS_DOWNLOAD_SEGMENT_REQUEST   = 0x00, 
-         SDO_CCS_INITIATE_DOWNLOAD_REQUEST  = 0x01,
-         SDO_CCS_INITIATE_UPLOAD_REQUEST    = 0x02,
-    } SdoClientCommandSpecifier;
-    
-    /** SDO segment protocol command specifiers */
-    typedef enum {
-         SDO_SCS_DOWNLOAD_SEGMENT_RESPONSE  = 0x01,
-         SDO_SCS_INITIATE_UPLOAD_RESPONSE   = 0x02,
-         SDO_SCS_INITIATE_DOWNLOAD_RESPONSE = 0x03,
-    } SdoServerCommandSpecifier;
-    
-    /* SDO constants --------------------------------------------------------*/
-    #define SDO_SIZE_INDICATOR_BIT  0b00000001
-    #define SDO_TRANSFER_TYPE_BIT   0b00000010
-    #define SDO_DATA_COUNT_BITS     0b00001100
-    #define SDO_TOGGLE_BIT          0b00010000
-    #define SDO_CS_BITS             0b11100000
-    
-    /* SDO macros -----------------------------------------------------------*/ 
-    #define SDO_GET_CS(data0)           ((data0 & SDO_CS_BITS) >> 5)
-    #define SDO_GET_DATA_COUNT(data0)   ((data0 & SDO_DATA_COUNT_BITS) >> 2)
-    
-    
-    /*=========================================================================
-     * NMT MESSAGE PARAMETERS
-     *=========================================================================
-     */
+    state.nmtState = State::INITIALIZED;
 
-    /** NMT node control protocol command specifiers */
-    typedef enum {
-         NMT_CS_START       = 0x01,
-         NMT_CS_STOP        = 0x02,
-         NMT_CS_ENTER_PREOP = 0x80,
-         NMT_CS_RESET_NODE  = 0x81,
-         NMT_CS_RESET_COM   = 0x82
-    } NmtCommandSpecifier;
+    bLoopbackOn = bLoop;
 
-Node::Node (ServiceProvider * pProvider)
-{
     pMyProvider = pProvider;
-    pProvider->AddNode(this);
+    pProvider->AddNode(this);    
 }
 
 /*=============================================================================
@@ -121,29 +54,41 @@
  *=============================================================================
  */
 
-int Node::DispatchMessage(CanOpenMessage *msg)
+int Node::DispatchMessage(CanOpenMessage *canOpenMsg)
 {
-    int command = MESSAGE_GET_COMMAND(msg->id);
-    int nodeId  = MESSAGE_GET_NODEID(msg->id);
+    int command = MESSAGE_GET_COMMAND(canOpenMsg->id);
 
-    //printf("*** N.Dispatch: got com and id\r\n");
-    
     switch (command) {
         case CANOPEN_FUNCTION_CODE_NMT:
-            printf("*** N.Dispatch: it's an NMT Control!!!\r\n");
-            if (msg->data[0] == nodeId) {
-                HandleNodeControl ((int)msg->data[1]);
+            printf("   NMT Control:   \r\n");
+            HandleNodeControl(canOpenMsg);
+            break;
+
+        case CANOPEN_FUNCTION_CODE_PDO1T:
+        case CANOPEN_FUNCTION_CODE_PDO1R:
+        case CANOPEN_FUNCTION_CODE_PDO2T:
+        case CANOPEN_FUNCTION_CODE_PDO2R:
+        case CANOPEN_FUNCTION_CODE_PDO3T:
+        case CANOPEN_FUNCTION_CODE_PDO3R:
+        case CANOPEN_FUNCTION_CODE_PDO4T:
+        case CANOPEN_FUNCTION_CODE_PDO4R:
+            if (state.bPDO) {
+                HandlePdo(canOpenMsg);
             }
             break;
+
+        case CANOPEN_FUNCTION_CODE_SDOT:
+        case CANOPEN_FUNCTION_CODE_SDOR:
+            if (state.bSDO) {
+                HandleSdo(canOpenMsg);
+            }
+            break;
+
         default:
-            printf("*** N.Dispatch: some random message\r\n");
+            printf("   some random message\r\n");
             break;
     }
 
-    // ECHO ***********************
-    pMyProvider->PostMessage(msg);
-    // END ECHO *******************
-
     return 1;
 }
 
@@ -152,14 +97,191 @@
  *=============================================================================
  */
 
-int Node::ConsumePdo (const int pdoNum, char *const data)
+static void CopyBits(uint8_t *sourceData, 
+                uint8_t *destData,
+                uint8_t mappedBits,
+                uint8_t &sourceBitNum, 
+                uint8_t &destBitNum);
+
+int Node::HandlePdo (CanOpenMessage *canOpenMsg)
 {
-    return 0;
+    printf("   RPDO:\r\n");
+    
+    int result = 0;
+
+    if (CANOPEN_TYPE_REMOTE == canOpenMsg->type) {
+
+    } else {
+        int pdoNum = 0;
+        int bSearching = 1;
+
+        ObjectData * parameterObject;
+
+        /* search through PDO parameters until we find a match */
+        while (bSearching) {
+
+            //printf("      PDO %d?", pdoNum);
+            parameterObject = ScanIndex (0x1400 + pdoNum);
+            if (parameterObject) {
+
+                uint32_t *cobId = (uint32_t*) parameterObject->entries[1].pData;
+                if (canOpenMsg->id == *cobId) {
+                    bSearching = 0;
+                    
+                    //printf(" ... match!!!\r\n");
+                } else {
+                    pdoNum++;
+                    //printf(" ... nope\r\n");
+                }
+
+            } else {
+                bSearching = 0;
+                printf("\r\n      ERROR: No PDO Parameter match found\r\n");
+            }
+            
+            
+        }
+
+        /* if a matching parameter object was found then get mapping */
+        if (parameterObject) {
+            
+            ObjectData  *mappingObject = ScanIndex (0x1600 + pdoNum);
+            
+            if (mappingObject) {
+                SubIndexSize *numMappedVariables = (SubIndexSize*) mappingObject->entries[0].pData;
+                
+                /* variable to track position in data copy */
+                uint8_t sourceBitNum  = 0;
+                
+                int bError = 0;
+                
+                /* for each mapped variable, write in data from message */
+                for (SubIndexSize subIndexIterator = 1; subIndexIterator <= *numMappedVariables && !bError; subIndexIterator++) {
+                
+                    uint32_t *map = (uint32_t*)mappingObject->entries[subIndexIterator].pData;
+                    
+                    uint8_t         mappedBits     = (uint8_t)      (*map); 
+                    uint8_t         mappedBytes    = mappedBits / 8;
+                    SubIndexSize    mappedSubIndex = (SubIndexSize) (*map >> 8);
+                    IndexSize       mappedIndex    = (IndexSize)    (*map >> 16);
+                    
+                    printf("      mapped: %#06x, %#04x, %d\r\n",
+                            mappedIndex, mappedSubIndex, mappedBits);
+                    
+                    /* if less than 0x1000 then it is a dummy value */
+                    if (mappedIndex < 0x1000) {
+                        sourceBitNum += mappedBits;
+                    } else {
+                
+                        /* get the index object */
+                        ObjectData  *mappedObject = ScanIndex (mappedIndex);
+                        if (mappedObject) {
+                            
+                            /* get the subindex object */
+                            if (mappedSubIndex <= *(SubIndexSize*) mappedObject->entries[0].pData) {
+                                
+                                EntryData *destinationEntry = &mappedObject->entries[mappedSubIndex];                            
+                                
+                                /* can we write to it? */
+                                if (!(destinationEntry->properties & EntryData::PROPERTY_WRITEABLE)) {
+                                    printf("      ERROR: Mapped SubIndex is not writeable!\r\n"); 
+                                    bError = 1;
+                                }
+                                
+                                uint8_t sourceByteNum = sourceBitNum / 8;
+                                if (sourceByteNum + mappedBytes > canOpenMsg->dataCount) {
+                                    printf("      ERROR: Insufficient mapped data remaining!\r\n"); 
+                                    bError = 1;
+                                }
+                                
+                                if (destinationEntry->size < mappedBytes) {
+                                    printf("      ERROR: Too much data to pack into destination!\r\n"); 
+                                    bError = 1;
+                                }
+                                
+                                if (!bError) {
+                                    
+                                    //printf("      No Errors, copying data...\r\n");
+                                    
+                                    uint8_t* destData = (uint8_t*) destinationEntry->pData;
+                                    uint8_t* sourceData = canOpenMsg->data;
+                                    
+                                    uint8_t destBitNum = 0;
+                                    CopyBits(sourceData, destData, mappedBits, sourceBitNum, destBitNum);
+                                }                         
+                                
+                            } else {
+                                printf("      ERROR: Mapped SubIndex does not exist!\r\n"); 
+                                    bError = 1;
+                            }
+                            
+                        } else {
+                            printf("      ERROR: Mapped Index does not exist!\r\n"); 
+                                    bError = 1;
+                        }
+                    
+                    } /* if mappedIndex < 0x1000 */
+                    
+                               
+                }   /* for each mapping */
+                
+                if (!bError) {
+                    result = 1;
+                }
+                
+            } else {
+                printf("      ERROR: No PDO Mapping match found\r\n");
+            }
+            
+        } /* if parameter exists */
+        
+    } /* if remote message */
+
+    return result;
 }
 
-int Node::HandlePdoReadRequest (const int pdoNum)
+
+int Node::HandleSdo (CanOpenMessage *canOpenMsg)
 {
-    return 0;
+    
+    printf("   SDO:\r\n");
+    
+    int result = 0;
+    
+    int bSearching = 1;
+    
+    int sdoNum = 0;
+    int sdoType = 0;  /* 0=unknown, 1=receive/server, 2=transmit/client */
+    ObjectData * sdoObject;
+    
+    /* search through PDO parameters until we find a match */
+    while (bSearching) {
+
+        sdoObject = ScanIndex (0x1200 + sdoNum);
+        if (sdoObject) {
+            
+            uint32_t *receiveCobId  = (uint32_t*) sdoObject->entries[1].pData;
+            uint32_t *transmitCobId = (uint32_t*) sdoObject->entries[2].pData;
+            
+            if (canOpenMsg->id == *receiveCobId) {
+                bSearching = 0;
+                sdoType = 1;
+                printf("      Receive SDO\r\n");
+            } else if (canOpenMsg->id == *transmitCobId) {
+                bSearching = 0;
+                sdoType = 2;
+                printf("      Transmit SDO\r\n");
+            }
+            
+        } else {
+            printf("      ERROR: No SDO parameters found");
+            bSearching = 0;
+        }
+        
+        sdoNum++;
+    }
+    
+    return result;
 }
 
 int Node::ConsumeEmergency (void)
@@ -167,84 +289,109 @@
     return 0;
 }
 
-int Node::HandleNodeControl (int commandSpecifier)
+int Node::HandleNodeControl (CanOpenMessage *canOpenMsg)
 {
     int result = 0;
 
-    switch (commandSpecifier) {
-        case NMT_CS_START:
-            if (State::INITIALIZED != state.nmtState) {
-                state.nmtState = State::OPERATIONAL;
+    if (canOpenMsg->data[0] == nodeId) {
+
+        int commandSpecifier = (int)canOpenMsg->data[1];
+
+        switch (commandSpecifier) {
+            case NMT_CS_START:
+                printf("      NMT_CS_START\r\n");
+                if ((State::INITIALIZED    == state.nmtState) || 
+                    (State::PREOPERATIONAL == state.nmtState) || 
+                    (State::STOPPED        == state.nmtState)) 
+                {
+
+                    state.nmtState = State::OPERATIONAL;
+                    state.bBoot         = 0;
+                    state.bSDO          = 1;
+                    state.bEmergency    = 1;
+                    state.bSYNC         = 1;
+                    state.bLifeGuard    = 1;
+                    state.bPDO          = 1;
+                    state.bLSS          = 0;
+                    OnOperational();
+                    result = 1;
+                }
+                break;
+
+            case NMT_CS_STOP:
+                if (State::INITIALIZED != state.nmtState) {
+                    state.nmtState = State::STOPPED;
+                    state.bBoot         = 0;
+                    state.bSDO          = 0;
+                    state.bEmergency    = 0;
+                    state.bSYNC         = 0;
+                    state.bLifeGuard    = 1;
+                    state.bPDO          = 0;
+                    state.bLSS          = 1;
+                }
+                OnStopped();
+                result = 1;
+                break;
+
+            case NMT_CS_ENTER_PREOP:
+                state.nmtState = State::PREOPERATIONAL;
                 state.bBoot         = 0;
                 state.bSDO          = 1;
                 state.bEmergency    = 1;
                 state.bSYNC         = 1;
                 state.bLifeGuard    = 1;
-                state.bPDO          = 1;
-                state.bLSS          = 0;
-                OnOperational();
+                state.bPDO          = 0;
+                state.bLSS          = 1;
+                OnPreoperational();
                 result = 1;
-            }
-            break;
+                break;
 
-        case NMT_CS_STOP:
-            if (State::INITIALIZED != state.nmtState) {
-                state.nmtState = State::STOPPED;
-                state.bBoot         = 0;
+            case NMT_CS_RESET_NODE:
+            case NMT_CS_RESET_COM:
+                printf("      NMT_CS_RESET\r\n");
+
+                state.nmtState      = State::INITIALIZED;
+                state.bBoot         = 1;
                 state.bSDO          = 0;
                 state.bEmergency    = 0;
                 state.bSYNC         = 0;
+                state.bLifeGuard    = 0;
+                state.bPDO          = 0;
+                state.bLSS          = 0;
+
+                state.bLifeGuardToggle = 0;
+
+                /* boot message is actually just the first node guard/ heart beat message */
+                // TODO: wrap up into heartbeat/lifeguard message
+                CanOpenMessage msgBoot;
+                msgBoot.id      = CANOPEN_FUNCTION_CODE_NODE_GUARD | 5;
+                msgBoot.format  = CANOPEN_FORMAT_STANDARD;
+                msgBoot.type    = CANOPEN_TYPE_DATA;
+                msgBoot.dataCount = 1;
+                msgBoot.data[0] = 0;
+
+                pMyProvider->PostMessage(nodeId, &msgBoot);
+                
+                OnInitialize();
+
+                state.nmtState = State::PREOPERATIONAL;
+                state.bBoot         = 0;
+                state.bSDO          = 1;
+                state.bEmergency    = 1;
+                state.bSYNC         = 1;
                 state.bLifeGuard    = 1;
                 state.bPDO          = 0;
                 state.bLSS          = 1;
-            }
-            OnStopped();
-            result = 1;
-            break;
-
-        case NMT_CS_ENTER_PREOP:
-            state.nmtState = State::PREOPERATIONAL;
-            state.bBoot         = 0;
-            state.bSDO          = 1;
-            state.bEmergency    = 1;
-            state.bSYNC         = 1;
-            state.bLifeGuard    = 1;
-            state.bPDO          = 0;
-            state.bLSS          = 1;
-            OnPreoperational();
-            result = 1;
-            break;
+                
+                OnPreoperational();
 
-        case NMT_CS_RESET_NODE:
-        case NMT_CS_RESET_COM:
-            
-            state.nmtState      = State::INITIALIZED;
-            state.bBoot         = 1;
-            state.bSDO          = 0;
-            state.bEmergency    = 0;
-            state.bSYNC         = 0;
-            state.bLifeGuard    = 0;
-            state.bPDO          = 0;
-            state.bLSS          = 0;
-            
-            state.bLifeGuardToggle = 0;
-            
-            /* boot message is actually just the first node guard/ heart beat message */
-            // TODO: wrap up into heartbeat/lifeguard message             
-            CanOpenMessage msgBoot;
-            msgBoot.id      = CANOPEN_FUNCTION_CODE_NODE_GUARD | 5;
-            msgBoot.format  = CANOPEN_FORMAT_STANDARD;
-            msgBoot.type    = CANOPEN_TYPE_DATA;
-            msgBoot.dataCount = 1;
-            msgBoot.data[0] = 0;
-            
-            pMyProvider->PostMessage(&msgBoot);
-            
-            result = 1;
-            break;
+                result = 1;
+                break;
 
-        default:
-            break;
+            default:
+                break;
+        }
+
     }
 
     return result;
@@ -265,29 +412,256 @@
  *=============================================================================
  */
 
+void Node::FixedUpdate (uint32_t time)
+{
+    timeSinceLastTick = time - timeCurrentTick;
+    timeCurrentTick = time; 
+    
+    if (State::OPERATIONAL == state.nmtState) {
+        OnFixedUpdate();
+    }
+       
+}
+
 void Node::Update (void)
 {
-    if (State::OPERATIONAL == state.nmtState) {
+     if (State::OPERATIONAL == state.nmtState) {
         OnUpdate();
     }
-    
-    // TODO: check elapsed time to see if it is time for fixed update
 }
 
 /*=============================================================================
- * Methods to implement node control in derived classes
+ * Other Member Functions
  *=============================================================================
  */
 
+int Node::PostTPDO (int specifiedCobId)
+{
+    printf("   TPDO:\r\n");
+    
+    int result = 0;
+    
+    /* initialize a blank message -------------------------------------------*/
+    CanOpenMessage msg;
+
+    msg.id          = specifiedCobId;
+    msg.dataCount   = 0;
+    msg.type        = CANOPEN_TYPE_DATA;
+    msg.format      = CANOPEN_FORMAT_STANDARD;
+
+    msg.data[0] = 0;
+    msg.data[1] = 0;
+    msg.data[2] = 0;
+    msg.data[3] = 0;
+    msg.data[4] = 0;
+    msg.data[5] = 0;
+    msg.data[6] = 0;
+    msg.data[7] = 0;
+    
+    int pdoNum = 0;
+    int bSearching = 1;
+    
+    /* Find the mapped data and send ----------------------------------------*/
+    
+    ObjectData * parameterObject;
+    
+    /* search through PDO parameters until we find a match */
+    while (bSearching) {
+    
+        //printf("      PDO %d?", pdoNum);
+        parameterObject = ScanIndex (0x1800 + pdoNum);
+        if (parameterObject) {
+    
+            uint32_t *cobId = (uint32_t*) parameterObject->entries[1].pData;
+            if (specifiedCobId == *cobId) {
+                bSearching = 0;
+                
+                //printf(" ... match!!!\r\n");
+            } else {
+                pdoNum++;
+                //printf(" ... nope\r\n");
+            }
+    
+        } else {
+            bSearching = 0;
+            printf("\r\n      ERROR: No PDO Parameter match found\r\n");
+        }
+    }
+    
+    /* if a matching parameter object was found then get mapping */
+    if (parameterObject) {
+        
+        ObjectData  *mappingObject = ScanIndex (0x1A00 + pdoNum);
+        
+        if (mappingObject) {
+            SubIndexSize *numMappedVariables = (SubIndexSize*) mappingObject->entries[0].pData;
+            
+            //printf("      numMappedVariables:    %d\r\n", *numMappedVariables);
+            
+            /* variable to track position in data copy */
+            uint8_t destBitNum  = 0;
+            
+            int bError = 0;
+            
+            /* for each mapped variable, write in data from message */
+            for (SubIndexSize subIndexIterator = 1; subIndexIterator <= *numMappedVariables && !bError; subIndexIterator++) {
+            
+                uint32_t *map = (uint32_t*)mappingObject->entries[subIndexIterator].pData;
+                
+                uint8_t         mappedBits     = (uint8_t)      (*map); 
+                uint8_t         mappedBytes    = mappedBits / 8;
+                SubIndexSize    mappedSubIndex = (SubIndexSize) (*map >> 8);
+                IndexSize       mappedIndex    = (IndexSize)    (*map >> 16);
+                
+                msg.dataCount += mappedBytes;
+                
+                //printf("      mapped: %#06x, %#04x, %d\r\n",
+                //        mappedIndex, mappedSubIndex, mappedBits);
+                
+                /* if less than 0x1000 then it is a dummy value */
+                if (mappedIndex < 0x1000) {
+                    destBitNum += mappedBits;
+                    //printf("      No Errors, skipping VOID data...\r\n");
+                } else {
+                
+                    /* push into the TPDO data */
+                    ObjectData  *mappedObject = ScanIndex (mappedIndex);
+                    if (mappedObject) {
+                        
+                        /* get the subindex object */
+                        if (mappedSubIndex <= *(SubIndexSize*) mappedObject->entries[0].pData) {
+                            
+                            EntryData *sourceEntry = &mappedObject->entries[mappedSubIndex];                            
+                            
+                            /* can we write to it? */
+                            if (!(sourceEntry->properties & EntryData::PROPERTY_READABLE)) {
+                                printf("      ERROR: Mapped SubIndex is not readable!\r\n"); 
+                                bError = 1;
+                            }
+                            
+                            uint8_t destByteNum = destBitNum / 8;
+                            if (destByteNum + mappedBytes > 8) {
+                                printf("      ERROR: Too much data to pack into destination!\r\n"); 
+                                bError = 1;
+                            }
+                            
+                            if (sourceEntry->size < mappedBytes) {
+                                printf("      ERROR: trying to grab too much information!\r\n"); 
+                                bError = 1;
+                            }
+                            
+                            if (!bError) {
+                                
+                                //printf("      No Errors, copying data...\r\n");
+                                
+                                uint8_t *destData   = msg.data;
+                                uint8_t *sourceData = (uint8_t*)sourceEntry->pData;
+                                
+                                uint8_t sourceBitNum = 0;
+                                CopyBits(sourceData, destData, mappedBits, sourceBitNum, destBitNum);
+                            }                         
+                            
+                        } else {
+                            printf("      ERROR: Mapped SubIndex does not exist!\r\n"); 
+                                bError = 1;
+                        }
+                        
+                    } else {
+                        printf("      ERROR: Mapped Index does not exist!\r\n"); 
+                                bError = 1;
+                    }
+                    
+                } /* if mappedIndex < 0x1000 */
+                
+                           
+            }   /* for each mapping */
+            
+            if (!bError) {
+                result = 1;
+                
+                /* Send the message we built up */
+                pMyProvider->PostMessage(nodeId, &msg);
+            }
+            
+        } else {
+            printf("      ERROR: No PDO Mapping match found\r\n");
+        } /* if mappingObject exists */
+        
+    } /* if parameter exists */
+
+    
+    return result;
+}
+
+/*=============================================================================
+ * Private functions
+ *=============================================================================
+ */
+
+void ChangeState(int newState) {
+    
+}
+
 /*=============================================================================
  * Local functions
  *=============================================================================
  */
 
+void CopyBits(uint8_t *sourceData, 
+                uint8_t *destData,
+                uint8_t mappedBits,
+                uint8_t &sourceBitNum, 
+                uint8_t &destBitNum)
+{    
+    uint8_t sourceByteNum;
+    uint8_t destByteNum;
 
+    uint8_t bitCounter = 0;
+
+    if ((mappedBits % 8) == 0 && (sourceBitNum % 8) == 0 && (destBitNum % 8) == 0) {
+                             
+        //printf("         Loading BYTEwise...\r\n");       
+        /* load in by bytes */
+        uint8_t destByteNum = 0;
+        while (bitCounter < mappedBits) {
+            
+            
+            destByteNum   = destBitNum   / 8;
+            sourceByteNum = sourceBitNum / 8; 
+            
+                destData[destByteNum] = sourceData[sourceByteNum];
+                
+            destBitNum   += 8;
+            sourceBitNum += 8;
+            bitCounter   += 8;
+        }
+        
+    } else {
+         
+        
+        //printf("         Loading BITwise..."); 
+        /* not a multiple of 8, so do bit by bit */
+        while (bitCounter < mappedBits) { 
+        
+            destByteNum   = destBitNum   / 8;
+            sourceByteNum = sourceBitNum / 8; 
+             
+                /* clear the destination bit */
+                destData[destByteNum] &= ~(1 << destBitNum); 
+                /* get source bit value */
+                uint8_t destValue = (sourceData[sourceByteNum] & (1 << (sourceBitNum % 8))) >> (sourceBitNum % 8) << destBitNum;
+                /* set dest bit */
+                destData[destByteNum] |= destValue;      
+            
+            destBitNum++;
+            sourceBitNum++;  
+            bitCounter++;                    
+        }
+    }        
+}
 
 /*=============================================================================
- *============================================================================= 
+ *=============================================================================
  * Methods to handle message requests and responses
  * Called by the node, usually during Update() or during handling of
  * incoming messages.
@@ -303,45 +677,45 @@
  *  @note
  *  @param
  */
-void RequestPdo (int pdoNum){}
+void RequestPdo (int pdoNum) {}
 
 /** Build and send a PDO
  *  @note
  *  @param
  */
-void ProducePdo (int pdoNum, char * data){}
+void ProducePdo (int pdoNum, char * data) {}
 
 
 /* SDO (7.2.4) ----------------------------------------------------------*/
 
 /** initiate SDO download
  *  @note Handles automatically whether it will be a expedited transfer or
- *  or if message will be split into   
+ *  or if message will be split into
  *  @param
  *
  *  Node will create a big data array and Service provide will have to
  *  iterate through and send all of the data.  ServiceProvider will pass
  *  the confirmation to the node, and the node will free up it's buffer.
  */
-void DownloadSdo (int sdoNum, int index, int subindex, int size, char * data){}
+void DownloadSdo (int sdoNum, int index, int subindex, int size, char * data) {}
 
 /** initiate SDO upload
- *  @note 
+ *  @note
  *  @param
  */
-void UploadSdo (int sdoNum, int index, int subindex){}
+void UploadSdo (int sdoNum, int index, int subindex) {}
 
 /** Acknowledge that SDO was recieved properly
- *  @note 
+ *  @note
  *  @param
  */
-void ConfirmSdo (int sdoNum, int bSuccess){}
+void ConfirmSdo (int sdoNum, int bSuccess) {}
 
 /** Abort current SDO transfer
- *  @note 
+ *  @note
  *  @param
  */
-void AbortSdo (int sdoNum){}
+void AbortSdo (int sdoNum) {}
 
 
 /* Emergency object (7.2.7) ---------------------------------------------*/
@@ -356,7 +730,10 @@
  *  @note
  *  @param
  */
-int  SendNodeControl (NmtCommandSpecifier cs, unsigned int nodeId){return 0;}
+int  SendNodeControl (NmtCommandSpecifier cs, unsigned int nodeId)
+{
+    return 0;
+}
 
 
 /* ---- Error Control (7.2.8.2.2) ---------------------------------------*/
@@ -365,13 +742,19 @@
  *  @note
  *  @param
  */
-int  RequestErrorControl (NmtCommandSpecifier cs, unsigned int nodeId){return 0;}
+int  RequestErrorControl (NmtCommandSpecifier cs, unsigned int nodeId)
+{
+    return 0;
+}
 
 /** Build a CANOpen error control response
  *  @note
  *  @param
  */
-int  RespondErrorControl (NmtCommandSpecifier cs, unsigned int nodeId){return 0;}
+int  RespondErrorControl (NmtCommandSpecifier cs, unsigned int nodeId)
+{
+    return 0;
+}
 
 
 } /* namspace ppCANOpen */
diff -r 2034b04c86d2 -r 22a337cdc0e3 source/ServiceProvider.cpp
--- a/source/ServiceProvider.cpp	Sat Jan 09 17:15:29 2016 +0000
+++ b/source/ServiceProvider.cpp	Sat Feb 13 20:22:59 2016 +0000
@@ -25,11 +25,11 @@
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
+#include "mbed.h"
 
 #include "ServiceProvider.h"
 #include "Node.h"
 
-#include "CanOpenHandle.h"
 #include "canopen_api.h"
 
 #include <string.h>
@@ -37,107 +37,233 @@
 
 namespace ppCANOpen
 {
-    
-/*=============================================================================
- * Construction
- *=============================================================================
- */ 
+
+
 
-ServiceProvider::ServiceProvider (void) : nodeCount(0) 
+ServiceProvider::ServiceProvider (void) : nodeCount(0)
 {
-    
-    //messageCount = 0;
-    
+
+    bInboxUnlocked = 1;
+
     //memset (nodes, 0, sizeof(Node) * SERVICE_MAX_NODES ); // TODO: fix incorrect <--
     for (int i = 0; i < SERVICE_MAX_NODES; i++) {
-        nodes[i] = 0;   
+        nodes[i] = 0;
     }
-    
-    hCanOpen = new CanOpenHandle;
-    CanOpenApiInit(hCanOpen);
+
+    CanOpenApiInit((void *)this, &ReadIT_CWrapper, &UpdateNodes_CWrapper);
 }
 
 ServiceProvider::~ServiceProvider (void)
 {
-    delete hCanOpen;   
 }
 
-/*=============================================================================
- * main loop
- *=============================================================================
- */ 
+
+/* ============================================================================
+ * Public Methods
+ * ============================================================================
+ */
+
+
+/* Main application ---------------------------------------------------------*/
 
 void ServiceProvider::Run (void)
 {
-    while (1) {
+    //while (1) {
+
+    
+    /* Check for new messages.  
+     * Dispatch one at a time in order for nodes to handle incoming data 
+     */
+    bInboxUnlocked = 0;
+    if (! inbox.empty()) {
+
+        /* Handle things inside of Service Provider first if necessary */
+        if (CANOPEN_FUNCTION_CODE_TIME == MESSAGE_GET_COMMAND(inbox.front().id)) {
+           
+           HandleTimeMessage(&inbox.front());
+           
+        } else {
+        /* Send out remaining messages to the Nodes */
         
-        /* Check for new messages */
-        CanOpenMessage msg;
-        if (hCanOpen->can) {
-            if (CanOpenApiRead (hCanOpen, &msg)) {
-                
-                //printf("*** SP.Run: About to dispatch\r\n");
-                /* if new message exists, give it to the nodes*/
-                /* update all of the nodes */
-                for (int i=0; i < nodeCount; i++) {
-                    nodes[i]->DispatchMessage(&msg);                 
-                }
-                //printf("*** SP.Run: dispatched\r\n");
-            }
-        }    
-   
-        /* update all of the nodes */
-        for (int i=0; i < nodeCount; i++) {
-            nodes[i]->Update();            
-        }
-        
-        /* send out the responses */
-        while (! outbox.empty()) {
-            
-            printf("msg id: %d\r\n", outbox.front().id);
-            if (CanOpenApiWrite(hCanOpen, &outbox.front())) {
-                outbox.pop();
-            } else {
-                   printf("Could not send last message.  Trying again\r\n");
+            // TODO if time stamp message, then the SP can sync the clock for all nodes
+    
+            /* if new message exists, give it to the nodes*/
+            for (int i=0; i < nodeCount; i++) {
+                nodes[i]->DispatchMessage(&inbox.front());
             }
         }
 
-    }    
+        inbox.pop();
+    }
+    bInboxUnlocked = 1;
+
+    /* Update all of the nodes */
+    for (int i=0; i < nodeCount; i++) {
+        nodes[i]->Update();
+    }
+
+    /* send out the responses */
+    while (! outbox.empty()) {
+        if (CanOpenApiWrite(&outbox.front().message)) {
+            /* send message externally first.
+             * Since we want to keep the message alive and try to  resend, we
+             * do not want to keep resending the message internally over and
+             * over again.*/
+             
+            /* dispatch to the internal nodes */
+            for (int i=0; i < nodeCount; i++) {
+                if ((nodes[i]->bLoopbackOn) || 
+                    (nodes[i]->nodeId != outbox.front().nodeId)) {
+                    
+                    nodes[i]->DispatchMessage(&outbox.front().message);
+                }
+            }
+            
+            outbox.pop();
+        } else {
+            printf("Could not send last message.  Trying again\r\n");
+        }
+    }
+
+    wait (.005);
+
+    //}
 }
 
-/*=============================================================================
- * Register a node to get messages and get update calls
- *=============================================================================
- */ 
+void ServiceProvider::UpdateNodes(void)
+{
+    uint32_t time = GetTime();
+    
+    //printf("S::Run() update nodes\r\n");
+    /* update all of the nodes */
+    for (int i=0; i < nodeCount; i++) {
+        nodes[i]->FixedUpdate(time);
+    }
+}
 
-int ServiceProvider::AddNode (Node * node) 
+void ServiceProvider::UpdateNodes_CWrapper(void *pServiceObject)
+{
+    ServiceProvider * pTempProvider = static_cast <ServiceProvider *> (pServiceObject);
+    pTempProvider->UpdateNodes();
+}
+
+
+/* Node Management ----------------------------------------------------------*/
+
+int ServiceProvider::AddNode (Node * node)
 {
     int result = 1;
-    
+
     if (nodeCount < SERVICE_MAX_NODES) {
         nodes[nodeCount++] = node;
     } else {
         result = 0;
     }
-    
+
     return result;
 }
 
-/* ========================================================================
- * Adds a message to the message queue outbox
- * ========================================================================
+
+/* CanMessage Transmission --------------------------------------------------*/
+
+void ServiceProvider::PostMessage (int nodeId, CanOpenMessage * msg)
+{
+    outbox.push(InternalMessage(nodeId, *msg));
+}
+
+void ServiceProvider::PostNmtControl (char targetNodeId, NmtCommandSpecifier cs)
+{
+    CanOpenMessage msg;
+
+    msg.id          = 0;
+    msg.dataCount   = 2;
+    msg.type        = CANOPEN_TYPE_DATA;
+    msg.format      = CANOPEN_FORMAT_STANDARD;
+
+    // NOTE: memcpy is freezing execution
+    //memcpy(msg.data, canMsg.data, canMsg.len);
+    msg.data[0] = targetNodeId;
+    msg.data[1] = (char) cs;
+    //msg.data[2] =
+    //msg.data[3] =
+    //msg.data[4] =
+    //msg.data[5] =
+    //msg.data[6] =
+    //msg.data[7] =
+
+    PostMessage(0, &msg);
+}
+
+
+/* ============================================================================
+ * Private Methods
+ * ============================================================================
  */
 
-void ServiceProvider::PostMessage (CanOpenMessage * msg)
+
+/* CanMessage Reading -------------------------------------------------------*/
+
+void ServiceProvider::ReadIT(void)
+{
+
+    //printf("S::ReadIT()\r\n");
+
+    CanOpenMessage msg;
+    if(CanOpenApiRead (&msg)) {
+        if (bInboxUnlocked) {
+            ReadIT_clearBuffer();
+            inbox.push(msg);
+        } else {
+            inboxBuffer.push(msg);
+            printf("!!!!!!!!!!!!!!!!!!!!!!!INBOX LOCKED!!!!!!!!!!!!!!!!!!!!!!!!!!!\r\n");
+        }
+    }
+}
+
+void ServiceProvider::ReadIT_clearBuffer(void)
+{
+    while (! inboxBuffer.empty()) {
+        inbox.push (inboxBuffer.front());
+        inboxBuffer.pop();
+    }
+}
+
+void ServiceProvider::ReadIT_CWrapper(void *pServiceObject)
 {
-    outbox.push(*msg);
+    ServiceProvider * pTempProvider = static_cast <ServiceProvider *> (pServiceObject);
+    pTempProvider->ReadIT();
+}
+
+
+/* Elapsed Time -------------------------------------------------------------*/
+
+uint32_t ServiceProvider::GetTime(void)
+{
+    return (CanOpenApiGetHardwareTime() + elapsedTimeOffset) & 0x0FFFFFFF;
 }
 
+void ServiceProvider::HandleTimeMessage(CanOpenMessage *canOpenMsg)
+{
+    if (6 == canOpenMsg->dataCount) {        
+        
+        uint32_t timeStamp_ms = (uint32_t)canOpenMsg->data[0] +
+                                (uint32_t)canOpenMsg->data[1] << 8 +
+                                (uint32_t)canOpenMsg->data[2] << 16 +
+                                (uint32_t)canOpenMsg->data[3] << 24;
+        
+        // Nothing uses Days yet, going to ignore for now.                        
+        // uint32_t timeStamp_days = (uint32_t)canOpenMsg->data[4] +
+        //                           (uint32_t)canOpenMsg->data[5] << 8;
+        
+        uint32_t hwTime = CanOpenApiGetHardwareTime() & 0x0FFFFFFF;
+    
+        elapsedTimeOffset = ((int32_t)timeStamp_ms) - ((int32_t)hwTime);
+        
+    // Else TODO: send error
+    }
+}
+
+
+
 } /* namspace ppCANOpen */
 
-
-
-
-
-
-