Power meter (Part 3)

Part 1, 2, 3

Once the hardware is ready, let’s have a look at the data and the software used to read these data.

The customer information can operate in two different modes:

  • historic: for compatibility reasons with older meters, this is the default mode.
  • standard: this new mode, available from the latest Linky meters. It is faster than the historic mode, and has more information, with a specific format. Just ask your provider for setting remotely this mode on your meter.

We will start with the historic mode which specifications are available from the technical documentation. The baud rate in this mode is 1200.
Each byte contains 1 start bit, 7 data bits, 1 parity bit and 1 stop bit.

Data is transferred as frames which are delimited by a STX character (0x02) and and a ETX character (0x03).

Each frame contains a set of data; each set of data is delimited by a LF character (0x10) and a CR character (0x0D). Each set of data contains one keyword (which length is variable and always less or equal 8 capital characters), one space, data (variable length of alpha numeric characters), one space and one checksum character as shown in the picture below

The checksum is computed with the keyword, a space and the data it self. It consists in adding all characters, applying a modulo 64 and adding the value 0x20. In this way, the checksum is printed in human readable character (from ‘space’ (0x20) to ‘_’ (0x5F)). Next is an example of code used to compute this checksum: (updated)

inline char computeChecksum(const char *aKeyword, const char *aData) 
{
	char sum = 0;
	while(*aKeyword) 
	{
		sum += *aKeyword;
		aKeyword += 1;
	}
	sum += 0x20;
	while(*aData) 
	{
		sum += *aData;
		aData += 1;
	}
	sum = ((sum & 0x3F) + 0x20);
	return (sum);
}

While talking about general purpose functions, let’s deal a bit with the parity check. The Linky power meter uses 7 bits data and a parity bit. This bit is set so that the ultimate parity of the data and the parity bits are even (even number of bits set to ‘1’). Although, faster algorithms exist, as we have kind of plenty of time (1200 baud) the classic method shall be used, as shown below

inline bool getParity(uint8_t byteValue) 
{ 
	bool parity = false; 
	while (byteValue) 
	{ 
		parity = !parity; 
		byteValue &= (byteValue - 1); 
	}      
	return (parity); 
} 

Reading a frame consists in listening to the incoming STX character, collecting consecutive bytes, checking their parity, extracting the 7 data bits, storing the byte in a vector and do that until the ETX occurs. An extra feature prevents the function from running for ever in case of lack of data for a certain amount of time (timeout).

bool readFrame
(
char *aFrame,
const uint16_t frameSize, 
const uint16_t timeOut,
const bool checkParity
)
{
	/* reset the content of the array */
	*aFrame = '\0';	
	bool waitForEtx = true;
	bool waitForStx = true;
	uint16_t bytesCount = 0;
	uint32_t startTime = millis();
	while (waitForStx) 
	{
		if (((millis() - startTime) > timeOut) || (bytesCount == frameSize))
		/* timeout and overflow protect */
		{
			return false;
		}
		/* if data is available in the incoming buffer */
		if (PwrMtrSerial.available())
		{			
			/* read the incoming data */
			char dataIn = PwrMtrSerial.read();
			if (checkParity && getParity(dataIn))
			/* check partity: true if odd, even (false) is expected */
			{
				return false;
			}
			/* mask the parity bit */
			dataIn &= 0x7F;
			/* parse incoming data */
			if (waitForEtx)
			{
				if (dataIn == 0x02)
				/* start of text */
				{
					/* change state */
					waitForEtx = false;
				}

			}
			else
			{
				if (dataIn == 0x03)
				/* end of text */
				{
					/* append termination character */
					*(aFrame + bytesCount) = '\0';	
					/* change state of readiness */
					waitForStx = false;
				}
				else 
				{
					/* record incoming character and increment pointer */
					*(aFrame + bytesCount) = dataIn;
					bytesCount += 1;
				}			
			}
		}
		else
		{
			delay(1);
		}
	}
	return (true);
}

Leave a Reply

You must be logged in to post a comment.