EnOcean programming in c#

How to parse EnOcean messages


using System;

namespace EnOcean
{

       /// <summary>
       /// Base Class for all EnOcean Telegrams
       /// </summary>

       public class EOTelegram
       {

          public const int EO_Telegram_Length = 14;

          #region EnOcean Message Structure Constants
          public const int EO_HSEQ = 2;
          public const int EO_ORG = 3;
          public const int EO_DB3 = 4;
          public const int EO_DB2 = 5;
          public const int EO_DB1 = 6;
          public const int EO_DB0 = 7;
          public const int EO_ID3 = 8;
          public const int EO_ID2 = 9;
          public const int EO_ID1 = 10;
          public const int EO_ID0 = 11;
          public const int EO_Status = 12;
          #endregion

          /// <summary>
          /// Data buffer for EnOcean Telegram
          /// </summary>

          protected byte[] moBuffer;

          /// <summary>
          /// Constructor. Creates buffer with zeroed bytes. And sets header sync bytes.
          /// This constrauctor is usually used for sending messages.
          /// </summary>

          public EOTelegram ()
          {
             moBuffer = new byte[EO_Telegram_Length];
             // Reset buffer with 0 bytes
             for (int liteller = 0; liteller < moBuffer.Length; liteller++) {
                moBuffer[liteller] = 0;
             }
             #region default values in sync bytes
             // Message always starts with two sync bytes!
             moBuffer[0] = 0xa5;
             moBuffer[1] = 0x5a;
             moBuffer[EO_HSEQ] = 0x6b;
             // Only when sending
             #endregion
          }

          /// <summary>
          /// Constructor, using buffered bytes. The buffer is checked for basic properties to make sure
          /// message structure is as defined by standard.
          /// </summary>

          /// <param name="poBuffer">
          /// A <see cref="System.Byte[]"/>. A buffer that has correct length and sync bytes.
          /// </param>
          public EOTelegram (byte[] poBuffer)
          {
             if (poBuffer == null) {
                throw new Exception ("EnOcean data is empty. ignoring");
             }

             if (poBuffer.Length != EO_Telegram_Length) {
                throw new Exception ("EnOcean data length should be " + EO_Telegram_Length + 
                                     ". we received: " + poBuffer.Length);
             }

             //Check two sync bytes.
             if (poBuffer[0] != 0xa5 || poBuffer[1] != 0x5a) {
                throw new Exception ("EnOcean data wrong sync bytes. " + Tools.BufferToHexString (moBuffer));
             }

             moBuffer = poBuffer;
          }


          /// <summary>
          /// Basic formatting of EnOcean message to string. Provides telegram name, AddressID, data and status.
          /// </summary>

          /// <returns>
          /// A <see cref="System.String"/>
          /// </returns>
          public override string ToString ()
          {
             return string.Format ("[{0}: ID={1}, Data={2}, Status=[{3}]]", TelegramName (), ID, 
                                   DataToString (), StatusToString ());
          }

          #region Base mthods that need to bee overriden in super classes.
          public virtual string StatusToString ()
          {
             throw new Exception ("Not allowed to call this method on Base type");
          }

          /// <summary>
          /// String representation of the numbers of the EEP (EnOcean Equipment Profile)
          /// </summary>

          /// <returns>
          /// A <see cref="System.String"/>
          /// </returns>
          public virtual string EEP ()
          {
             throw new Exception ("Not allowed to call this method on Base type");
          }

          /// <summary>
          /// String representation of the EEP (EnOcean Equipment Profile)
          /// </summary>

          /// <returns>
          /// A <see cref="System.String"/>
          /// </returns>
          public virtual string EEPName ()
          {
             throw new Exception ("Not allowed to call this method on Base type");
          }

          /// <summary>
          /// Telegram name
          /// </summary>

          /// <returns>
          /// A <see cref="System.String"/>
          /// </returns>
          public virtual string TelegramName ()
          {
             throw new Exception ("Not allowed to call this method on Base type");
          }
          #endregion

          /// <summary>
          /// This is an example method to process the data of a message and describe the data in detailed tekst.
          /// Please override this method to provide more detailed description
          /// </summary>

          /// <returns>
          /// A <see cref="System.String"/> describing the contents of this message.
          /// </returns>
          public virtual string DataToString ()
          {
             return DataBytes;
          }

          #region Helper functions for modification to messages

          /// <summary>
          /// Set the byte at a certain position in the buffer.
          /// </summary>

          /// <param name="piPosition">
          /// A <see cref="System.Int32"/>. Position number, see also constants EO_...
          /// </param>
          /// <param name="pnValue">
          /// A <see cref="System.Byte"/>
          /// </param>
          public void SetDataByte (int piPosition, byte pnValue)
          {
             moBuffer[piPosition] = pnValue;
          }

