T h last step to complete the binding is to implement the new enOcean profile on the Raspberry Pi board.

 

I already build the EOLink library in round n.2. Now I'm going to add a new profile and making some changes the eoGateway class in order to handle incoming commands

 

 

 

Commands format

 

The enOcean profile D2-01 has variable length telegrams (D2 identifies telegrams as VLD -variable length data- telegrams).

 

To set an actuator status, the radio packet is the following

 

 

 

 

DB_2

DB_1

DB_0

DB_2.Bit 7 <-- 0

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

Bit ofs 0--> 23

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

 

 

CMD

Dim value

I/O Channel

 

Output value

 

 

 

where

 

  • CMD is the command identifier (0x01 in this case)

  • Dim value defines which action needs to be taken on the actuator. In my case, it's 0x00 (Switch to new output value)

  • I/O Channel is identifies the actuator to operate. The value range from 0 (0x00) to 29 (0x1D).  There are two special values: 0x1E, which means "operate on all the channels supported by the device" and 0x1F, which means  "operate on input channel (for mains supply)".

  • Output value is the value to set the actuator to. The profile supports also dimming, so output value can 0 to set the output value to 0% or OFF or any value from 1 to 100 to set the output value from 1% to 100% or ON.

 

 

 

Another command I need to implement is the "Actuator status query". The telegram in this case is made by two bytes

 

 

 

 

DB_1

DB_0

DB_1.Bit 7 <-- 0

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

Bit ofs 0--> 15

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

 

 

CMD

Dim value

I/O Channel

 

 

 

where

 

  • CMD is the command identifier (0x03 in this case)

  • I/O Channel identifies the actuator to query

 

 

 

The last command to implement is the "Actuator status response", which is sent when an "Actuator status query" command is received or when the status of a channel has changed. The telegram in this case is a little more complex

 

 

 

 

DB_2

DB_1

DB_0

DB_2.Bit 7 <-- 0

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

Bit ofs 0--> 23

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

 

PF

PFD

 

 

CMD

OC

EL

I/O Channel

LC

Output value

 

where

 

  • PF is the "Power failure" flag. In my case it is 0 since power failure is not supported

 

  • PFD is the "Power failure detection" flag . In my case it is 0, since power failure is not supported

 

  • CMD is the command identifier (0x04 in this case)

  • OC is the "Over current switch off" flag. it is 0 since ovecurrent switch is not supported

  • EL is the error level. Possible values are

    • 00: hardware OK

    • 01: hardware warning

    • 10: hardware failure

    • 11: not supported

  • I/O Channel identifies the actuator the telegram refers to

  • LC is the "Local control" flag

  • Output value is the new I/O channel value

 

 

 

 

 

Implement the new enOcean profile

 

To implement the new profile, I created a new class derived from base class eoProfile.

 

 

 

class eoD2EEProfile: public eoEEProfile

 

{

 

public:

 

       /**

 

       * Constructor.

 

       * @param size Default size of the expected message.

 

       */

 

       eoD2EEProfile(uint16_t size);

 

       virtual ~eoD2EEProfile();

 

 

 

       virtual eoReturn GetValue(CHANNEL_TYPE type, float &value);

 

       virtual eoReturn GetValue(CHANNEL_TYPE type, float &value, uint8_t index);

 

       virtual eoReturn GetValue(CHANNEL_TYPE type, uint8_t &value, uint8_t index);

 

       virtual eoReturn SetValue(CHANNEL_TYPE type, float value);

 

       virtual eoReturn SetValue(CHANNEL_TYPE type, uint8_t value, uint8_t index);

 

       virtual eoReturn SetValue(CHANNEL_TYPE type, float value, uint8_t index);

 

       virtual eoReturn CreateTeachIN(eoMessage &m);

 

       virtual eoReturn Create(eoMessage &m);

 

       virtual eoReturn Parse(const eoMessage &msg);

 

       /**

 

       * Checks if the message is a VLD data Telegram as in [EEP]

 

       * @param m Message to check

 

       * @return true=yes; false=no

 

       */

 

       bool IsVLDData(const eoMessage &m);

 

 

 

private:

 

       uint8_t command; 

 

       uint32_t destinationID;

 

       uint8_t channel;

 

};

 

 

 

The most interesting methods in this class are the Parse and Create methods. The first one, parses an incoming radio packet and extract commands and/or status updates. The second one, create a new radio packet to send out a command, a status update or a status query. Before digging into this two methods, let's see the format of the enOcean radio packet.

 

 

 

eoReturn eoD2EEProfile::Create(eoMessage &m)

 

{

 

  if (command == CMD_ACTUATOR_SET)

 

  {

 

       tel.RORG = rorg;

 

       tel.SetDataLength(3);

 

       tel.destinationID = destinationID;

 

       tel.sourceID = 0;

 

 

 

       // bit 0..3: Not used

 

       // bit 4..7: cmd

 

       tel.data[0] = command;

 

       // bit 8..10: dim value -> 0x00 = switch to new output value

 

       // bit 11..15: IO channel

 

       tel.data[1] = 0x00 | (channel & 0x1F);

 

       // bit 16: not used

 

       // but 17..23: output value

 

       tel.data[2] = msg.data[2];

 

    

 

       return EO_OK;

 

  }

 

 

 

  if (command == CMD_ACTUATOR_QUERY)

 

  {

 

       tel.RORG = rorg;

 

       tel.SetDataLength(2);

 

       tel.destinationID = destinationID;

 

       tel.sourceID = 0;

 

 

 

       // bit 0..3: Not used

 

       // bit 4..7: cmd

 

       tel.data[0] = command;

 

       // bit 8..10: not used

 

       // bit 11..15: IO channel

 

       tel.data[1] = 0x00 | (channel & 0x1F);

 

    

 

       return EO_OK;

 

  }

 

 

 

  if (command == CMD_ACTUATOR_RESPONSE)

 

  {

 

       tel.RORG = rorg;

 

       tel.SetDataLength(3);

 

       tel.destinationID = destinationID;

 

       tel.sourceID = 0;

 

 

 

       // bit 0: power failure (0 -> not supported)

 

       // bit 1: power failure detected (0)

 

       // bit 2..3: not used

 

       // bit 4..7: cmd

 

       tel.data[0] = command;

 

       // bit 8: over current switch off (0 -> not supported)

 

       // bit 9..10: error level (0 -> hardware OK)

 

       // bit 11..15: IO channel

 

       tel.data[1] = 0x00 | (channel & 0x1F);

 

       // bit 16: local control (0 -> not supported)

 

       // but 17..23: output value

 

       tel.data[2] = msg.data[2];

 

    

 

       return EO_OK;

 

  }

 

 

 

  return NOT_SUPPORTED;

 

}

 

 

 

