I originally started building my own Cayenne Low Power Protocol(LPP) encoder because I could only find one other Github repository with a C# implementation. There hadn’t been any updates for a while and I wasn’t confident that I could make the code work on my nanoFramework and TinyCLR devices.

I started with the sample Mbed C code and did a largely mechanical conversion to C#. I then revisited some of the mathematics where floating point values were converted to an integer.
The original C++ code (understandably) had some language specific approaches which didn’t map well into C#
uint8_t CayenneLPP::addTemperature(uint8_t channel, float celsius) { if ((cursor + LPP_TEMPERATURE_SIZE) > maxsize) { return 0; } int16_t val = celsius * 10; buffer[cursor++] = channel; buffer[cursor++] = LPP_TEMPERATURE; buffer[cursor++] = val >> 8; buffer[cursor++] = val; return cursor; }
I then translated this code to C#
public void TemperatureAdd(byte channel, float celsius) { if ((index + TemperatureSize) > buffer.Length) { throw new ApplicationException("TemperatureAdd insufficent buffer capacity"); } short val = (short)(celsius * 10); buffer[index++] = channel; buffer[index++] = (byte)DataType.Temperature; buffer[index++] = (byte)(val >> 8); buffer[index++] = (byte)val; }
One of my sensors was sending values with more decimal places than LPP supported and I noticed the value was not getting rounded e.g. 2.99 ->2.9 not 3.0 etc. So I revised my implementation to use Math.Round (which is supported by the nanoFramework and TinyCLR).
public void DigitalInputAdd(byte channel, bool value) { #region Guard conditions if ((channel < Constants.ChannelMinimum) || (channel > Constants.ChannelMaximum)) { throw new ArgumentException($"channel must be between {Constants.ChannelMinimum} and {Constants.ChannelMaximum}", "channel"); } if ((index + Constants.DigitalInputSize) > buffer.Length) { throw new ApplicationException($"Datatype DigitalInput insufficent buffer capacity, {buffer.Length - index} bytes available"); } #endregion buffer[index++] = channel; buffer[index++] = (byte)Enumerations.DataType.DigitalInput; // I know this is fugly but it works on all platforms if (value) { buffer[index++] = 1; } else { buffer[index++] = 0; } }
I then extracted out the channel and buffer size validation but I’m not certain this makes the code anymore readable/understandable
public void DigitalInputAdd(byte channel, bool value) { IsChannelNumberValid(channel); IsBufferSizeSufficient(Enumerations.DataType.DigitalInput); buffer[index++] = channel; buffer[index++] = (byte)Enumerations.DataType.DigitalInput; // I know this is fugly but it works on all platforms if (value) { buffer[index++] = 1; } else { buffer[index++] = 0; } }
The code runs on netCore, nanoFramework, and TinyCLRV2 just needs a few more unit tests and it will be ready for production. I started with an LPP encoder which I needed for one of my applications. I’m also working an approach for a decoder which will run on all my target platforms with minimal modification or compile time directives.