          /// <summary>
          /// Get the byte from the buffer at a certain position.
          /// </summary>

          /// <param name="piPosition">
          /// A <see cref="System.Int32"/>. Position number, see also constants EO_...
          /// </param>
          /// <returns>
          /// A <see cref="System.Byte"/>
          /// </returns>
          public byte GetDataByte (int piPosition)
          {
             return moBuffer[piPosition];
          }

          /// <summary>
          /// Set Bits in Byte
          /// </summary>

          /// <param name="pnValue"> Byte to update
          /// A <see cref="System.Byte"/>
          /// </param>
          /// <param name="piBitPos"> Bit position, in order 7 6 5 4 3 2 1 0
          /// A <see cref="System.Int32"/>
          /// </param>
          /// <param name="piBitLen"> How many bits
          /// A <see cref="System.Int32"/>
          /// </param>
          /// <param name="piNewValue"> New value for bits
          /// A <see cref="System.Int32"/>
          /// </param>
          protected void SetDataBit (ref byte pnValue, int piBitPos, int piBitLen, int piNewValue)
          {
             pnValue = SetBits (pnValue, piBitPos, piBitLen, piNewValue);
          }

          /// <summary>
          /// Set Bits in Byte
          /// </summary>

          /// <param name="pnValue"> Byte to update
          /// A <see cref="System.Byte"/>
          /// </param>
          /// <param name="piBitPos"> Bit position, in order 7 6 5 4 3 2 1 0
          /// A <see cref="System.Int32"/>
          /// </param>
          /// <param name="piNewValue"> New value for bits
          /// A <see cref="System.Int32"/>
          /// </param>
          protected void SetDataBit (ref byte pnValue, int piBitPos, int piNewValue)
          {
             pnValue = SetBits (pnValue, piBitPos, 1, piNewValue);
          }


          /// <summary>
          /// Helper function, get bits from a byte.
          /// </summary>

          /// <param name="pnValue">
          /// A <see cref="System.Byte"/>. Byte which has the data.
          /// </param>
          /// <param name="piBitPos">
          /// A <see cref="System.Int32"/>. Bit position. First bit is zero.
          /// </param>
          /// <param name="piBitLen">
          /// A <see cref="System.Int32"/>. How many bits to receive.
          /// </param>
          /// <returns>
          /// A <see cref="System.Int32"/>. Return value.
          /// </returns>
          protected int GetDataBit (byte pnValue, int piBitPos, int piBitLen)
          {
             return GetBits (pnValue, piBitPos, piBitLen);
          }

          /// <summary>
          /// Helper function, get one bit from a byte.
          /// </summary>

          /// <param name="pnValue">
          /// A <see cref="System.Byte"/>. Byte which has the data.
          /// </param>
          /// <param name="piBitPos">
          /// A <see cref="System.Int32"/>. Bit position. First bit is zero.
          /// </param>
          /// <returns>
          /// A <see cref="System.Int32"/>. Return value.
          /// </returns>
          protected int GetDataBit (byte pnValue, int piBitPos)
          {
             return GetBits (pnValue, piBitPos, 1);
          }

          /// <summary>
          /// Return the specified bits
          /// </summary>

          /// <param name="pnByte">Data from which to select
          /// A <see cref="System.Byte"/>
          /// </param>
          /// <param name="piOffset">Start position from where to start getting bits counting from the left.
          /// First bit is zero. A <see cref="System.Int32"/>
          /// </param>
          /// <param name="piCount">Number of bits to get.
          /// A <see cref="System.Int32"/>
          /// </param>
          /// <returns>Retrieved bits
          /// A <see cref="System.Int32"/>
          /// </returns>
          private int GetBits (byte pnByte, int piOffset, int piCount)
          {
             if (piCount < 0 || piCount > 7) {
                throw new Exception ("Can only get from 0 - 7 bits. Count is wrong");
             }
             if (piOffset < 0 || piOffset > 7) {
                throw new Exception ("Can only offset from 0 - 7 bits. Offset is wrong");
             }
             return (pnByte >> (1 + piOffset - piCount)) & ((1 << piCount) - 1);
          }

          /// <summary>
          /// Set the specified bits
          /// </summary>

