Support library for reading the Kamstrup FlowIQ2000 water meter via its serial interface.
Embed:
(wiki syntax)
Show/hide line numbers
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
Generated on Wed Jul 13 2022 16:07:26 by
1.7.2