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;
}
}
}
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.