          /// <param name="pnByte">Data which to use
          /// A <see cref="System.Byte"/>
          /// </param>
          /// <param name="piOffset">Start position from where to start setting bits counting from the left.
          /// First bit is zero. A <see cref="System.Int32"/>
          /// </param>
          /// <param name="piCount">Number of bits to get.
          /// A <see cref="System.Int32"/>
          /// </param>
          /// <param name="piValue">New value for bits.
          /// A <see cref="System.int"/>
          /// </param>
          /// <returns>Changed Byte
          /// A <see cref="System.Byte"/>
          /// </returns>
          private byte SetBits (byte pnByte, int piOffset, int piCount, int piValue)
          {
             if (piCount < 0 || piCount > 7) {
                throw new Exception ("Can only set from 0 - 7 bits. Count is wrong");
             }
             if (piOffset < 0 || piOffset > 7) {
                throw new Exception ("Can only offset from 0 - 7 bits. Offset is wrong");
             }
             if ((piOffset - piCount) < -1) {
                throw new Exception ("Invalid combination of Offset and count.");
             }

             byte lnMask = (byte)(0xff << (8 - piCount)); lnMask = (byte)~(lnMask >> (7 - piOffset));
             byte lnData = (byte)(piValue << (piOffset - piCount + 1));

             return (byte)(lnData + (pnByte & lnMask));
          }
          #endregion


          /// <summary>
          /// Represenation of the data bytes in a telegram. DataByte0 : DataByte1 : DataByte2 : DataByte3
          /// </summary>

          public virtual string DataBytes {
             get { return Tools.HexToString (moBuffer[EO_DB0]) + ":" + Tools.HexToString (moBuffer[EO_DB1]) + ":" + 
                          Tools.HexToString (moBuffer[EO_DB2]) + ":" + Tools.HexToString (moBuffer[EO_DB3]); }
             set {
                string[] laDataBs = value.Split (':');
                if (laDataBs.Length != 4) {
                   throw new Exception ("EnOcean Data string invalid, expecting format 00:00:00:00, but i got: " + value);
                }

                moBuffer[EO_DB0] = Tools.StringtoByte (laDataBs[0]);
                moBuffer[EO_DB1] = Tools.StringtoByte (laDataBs[1]);
                moBuffer[EO_DB2] = Tools.StringtoByte (laDataBs[2]);
                moBuffer[EO_DB3] = Tools.StringtoByte (laDataBs[3]);
             }
          }

          /// <summary>
          /// AddressID from the telegram. IDByte3 : IDByte2 : IDByte1: IDByte0
          /// </summary>

          public string ID {
             get { return Tools.HexToString (moBuffer[EO_ID3]) + ":" + Tools.HexToString (moBuffer[EO_ID2]) + ":" + 
                          Tools.HexToString (moBuffer[EO_ID1]) + ":" + Tools.HexToString (moBuffer[EO_ID0]); }
             set {
                string[] laIDs = value.Split (':');
                if (laIDs.Length != 4) {
                   throw new Exception ("EnOcean ID string invalid, expecting format 00:00:00:00, but i got: " + value);
                }

                moBuffer[EO_ID3] = Tools.StringtoByte (laIDs[0]);
                moBuffer[EO_ID2] = Tools.StringtoByte (laIDs[1]);
                moBuffer[EO_ID1] = Tools.StringtoByte (laIDs[2]);
                moBuffer[EO_ID0] = Tools.StringtoByte (laIDs[3]);
             }
          }

          /// <summary>
          /// Calculate and set checksum in buffer.
          /// Checksum is a sum of all bytes except first 2 sync bytes and last checksum byte!
          /// </summary>

          public void SetCheckSum ()
          {
             long liSum = 0;
             byte liChecksum = 0;
             if (moBuffer != null) {
                if (moBuffer.Length == EO_Telegram_Length) {

                   for (int liteller = 2; liteller < (EO_Telegram_Length - 1); liteller++) {
                      liSum += moBuffer[liteller];
                   }
                   liChecksum = Convert.ToByte (liSum & 0xff);
                   moBuffer[EO_Telegram_Length - 1] = liChecksum;
                } else {
                   throw new Exception ("Buffer length not correct");
                }
             } else {
                throw new Exception ("Buffer not ready. Strange");
             }
          }

          /// <summary>
          /// Return raw buffer.
          /// </summary>

          /// <returns>
          /// A <see cref="System.Byte[]"/>.
          /// </returns>
          public byte[] GetBuffer ()
          {
             return moBuffer;
          }
       }
}

2 thoughts on “EnOcean programming in c#

  1. I am trying to implement EnOcean in Domoticz, but am stuck with the FAM-USB ID.
    Is it possible you can post your complete project, or send me the project?

    Thanks for the guide! Excellent work!

    • Sorry, but I do not have a complete project, just these tidbits.

      You are better of looking at some plugin implementations I helped. Please look for enocean in mios for micasaverde or on openhab.

      Good luck.

Comments are closed.