Changes to the eoGateway class

 

I changed the oeGateway class to recognize incoming commands.

 

First of all, I added the code to rea dthe enOcean ID from the transceiver

 

 

 

eoReturn eoGateway::Open(const char *port)

 

{

 

       eoReturn result = stream->Open(port);

 

       if (result != EO_OK)

 

             return result;

 

   

 

       eoSerialCommand cmd(&gateway);

 

 

 

       //Read Sw and HW version of the device

 

       CO_RD_VERSION_RESPONSE version;

 

       result = cmd.ReadVersion(version);

 

       if (result == EO_OK)

 

       {

 

              printf("%s %i.%i.%i.%i, ID:0x%08X on %s\n",

 

                       version.appDescription,

 

                       version.appVersion[0], version.appVersion[1],

 

                       version.appVersion[2], version.appVersion[3],

 

                       version.chipID,

 

                       port);

 

           chipID = version.chipID;                

 

       }

 

       else

 

       {

 

              //Failed to read the version of the device

 

             printf("Failed to retrieve USB300 version\n");

 

       }

 

 

 

       return result;    

 

}

 

 

 

Obviously I need the chip ID in order to filter the packets that are sent to the device.

 

Then I added a member variable to store the profile the gateway complies to.

 

Finally, I changed the Receive methods to handle packets sent to the gateway

 

 

 

uint16_t eoGateway::Receive()

 

{

 

       flags = 0;

 

       // Process the packet from the queue or receive one from UART

 

       if (!packet_queue.empty())

 

       {

 

             eoPacket *p = packet_queue.front();

 

             p->copyTo(packet);

 

             delete p;

 

             packet_queue.pop_front();

 

       }

 

       else

 

       {

 

             errorCode = (uint8_t)stream->Receive(&packet);

 

             if (errorCode != EO_OK)

 

                    return flags;

 

       }

 

       flags = RECV_PACKET;

 

 

 

 

 

       // check packet consistency

 

       errorCode = (uint8_t)eoConverter::packetToRadio(packet, telegram);

 

 

 

       if (errorCode == EO_OK)

 

             flags |= RECV_TELEGRAM;

 

       else

 

             return flags;

 

 

 

       // get device information using the source ID in the telegram

 

       device = deviceManager->Get(telegram.sourceID);

 

 

 

       if (LearnMode)

 

             errorCode = (uint8_t)learnHandler(telegram);

 

       else

 

             errorCode = (uint8_t)normalHandler(telegram);

 

 

 

       if (device != NULL)

 

       {

 

             // if the source device is learned, try to parse message

 

             if (flags & RECV_MESSAGE)

 

              {

 

                     if (device->GetProfile()->Parse(message) == EO_OK)

 

                    {

 

                           flags |= RECV_PROFILE;

 

                     }

 

              }

 

       }

 

       else

 

       {

 

             // check if the telegram is directed to this device

 

              if (gateway.chipID == telegram.destinationID)

 

             {

 

                    // telegram is for this device: try to parse

 

                    if ((profile != NULL) & (profile->Parse(message) == E_OK))

 

                           flags |= RECV_COMMAND;

 

             }

 

       }

 

 

 

       return flags;

 

}

 

 

 

The RECV_COMMAND flag is checked by the caller of the eoGateway::Receive method to check for incoming commands.

 

 

 

Sending commands

 

To send commands without breaking up the interface implemented by the eoProfile class, I decided to set the command to perform and the I/O channel of interest by invoking the SetValue method with E_COMMAND for  the  channelType parameter and 0 or 1 for the index parameter to set respectively the command and the I/O channel. So the code to send an actuator status change is the following

 

   eoProfile *sendProf = eoProfileFactory::CreateProfile(0xD2,0x01,0x00);

 

 

 

   eoMessage mytel = eoMessage(4);

 

 

 

   // set command to perform (Actuator status change)

 

   sendProf->SetValue(E_COMMAND, (uint8_t)0x04, 0);

 

   // set I/O channel

 

   sendProf->SetValue(E_COMMAND, (uint8_t)aswitch, 1);

 

   // set new value

 

   sendProf->SetValue(E_IO_CHANNEL, (uint8_t)status, aswitch);

 

   sendProf->Create(mytel);

 

 

 

  enocean_gateway.Send(mytel);

 

 

 

 

 

 

 

 

Building and deploying

 

 

 

Build the EOLink library as I wrote in my second post and copy the libEOLink.a file (located in the "EOLink\ReleaseLib" folder) to the "/usr/lib" folder of your Raspberry Pi board. We are now re ready to test whether the two-way communication between OpenHAB and Raspberry works!