## Debugging my Power Monitor Code

The power monitor I built using a TI TMP-513 and TAP-20 has worked pretty well but has suffered from some occasional glitches in the voltage readings.  The problem seemed to be random and I attributed it to noise pickup.  The problem always appeared as spikes of higher than the expected level; I minimized the effect by averaging readings.

Yesterday, I was making some measurements and the effect was too big to ignore.  It was messing up what should have been nice smooth curves with almost a one volt error on a six volt reading.  I repeated the measurements several times and had similar errors, although at different voltages.

My first thought was that there might be glitches in the serial link between the power monitor and PC.  Examining the data revealed that real numbers were being sent and not the trash that results from serial port errors.

Looking closely at the data shows readings of 5.988, 5.992, 5.996, 6, 6.4, 6.8, 6.12, 6.16...  Wait a minute.  The 6.4 and 6.8 readings are clearly not part of the trend.  This looks like a coding problem.

Voltage is read from the TMP-513 via I2C in a subroutine in millivolts.  The data is read out as two bytes and converted to millivolts.  All voltage readings are handled the same way with no range shifting.  This subroutine doesn't appear to be the source of the glitches.

Taking a closer look at the data, if the readings were changed 6.4 to 6.04, and from 6.8 to 6.08, they would exactly fit the expected results.  It looks like there's an error in the conversion from millivolts to volts and millivolts for display and serial transmission.

The millivolt data is converted to integer volts and millivolts in the two statements that send the data to the LCD and to the serial port.  The same routine is used for both and is shown below.

The reading to the left of the decimal point in the conversion from mV to volts is handled by:

`DecToStr(BusVoltage/1000)`

Since BusVoltage is an integer value, dividing by 1000 yields only the integer volt term.

The reading to the right of the decimal point is the fractional part of the reading in mV:

`DecToStr(BusVoltage -(BusVoltage/1000*1000))`

This part of the code subtracts the integer voltage term from the entire term, leaving only the millivolt part of the reading.

So, what happens if the reading is 5996 mV?

The left side of the decimal is 5996/1000 which yields 5 - remember integer math truncates, not rounds.

On the right side of the decimal point is

5996 - ((5996/1000) * 1000) = 5996 - 5000 = 996 mV.

What happens if the reading is 6040 mV?  The left of the decimal point is 6 as it should be.  The right side is:

6040 - ((6040/1000) * 1000) = 6040 - 6000 = 40 mV.  The conversion routine yields 6.4 since the mV term is only two digits.  D'oh.  Leading zeros are lost on the mV term, shifting the remaining digits over.  This explains why the glitches are always an increase in value, and why they're never more than ~ one volt.

Code
``` LCD.WriteAt (1,1, "V: ", (DecToStr(BusVoltage/1000)),".",DecToStr(BusVoltage -(BusVoltage/1000*1000))," V" ) CDC.Write (1,1, "V: ", (DecToStr(BusVoltage/1000)),".",DecToStr(BusVoltage -(BusVoltage/1000*1000))," V",13,10 )
```

Fortunately, Swordfish makes this an easy fix.  The DecToStr conversion routine provides for leading zero padding.

function DecToStr(pValue as type [, pPad as byte, pPadChar as char = "0"]) as string

• pValue - The number to convert. The argument can be of type boolean, byte, shortint, word, integer, longword or longint.

• pPad - An optional parameter which will pack the left hand side of the returned string with (pPad - length of numeric string) zeros.

• pPadChar - An optional parameter which changes the default packing character.

This function will convert a number and return a string decimal string representation of the number passed. Optional pPad and pPadChar arguments can be used to format the returned string value. For example,

MyString = DecToStr(42) // result is "42"

MyString = DecToStr(42,5) // result is "00042"

MyString = DecToStr(42,5,"#") // result is "###42"

#### Conclusions

When troubleshooting software, take a systematic approach to determine where the problem may be.  When you have isolated a portion of the code that may be the problem, logically try some test cases to see what happens step by step.

Troubleshooting the power monitor code was made easier by having a clear case where the glitches occurred.  If the data was random instead of a smooth curve, this fault would have been very difficult to isolate.

Posted: 6 years 7 months ago
The plot shows voltage level vs. time, just in case that's not clear. There are about 3000 measurements over the span shown.
Posted: 6 years 7 months ago by majenko
Well hunted down there, sir.

That one would have probably had me stumped for hours. But then, not used to using BASIC, and having it all handed to me on a plate with sprintf() in C, these things aren't upper-most in my debugging mind
Posted: 6 years 7 months ago
By the way, I'm certain there are many other ways to parse the mV data into decimal volts. This was more about the process of isolating the problem, following a logical sequence.

Finding the right test case was definitely the key to understanding and solving the mystery.
Posted: 6 years 7 months ago
Using padding in the DecimalToStr command did indeed correct the conversion problem. No more of the glitch problems but also what I thought was just measurement noise is gone too. Below is a plot similar to those that had the glitches. This plot shows a battery discharge curve; the plot shows data taken every second for nearly an hour - over 3000 measurements. Man is the curve pretty!

The curve has a bit of inflection at the right side, caused by my LM-317-based constant current load falling off below around 3 volts. If the same load had continued, the line would be nearly vertical; the battery is just about dead at that point.