Support library for reading the Kamstrup FlowIQ2000 water meter via its serial interface.
This library provides support for reading a Kamstrup FlowIQ2000 water meter's serial interface. Interfacing to the meter from the C027 board requires a small interface circuit (note that this diagram also includes a water pump control circuit, which is needed by the IotMessage library):
This library is intended for use with the WaterMeterDemo project.
FlowIq2100.cpp
- Committer:
- RobMeades
- Date:
- 2015-05-22
- Revision:
- 0:0730cba56168
File content as of revision 0:0730cba56168:
/* FlowIQ Water Meter HW driver for Water Meter Demo * * Copyright (C) u-blox Melbourn Ltd * u-blox Melbourn Ltd, Melbourn, UK * * All rights reserved. * * This source file is the sole property of u-blox Melbourn Ltd. * Reproduction or utilization of this source in whole or part is * forbidden without the written consent of u-blox Melbourn Ltd. */ /** * @file flowiq_2100.cpp * This file implements the water meter API to a Kamstrup * FlowIQ 2100 water meter for the MWC demo 2015. * * The meter follows a single subset of the fixed length * message format defined by the Sensus UI-1203R20 interace. * The pwrClk line is first toggled from low to high * a number of times to power up the meter. * The meter indicates that it is powered up by pulling * the data line low. What follows is a fixed length * message, data bits being toggled out of the meter by * sending the pwrClk line from low to high. Each byte * is preceded by a low start bit (the first of which * is the one triggered by the pwrClk operation), seven * data bits, an even parity bit and a high stop bit. * There are 25 bytes of message as follows: * * 0x56, 'V' Start message * 0x3B, ';' Start field * 0x52, 'R' Register field type * 0x42, 'B' Start numeric data * 0x30, '0' Volume: 020550019 * 0x32, '2' * 0x30, '0' * 0x35, '5' * 0x35, '5' * 0x30, '0' * 0x30, '0' * 0x31, '1' * 0x39, '9' * 0x3B, ';' Start field * 0x49, 'I' Serial field type * 0x42, 'B' Start numeric data * 0x36, '6' Serial: 63268874 * 0x33, '3' * 0x32, '2' * 0x36, '6' * 0x38, '8' * 0x38, '8' * 0x37, '7' * 0x34, '4' * 0x0D, 'CR' End Message * */ #include <stdint.h> #include <stdio.h> #include <mbed.h> #ifdef TARGET_UBLOX_C027 #include "C027_api.h" #else #error "This example is targeted for the C027N platform" #endif #include <math.h> // for pow() #include <WaterMeterApi.hpp> // ---------------------------------------------------------------- // GENERAL COMPILE-TIME CONSTANTS // ---------------------------------------------------------------- /// The parity the meter uses (if false then it's ODD). #define PARITY_IS_EVEN true /// The default clock rate for the meter in Hz #define METER_DEFAULT_CLOCK_RATE_HZ 1000 /// The length of half a clock period in microseconds used when // powering up the meter. #define HALF_CLOCK_PERIOD_PWR_ON_US 1000 /// The maximum number of clocks that are sent to power up the // meter. #define MAX_PWR_ON_CLOCKS 10 /// The time for which pwr/clk must be kept low to effect // a reset of the interface. #define METER_RESET_PERIOD_US 200000 /// The length of the fixed-length message from the meter. #define METER_FIXED_MSG_LEN 25 /// The number of characters into the fixed length message that the // volume reading can be found. #define VOLUME_READING_OFFSET 4 /// The number of characters in the volume reading. #define VOLUME_READING_LEN 9 /// The number of characters into the fixed length message that the // serial number can be found. #define SERIAL_NUMBER_OFFSET 16 /// The number of characters in the serial number. #define SERIAL_NUMBER_LEN 8 // ---------------------------------------------------------------- // PRIVATE VARIABLES // ---------------------------------------------------------------- // ---------------------------------------------------------------- // GENERIC PRIVATE FUNCTIONS // ---------------------------------------------------------------- /// Toggle the pwrClk line to wake the meter up, which is // indicated by the data line going low, the start bit // (so this function swallows the first start bit). bool WaterMeterHandler::pwrOn (void) { bool success = false; uint32_t x; if (ready) { // Start things up by pumping the power/clock line until // the data line goes low or we run out of tries. if (debug) { printf ("\nToggling clock at startup... "); } for (x = 0; (*pData == 1) && (x < MAX_PWR_ON_CLOCKS); x++) { *pPwrClk = 0; wait_us (HALF_CLOCK_PERIOD_PWR_ON_US); *pPwrClk = 1; wait_us (HALF_CLOCK_PERIOD_PWR_ON_US); } if (x < MAX_PWR_ON_CLOCKS) { success = true; if (debug) { printf (" success after %d toggle(s).\n", x); } } else { if (debug) { printf (" failed.\n"); } } } else { printf ("!!! pwrOn(): not initialised, call init() first.\n"); } return success; } /// Power down the meter interface by setting // the pwrClk line low. void WaterMeterHandler::pwrOff (void) { if (ready) { if (debug) { printf ("\nSetting pwrClk low for %d ms to power off meter interface.\n", METER_RESET_PERIOD_US / 1000); } *pPwrClk = 0; } wait_us (METER_RESET_PERIOD_US); } /// Read a start bit from the water meter (should be 0). bool WaterMeterHandler::readStartBit (void) { bool success = false; if (ready) { // Toggle power/clock to get the start bit. *pPwrClk = 0; wait_us (halfClkPeriodUs); *pPwrClk = 1; wait_us (halfClkPeriodUs); if (!*pData) { success = true; } } else { printf ("!!! readStartBit(): not initialised, call init() first.\n"); } return success; } /// Read a stop bit from the water meter (should be 1). bool WaterMeterHandler::readStopBit (void) { bool success = false; if (ready) { // Toggle power/clock to get the stop bit. *pPwrClk = 0; wait_us (halfClkPeriodUs); *pPwrClk = 1; wait_us (halfClkPeriodUs); if (*pData) { success = true; } } else { printf ("!!! readStopBit(): not initialised, call init() first.\n"); } return success; } /// Read a bit from the water meter by toggling // the pwrClk line low and then high. bool WaterMeterHandler::readDataBit (void) { bool bitValue = false; if (ready) { // Toggle power/clock to get a data bit. *pPwrClk = 0; wait_us (halfClkPeriodUs); *pPwrClk = 1; wait_us (halfClkPeriodUs); if (*pData) { bitValue = true; } } else { printf ("!!! readDataBit: not initialised, call init() first.\n"); } return bitValue; } /// Check that parity is good. bool WaterMeterHandler::parityIsGood (uint8_t numOnes) { bool success = false; #if PARITY_IS_EVEN if ((numOnes & 0x01) == 0) { success = true; } #else if ((numOnes & 0x01) == 1) { success = true; } #endif return success; } /// Read a character from the water meter and check parity. bool WaterMeterHandler::readChar (char *pChar) { bool success = false; bool bitValue; uint8_t numOnes = 0; char dataByte = 0; uint8_t x; // Read the 7 data bits plus parity for (x = 0; x < 8; x++) { bitValue = readDataBit(); if (bitValue) { numOnes++; // Keep track of this for parity } dataByte |= ((bitValue && 0x01) << x); } // Mask off the parity bit dataByte &= 0x7F; if (debug) { printf ("data 0x%.2x ", dataByte); if ((dataByte & 0x7F) >= 0x20) { printf ("(%c) ", dataByte); } else { printf (" "); } } if (parityIsGood (numOnes)) { if (debug) { printf ("(parity GOOD) "); } success = true; if (pChar != NULL) { *pChar = dataByte; } } else { if (debug) { printf ("(parity BAD) "); } } return success; } /// Read a fixed length message from the water meter. uint32_t WaterMeterHandler::readFixedLengthMsg (char *pChars, uint32_t numChars) { uint32_t x = 0; // First, power the meter up until we get a start bit if (pwrOn()) { bool dataIsGood = true; // Now read the data for (x = 0; (x < numChars) && dataIsGood; x++) { if (debug) { printf ("Char: %.2d ", x + 1); } // Read a data byte with parity dataIsGood = readChar (&(pChars[x])); if (dataIsGood) { // If parity was good, read a stop bit dataIsGood = readStopBit(); if (debug) { if (dataIsGood) { printf ("StopBit "); } else { printf (" "); } } if (dataIsGood && (x < numChars - 1)) { // If the stop bit was good and we aren't at // the end, read the next start bit dataIsGood = readStartBit(); if (debug) { if (dataIsGood) { printf ("StartBit \n"); } else { printf (" \n"); } } } } } } // Power the meter off afterwards pwrOff(); return x; } // Convert a series of ASCII numeric characters into a uint32_t value. bool WaterMeterHandler::decCharsToUint32 (char * pChars, uint32_t numChars, uint32_t * pValue) { bool success = true; double value = 0; if ((numChars > 0) && (pChars != NULL)) { double multiplier; uint32_t x; multiplier = pow ((double) 10, double (numChars - 1)); for (x = 0; (x < numChars) && success; x++) { if ((pChars[x] >= 0x30) && (pChars[x] <= 0x39)) { value += (pChars[x] - 0x30) * multiplier; multiplier /= 10; if (multiplier == 0) { multiplier = 1; } } else { success = false; } } } else { success = false; } if (success && (pValue != NULL)) { if (value > 0xFFFFFFFF) { success = false; if (debug) { printf ("Result is too big for an uint32_t (%f).\n", value); } } else { *pValue = (uint32_t) int (value); } } return success; } // ---------------------------------------------------------------- // PUBLIC FUNCTIONS // ---------------------------------------------------------------- /// Constructor WaterMeterHandler::WaterMeterHandler(void) { ready = false; debug = false; pData = NULL; pPwrClk = NULL; halfClkPeriodUs = (1000000 / METER_DEFAULT_CLOCK_RATE_HZ) / 2; if (debug) { printf ("WaterMeterHandler DEBUG prints are on.\n"); } } /// Destructor WaterMeterHandler::~WaterMeterHandler(void) { ready = false; pData = NULL; pPwrClk = NULL; } /// Set debug output void WaterMeterHandler::setDebugOn (bool onNotOff) { debug = false; if (onNotOff) { debug = true; } if (debug) { printf ("\nWaterMeterHandler DEBUG prints are on.\n"); } else { printf ("\nWaterMeterHandler DEBUG prints are off.\n"); } } /// Initialise the water meter and set alternative // values for the data/pwrClk pins and the clock // rate used when reading data. Then read a character // from the meter to ensure it's there. bool WaterMeterHandler::init (PinName dataPin, PinName pwrClkPin, uint32_t clkRateHz) { // Set context data pData = new DigitalIn (dataPin); pPwrClk = new DigitalOut (pwrClkPin); if (clkRateHz > 1000000) { clkRateHz = 1000000; } halfClkPeriodUs = (1000000 / clkRateHz) / 2; ready = true; // Power up the module and read a character. if (pwrOn()) { if (!readChar (NULL)) { ready = false; } } // Power it off again. pwrOff(); return ready; } /// Take a water volume reading from the meter. bool WaterMeterHandler::readLitres (uint32_t *pValue) { bool success = false; char buffer[METER_FIXED_MSG_LEN]; uint32_t charsRead; if (debug) { printf ("Reading volume from meter...\n"); } charsRead = readFixedLengthMsg (buffer, sizeof (buffer)); if (debug) { printf ("\n%d byte(s) read of %d needed,\n", charsRead, VOLUME_READING_OFFSET + VOLUME_READING_LEN); } if (charsRead >= VOLUME_READING_OFFSET + VOLUME_READING_LEN) { if (debug) { printf ("Converting %.*s to ", VOLUME_READING_LEN, &(buffer[VOLUME_READING_OFFSET])); } success = decCharsToUint32 (&(buffer[VOLUME_READING_OFFSET]), VOLUME_READING_LEN, pValue); if (debug) { if (success) { if (pValue != NULL) { printf ("%d.\n", *pValue); } else { printf ("[success].\n"); } } else { printf ("[conversion failed].\n"); } } } return success; } /// Read the serial number from the meter. bool WaterMeterHandler::readSerialNumber (uint32_t *pValue) { bool success = false; char buffer[METER_FIXED_MSG_LEN]; uint32_t charsRead; if (debug) { printf ("Reading serial number from meter...\n"); } charsRead = readFixedLengthMsg (buffer, sizeof (buffer)); if (debug) { printf ("\n%d byte(s) read of %d needed,\n", charsRead, SERIAL_NUMBER_OFFSET + SERIAL_NUMBER_LEN); } if (charsRead >= SERIAL_NUMBER_OFFSET + SERIAL_NUMBER_LEN) { if (debug) { printf ("Converting %.*s to ", SERIAL_NUMBER_LEN, &(buffer[SERIAL_NUMBER_OFFSET])); } success = decCharsToUint32 (&(buffer[SERIAL_NUMBER_OFFSET]), SERIAL_NUMBER_LEN, pValue); if (debug) { if (success) { if (pValue != NULL) { printf ("%d.\n", *pValue); } else { printf ("[success].\n"); } } else { printf ("[conversion failed].\n"); } } } return success; } // End Of File