Arduino Wattmeter v1
Arduino Wattmeter v1
void loop() {
if (primReady == true) {
/*** Secondary avearaging Mean Volt ***/
secMeanVolt -= secMeanVoltArr[secArrCnt]; // Subtract oldest
value strored in array from average
secMeanVolt += primMeanVoltCopy; // Add newest value
to average
secMeanVoltArr[secArrCnt] = primMeanVoltCopy; // Store newest
value in array
/*** Secondary avearaging Mean Squared Volt ***/
secSquaredVolt -= secSquaredVoltArr[secArrCnt]; // the same for
squared voltage
secSquaredVolt += primSquaredVoltCopy;
secSquaredVoltArr[secArrCnt] = primSquaredVoltCopy;
/*** Secondary avearaging Mean Current ***/
secMeanCurr -= secMeanCurrArr[secArrCnt]; // and mean current
secMeanCurr += primMeanCurrCopy;
secMeanCurrArr[secArrCnt] = primMeanCurrCopy;
/*** Secondary avearaging Mean Squared Current ***/
secSquaredCurr -= secSquaredCurrArr[secArrCnt]; // squared current
secSquaredCurr += primSquaredCurrCopy;
secSquaredCurrArr[secArrCnt] = primSquaredCurrCopy;
/*** Secondary avearaging Mean Power ***/
secMeanPow -= secMeanPowArr[secArrCnt]; // and power
secMeanPow += primMeanPowCopy;
secMeanPowArr[secArrCnt] = primMeanPowCopy;
/*** Array Pointer ***/
secArrCnt++; // Increase secondary
averaging array pointer
if(secArrCnt >= secAvLength) {
secArrCnt = 0;
}
/*** Cumulative values ***/
extraLongAddLong(tFlux, primMeanVoltCopy); // Add primary averaged
voltage to total Flux
extraLongAddLong(tCharge, primMeanCurrCopy); // Add primary averaged
current to total Charge
extraLongAddLong(tEnergy, primMeanPowCopy); // Add primary averaged
power to total Energy
primReady = false;
}
/*** Update display ***/
if (vCnt > 1000) { // After 1000 channel samples refresh values
on display
vCnt = 0;
/*** Calculate values Voltage ***/
paramValues[0] = float(secMeanVolt) * Vscale / totAverage;
// calc. mean Voltage
paramValues[1] = sqrt(float(secSquaredVolt) / totAverage) * Vscale;
// calc. RMS Voltage
paramValues[2] = sqrt((paramValues[1]*paramValues[1])-
(paramValues[0]*paramValues[0])); // calc. standard deviation Voltage:
Vsdev=sqrt(Vrms^2 - Vmean^2)
paramValues[3] = float(primMaxVolt) * Vscale;
// Max voltage
paramValues[4] = float(primMinVolt) * Vscale;
// Min voltage
/*** Calculate values Current ***/
paramValues[7] = float(secMeanCurr) * Cscale / totAverage;
// calc. mean Current
paramValues[8] = sqrt(float(secSquaredCurr) / totAverage) * Cscale;
// calc. RMS Current
paramValues[9] = sqrt((paramValues[8]*paramValues[8])-
(paramValues[7]*paramValues[7])); // calc. standard deviation Current:
Csdev=sqrt(Crms^2 - Cmean^2)
paramValues[10] = float(primMaxCurr) * Cscale;
// Max current
paramValues[11] = float(primMinCurr) * Cscale;
// Min current
/*** Calculate values Power ***/
paramValues[14] = float(secMeanPow) * Vscale * Cscale / totAverage;
// Real Power
paramValues[17] = float(primMaxPow) * Vscale * Cscale;
// Max Power
paramValues[18] = float(primMinPow) * Vscale * Cscale;
// Min Power
paramValues[15] = paramValues[1] * paramValues[8];
// Apparent Power = Urms * Irms
paramValues[16] = sqrt((paramValues[15]*paramValues[15])-
(paramValues[14]*paramValues[14])); // Reactive power
/*** Flux, Charge, Energy ***/
paramValues[5] = extraLongToFloat(tFlux) * Vscale / fSample;
// Flux
paramValues[12] = extraLongToFloat(tCharge) * Cscale / fSample;
// Charge
paramValues[19] = extraLongToFloat(tEnergy) * Vscale * Cscale / fSample;
// Energy
/*** Phase & Time ***/
paramValues[20] = acos(paramValues[14]/paramValues[15]);
// Phase = acos(P/S)
paramValues[21] = (float) round(timeCnt);
// Time
/*** Voltage Frequency ***/
if(vfmPeriodsTime > 10000 && vfmPeriods > 3) {
vfmUpperTh = 2000;
// Prevent asynchronious reading
paramValues[6] = float(vfmPeriods) * fSample / float(vfmPeriodsTime-2);
// calc. voltage frequency
if(pow(10, paramRange[2]) / paramValues[2] > 100) {
paramValues[6] = 0.0; }
// Make zero if signal is too small
vfmTime = 0;
// Start new voltage freqency measurement
}
vfmUpperTh = int((paramValues[0] + (paramValues[2] / 2)) / Vscale);
// Upper voltage threshold
vfmLowerTh = int((paramValues[0] - (paramValues[2] / 2)) / Vscale);
// lower voltage threshold
/*** Current Frequency ***/
if(cfmPeriodsTime > 10000 && cfmPeriods > 3) {
cfmUpperTh = 2000;
// Prevent asynchronious reading
paramValues[13] = float(cfmPeriods) * fSample / float(cfmPeriodsTime-
2); // calc. current frequency
if(pow(10, paramRange[9]) / paramValues[9] > 100) {
paramValues[13] = 0.0; }
// Make zero if signal is too small
cfmTime = 0;
// Start new voltage freqency measurement
}
cfmUpperTh = int((paramValues[7] + (paramValues[9] / 2)) / Cscale);
// Upper current threshold
cfmLowerTh = int((paramValues[7] - (paramValues[9] / 2)) / Cscale);
// Lower current threshold
void initiateADC() {
ADCSRB = 0x00;
DIDR0 = 0x30; // digital inputs disabled for A4 & A5
ADMUX = 0xC4; // measuring on ADC4, left adjust, internal 1.1V
ref
ADCSRA = 0xAE; // AD-converter on, interrupt enabled, prescaler =
64
ADCSRB = 0x40; // AD channels MUX on, free running mode
bitWrite(ADCSRA, 6, 1); // Start the conversion by setting bit 6 (=ADSC)
in ADCSRA
}
void lcdInitiate() {
/*** Initialize 2*16 char LCD ***/
/*** LCD DB4...7 => PD4...7 (D4...7), RS => PD2 (D2), R/W => PD3 (D3), E =>
PB0 (D8) ***/
bitClear(PORTB, 0); // LCD enable = low
bitSet(DDRB, 0); // LCD enable = output
PORTD = PORTD & 0x03; // LCD D4...7 & R/W & RS = low
DDRD = DDRD | 0xFC; // LCD D4...7 & R/W & RS = output
muDelay(50000);
byte PD = PORTD & 0x03; // LCD in 4 bit mode
PORTD = PD | 0x20;
__asm__("nop\n\t");
bitSet(PORTB, 0); // E high
__asm__("nop\n\tnop\n\tnop\n\tnop\n\t");
bitClear(PORTB, 0); // E low
__asm__("nop\n\tnop\n\t");
// Write function set three times, else the second line isn't functional.
lcdWrite(true, 0x28); // 2 line mode, 5*7 dots
muDelay(40);
lcdWrite(true, 0x28); // 2 line mode, 5*7 dots
muDelay(40);
lcdWrite(true, 0x28); // 2 line mode, 5*7 dots
muDelay(40);
lcdWrite(true, 0x0C); // display on, cursor off, blink off
lcdWrite(true, 0x01); // display clear
lcdWrite(true, 0x06); // increment mode, entire shift off
}
void lcdPrintString(const char* strArr) {
/*** Write a text string to the LCD ***/
for(byte ci=0; ci<strlen(strArr); ci++) {
lcdWrite(false, strArr[ci]);
}
}
void lcdCheckBusyFlag() {
/*** check until LCD is ready ***/
byte dVal;
DDRD = DDRD & 0x0F; // PD4...7 = input
bitSet(PORTD, 3); // R/W = 1
bitClear(PORTD, 2); // RS=0
do {
bitSet(PORTB, 0); // enable = 1
__asm__("nop\n\tnop\n\t");
dVal = PIND & 0xF0; // Read MS nibble
bitClear(PORTB, 0); // enable = 0
__asm__("nop\n\tnop\n\tnop\n\t");
bitSet(PORTB, 0); // enable = 1
__asm__("nop\n\tnop\n\t");
dVal = dVal | (PIND >> 4); // Read LS nibble
bitClear(PORTB, 0); // enable = 0
__asm__("nop\n\tnop\n\tnop\n\t");
} while(bitRead(dVal, 7) == HIGH);
bitClear(PORTD, 3); // R/W = 0
}
void lcdCustomChars() {
/*** Make some custom characters ***/
lcdWrite(true, 0x48); // Char 0x01 = phi
lcdWrite(false, 0x02);
lcdWrite(false, 0x15);
lcdWrite(false, 0x15);
lcdWrite(false, 0x0E);
lcdWrite(false, 0x04);
lcdWrite(false, 0x04);
lcdWrite(false, 0x04);
lcdWrite(false, 0x00);
lcdWrite(true, 0x50); // Char 0x02 = deg
lcdWrite(false, 0x0C);
lcdWrite(false, 0x12);
lcdWrite(false, 0x12);
lcdWrite(false, 0x0C);
lcdWrite(false, 0x00);
lcdWrite(false, 0x00);
lcdWrite(false, 0x00);
lcdWrite(false, 0x00);
lcdWrite(true, 0x80); // First char address
}
void lcdTechNot(float fval, long lval, int rangeExp, int digits, const char*
unit, boolean overFlow, byte pos, boolean alignR) {
/*** Write a value in Technical Notation on the LCD ***/
/*** fval = float value, ore use lval = long int value ***/
/*** rangeExp = exponent of the limit value (e.g. rangeExp=2 => 10^2=100 as
limit), 99=autorange, 100 = use the long int value ***/
/*** digits = number of digits ***/
/*** unit = the unit: "V", "A",... ***/
/*** overFlow = external overflow indicator, e.g. from ADC. Puts the "^"
char between number and unit ***/
/*** pos & alignR = LCD position & right align: last position if
alignR=true, first position if alignR=false) ***/
byte totChar = 11; // Total number of chars to fill
on the LCD (to ensure the old valu is erased)
char valStr[totChar]; // Text string
int strPnt = 0; // Text string pointer
digits -= 1;
int decpoint = digits + 3; // No decimal point if val is
long
char vvArr[] = {'f','p','n',0xE4,'m',' ','k','M','G','T','P'}; // prefix
array
int expo; // Exponent
if (rangeExp == 99) { // If auto range ...
expo = floor(log10(fabs(fval))); // ... use the value exponent ...
}
else {
expo = rangeExp - 1; // ... else use a fixed exponent
as range
}
if(rangeExp != 100) { // If the float value is used ...
lval = long(round(fval / pow(10, expo - digits))); // ... calculate the
value in N digits.
if(lval >= round(pow(10, digits + 1))) { // Fix floating
point errors
lval /= 10;
expo++;
}
decpoint = digits - ((expo+15) % 3); // Calc. decimal point position
}
else { // If the long int value is used
...
expo = 0; // ... prevent using a prefix.
}
if(lval < 0) { // If the value is negative ...
lval = abs(lval); // ... make the value absolute
...
valStr[strPnt++] = 0x2D; // ... and put a negative sign =>
string
}
long dec;
byte num;
boolean prtStart = false; // Don't push any number into the
string yet (no leading zero's)
for(int nd=digits; nd>=0; nd--) { // For the number of digits the
value is long ...
dec = long(round(pow(10, nd))); // calc. the 10th power number
for the current digit,
num = 0; // start value is 0.
while(lval >= dec) { // While the 10th power number is
smaller than the value ...
lval -= dec; // decrease the value,
num++; // increase the current digit.
if(num > 9) { // If digit > 9 ...
num = 0x3C; // digit is "<" if value doesn't
fit
lval += dec; // for all digit places
break;
}
}
if(prtStart == true || num != 0 || nd == 0) { // If printing is active
or digit is not zero or LS digit
prtStart = true; // printing is active,
valStr[strPnt++] = num | 0x30; // digit => string.
}
else if(prtStart == false && decpoint == nd) { // If no number is printed
yet and decimal point must be printed
prtStart = true; // printing is active,
valStr[strPnt++] = 0x30; // 0 => string.
}
if(decpoint == nd && nd != 0) { // If decimal point must
be printed and it is not the LS digit
valStr[strPnt++] = 0x2E; // decimal point =>
string.
}
}
if(overFlow == true) {
valStr[strPnt++] = 0x5E; // "^" => string if there the
external overflow flag is set
}
else {
valStr[strPnt++] = 0x20; // or a white space => string
}
int ediv = floor(expo / 3.0); // Calc. prefix pointer
if (ediv != 0) {
valStr[strPnt++] = vvArr[ediv+5]; // Prefix => string
}
byte ci;
for(ci=0; ci<strlen(unit); ci++) { // For every unit character ...
valStr[strPnt++] = unit[ci]; // ... unit char = string.
}
if(alignR == true) { // If the alignment is right ...
pos -= totChar; // ... calc. new start position.
}
lcdWrite(true, pos); // Position cursor LCD
for(byte iw=0; iw<(totChar-strPnt); iw++) { // For every unoccupied
preceding character ...
lcdWrite(false, 0x20); // ... send white spaces to LCD to
erase old text
}
for(ci=0; ci<strPnt; ci++) { // For every character ...
lcdWrite(false, valStr[ci]); // ... send character to LCD
}
}
__asm__("nop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\t");
}
}
Wattmeter code
/************************************/
/*** Arduino Wattmeter v1.0 ***/
/*** Board: Arduino Nano 3.0 ***/
/*** By: Freddy Alferink ***/
/*** http://meettechniek.info ***/
/*** May 2014 ***/
/************************************/
void setup() {
MCUCR &= 0xEF; // Enable pull-up resistors
DDRB &= 0xE1; // Buttons on D9...12 are inputs
DDRB |= 0x20; // Overflow LED on D13 is output
PORTB |= 0x1E; // Pull-up resistor for the buttons
bitClear(PORTB, 5); // Overflow LED off
initiateADC(); // initiate AD converter
lcdInitiate(); // initiate LCD
lcdCustomChars(); // Make custom characters
lcdWrite(true, LCDlinePos[0]); // LCD cursor first line position 0
lcdPrintString("Universal power");
lcdWrite(true, LCDlinePos[1]); // LCD cursor second line position 0
lcdPrintString("meter v1.0");
for(int md=0; md<100; md++) { // Wait 2 seconds
muDelay(20000);
}
lcdWrite(true, LCDlinePos[0]); // LCD cursor first line position 0
lcdPrintString("meettechniek ");
lcdWrite(true, LCDlinePos[1]); // LCD cursor second line position 0
lcdPrintString(" .info");
for(int md=0; md<100; md++) { // Wait 2 seconds
muDelay(20000);
}
setProperties(); // Set properties and Clear measured
values
updateParamLabels(); // Write Parameter labels to LCD
updateParamValues(); // Write parameter values to LCD
sei(); // set interrupt flag
}
void loop() {
if (primReady == true) {
/*** Secondary avearaging Mean Volt ***/
secMeanVolt -= secMeanVoltArr[secArrCnt]; // Subtract oldest
value strored in array from average
secMeanVolt += primMeanVoltCopy; // Add newest value
to average
secMeanVoltArr[secArrCnt] = primMeanVoltCopy; // Store newest
value in array
/*** Secondary avearaging Mean Squared Volt ***/
secSquaredVolt -= secSquaredVoltArr[secArrCnt]; // the same for
squared voltage
secSquaredVolt += primSquaredVoltCopy;
secSquaredVoltArr[secArrCnt] = primSquaredVoltCopy;
/*** Secondary avearaging Mean Current ***/
secMeanCurr -= secMeanCurrArr[secArrCnt]; // and mean current
secMeanCurr += primMeanCurrCopy;
secMeanCurrArr[secArrCnt] = primMeanCurrCopy;
/*** Secondary avearaging Mean Squared Current ***/
secSquaredCurr -= secSquaredCurrArr[secArrCnt]; // squared current
secSquaredCurr += primSquaredCurrCopy;
secSquaredCurrArr[secArrCnt] = primSquaredCurrCopy;
/*** Secondary avearaging Mean Power ***/
secMeanPow -= secMeanPowArr[secArrCnt]; // and power
secMeanPow += primMeanPowCopy;
secMeanPowArr[secArrCnt] = primMeanPowCopy;
/*** Array Pointer ***/
secArrCnt++; // Increase secondary
averaging array pointer
if(secArrCnt >= secAvLength) {
secArrCnt = 0;
}
/*** Cumulative values ***/
extraLongAddLong(tFlux, primMeanVoltCopy); // Add primary averaged
voltage to total Flux
extraLongAddLong(tCharge, primMeanCurrCopy); // Add primary averaged
current to total Charge
extraLongAddLong(tEnergy, primMeanPowCopy); // Add primary averaged
power to total Energy
primReady = false;
}
/*** Update display ***/
if (vCnt > 1000) { // After 1000 channel samples refresh values
on display
vCnt = 0;
/*** Calculate values Voltage ***/
paramValues[0] = float(secMeanVolt) * Vscale / totAverage;
// calc. mean Voltage
paramValues[1] = sqrt(float(secSquaredVolt) / totAverage) * Vscale;
// calc. RMS Voltage
paramValues[2] = sqrt((paramValues[1]*paramValues[1])-
(paramValues[0]*paramValues[0])); // calc. standard deviation Voltage:
Vsdev=sqrt(Vrms^2 - Vmean^2)
paramValues[3] = float(primMaxVolt) * Vscale;
// Max voltage
paramValues[4] = float(primMinVolt) * Vscale;
// Min voltage
/*** Calculate values Current ***/
paramValues[7] = float(secMeanCurr) * Cscale / totAverage;
// calc. mean Current
paramValues[8] = sqrt(float(secSquaredCurr) / totAverage) * Cscale;
// calc. RMS Current
paramValues[9] = sqrt((paramValues[8]*paramValues[8])-
(paramValues[7]*paramValues[7])); // calc. standard deviation Current:
Csdev=sqrt(Crms^2 - Cmean^2)
paramValues[10] = float(primMaxCurr) * Cscale;
// Max current
paramValues[11] = float(primMinCurr) * Cscale;
// Min current
/*** Calculate values Power ***/
paramValues[14] = float(secMeanPow) * Vscale * Cscale / totAverage;
// Real Power
paramValues[17] = float(primMaxPow) * Vscale * Cscale;
// Max Power
paramValues[18] = float(primMinPow) * Vscale * Cscale;
// Min Power
paramValues[15] = paramValues[1] * paramValues[8];
// Apparent Power = Urms * Irms
paramValues[16] = sqrt((paramValues[15]*paramValues[15])-
(paramValues[14]*paramValues[14])); // Reactive power
/*** Flux, Charge, Energy ***/
paramValues[5] = extraLongToFloat(tFlux) * Vscale / fSample;
// Flux
paramValues[12] = extraLongToFloat(tCharge) * Cscale / fSample;
// Charge
paramValues[19] = extraLongToFloat(tEnergy) * Vscale * Cscale / fSample;
// Energy
/*** Phase & Time ***/
paramValues[20] = acos(paramValues[14]/paramValues[15]);
// Phase = acos(P/S)
paramValues[21] = (float) round(timeCnt);
// Time
/*** Voltage Frequency ***/
if(vfmPeriodsTime > 10000 && vfmPeriods > 3) {
vfmUpperTh = 2000;
// Prevent asynchronious reading
paramValues[6] = float(vfmPeriods) * fSample / float(vfmPeriodsTime-2);
// calc. voltage frequency
if(pow(10, paramRange[2]) / paramValues[2] > 100) {
paramValues[6] = 0.0; }
// Make zero if signal is too small
vfmTime = 0;
// Start new voltage freqency measurement
}
vfmUpperTh = int((paramValues[0] + (paramValues[2] / 2)) / Vscale);
// Upper voltage threshold
vfmLowerTh = int((paramValues[0] - (paramValues[2] / 2)) / Vscale);
// lower voltage threshold
/*** Current Frequency ***/
if(cfmPeriodsTime > 10000 && cfmPeriods > 3) {
cfmUpperTh = 2000;
// Prevent asynchronious reading
paramValues[13] = float(cfmPeriods) * fSample / float(cfmPeriodsTime-
2); // calc. current frequency
if(pow(10, paramRange[9]) / paramValues[9] > 100) {
paramValues[13] = 0.0; }
// Make zero if signal is too small
cfmTime = 0;
// Start new voltage freqency measurement
}
cfmUpperTh = int((paramValues[7] + (paramValues[9] / 2)) / Cscale);
// Upper current threshold
cfmLowerTh = int((paramValues[7] - (paramValues[9] / 2)) / Cscale);
// Lower current threshold
void initiateADC() {
ADCSRB = 0x00;
DIDR0 = 0x30; // digital inputs disabled for A4 & A5
ADMUX = 0xC4; // measuring on ADC4, left adjust, internal 1.1V
ref
ADCSRA = 0xAE; // AD-converter on, interrupt enabled, prescaler =
64
ADCSRB = 0x40; // AD channels MUX on, free running mode
bitWrite(ADCSRA, 6, 1); // Start the conversion by setting bit 6 (=ADSC)
in ADCSRA
}
void lcdInitiate() {
/*** Initialize 2*16 char LCD ***/
/*** LCD DB4...7 => PD4...7 (D4...7), RS => PD2 (D2), R/W => PD3 (D3), E =>
PB0 (D8) ***/
bitClear(PORTB, 0); // LCD enable = low
bitSet(DDRB, 0); // LCD enable = output
PORTD = PORTD & 0x03; // LCD D4...7 & R/W & RS = low
DDRD = DDRD | 0xFC; // LCD D4...7 & R/W & RS = output
muDelay(50000);
byte PD = PORTD & 0x03; // LCD in 4 bit mode
PORTD = PD | 0x20;
__asm__("nop\n\t");
bitSet(PORTB, 0); // E high
__asm__("nop\n\tnop\n\tnop\n\tnop\n\t");
bitClear(PORTB, 0); // E low
__asm__("nop\n\tnop\n\t");
// Write function set three times, else the second line isn't functional.
lcdWrite(true, 0x28); // 2 line mode, 5*7 dots
muDelay(40);
lcdWrite(true, 0x28); // 2 line mode, 5*7 dots
muDelay(40);
lcdWrite(true, 0x28); // 2 line mode, 5*7 dots
muDelay(40);
lcdWrite(true, 0x0C); // display on, cursor off, blink off
lcdWrite(true, 0x01); // display clear
lcdWrite(true, 0x06); // increment mode, entire shift off
}
void lcdCheckBusyFlag() {
/*** check until LCD is ready ***/
byte dVal;
DDRD = DDRD & 0x0F; // PD4...7 = input
bitSet(PORTD, 3); // R/W = 1
bitClear(PORTD, 2); // RS=0
do {
bitSet(PORTB, 0); // enable = 1
__asm__("nop\n\tnop\n\t");
dVal = PIND & 0xF0; // Read MS nibble
bitClear(PORTB, 0); // enable = 0
__asm__("nop\n\tnop\n\tnop\n\t");
bitSet(PORTB, 0); // enable = 1
__asm__("nop\n\tnop\n\t");
dVal = dVal | (PIND >> 4); // Read LS nibble
bitClear(PORTB, 0); // enable = 0
__asm__("nop\n\tnop\n\tnop\n\t");
} while(bitRead(dVal, 7) == HIGH);
bitClear(PORTD, 3); // R/W = 0
}
void lcdCustomChars() {
/*** Make some custom characters ***/
lcdWrite(true, 0x48); // Char 0x01 = phi
lcdWrite(false, 0x02);
lcdWrite(false, 0x15);
lcdWrite(false, 0x15);
lcdWrite(false, 0x0E);
lcdWrite(false, 0x04);
lcdWrite(false, 0x04);
lcdWrite(false, 0x04);
lcdWrite(false, 0x00);
lcdWrite(true, 0x50); // Char 0x02 = deg
lcdWrite(false, 0x0C);
lcdWrite(false, 0x12);
lcdWrite(false, 0x12);
lcdWrite(false, 0x0C);
lcdWrite(false, 0x00);
lcdWrite(false, 0x00);
lcdWrite(false, 0x00);
lcdWrite(false, 0x00);
lcdWrite(true, 0x80); // First char address
}
void lcdTechNot(float fval, long lval, int rangeExp, int digits, const char*
unit, boolean overFlow, byte pos, boolean alignR) {
/*** Write a value in Technical Notation on the LCD ***/
/*** fval = float value, ore use lval = long int value ***/
/*** rangeExp = exponent of the limit value (e.g. rangeExp=2 => 10^2=100 as
limit), 99=autorange, 100 = use the long int value ***/
/*** digits = number of digits ***/
/*** unit = the unit: "V", "A",... ***/
/*** overFlow = external overflow indicator, e.g. from ADC. Puts the "^"
char between number and unit ***/
/*** pos & alignR = LCD position & right align: last position if
alignR=true, first position if alignR=false) ***/
byte totChar = 11; // Total number of chars to fill
on the LCD (to ensure the old valu is erased)
char valStr[totChar]; // Text string
int strPnt = 0; // Text string pointer
digits -= 1;
int decpoint = digits + 3; // No decimal point if val is
long
char vvArr[] = {'f','p','n',0xE4,'m',' ','k','M','G','T','P'}; // prefix
array
int expo; // Exponent
if (rangeExp == 99) { // If auto range ...
expo = floor(log10(fabs(fval))); // ... use the value exponent ...
}
else {
expo = rangeExp - 1; // ... else use a fixed exponent
as range
}
if(rangeExp != 100) { // If the float value is used ...
lval = long(round(fval / pow(10, expo - digits))); // ... calculate the
value in N digits.
if(lval >= round(pow(10, digits + 1))) { // Fix floating
point errors
lval /= 10;
expo++;
}
decpoint = digits - ((expo+15) % 3); // Calc. decimal point position
}
else { // If the long int value is used
...
expo = 0; // ... prevent using a prefix.
}
if(lval < 0) { // If the value is negative ...
lval = abs(lval); // ... make the value absolute
...
valStr[strPnt++] = 0x2D; // ... and put a negative sign =>
string
}
long dec;
byte num;
boolean prtStart = false; // Don't push any number into the
string yet (no leading zero's)
for(int nd=digits; nd>=0; nd--) { // For the number of digits the
value is long ...
dec = long(round(pow(10, nd))); // calc. the 10th power number
for the current digit,
num = 0; // start value is 0.
while(lval >= dec) { // While the 10th power number is
smaller than the value ...
lval -= dec; // decrease the value,
num++; // increase the current digit.
if(num > 9) { // If digit > 9 ...
num = 0x3C; // digit is "<" if value doesn't
fit
lval += dec; // for all digit places
break;
}
}
if(prtStart == true || num != 0 || nd == 0) { // If printing is active
or digit is not zero or LS digit
prtStart = true; // printing is active,
valStr[strPnt++] = num | 0x30; // digit => string.
}
else if(prtStart == false && decpoint == nd) { // If no number is printed
yet and decimal point must be printed
prtStart = true; // printing is active,
valStr[strPnt++] = 0x30; // 0 => string.
}
if(decpoint == nd && nd != 0) { // If decimal point must
be printed and it is not the LS digit
valStr[strPnt++] = 0x2E; // decimal point =>
string.
}
}
if(overFlow == true) {
valStr[strPnt++] = 0x5E; // "^" => string if there the
external overflow flag is set
}
else {
valStr[strPnt++] = 0x20; // or a white space => string
}
int ediv = floor(expo / 3.0); // Calc. prefix pointer
if (ediv != 0) {
valStr[strPnt++] = vvArr[ediv+5]; // Prefix => string
}
byte ci;
for(ci=0; ci<strlen(unit); ci++) { // For every unit character ...
valStr[strPnt++] = unit[ci]; // ... unit char = string.
}
if(alignR == true) { // If the alignment is right ...
pos -= totChar; // ... calc. new start position.
}
lcdWrite(true, pos); // Position cursor LCD
for(byte iw=0; iw<(totChar-strPnt); iw++) { // For every unoccupied
preceding character ...
lcdWrite(false, 0x20); // ... send white spaces to LCD to
erase old text
}
for(ci=0; ci<strPnt; ci++) { // For every character ...
lcdWrite(false, valStr[ci]); // ... send character to LCD
}
}
__asm__("nop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\t");
}
}