Rob Meades / WaterMeterSupport
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers FlowIq2100.cpp Source File

FlowIq2100.cpp

00001 /* FlowIQ Water Meter HW driver for Water Meter Demo
00002  * 
00003  * Copyright (C) u-blox Melbourn Ltd
00004  * u-blox Melbourn Ltd, Melbourn, UK
00005  * 
00006  * All rights reserved.
00007  *
00008  * This source file is the sole property of u-blox Melbourn Ltd.
00009  * Reproduction or utilization of this source in whole or part is
00010  * forbidden without the written consent of u-blox Melbourn Ltd.
00011  */
00012 
00013 /**
00014  * @file flowiq_2100.cpp
00015  * This file implements the water meter API to a Kamstrup
00016  * FlowIQ 2100 water meter for the MWC demo 2015.
00017  *
00018  * The meter follows a single subset of the fixed length
00019  * message format defined by the Sensus UI-1203R20 interace.
00020  * The pwrClk line is first toggled from low to high
00021  * a number of times to power up the meter.
00022  * The meter indicates that it is powered up by pulling
00023  * the data line low.  What follows is a fixed length
00024  * message, data bits being toggled out of the meter by
00025  * sending the pwrClk line from low to high.  Each byte
00026  * is preceded by a low start bit (the first of which
00027  * is the one triggered by the pwrClk operation), seven
00028  * data bits, an even parity bit and a high stop bit.
00029  * There are 25 bytes of message as follows:
00030  *
00031  * 0x56, 'V'   Start message
00032  * 0x3B, ';'   Start field
00033  * 0x52, 'R'   Register field type
00034  * 0x42, 'B'   Start numeric data
00035  * 0x30, '0'   Volume: 020550019
00036  * 0x32, '2'
00037  * 0x30, '0'
00038  * 0x35, '5'
00039  * 0x35, '5'
00040  * 0x30, '0'
00041  * 0x30, '0'
00042  * 0x31, '1'
00043  * 0x39, '9'
00044  * 0x3B, ';'   Start field
00045  * 0x49, 'I'   Serial field type
00046  * 0x42, 'B'   Start numeric data
00047  * 0x36, '6'   Serial: 63268874
00048  * 0x33, '3'
00049  * 0x32, '2'
00050  * 0x36, '6'
00051  * 0x38, '8'
00052  * 0x38, '8'
00053  * 0x37, '7'
00054  * 0x34, '4'
00055  * 0x0D, 'CR'  End Message
00056  *
00057  */
00058 
00059 #include <stdint.h>
00060 #include <stdio.h>
00061 #include <mbed.h>
00062 #ifdef TARGET_UBLOX_C027
00063  #include "C027_api.h"
00064 #else
00065  #error "This example is targeted for the C027N platform"
00066 #endif
00067 #include <math.h> // for pow()
00068 #include <WaterMeterApi.hpp>
00069 
00070 // ----------------------------------------------------------------
00071 // GENERAL COMPILE-TIME CONSTANTS
00072 // ----------------------------------------------------------------
00073 
00074 /// The parity the meter uses (if false then it's ODD).
00075 #define PARITY_IS_EVEN true
00076 
00077 /// The default clock rate for the meter in Hz
00078 #define METER_DEFAULT_CLOCK_RATE_HZ 1000
00079 
00080 /// The length of half a clock period in microseconds used when
00081 // powering up the meter.
00082 #define HALF_CLOCK_PERIOD_PWR_ON_US 1000
00083 
00084 /// The maximum number of clocks that are sent to power up the
00085 // meter.
00086 #define MAX_PWR_ON_CLOCKS 10
00087 
00088 /// The time for which pwr/clk must be kept low to effect
00089 // a reset of the interface.
00090 #define METER_RESET_PERIOD_US 200000
00091 
00092 /// The length of the fixed-length message from the meter.
00093 #define METER_FIXED_MSG_LEN 25
00094 
00095 /// The number of characters into the fixed length message that the
00096 // volume reading can be found.
00097 #define VOLUME_READING_OFFSET 4
00098 
00099 /// The number of characters in the volume reading.
00100 #define VOLUME_READING_LEN 9
00101 
00102 /// The number of characters into the fixed length message that the
00103 // serial number can be found.
00104 #define SERIAL_NUMBER_OFFSET 16
00105 
00106 /// The number of characters in the serial number.
00107 #define SERIAL_NUMBER_LEN 8
00108 
00109 // ----------------------------------------------------------------
00110 // PRIVATE VARIABLES
00111 // ----------------------------------------------------------------
00112 
00113 // ----------------------------------------------------------------
00114 // GENERIC PRIVATE FUNCTIONS
00115 // ----------------------------------------------------------------
00116 
00117 /// Toggle the pwrClk line to wake the meter up, which is
00118 // indicated by the data line going low, the start bit
00119 // (so this function swallows the first start bit).
00120 bool WaterMeterHandler::pwrOn (void)
00121 {
00122     bool success = false;
00123     uint32_t x;
00124 
00125     if (ready)
00126     {
00127         // Start things up by pumping the power/clock line until
00128         // the data line goes low or we run out of tries.
00129         if (debug)
00130         {
00131             printf ("\nToggling clock at startup... ");
00132         }
00133 
00134         for (x = 0; (*pData == 1) && (x < MAX_PWR_ON_CLOCKS); x++)
00135         {
00136             *pPwrClk = 0;
00137             wait_us (HALF_CLOCK_PERIOD_PWR_ON_US);
00138             *pPwrClk = 1;
00139             wait_us (HALF_CLOCK_PERIOD_PWR_ON_US);
00140         }
00141 
00142         if (x < MAX_PWR_ON_CLOCKS)
00143         {
00144             success = true;
00145             if (debug)
00146             {
00147                 printf (" success after %d toggle(s).\n", x);
00148             }
00149         }
00150         else
00151         {
00152             if (debug)
00153             {
00154                 printf (" failed.\n");
00155             }
00156         }
00157     }
00158     else
00159     {
00160         printf ("!!! pwrOn(): not initialised, call init() first.\n");
00161     }
00162 
00163     return success;
00164 }
00165 
00166 /// Power down the meter interface by setting
00167 // the pwrClk line low.
00168 void WaterMeterHandler::pwrOff (void)
00169 {
00170     if (ready)
00171     {
00172         if (debug)
00173         {
00174             printf ("\nSetting pwrClk low for %d ms to power off meter interface.\n", METER_RESET_PERIOD_US / 1000);
00175         }
00176 
00177         *pPwrClk = 0;
00178     }
00179     wait_us (METER_RESET_PERIOD_US);
00180 }
00181 
00182 /// Read a start bit from the water meter (should be 0).
00183 bool WaterMeterHandler::readStartBit (void)
00184 {
00185     bool success = false;
00186 
00187     if (ready)
00188     {
00189         // Toggle power/clock to get the start bit.
00190         *pPwrClk = 0;
00191         wait_us (halfClkPeriodUs);
00192         *pPwrClk = 1;
00193         wait_us (halfClkPeriodUs);
00194         if (!*pData)
00195         {
00196             success = true;
00197         }
00198     }
00199     else
00200     {
00201         printf ("!!! readStartBit(): not initialised, call init() first.\n");
00202     }
00203 
00204     return success;
00205 }
00206 
00207 /// Read a stop bit from the water meter (should be 1).
00208 bool WaterMeterHandler::readStopBit (void)
00209 {
00210     bool success = false;
00211 
00212     if (ready)
00213     {
00214         // Toggle power/clock to get the stop bit.
00215         *pPwrClk = 0;
00216         wait_us (halfClkPeriodUs);
00217         *pPwrClk = 1;
00218         wait_us (halfClkPeriodUs);
00219         if (*pData)
00220         {
00221             success = true;
00222         }
00223     }
00224     else
00225     {
00226         printf ("!!! readStopBit(): not initialised, call init() first.\n");
00227     }
00228 
00229     return success;
00230 }
00231 
00232 /// Read a bit from the water meter by toggling
00233 // the pwrClk line low and then high.
00234 bool WaterMeterHandler::readDataBit (void)
00235 {
00236     bool bitValue = false;
00237 
00238     if (ready)
00239     {
00240         // Toggle power/clock to get a data bit.
00241         *pPwrClk = 0;
00242         wait_us (halfClkPeriodUs);
00243         *pPwrClk = 1;
00244         wait_us (halfClkPeriodUs);
00245         if (*pData)
00246         {
00247             bitValue = true;
00248         }
00249     }
00250     else
00251     {
00252         printf ("!!! readDataBit: not initialised, call init() first.\n");
00253     }
00254 
00255     return bitValue;
00256 }
00257 
00258 /// Check that parity is good.
00259 bool WaterMeterHandler::parityIsGood (uint8_t numOnes)
00260 {
00261     bool success = false;
00262 
00263 #if PARITY_IS_EVEN
00264     if ((numOnes & 0x01) == 0)
00265     {
00266         success = true;
00267     }
00268 #else
00269     if ((numOnes & 0x01) == 1)
00270     {
00271        success = true;
00272     }
00273 #endif
00274 
00275     return success;
00276 }
00277 
00278 /// Read a character from the water meter and check parity.
00279 bool WaterMeterHandler::readChar (char *pChar)
00280 {
00281     bool success = false;
00282     bool bitValue;
00283     uint8_t numOnes = 0;
00284     char dataByte = 0;
00285     uint8_t x;
00286 
00287     // Read the 7 data bits plus parity
00288     for (x = 0; x < 8; x++)
00289     {
00290         bitValue = readDataBit();
00291         if (bitValue)
00292         {
00293             numOnes++;  // Keep track of this for parity
00294         }
00295         dataByte |= ((bitValue && 0x01) << x);
00296     }
00297 
00298     // Mask off the parity bit
00299     dataByte &= 0x7F;
00300 
00301 if (debug)
00302 {
00303     printf ("data 0x%.2x ", dataByte);
00304 
00305     if ((dataByte & 0x7F) >= 0x20)
00306     {
00307         printf ("(%c) ", dataByte);
00308     }
00309     else
00310     {
00311         printf ("    ");
00312     }
00313 }
00314 
00315     if (parityIsGood (numOnes))
00316     {
00317         if (debug)
00318         {
00319             printf ("(parity GOOD) ");
00320         }
00321 
00322         success = true;
00323         if (pChar != NULL)
00324         {
00325             *pChar = dataByte;
00326         }
00327     }
00328     else
00329     {
00330         if (debug)
00331         {
00332             printf ("(parity BAD)  ");
00333         }
00334     }
00335 
00336     return success;
00337 }
00338 
00339 /// Read a fixed length message from the water meter.
00340 uint32_t WaterMeterHandler::readFixedLengthMsg (char *pChars, uint32_t numChars)
00341 {
00342     uint32_t x = 0;
00343 
00344     // First, power the meter up until we get a start bit
00345     if (pwrOn())
00346     {
00347         bool dataIsGood = true;
00348         // Now read the data
00349         for (x = 0; (x < numChars) && dataIsGood; x++)
00350         {
00351             if (debug)
00352             {
00353                 printf ("Char: %.2d ", x + 1);
00354             }
00355 
00356             // Read a data byte with parity
00357             dataIsGood = readChar (&(pChars[x]));
00358             if (dataIsGood)
00359             {
00360                 // If parity was good, read a stop bit
00361                 dataIsGood = readStopBit();
00362                 if (debug)
00363                 {
00364                     if (dataIsGood)
00365                     {
00366                         printf ("StopBit ");
00367                     }
00368                     else
00369                     {
00370                         printf ("        ");
00371                     }
00372                 }
00373 
00374                 if (dataIsGood && (x < numChars - 1))
00375                 {
00376                     // If the stop bit was good and we aren't at
00377                     // the end, read the next start bit
00378                     dataIsGood = readStartBit();
00379                     if (debug)
00380                     {
00381                         if (dataIsGood)
00382                         {
00383                             printf ("StartBit \n");
00384                         }
00385                         else
00386                         {
00387                             printf ("        \n");
00388                         }
00389                     }
00390                 }
00391             }
00392         }
00393     }
00394 
00395     // Power the meter off afterwards
00396     pwrOff();
00397 
00398     return x;
00399 }
00400 
00401 // Convert a series of ASCII numeric characters into a uint32_t value.
00402 bool WaterMeterHandler::decCharsToUint32 (char * pChars, uint32_t numChars, uint32_t * pValue)
00403 {
00404     bool success = true;
00405     double value = 0;
00406 
00407     if ((numChars > 0) && (pChars != NULL))
00408     {
00409         double multiplier;
00410         uint32_t x;
00411 
00412         multiplier = pow ((double) 10, double (numChars - 1));
00413 
00414         for (x = 0; (x < numChars) && success; x++)
00415         {
00416             if ((pChars[x] >= 0x30) && (pChars[x] <= 0x39))
00417             {
00418                 value += (pChars[x] - 0x30) * multiplier;
00419                 multiplier /= 10;
00420                 if (multiplier == 0)
00421                 {
00422                     multiplier = 1;
00423                 }
00424             }
00425             else
00426             {
00427                 success = false;
00428             }
00429         }
00430     }
00431     else
00432     {
00433         success = false;
00434     }
00435 
00436     if (success && (pValue != NULL))
00437     {
00438         if (value > 0xFFFFFFFF)
00439         {
00440             success = false;
00441             if (debug)
00442             {
00443                 printf ("Result is too big for an uint32_t (%f).\n", value);
00444             }
00445         }
00446         else
00447         {
00448             *pValue = (uint32_t) int (value);
00449         }
00450     }
00451 
00452     return success;
00453 }
00454 
00455 // ----------------------------------------------------------------
00456 // PUBLIC FUNCTIONS
00457 // ----------------------------------------------------------------
00458 
00459 /// Constructor
00460 WaterMeterHandler::WaterMeterHandler(void)
00461 {
00462     ready = false;
00463     debug = false;
00464     pData = NULL;
00465     pPwrClk = NULL;
00466     halfClkPeriodUs = (1000000 / METER_DEFAULT_CLOCK_RATE_HZ) / 2;
00467     if (debug)
00468     {
00469         printf ("WaterMeterHandler DEBUG prints are on.\n");
00470     }
00471 }
00472 
00473 /// Destructor
00474 WaterMeterHandler::~WaterMeterHandler(void)
00475 {
00476     ready = false;
00477     pData = NULL;
00478     pPwrClk = NULL;
00479 }
00480 
00481 /// Set debug output
00482 void WaterMeterHandler::setDebugOn (bool onNotOff)
00483 {
00484     debug = false;
00485 
00486     if (onNotOff)
00487     {
00488         debug = true;
00489     }
00490 
00491     if (debug)
00492     {
00493         printf ("\nWaterMeterHandler DEBUG prints are on.\n");
00494     }
00495     else
00496     {
00497         printf ("\nWaterMeterHandler DEBUG prints are off.\n");
00498     }
00499 }
00500 
00501 /// Initialise the water meter and set alternative
00502 // values for the data/pwrClk pins and the clock
00503 // rate used when reading data.  Then read a character
00504 // from the meter to ensure it's there.
00505 bool WaterMeterHandler::init (PinName dataPin, PinName pwrClkPin, uint32_t clkRateHz)
00506 {
00507     // Set context data
00508     pData = new DigitalIn (dataPin);
00509     pPwrClk = new DigitalOut (pwrClkPin);
00510 
00511     if (clkRateHz > 1000000)
00512     {
00513         clkRateHz = 1000000;
00514     }
00515     halfClkPeriodUs = (1000000 / clkRateHz) / 2;
00516 
00517     ready = true;
00518     // Power up the module and read a character.
00519     if (pwrOn())
00520     {
00521         if (!readChar (NULL))
00522         {
00523             ready = false;
00524         }
00525     }
00526 
00527     // Power it off again.
00528     pwrOff();
00529 
00530     return ready;
00531 }
00532 
00533 /// Take a water volume reading from the meter.
00534 bool WaterMeterHandler::readLitres (uint32_t  *pValue)
00535 {
00536     bool success = false;
00537     char buffer[METER_FIXED_MSG_LEN];
00538     uint32_t charsRead;
00539 
00540     if (debug)
00541     {
00542         printf ("Reading volume from meter...\n");
00543     }
00544     charsRead = readFixedLengthMsg (buffer, sizeof (buffer));
00545 
00546     if (debug)
00547     {
00548         printf ("\n%d byte(s) read of %d needed,\n", charsRead, VOLUME_READING_OFFSET + VOLUME_READING_LEN);
00549     }
00550 
00551     if (charsRead >= VOLUME_READING_OFFSET + VOLUME_READING_LEN)
00552     {
00553         if (debug)
00554         {
00555             printf ("Converting %.*s to ", VOLUME_READING_LEN, &(buffer[VOLUME_READING_OFFSET]));
00556         }
00557 
00558         success = decCharsToUint32 (&(buffer[VOLUME_READING_OFFSET]), VOLUME_READING_LEN, pValue);
00559 
00560         if (debug)
00561         {
00562             if (success)
00563             {
00564                 if (pValue != NULL)
00565                 {
00566                     printf ("%d.\n", *pValue);
00567                 }
00568                 else
00569                 {
00570                     printf ("[success].\n");
00571                 }
00572             }
00573             else
00574             {
00575                 printf ("[conversion failed].\n");
00576             }
00577         }
00578     }
00579 
00580     return success;
00581 }
00582 
00583 /// Read the serial number from the meter.
00584 bool WaterMeterHandler::readSerialNumber (uint32_t *pValue)
00585 {
00586     bool success = false;
00587     char buffer[METER_FIXED_MSG_LEN];
00588     uint32_t charsRead;
00589 
00590     if (debug)
00591     {
00592         printf ("Reading serial number from meter...\n");
00593     }
00594     charsRead = readFixedLengthMsg (buffer, sizeof (buffer));
00595 
00596     if (debug)
00597     {
00598         printf ("\n%d byte(s) read of %d needed,\n", charsRead, SERIAL_NUMBER_OFFSET + SERIAL_NUMBER_LEN);
00599     }
00600 
00601     if (charsRead >= SERIAL_NUMBER_OFFSET + SERIAL_NUMBER_LEN)
00602     {
00603         if (debug)
00604         {
00605             printf ("Converting %.*s to ", SERIAL_NUMBER_LEN, &(buffer[SERIAL_NUMBER_OFFSET]));
00606         }
00607 
00608         success = decCharsToUint32 (&(buffer[SERIAL_NUMBER_OFFSET]), SERIAL_NUMBER_LEN, pValue);
00609 
00610         if (debug)
00611         {
00612             if (success)
00613             {
00614                 if (pValue != NULL)
00615                 {
00616                     printf ("%d.\n", *pValue);
00617                 }
00618                 else
00619                 {
00620                     printf ("[success].\n");
00621                 }
00622             }
00623             else
00624             {
00625                 printf ("[conversion failed].\n");
00626             }
00627         }
00628     }
00629 
00630     return success;
00631 }
00632 
00633 // End Of File