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.

Dependencies:   CANnucleo mbed ppCANOpen

Files at this revision

API Documentation at this revision

Comitter:
ptpaterson
Date:
Sat Mar 19 01:44:35 2016 +0000
Parent:
9:8352cfe17ab1
Commit message:
Final Submission (probs)

Changed in this revision

Application/include/Node_pin0808.h Show annotated file Show diff for this revision Revisions of this file
Application/include/SerialBuffered.h Show annotated file Show diff for this revision Revisions of this file
Application/source/Node_pin0808.cpp Show annotated file Show diff for this revision Revisions of this file
Application/source/SerialBuffered.cpp Show annotated file Show diff for this revision Revisions of this file
Application/source/main.cpp Show annotated file Show diff for this revision Revisions of this file
CANnucleo.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show diff for this revision Revisions of this file
ppCANOpen.lib Show annotated file Show diff for this revision Revisions of this file
diff -r 8352cfe17ab1 -r ec59d628ebdc Application/include/Node_pin0808.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Application/include/Node_pin0808.h	Sat Mar 19 01:44:35 2016 +0000
@@ -0,0 +1,269 @@
+/**
+ ******************************************************************************
+ * @file
+ * @author  Paul Paterson
+ * @version
+ * @date    2015-12-14
+ * @brief   CANOpen implementation library
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>&copy; COPYRIGHT(c) 2015 Paul Paterson
+ *
+ * All rights reserved.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PPCAN_NODE_PIN0808_H
+#define PPCAN_NODE_PIN0808_H
+
+#include "Node.h"
+
+namespace ppCANOpen
+{
+    
+/** Custom Object Dictionary for pinball io_0808 device.
+  */
+class Node_pin0808 : public Node
+{
+    
+public:
+    Node_pin0808(int id, ServiceProvider * provider, int bLoop = 0);
+    virtual ~Node_pin0808(void);
+
+private:
+    /* constants */
+    /** milliseconds necessary to debounce all input */
+    static const uint8_t DEBOUNCE_TIME = 3;
+
+    /* IO details */
+    uint8_t prevInputBuffers[1];
+    uint8_t inputDebounce[8];    
+    
+    uint8_t prevOutputBuffers[1];
+    int scheduleIndexTime; 
+
+    /**************************************************************************
+     * Implementation Overrides 
+     **************************************************************************
+     */
+
+    /* Application run */
+    virtual void OnFixedUpdate(void);
+    virtual void OnUpdate(void);
+
+    /* SYNC */
+    virtual void OnSync (uint8_t counter);
+
+    /* NMT Control */  
+    virtual void OnInitialize (void);
+    virtual void OnPreoperational (void);
+    virtual void OnOperational (void);
+    virtual void OnStopped (void);
+    
+    
+    /**************************************************************************
+     * Object Dictionary 
+     **************************************************************************
+     */
+    
+    /* Communication --------------------------------------------------------*/
+    /* index 0x1200 :   SDO Server */
+    ObjectData      Obj1200;
+    SubIndexSize    Obj1200_highestSubIndex;
+    uint32_t        Obj1200_ReceiveCobId;
+    uint32_t        Obj1200_TransmitCobId;
+    EntryData       Obj1200_entries[3];
+    
+    /* index 0x1400 :   Receive PDO 1 Parameter (Write Digital Output) */
+    ObjectData      Obj1400;
+    SubIndexSize    Obj1400_highestSubIndex;
+    uint32_t        Obj1400_CobId;
+    uint8_t         Obj1400_TransmissionType;
+    uint16_t        Obj1400_InhibitTime;
+    uint8_t         Obj1400_CompatibilityEntry;
+    uint16_t        Obj1400_EventTimer;
+    EntryData       Obj1400_entries[6];
+    
+    /* index 0x1401 :   Receive PDO 2 Parameter (Write Output Configuration) */
+    ObjectData      Obj1401;
+    SubIndexSize    Obj1401_highestSubIndex;
+    uint32_t        Obj1401_CobId;
+    uint8_t         Obj1401_TransmissionType;
+    uint16_t        Obj1401_InhibitTime;
+    uint8_t         Obj1401_CompatibilityEntry;
+    uint16_t        Obj1401_EventTimer;
+    EntryData       Obj1401_entries[6];
+    
+    /* index 0x1402 :   Receive PDO 3 Parameter (Write/Clear Autotrigger Rule) */
+    ObjectData      Obj1402;
+    SubIndexSize    Obj1402_highestSubIndex;
+    uint32_t        Obj1402_CobId;
+    uint8_t         Obj1402_TransmissionType;
+    uint16_t        Obj1402_InhibitTime;
+    uint8_t         Obj1402_CompatibilityEntry;
+    uint16_t        Obj1402_EventTimer;
+    EntryData       Obj1402_entries[6];
+    
+    /* index 0x1403 :   Receive PDO 5 Parameter (input changes) */
+    ObjectData      Obj1403;
+    SubIndexSize    Obj1403_highestSubIndex;
+    uint32_t        Obj1403_CobId;
+    uint8_t         Obj1403_TransmissionType;
+    uint16_t        Obj1403_InhibitTime;
+    uint8_t         Obj1403_CompatibilityEntry;
+    uint16_t        Obj1403_EventTimer;
+    EntryData       Obj1403_entries[6];
+    
+    /* index 0x1600 :   Receive PDO 1 Mapping */
+    ObjectData      Obj1600;
+    SubIndexSize    Obj1600_highestSubIndex;
+    uint32_t        Obj1600_Map;
+    EntryData       Obj1600_entries[2];
+    
+    /* index 0x1601 :   Transmit PDO 1 Mapping */
+    ObjectData      Obj1601;
+    SubIndexSize    Obj1601_highestSubIndex;
+    uint32_t        Obj1601_Map;
+    EntryData       Obj1601_entries[2];
+    
+    /* index 0x1602 :   Transmit PDO 1 Mapping */
+    ObjectData      Obj1602;
+    SubIndexSize    Obj1602_highestSubIndex;
+    uint32_t        Obj1602_Map;
+    EntryData       Obj1602_entries[2];
+    
+    /* index 0x1603 :   Receive PDO 2 Mapping */
+    ObjectData      Obj1603;
+    SubIndexSize    Obj1603_highestSubIndex;
+    uint32_t        Obj1603_Map;
+    EntryData       Obj1603_entries[2];
+    
+    
+    /* index 0x1800 :   Transmit PDO 1 Parameter */
+    ObjectData      Obj1800;
+    SubIndexSize    Obj1800_highestSubIndex;
+    uint32_t        Obj1800_CobId;
+    uint8_t         Obj1800_TransmissionType;
+    uint16_t        Obj1800_InhibitTime;
+    uint8_t         Obj1800_CompatibilityEntry;
+    uint16_t        Obj1800_EventTimer;
+    EntryData       Obj1800_entries[6];
+    
+    /* index 0x1A00 :   Transmit PDO 1 Mapping */
+    ObjectData      Obj1A00;
+    SubIndexSize    Obj1A00_highestSubIndex;
+    uint32_t        Obj1A00_MapInput;
+    uint32_t        Obj1A00_MapVoid16;
+    uint32_t        Obj1A00_MapChange;
+    uint32_t        Obj1A00_MapSourceId;
+    EntryData       Obj1A00_entries[6];
+    
+    
+    /* Manufacturer Specific ------------------------------------------------*/
+    /* index 0x2001 : Pin Output Configurations */
+    struct OutputConfiguration {
+        static const uint8_t PULSE          = 0x01;
+        static const uint8_t PATTER         = 0x02;
+        static const uint8_t PULSED_PATTER  = 0x04;
+        //static const uint8_t SCHEDULED      = 0x08;
+          
+        uint8_t     type;
+        uint8_t     pulse_ms;
+        uint8_t     pwm_on;
+        uint8_t     pwm_off;
+        uint32_t    writeData;
+    };
+    
+    ObjectData          Obj2001;
+    SubIndexSize        Obj2001_highestSubIndex;
+    OutputConfiguration writeOutputConfig;
+    OutputConfiguration outputConfigs[8];
+    OutputConfiguration outputTimers[8]; /* not actually part of dictionary */
+    EntryData           Obj2001_entries[10];
+    
+    /* index 0x2002 : Output Schedule Configuration */
+    ObjectData      Obj2002;
+    SubIndexSize    Obj2002_highestSubIndex;
+    uint32_t        scheduleConfig;
+    uint32_t        schedules[8];  
+    EntryData       Obj2002_entries[10];
+    
+    /* index 0x2100 : Input change data */
+    ObjectData      Obj2100;
+    SubIndexSize    Obj2100_highestSubIndex;
+    uint8_t         inputChangeMask[1];  
+    /* uint8_t         nodeId; */
+    EntryData       Obj2100_entries[3];
+        
+    /* index 0x2200 : Autotrigger Rules */
+    struct AutotriggerRule {
+        uint8_t     sourceId;
+        uint8_t     input; /* 0-4: inputNum (0-23)
+                              5-7: 0 on inactive
+                                   1 on active
+                                   2 on any change
+                                   3 ???
+                            */
+        uint8_t     setMask[3];
+        uint8_t     clearMask[3];  
+    };
+    
+    struct AutotriggerMessage {
+        uint8_t     input[3];
+        uint8_t     change[3];
+        uint8_t     sourceId;
+    };
+    
+    ObjectData          Obj2200;
+    SubIndexSize        Obj2200_highestSubIndex;
+    AutotriggerRule     writeRule;
+    AutotriggerMessage  autotriggerMessage;
+    AutotriggerRule     rules[24];
+    EntryData           Obj2200_entries[27];
+
+    
+    /* Device Specific ------------------------------------------------------*/
+    /* index 0x6000 :   Mapped variable "inputs" */
+    ObjectData      Obj6000;
+    SubIndexSize    Obj6000_highestSubIndex; 
+    uint8_t         readInputBuffers[1];
+    EntryData       Obj6000_entries[2];
+    
+    /* index 0x6005 :   Global Input Interrupt Enable */
+    ObjectData      Obj6005;
+    SubIndexSize    Obj6005_highestSubIndex; 
+    uint8_t         bInputInterruptEnable;
+    EntryData       Obj6005_entries[2];
+    
+    /* index 0x6006 :   Any Change Interrupt Mask */
+    ObjectData      Obj6006;
+    SubIndexSize    Obj6006_highestSubIndex; 
+    uint8_t         inputInterruptMask[1];
+    EntryData       Obj6006_entries[2];
+    
+    /* index 0x6200 :   Mapped variable "outputs" */
+    ObjectData      Obj6200;
+    SubIndexSize    Obj6200_highestSubIndex; 
+    uint8_t         writeOutputBuffers[1];
+    EntryData       Obj6200_entries[2];
+    
+    /* Scan Method */
+    virtual ObjectData * ScanIndex(IndexSize index);
+};
+
+} /* namespace ppCANOpen */
+
+#endif // PPCAN_NODE_PIN0808_H
diff -r 8352cfe17ab1 -r ec59d628ebdc Application/include/SerialBuffered.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Application/include/SerialBuffered.h	Sat Mar 19 01:44:35 2016 +0000
@@ -0,0 +1,61 @@
+#ifndef _SERIAL_BUFFERED_H_
+#define _SERIAL_BUFFERED_H_
+
+/**
+ * Buffered serial class.
+ */
+class SerialBuffered : public Serial {
+public:
+    /**
+     * Create a buffered serial class.
+     *
+     * @param tx A pin for transmit.
+     * @param rx A pin for receive.
+     */
+    SerialBuffered(PinName tx, PinName rx);
+
+    /**
+     * Destroy.
+     */
+    virtual ~SerialBuffered();
+
+    /**
+     * Get a character.
+     *
+     * @return A character. (-1:timeout)
+     */
+    int getc();
+
+    /**
+     * Returns 1 if there is a character available to read, 0 otherwise.
+     */
+    int readable();
+
+    /**
+     * Set timeout for getc().
+     *
+     * @param ms milliseconds. (-1:Disable timeout)
+     */
+    void setTimeout(int ms);
+
+    /**
+     * Read requested bytes.
+     *
+     * @param bytes A pointer to a buffer.
+     * @param requested Length.
+     *
+     * @return Readed byte length.
+     */
+    size_t readBytes(uint8_t *bytes, size_t requested);
+
+private:
+    void handleInterrupt();
+    static const int BUFFERSIZE = 2048;
+    uint8_t buffer[BUFFERSIZE];            // points at a circular buffer, containing data from m_contentStart, for m_contentSize bytes, wrapping when you get to the end
+    uint16_t indexContentStart;   // index of first bytes of content
+    uint16_t indexContentEnd;     // index of bytes after last byte of content
+    int timeout;
+    Timer timer;
+};
+
+#endif
diff -r 8352cfe17ab1 -r ec59d628ebdc Application/source/Node_pin0808.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Application/source/Node_pin0808.cpp	Sat Mar 19 01:44:35 2016 +0000
@@ -0,0 +1,700 @@
+/**
+ ******************************************************************************
+ * @file
+ * @author  Paul Paterson
+ * @version
+ * @date    2015-12-14
+ * @brief   CANOpen implementation library
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>&copy; COPYRIGHT(c) 2015 Paul Paterson
+ *
+ * All rights reserved.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ 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 "Node_pin0808.h"
+#include "ServiceProvider.h"
+
+#include "stdio.h"
+#include "mbed.h"
+
+PortIn mbedInputs(PortB, 0x00FF);
+PortOut mbedOutputs(PortB, 0xFF00);
+
+namespace ppCANOpen
+{
+
+/******************************************************************************
+ * Constructor/Destructor
+ ******************************************************************************
+ */
+
+Node_pin0808::Node_pin0808 (int id, ServiceProvider * provider, int bLoop)
+    : Node(id, provider, bLoop)
+{
+    dictionary = new ObjectData[22];
+
+
+    /* Init values ***********************************************************/
+    memset(prevInputBuffers, 0, sizeof(prevInputBuffers));
+    memset(inputDebounce,    0, sizeof(inputDebounce));
+    
+    memset(prevOutputBuffers,0, sizeof(prevOutputBuffers));
+
+    /* Init Object Dictionary ************************************************/
+
+    /* Communication Objects ================================================*/
+    
+    /* SDO ------------------------------------------------------------------*/
+    /* index 0x1200  */
+    Obj1200_highestSubIndex = 2;
+    Obj1200_ReceiveCobId    = 0x600 + nodeId;
+    Obj1200_TransmitCobId   = 0x580 + nodeId;
+    Obj1200_entries[0] = EntryData((void*)&Obj1200_highestSubIndex, sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1200_entries[1] = EntryData((void*)&Obj1200_ReceiveCobId,    sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1200_entries[2] = EntryData((void*)&Obj1200_TransmitCobId,   sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1200 = ObjectData(Obj1200_entries, 0x1200, sizeof(Obj1200_entries) / sizeof(Obj1200_entries[0]));
+    
+    /* RPDO -----------------------------------------------------------------*/
+    /* index 0x1400  */
+    Obj1400_highestSubIndex     = 5;
+    Obj1400_CobId               = 0x200 + nodeId;
+    Obj1400_TransmissionType    = 1;
+    Obj1400_InhibitTime         = 0;
+    Obj1400_CompatibilityEntry  = 0;
+    Obj1400_EventTimer          = 0;
+    Obj1400_entries[0] = EntryData((void*)&Obj1400_highestSubIndex,    sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1400_entries[1] = EntryData((void*)&Obj1400_CobId,              sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1400_entries[2] = EntryData((void*)&Obj1400_TransmissionType,   sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1400_entries[3] = EntryData((void*)&Obj1400_InhibitTime,        sizeof(uint16_t), EntryData::TYPE_UINT16, EntryData::PROPERTY_READABLE);
+    Obj1400_entries[4] = EntryData((void*)&Obj1400_CompatibilityEntry, sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1400_entries[5] = EntryData((void*)&Obj1400_EventTimer,         sizeof(uint16_t), EntryData::TYPE_UINT16, EntryData::PROPERTY_READABLE);
+    Obj1400 = ObjectData(Obj1400_entries, 0x1400, sizeof(Obj1400_entries) / sizeof(Obj1400_entries[0]));
+
+    /* index 0x1401  */
+    Obj1401_highestSubIndex     = 5;
+    Obj1401_CobId               = 0x300 + nodeId;
+    Obj1401_TransmissionType    = 1;
+    Obj1401_InhibitTime         = 0;
+    Obj1401_CompatibilityEntry  = 0;
+    Obj1401_EventTimer          = 0;
+    Obj1401_entries[0] = EntryData((void*)&Obj1401_highestSubIndex,    sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1401_entries[1] = EntryData((void*)&Obj1401_CobId,              sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1401_entries[2] = EntryData((void*)&Obj1401_TransmissionType,   sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1401_entries[3] = EntryData((void*)&Obj1401_InhibitTime,        sizeof(uint16_t), EntryData::TYPE_UINT16, EntryData::PROPERTY_READABLE);
+    Obj1401_entries[4] = EntryData((void*)&Obj1401_CompatibilityEntry, sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1401_entries[5] = EntryData((void*)&Obj1401_EventTimer,         sizeof(uint16_t), EntryData::TYPE_UINT16, EntryData::PROPERTY_READABLE);
+    Obj1401 = ObjectData(Obj1401_entries, 0x1401, sizeof(Obj1401_entries) / sizeof(Obj1401_entries[0]));
+
+    /* index 0x1402  */
+    Obj1402_highestSubIndex     = 5;
+    Obj1402_CobId               = 0x400 + nodeId;
+    Obj1402_TransmissionType    = 1;
+    Obj1402_InhibitTime         = 0;
+    Obj1402_CompatibilityEntry  = 0;
+    Obj1402_EventTimer          = 0;
+    Obj1402_entries[0] = EntryData((void*)&Obj1402_highestSubIndex,    sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1402_entries[1] = EntryData((void*)&Obj1402_CobId,              sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1402_entries[2] = EntryData((void*)&Obj1402_TransmissionType,   sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1402_entries[3] = EntryData((void*)&Obj1402_InhibitTime,        sizeof(uint16_t), EntryData::TYPE_UINT16, EntryData::PROPERTY_READABLE);
+    Obj1402_entries[4] = EntryData((void*)&Obj1402_CompatibilityEntry, sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1402_entries[5] = EntryData((void*)&Obj1402_EventTimer,         sizeof(uint16_t), EntryData::TYPE_UINT16, EntryData::PROPERTY_READABLE);
+    Obj1402 = ObjectData(Obj1402_entries, 0x1402, sizeof(Obj1402_entries) / sizeof(Obj1402_entries[0]));
+
+    /* index 0x1403  */
+    Obj1403_highestSubIndex     = 5;
+    Obj1403_CobId               = 0x181;
+    Obj1403_TransmissionType    = 1;
+    Obj1403_InhibitTime         = 0;
+    Obj1403_CompatibilityEntry  = 0;
+    Obj1403_EventTimer          = 0;
+    Obj1403_entries[0] = EntryData((void*)&Obj1403_highestSubIndex,    sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1403_entries[1] = EntryData((void*)&Obj1403_CobId,              sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1403_entries[2] = EntryData((void*)&Obj1403_TransmissionType,   sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1403_entries[3] = EntryData((void*)&Obj1403_InhibitTime,        sizeof(uint16_t), EntryData::TYPE_UINT16, EntryData::PROPERTY_READABLE);
+    Obj1403_entries[4] = EntryData((void*)&Obj1403_CompatibilityEntry, sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1403_entries[5] = EntryData((void*)&Obj1403_EventTimer,         sizeof(uint16_t), EntryData::TYPE_UINT16, EntryData::PROPERTY_READABLE);
+    Obj1403 = ObjectData(Obj1403_entries, 0x1403, sizeof(Obj1403_entries) / sizeof(Obj1403_entries[0]));
+
+    /* index 0x1600  */
+    Obj1600_highestSubIndex = 1;
+    Obj1600_Map             = 0x62000108;
+    Obj1600_entries[0] = EntryData((void*)&Obj1600_highestSubIndex, sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1600_entries[1] = EntryData((void*)&Obj1600_Map,             sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1600 = ObjectData(Obj1600_entries, 0x1600, sizeof(Obj1600_entries) / sizeof(Obj1600_entries[0]));
+    
+    /* index 0x1601  */
+    Obj1601_highestSubIndex = 1;
+    Obj1601_Map             = 0x20010140;
+    Obj1601_entries[0] = EntryData((void*)&Obj1601_highestSubIndex, sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1601_entries[1] = EntryData((void*)&Obj1601_Map,             sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1601 = ObjectData(Obj1601_entries, 0x1601, sizeof(Obj1601_entries) / sizeof(Obj1601_entries[0]));
+
+    /* index 0x1602  */
+    Obj1602_highestSubIndex = 1;
+    Obj1602_Map             = 0x22000140;
+    Obj1602_entries[0] = EntryData((void*)&Obj1602_highestSubIndex, sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1602_entries[1] = EntryData((void*)&Obj1602_Map,             sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1602 = ObjectData(Obj1602_entries, 0x1602, sizeof(Obj1602_entries) / sizeof(Obj1602_entries[0]));
+
+    /* index 0x1603  */
+    Obj1603_highestSubIndex = 1;
+    Obj1603_Map             = 0x22000238;
+    Obj1603_entries[0] = EntryData((void*)&Obj1603_highestSubIndex, sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1603_entries[1] = EntryData((void*)&Obj1603_Map,             sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1603 = ObjectData(Obj1603_entries, 0x1603, sizeof(Obj1603_entries) / sizeof(Obj1603_entries[0]));
+
+    /* TPDO -----------------------------------------------------------------*/
+    /* index 0x1800  */
+    Obj1800_highestSubIndex     = 5;
+    Obj1800_CobId               = 0x181;
+    Obj1800_TransmissionType    = 0xFE;  /* event driven Manuf specific */
+    Obj1800_InhibitTime         = 0;     // TODO review if this is needed
+    Obj1800_CompatibilityEntry  = 0;
+    Obj1800_EventTimer          = 0;
+    Obj1800_entries[0] = EntryData((void*)&Obj1800_highestSubIndex,    sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1800_entries[1] = EntryData((void*)&Obj1800_CobId,              sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1800_entries[2] = EntryData((void*)&Obj1800_TransmissionType,   sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1800_entries[3] = EntryData((void*)&Obj1800_InhibitTime,        sizeof(uint16_t), EntryData::TYPE_UINT16, EntryData::PROPERTY_READABLE);
+    Obj1800_entries[4] = EntryData((void*)&Obj1800_CompatibilityEntry, sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1800_entries[5] = EntryData((void*)&Obj1800_EventTimer,         sizeof(uint16_t), EntryData::TYPE_UINT16, EntryData::PROPERTY_READABLE);
+    Obj1800 = ObjectData(Obj1800_entries, 0x1800, sizeof(Obj1800_entries) / sizeof(Obj1800_entries[0]));
+
+    /* index 0x1A00  */
+    Obj1A00_highestSubIndex = 5;
+    Obj1A00_MapInput        = 0x60000108;
+    Obj1A00_MapVoid16       = 0x00000010;
+    Obj1A00_MapChange       = 0x21000108;
+    Obj1A00_MapSourceId     = 0x21000208;
+    Obj1A00_entries[0] = EntryData((void*)&Obj1A00_highestSubIndex, sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj1A00_entries[1] = EntryData((void*)&Obj1A00_MapInput,        sizeof(uint8_t),  EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1A00_entries[2] = EntryData((void*)&Obj1A00_MapVoid16,       sizeof(uint16_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1A00_entries[3] = EntryData((void*)&Obj1A00_MapChange,       sizeof(uint8_t),  EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1A00_entries[4] = EntryData((void*)&Obj1A00_MapVoid16,       sizeof(uint16_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1A00_entries[5] = EntryData((void*)&Obj1A00_MapSourceId,     sizeof(uint8_t),  EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj1A00 = ObjectData(Obj1A00_entries, 0x1A00, sizeof(Obj1A00_entries) / sizeof(Obj1A00_entries[0]));
+
+
+    /* Manufacturer Specific Objects ========================================*/
+
+    /* index 0x2001 : Pin Output Configurations */
+    Obj2001_highestSubIndex = 8;
+    memset(&writeOutputConfig, 0, sizeof(writeOutputConfig));
+    memset(outputConfigs, 0, sizeof(outputConfigs));
+    outputConfigs[0].type       = 0x08;
+    memset(outputTimers, 0, sizeof(outputTimers));
+    Obj2001_entries[0] = EntryData((void*)&Obj2001_highestSubIndex, sizeof(uint8_t),  EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj2001_entries[1] = EntryData((void*)&writeOutputConfig, sizeof(OutputConfiguration), EntryData::TYPE_UINT64, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2001_entries[2] = EntryData((void*)&outputConfigs[0],  sizeof(OutputConfiguration), EntryData::TYPE_UINT64, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2001_entries[3] = EntryData((void*)&outputConfigs[1],  sizeof(OutputConfiguration), EntryData::TYPE_UINT64, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2001_entries[4] = EntryData((void*)&outputConfigs[2],  sizeof(OutputConfiguration), EntryData::TYPE_UINT64, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2001_entries[5] = EntryData((void*)&outputConfigs[3],  sizeof(OutputConfiguration), EntryData::TYPE_UINT64, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2001_entries[6] = EntryData((void*)&outputConfigs[4],  sizeof(OutputConfiguration), EntryData::TYPE_UINT64, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2001_entries[7] = EntryData((void*)&outputConfigs[5],  sizeof(OutputConfiguration), EntryData::TYPE_UINT64, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2001_entries[8] = EntryData((void*)&outputConfigs[6],  sizeof(OutputConfiguration), EntryData::TYPE_UINT64, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2001_entries[9] = EntryData((void*)&outputConfigs[7],  sizeof(OutputConfiguration), EntryData::TYPE_UINT64, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2001 = ObjectData(Obj2001_entries, 0x2001, sizeof(Obj2001_entries) / sizeof(Obj2001_entries[0]));
+
+    /* index 0x2002 : Output Schedule Configuration */
+    Obj2002_highestSubIndex = 1;
+    scheduleConfig  = 0x01;
+    schedules[0] = 0xFF00F0F0;
+    Obj2002_entries[0] = EntryData((void*)&Obj2002_highestSubIndex, sizeof(uint8_t),  EntryData::TYPE_UINT8, EntryData::PROPERTY_READABLE);
+    Obj2002_entries[1] = EntryData((void*)&scheduleConfig, sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2002_entries[2] = EntryData((void*)&schedules[0],   sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2002_entries[3] = EntryData((void*)&schedules[1],   sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2002_entries[4] = EntryData((void*)&schedules[2],   sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2002_entries[5] = EntryData((void*)&schedules[3],   sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2002_entries[6] = EntryData((void*)&schedules[4],   sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2002_entries[7] = EntryData((void*)&schedules[5],   sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2002_entries[8] = EntryData((void*)&schedules[6],   sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2002_entries[9] = EntryData((void*)&schedules[7],   sizeof(uint32_t), EntryData::TYPE_UINT32, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2002 = ObjectData(Obj2002_entries, 0x2002, sizeof(Obj2002_entries) / sizeof(Obj2002_entries[0]));
+
+    /* Index 2100 */
+    Obj2100_highestSubIndex = 2;
+    inputChangeMask[0] = 0;
+    /* nodeId; */
+    Obj2100_entries[0] = EntryData((void*)&Obj2100_highestSubIndex, sizeof(uint8_t), EntryData::TYPE_UINT8, EntryData::PROPERTY_READABLE);
+    Obj2100_entries[1] = EntryData((void*)&inputChangeMask[0],      sizeof(uint8_t), EntryData::TYPE_UINT8, EntryData::PROPERTY_READABLE);
+    Obj2100_entries[2] = EntryData((void*)&nodeId,                  sizeof(uint8_t), EntryData::TYPE_UINT8, EntryData::PROPERTY_READABLE);
+    Obj2100 = ObjectData(Obj2100_entries, 0x2100, sizeof(Obj2100_entries) / sizeof(Obj2100_entries[0]));
+    
+    /* Index 2200 */
+    Obj2200_highestSubIndex = 2;
+    memset(&writeRule,          0, sizeof(AutotriggerRule));
+    memset(&autotriggerMessage, 0, sizeof(AutotriggerMessage));
+    memset(rules, 0, sizeof(rules));
+    Obj2200_entries[0]  = EntryData((void*)&Obj2200_highestSubIndex, sizeof(uint8_t),            EntryData::TYPE_UINT8,  EntryData::PROPERTY_READABLE);
+    Obj2200_entries[1]  = EntryData((void*)&writeRule,               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2200_entries[2]  = EntryData((void*)&autotriggerMessage,      sizeof(AutotriggerMessage), EntryData::TYPE_UINT32, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj2200_entries[3]  = EntryData((void*)&rules[0],                sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[4]  = EntryData((void*)&rules[1],                sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[5]  = EntryData((void*)&rules[2],                sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[6]  = EntryData((void*)&rules[3],                sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[7]  = EntryData((void*)&rules[4],                sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[8]  = EntryData((void*)&rules[5],                sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[9]  = EntryData((void*)&rules[6],                sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[10] = EntryData((void*)&rules[7],                sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[11] = EntryData((void*)&rules[8],                sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[12] = EntryData((void*)&rules[9],                sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[13] = EntryData((void*)&rules[10],               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[14] = EntryData((void*)&rules[11],               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[15] = EntryData((void*)&rules[12],               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[16] = EntryData((void*)&rules[13],               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[17] = EntryData((void*)&rules[14],               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[18] = EntryData((void*)&rules[15],               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[19] = EntryData((void*)&rules[16],               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[20] = EntryData((void*)&rules[17],               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[21] = EntryData((void*)&rules[18],               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[22] = EntryData((void*)&rules[19],               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[23] = EntryData((void*)&rules[20],               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[24] = EntryData((void*)&rules[21],               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[25] = EntryData((void*)&rules[22],               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200_entries[26] = EntryData((void*)&rules[23],               sizeof(AutotriggerRule),    EntryData::TYPE_UINT32, EntryData::PROPERTY_READABLE);
+    Obj2200 = ObjectData(Obj2200_entries, 0x2200, sizeof(Obj2200_entries) / sizeof(Obj2200_entries[0]));
+
+    /* Device Profile Specific Objects ======================================*/
+
+    /* Index 6000 */
+    Obj6000_highestSubIndex = 1;
+    readInputBuffers[0]  = 0;
+    Obj6000_entries[0] = EntryData((void*)&Obj6000_highestSubIndex, sizeof(uint8_t), EntryData::TYPE_UINT8, EntryData::PROPERTY_READABLE);
+    Obj6000_entries[1] = EntryData((void*)&readInputBuffers,        sizeof(uint8_t), EntryData::TYPE_UINT8, EntryData::PROPERTY_READABLE);
+    Obj6000 = ObjectData(Obj6000_entries, 0x6000, sizeof(Obj6000_entries) / sizeof(Obj6000_entries[0]));
+    
+    /* Index 6005 */
+    Obj6005_highestSubIndex = 1;
+    bInputInterruptEnable   = 0x01;
+    Obj6005_entries[0] = EntryData((void*)&Obj6005_highestSubIndex, sizeof(uint8_t), EntryData::TYPE_UINT8, EntryData::PROPERTY_READABLE);
+    Obj6005_entries[1] = EntryData((void*)&bInputInterruptEnable,   sizeof(uint8_t), EntryData::TYPE_UINT8, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj6005 = ObjectData(Obj6005_entries, 0x6005, sizeof(Obj6005_entries) / sizeof(Obj6005_entries[0]));
+    
+    /* Index 6006 */
+    Obj6006_highestSubIndex = 1;
+    inputInterruptMask[0]      = 0xFF;
+    Obj6006_entries[0] = EntryData((void*)&Obj6006_highestSubIndex, sizeof(uint8_t), EntryData::TYPE_UINT8, EntryData::PROPERTY_READABLE);
+    Obj6006_entries[1] = EntryData((void*)&inputInterruptMask,      sizeof(uint8_t), EntryData::TYPE_UINT8, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj6006 = ObjectData(Obj6006_entries, 0x6006, sizeof(Obj6006_entries) / sizeof(Obj6006_entries[0]));
+
+    /* Index 6200 */
+    Obj6200_highestSubIndex = 1;
+    writeOutputBuffers[0] = 0;
+    Obj6200_entries[0] = EntryData((void*)&Obj6200_highestSubIndex, sizeof(uint8_t), EntryData::TYPE_UINT8, EntryData::PROPERTY_READABLE);
+    Obj6200_entries[1] = EntryData((void*)&writeOutputBuffers,      sizeof(uint8_t), EntryData::TYPE_UINT8, EntryData::PROPERTY_READ_WRITEABLE);
+    Obj6200 = ObjectData(Obj6200_entries, 0x6200, sizeof(Obj6200_entries) / sizeof(Obj6200_entries[0]));
+
+    /* Set up the whole dictionary */
+    dictionary[0]  = Obj1200;
+    dictionary[1]  = Obj1400;
+    dictionary[2]  = Obj1401;
+    dictionary[3]  = Obj1402;
+    dictionary[4]  = Obj1403;
+    dictionary[5]  = Obj1600;
+    dictionary[6]  = Obj1601;
+    dictionary[7]  = Obj1602;
+    dictionary[8]  = Obj1603;
+    dictionary[10] = Obj1800;
+    dictionary[11] = Obj1A00;
+    dictionary[12] = Obj2001;
+    dictionary[13] = Obj2002;
+    dictionary[14] = Obj2100;
+    dictionary[15] = Obj2200;
+    dictionary[16] = Obj6000;
+    dictionary[17] = Obj6005;
+    dictionary[18] = Obj6006;
+    dictionary[19] = Obj6200;
+
+}
+
+Node_pin0808::~Node_pin0808(void)
+{
+    delete dictionary;
+}
+
+/******************************************************************************
+ * Application Run Implementation
+ ******************************************************************************
+ */
+
+void Node_pin0808::OnFixedUpdate (void)
+{
+    /**************************************************************************
+     * INPUTS
+     **************************************************************************
+     */
+
+    uint8_t inputSample = ~(uint8_t)(mbedInputs.read());
+    
+    uint8_t inputChange = readInputBuffers[0] ^ inputSample; 
+    
+    /* Loop through all of the inputs */
+    for (int in = 0; in < 8; in++) {
+        if (inputChange & (1 << in)) {
+            
+            inputDebounce[in] += (uint8_t)(timeSinceLastTick);
+            if (inputDebounce[in] > DEBOUNCE_TIME) {
+                readInputBuffers[0] &= ~(1 << in);
+                readInputBuffers[0] |=  (inputSample & (1 << in));
+                
+                inputDebounce[in] = 0; 
+            }
+            
+        } else {
+            inputDebounce[in] = 0;   
+        }
+    }
+    
+
+    /**************************************************************************
+     * OUTPUTS
+     **************************************************************************
+     */
+
+    /* create a copy of the current outputs and edit as we go */
+    uint8_t newOutput = (uint8_t)(mbedOutputs >> 8);
+
+    /* big operation, so let's only do it once! */
+    int scheduleIndexer = 1 << ((timeCurrentTick * 100) / 3125) % 32;    
+
+    /* Loop through all of the outputs */
+    for (int out = 0; out < 8; out++) {
+
+        if (scheduleConfig & (1 << out)) {
+            
+            if (schedules[out] & scheduleIndexer) {
+                writeOutputBuffers[0] |= (1 << out);
+            } else {
+                writeOutputBuffers[0] &= ~(1 << out);
+            }
+        }
+        
+        /* update GPIO's immediately only if new signal was given */
+        int outputBufferChanges = writeOutputBuffers[0] ^ prevOutputBuffers[0];
+        
+        if (outputBufferChanges & (1 << out)) {
+            if ((1 << out) & writeOutputBuffers[0]) {
+                /* switched on */
+                newOutput |= (1 << out);
+
+                /* reset timers */
+                if (outputConfigs[out].type) {
+                    outputTimers[out] = OutputConfiguration(outputConfigs[out]);
+                }
+            } else {
+                /* switched off */
+                newOutput &= ~(1 << out);
+                memset(&outputTimers[out], 0, sizeof(outputTimers[out]));
+            }
+        }
+
+        /* if there is a pulse run down the timer */
+        if (outputTimers[out].type & 0x01) {
+
+            if (outputTimers[out].pulse_ms >= timeSinceLastTick) {
+                outputTimers[out].pulse_ms -= timeSinceLastTick;
+            } else {
+                /* time past would put timer below 0 */
+                outputTimers[out].pulse_ms = 0;
+            }
+
+            /* if time has run out */
+            if (0 == outputTimers[out].pulse_ms) {
+
+                /* just turn off pulse */
+                outputTimers[out].type &= 0xFE;
+                newOutput &= ~(1 << out);
+
+                /* if there is a patter, then output buffer will stay on */
+                /* but if there is a pulsed-patter, then it should still turn off */
+                if (!(outputTimers[out].type & 0x02) ||
+                        (outputTimers[out].type & (0x02 | 0x04))) {
+                    /* turn buffer off */
+                    outputTimers[out].type = 0;
+                    writeOutputBuffers[0] &= ~(1 << out);
+                }
+            }
+        }
+
+        /* two ways to patter:
+         * wait until after pulse (pulse-then-patter),
+         * or config so pulse happens at same time (pulsed-patter)
+         */
+        if (((outputTimers[out].type & 0x02)          && !(outputTimers[out].type & 0x01)) ||
+                ((outputTimers[out].type & (0x02 | 0x04)) &&  (outputTimers[out].type & 0x01))) {
+
+            /* if output is on, run down pwm_on, else tun down pwm_off */
+            uint8_t *pw_timer;
+            if (newOutput & (1 << out)) {
+                pw_timer = &outputTimers[out].pwm_on;
+            } else {
+                pw_timer = &outputTimers[out].pwm_off;
+            }
+
+            if (*pw_timer >= timeSinceLastTick) {
+                *pw_timer -= timeSinceLastTick;
+            } else {
+                *pw_timer = 0;
+            }
+
+            if (*pw_timer == 0) {
+                newOutput ^= (1 << out);
+
+                outputTimers[out].pwm_on  = outputConfigs[out].pwm_on;
+                outputTimers[out].pwm_off = outputConfigs[out].pwm_off;
+            }
+        }
+
+
+    }
+
+    mbedOutputs = ((uint16_t)newOutput) << 8;
+
+    prevOutputBuffers[0] = writeOutputBuffers[0];
+}
+
+void Node_pin0808::OnUpdate (void)
+{    
+    /* Check for output configurations --------------------------------------*/
+    if (writeOutputConfig.writeData) {
+        uint8_t out = (uint8_t)(writeOutputConfig.writeData);
+        
+        writeOutputConfig.writeData = 0;
+        outputConfigs[out] = writeOutputConfig;
+        
+        memset(&writeOutputConfig, 0, sizeof(writeOutputConfig));
+    }
+
+
+    /* Check for rule configurations ----------------------------------------*/
+    if (writeRule.sourceId) {
+        printf("      Configuring Rules...\r\n");
+        if (writeRule.sourceId & 0x80) {
+            /* write new rule*/
+            
+            /* scan through array of rules for available (8th bit cleared) */
+            int ruleNum = 0;
+            while (ruleNum < 24) {
+                if (!(rules[ruleNum].sourceId & 0x80)) {
+                                        
+                    rules[ruleNum] = writeRule;
+                    printf("      Rule added: #%d\r\n", ruleNum);
+                    ruleNum = 24;
+                } else {
+                    ruleNum++;
+                    if (ruleNum == 24) {
+                        printf("      ERROR: Not enough rules available");
+                    }
+                }
+            }
+            
+        } else {
+            /* clear rules */
+            
+            /* scan through array of rules matching input and disable */
+            for (int ruleNum = 0; ruleNum < 24; ruleNum++) {
+                
+                /* check if rule enabled and input num matches */
+                if ((rules[ruleNum].sourceId & 0x80) &&
+                   ((rules[ruleNum].sourceId & 0x7F) == writeRule.sourceId) &&
+                   ((rules[ruleNum].input    & 0x1F) == (writeRule.input & 0x1F))) 
+                {
+                    memset(&rules[ruleNum], 0, sizeof(AutotriggerRule));
+                    printf("      Rule removed: #%d\r\n", ruleNum);
+                }
+                
+            }
+        }
+    
+        memset(&writeRule, 0, sizeof(AutotriggerRule));   
+    }
+
+    /* check for input changes  from other devices---------------------------*/
+    if (autotriggerMessage.sourceId) {
+        
+        printf("      detected change from node: %#04x\r\n", (autotriggerMessage.sourceId & 0x7F));
+        /* scan through array of rules matching inputs and direction */
+        for (int ruleNum = 0; ruleNum < 24; ruleNum++) {
+            
+            /* check if rule enabled and input num matches */
+            if ((rules[ruleNum].sourceId & 0x80) &&                                     /* rule is active AND */                  
+                (rules[ruleNum].sourceId == autotriggerMessage.sourceId) &&             /* rule source matches message source AND */ 
+                (autotriggerMessage.change[0] & (1 << (rules[ruleNum].input & 0x1F))))  /* change exists at input num */
+            {
+                
+                int ruleActivity = rules[ruleNum].input >> 5;
+                int inputActivity = (autotriggerMessage.input[0] >> (rules[ruleNum].input & 0x1F)) & 1;
+                
+                printf("         id match:      %#04x\r\n", ruleNum);
+                printf("         ruleActivity:  %#04x\r\n", ruleActivity);
+                printf("         inputActivity: %#04x\r\n", inputActivity);
+                
+                if ((2 == ruleActivity) || (inputActivity == ruleActivity)) {
+                    
+                    printf("            activating Rule...\r\n");
+                    printf("            setMask:   %#10x\r\n", rules[ruleNum].setMask[0]);
+                    printf("            clearMask: %#10x\r\n", ~rules[ruleNum].clearMask[0]);
+                    
+                    writeOutputBuffers[0] |= rules[ruleNum].setMask[0];
+                    //writeOutputBuffers[1] |= rules[ruleNum].setMask[1];
+                    //writeOutputBuffers[2] |= rules[ruleNum].setMask[2];
+                    
+                    writeOutputBuffers[0] &= ~rules[ruleNum].clearMask[0];
+                    //writeOutputBuffers[1] &= ~rules[ruleNum].clearMask[1];
+                    //writeOutputBuffers[2] &= ~rules[ruleNum].clearMask[2];
+                }
+            }
+            
+        }
+        
+        memset(&autotriggerMessage, 0, sizeof(AutotriggerMessage)); 
+    }
+
+    /* check for input changes from this device -----------------------------*/
+    /* update Index 2101-01 */
+    inputChangeMask[0] = readInputBuffers[0] ^ prevInputBuffers[0];
+
+    /* check for rules against this devices inputs */
+    if (inputChangeMask[0]) {
+        /* scan through array of rules matching inputs and direction */
+        for (int ruleNum = 0; ruleNum < 24; ruleNum++) {
+            
+            /* check if rule enabled and input num matches */
+            if ((rules[ruleNum].sourceId & 0x80) &&                             /* rule is active AND */                  
+                ((rules[ruleNum].sourceId & 0x7F) == nodeId) &&                 /* rule source is this nodeId AND */ 
+                (inputChangeMask[0] & (1 << (rules[ruleNum].input & 0x1F))))    /* change exists at input num */
+            {
+                
+                int ruleActivity = rules[ruleNum].input >> 5;
+                int inputActivity = (readInputBuffers[0] >> (rules[ruleNum].input & 0x1F)) & 1;
+                
+                if ((2 == ruleActivity) || (inputActivity == ruleActivity)) {
+                    writeOutputBuffers[0] |= rules[ruleNum].setMask[0];
+                    //writeOutputBuffers[1] |= rules[ruleNum].setMask[1];
+                    //writeOutputBuffers[2] |= rules[ruleNum].setMask[2];
+                    
+                    writeOutputBuffers[0] &= ~rules[ruleNum].clearMask[0];
+                    //writeOutputBuffers[1] &= ~rules[ruleNum].clearMask[1];
+                    //writeOutputBuffers[2] &= ~rules[ruleNum].clearMask[2];
+                }
+            }
+            
+        }
+    }
+
+    /* index 6005-01 configured to enable interrupt messages */
+    if (bInputInterruptEnable) {
+        
+        /* if change is within index 6006-01, interrupt mask */
+        if (inputChangeMask[0] & inputInterruptMask[0]) {
+            
+            /* send a message immediately */
+            PostTPDO(0x181);
+        }
+        
+    }
+    
+    prevInputBuffers[0] = readInputBuffers[0];
+}
+
+/******************************************************************************
+ * SYNC Implementation
+ ******************************************************************************
+ */
+
+void Node_pin0808::OnSync (uint8_t counter)
+{
+
+}
+
+/******************************************************************************
+ * NMT Control Implementation
+ ******************************************************************************
+ */
+
+void Node_pin0808::OnInitialize (void)
+{
+    printf("      Node_pin0808::INITIALIZE!\r\n");
+
+    mbedOutputs = 0xFFFF;
+    wait(1.0);
+    mbedOutputs = 0;
+
+    prevOutputBuffers[0] = 0;
+    writeOutputBuffers[0] = 0;
+    
+    prevInputBuffers[0] = 0;
+    readInputBuffers[0] = 0;
+}
+
+void Node_pin0808::OnPreoperational (void)
+{
+    printf("      Node_pin0808::PRE-OPERATIONAL!\r\n");
+}
+
+void Node_pin0808::OnOperational (void)
+{
+    printf("      Node_pin0808::OPERATIONAL!\r\n");
+
+    mbedOutputs = (writeOutputBuffers[0] << 16);
+}
+
+void Node_pin0808::OnStopped (void)
+{
+    printf("      Node_pin0808::STOPPED!\r\n");
+
+    mbedOutputs = 0;
+}
+
+/******************************************************************************
+ * Object Dictionary Handling
+ ******************************************************************************
+ */
+
+ObjectData * Node_pin0808::ScanIndex(IndexSize index)
+{
+    ObjectData * result = 0;
+
+    switch(index) {
+        case 0x1200: result = &dictionary[0]; break;
+        case 0x1400: result = &dictionary[1]; break;
+        case 0x1401: result = &dictionary[2]; break;
+        case 0x1402: result = &dictionary[3]; break;
+        case 0x1403: result = &dictionary[4]; break;
+        case 0x1600: result = &dictionary[5]; break;
+        case 0x1601: result = &dictionary[6]; break;
+        case 0x1602: result = &dictionary[7]; break;
+        case 0x1603: result = &dictionary[8]; break;
+        case 0x1800: result = &dictionary[10]; break;
+        case 0x1A00: result = &dictionary[11]; break;
+        case 0x2001: result = &dictionary[12]; break;
+        case 0x2002: result = &dictionary[13]; break;
+        case 0x2100: result = &dictionary[14]; break;
+        case 0x2200: result = &dictionary[15]; break;
+        case 0x6000: result = &dictionary[16]; break;
+        case 0x6005: result = &dictionary[17]; break;
+        case 0x6006: result = &dictionary[18]; break;
+        case 0x6200: result = &dictionary[19]; break;
+        default:
+            // TODO add error handling
+            break;
+    }
+
+    return result;
+}
+
+} /* namespace ppCANOpen */
diff -r 8352cfe17ab1 -r ec59d628ebdc Application/source/SerialBuffered.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Application/source/SerialBuffered.cpp	Sat Mar 19 01:44:35 2016 +0000
@@ -0,0 +1,99 @@
+#include "mbed.h"
+#include "SerialBuffered.h"
+
+/**
+ * Create a buffered serial class.
+ *
+ * @param tx A pin for transmit.
+ * @param rx A pin for receive.
+ */
+SerialBuffered::SerialBuffered(PinName tx, PinName rx) : Serial(tx, rx) {
+    indexContentStart = 0;
+    indexContentEnd = 0;
+    timeout = 1;
+    attach(this, &SerialBuffered::handleInterrupt);
+}
+
+/**
+ * Destroy.
+ */
+SerialBuffered::~SerialBuffered() {
+}
+
+/**
+ * Set timeout for getc().
+ *
+ * @param ms milliseconds. (-1:Disable timeout)
+ */
+void SerialBuffered::setTimeout(int ms) {
+    timeout = ms;
+}
+
+/**
+ * Read requested bytes.
+ *
+ * @param bytes A pointer to a buffer.
+ * @param requested Length.
+ *
+ * @return Readed byte length.
+ */
+size_t SerialBuffered::readBytes(uint8_t *bytes, size_t requested) {
+    int i = 0;
+    while (i < requested) {
+        int c = getc();
+        if (c < 0) {
+            break;
+        }
+        bytes[i] = c;
+        i++;
+    }
+    return i;
+}
+
+/**
+ * Get a character.
+ *
+ * @return A character. (-1:timeout)
+ */
+int SerialBuffered::getc() {
+    timer.reset();
+    timer.start();
+    while (indexContentStart == indexContentEnd) {
+        wait_ms(1);
+        if ((timeout > 0) && (timer.read_ms() > timeout)) {
+            /*
+             * Timeout occured.
+             */
+            // printf("Timeout occured.\n");
+            return EOF;
+        }
+    }
+    timer.stop();
+
+    uint8_t result = buffer[indexContentStart++];
+    indexContentStart =  indexContentStart % BUFFERSIZE;
+
+    return result;
+}
+
+/**
+ * Returns 1 if there is a character available to read, 0 otherwise.
+ */
+int SerialBuffered::readable() {
+    return indexContentStart != indexContentEnd;
+}
+
+void SerialBuffered::handleInterrupt() {
+    while (Serial::readable()) {
+        if (indexContentStart == ((indexContentEnd + 1) % BUFFERSIZE)) {
+            /*
+             * Buffer overrun occured.
+             */
+            // printf("Buffer overrun occured.\n");
+            Serial::getc();
+        } else {
+            buffer[indexContentEnd++] = Serial::getc();
+            indexContentEnd = indexContentEnd % BUFFERSIZE;
+        }
+    }
+}
diff -r 8352cfe17ab1 -r ec59d628ebdc Application/source/main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Application/source/main.cpp	Sat Mar 19 01:44:35 2016 +0000
@@ -0,0 +1,71 @@
+
+/** @file
+ *  @brief main program entry
+ */
+
+#include "mbed.h"
+#include "CAN.h"
+
+#include "ppCANOpen.h"
+#include "Node_pin0808.h"
+
+DigitalOut boardLed (LED1);
+
+void InputScan ()
+{
+    boardLed = !boardLed;
+}
+
+
+int main()
+{
+
+    printf ("\r\n----- MAIN -----\r\n");
+
+    /* blinker task*/
+    boardLed = 0;
+
+//#define MASTER
+#ifdef MASTER
+
+    /* CanOpen start */
+    ServiceProvider service;
+    printf ("----- READY -----\r\n");
+
+    Node_pin0808 node1(2, &service);
+    //Node_pin0808 node2(&service);
+
+    service.PostNmtControl(1, NMT_CS_RESET_NODE);
+    service.Run();
+    service.PostNmtControl(2, NMT_CS_START);
+    service.Run();
+    service.PostNmtControl(3, NMT_CS_START);
+    service.Run();
+    service.PostNmtControl(4, NMT_CS_START);
+    service.Run();
+    service.PostNmtControl(5, NMT_CS_START);
+    service.Run();
+    service.PostNmtControl(6, NMT_CS_START);
+    service.Run();
+
+    while (1) {
+        service.Run();
+    }
+
+#else
+
+    /* CanOpen start */
+    ServiceProvider service;
+    printf ("----- READY -----\r\n");
+
+    Node_pin0808 node1(3, &service);
+
+    while (1) {
+        service.Run();
+    }
+
+#endif
+
+    printf ("----- END -------\r\n\n");
+
+}
diff -r 8352cfe17ab1 -r ec59d628ebdc CANnucleo.lib
--- a/CANnucleo.lib	Wed Dec 23 10:38:02 2015 +0000
+++ b/CANnucleo.lib	Sat Mar 19 01:44:35 2016 +0000
@@ -1,1 +1,1 @@
-https://developer.mbed.org/users/hudakz/code/CANnucleo/#a3e2be3d49a2
+https://developer.mbed.org/users/ptpaterson/code/CANnucleo/#cdab1fd4ff26
diff -r 8352cfe17ab1 -r ec59d628ebdc main.cpp
--- a/main.cpp	Wed Dec 23 10:38:02 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-/*
- * An example showing how to use the CANnucleo library:
- *
- * Two affordable (less than $4 on ebay) STM32F103C8T6 boards (20kB SRAM, 64kB Flash), 
- * compatible with the NUCLEO-F103RB platform (20kB SRAM, 128kB Flash), 
- * are connected to the same CAN bus via transceivers (MCP2551 or TJA1040, or etc.). 
- * CAN transceivers are not part of NUCLEO boards, therefore must be added by you. 
- * Remember also that CAN bus (even a short one) must be terminated with 120 Ohm resitors at both ends.
- *
- * For more details see the wiki page <https://developer.mbed.org/users/hudakz/code/CAN_Nucleo_Hello/>
- *
- * NOTE: If you'd like to use the official NUCLEO-F103RB boards
- *       comment out the line #define TARGET_STM32F103C8T6  1
- *
- * The same code is used for both NUCLEO boards, but:
- *      For board #1 compile the example without any change.
- *      For board #2 comment out the line #define BOARD1 1 before compiling 
- *
- * Once the binaries have been downloaded to the boards reset board #1.
- *
- */ 
-
-#include "mbed.h"
-#include "CAN.h"
-
-#define BOARD1    1                 // comment out this line when compiling for board #2
-
-#if defined(BOARD1)
-    #define RX_ID   0x100
-    #define TX_ID   0x101
-#else
-    #define RX_ID   0x101
-    #define TX_ID   0x100
-#endif
-
-// See wiki page <https://developer.mbed.org/users/hudakz/code/CAN_Nucleo_Hello/>
-//#define TARGET_STM32F103C8T6  1     // comment out this line if you'd like to use the official NUCLEO-F103RB boards
-                                    
-#if defined(TARGET_STM32F103C8T6)  
-    DigitalOut  led(PC_13);
-#else
-    DigitalOut  led(LED1);
-#endif
-
-int             ledReceived;
-Timer           timer;
-CAN             can(PA_11, PA_12);  // CAN Rx pin name, CAN Tx pin name, Automatic recovery from bus-off state enabled by default
-CANMessage      rxMsg;
-CANMessage      txMsg;
-int             counter = 0;
-volatile bool   msgAvailable = false;
-
-/**
- * @brief   'CAN receive-complete' interrup handler.
- * @note    Called on arrival of new CAN message.
- *          Keep it as short as possible.
- * @param   
- * @retval  
- */
-void onMsgReceived() {
-    msgAvailable = true;
-}
-
-/**
- * @brief   Main
- * @note
- * @param 
- * @retval
- */
-int main() {
-    can.frequency(1000000);                     // set bit rate to 1Mbps
-    can.attach(&onMsgReceived, CAN::RxIrq);     // attach 'CAN receive-complete' interrupt handler
-    
-#if defined(BOARD1)
-    #if defined(TARGET_STM32F103C8T6)
-        led = 0;    // turn LED on
-    #else
-        led = 1;    // turn LED on
-    #endif
-    timer.start();
-#else
-    #if defined(TARGET_STM32F103C8T6)
-        led = 1;    // turn LED off
-    #else
-        led = 0;    // turn LED off
-    #endif
-#endif
-
-    while(1) {
-        if(timer.read() >= 1.0) {               // check for timeout
-            timer.stop();                       // stop timer
-            timer.reset();                      // reset timer (to avaoid repeated send)
-            counter++;                          // increment counter
-            txMsg.clear();                      // clear Tx message storage
-            txMsg.id = TX_ID;                   // set ID
-            txMsg << counter;                   // append first data item (make sure that CAN message total data lenght <= 8 bytes!)
-            txMsg << led.read();                // append second data item (make sure that CAN message total data lenght <= 8 bytes!)
-            can.write(txMsg);                   // transmit message
-            printf("CAN message sent\r\n");
-            
-            #if defined(TARGET_STM32F103C8T6)
-                led = 1;                        // turn LED off
-            #else
-                led = 0;                        // turn LED off
-            #endif
-        }
-        if(msgAvailable) {
-            msgAvailable = false;               // reset flag for next use
-            can.read(rxMsg);                    // read message into Rx message storage
-            printf("CAN message received:\r\n");
-            printf("  ID     = %#x\r\n", rxMsg.id);
-            printf("  Type   = %d\r\n", rxMsg.type);
-            printf("  Format = %d\r\n", rxMsg.format);
-            printf("  Length = %d\r\n", rxMsg.len);
-            printf("  Data   =");            
-            for(int i = 0; i < rxMsg.len; i++)
-                printf(" %x", rxMsg.data[i]);
-            printf("\r\n");            
-            if(rxMsg.id == RX_ID) {             // if ID matches
-                rxMsg >> counter;               // extract first data item
-                rxMsg >> ledReceived;           // extract second data item
-                led = ledReceived;              // set LED
-                printf("counter = %d\r\n", counter);
-                timer.start();
-            }
-        }
-    }
-}
-
diff -r 8352cfe17ab1 -r ec59d628ebdc ppCANOpen.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ppCANOpen.lib	Sat Mar 19 01:44:35 2016 +0000
@@ -0,0 +1,1 @@
+http://developer.mbed.org/users/ptpaterson/code/ppCANOpen/#22a337cdc0e3