mBuino program that shows how to read VCC using the blink of a LED

Dependencies:   USBDevice mbed

mBuino PowerMeter

This program shows how to read the current voltage on the 3V3 (VCC) pin of mBuino using the blink of a LED.

Since mBuino uses the VCC voltage as reference for analog reads, all readings are relative. To get an absolute reading an external reference voltage is needed. An easy way to get an external reference is by using a zener-diode with proper characteristics.

Not having such a zener around made me look for alternatives. I found some on this page: http://www.talkingelectronics.com/projects/200TrCcts/200TrCcts.html#52

/media/uploads/maxint/reference_voltage_using_diode.jpg

Use a LED!

The most interesting method I found was to use the LEDs on mBuino! All needed to use this is an extra connection. I used a 100R resistor as connection between pin 14 and LED6. Pin 14 is the small dot right above the one of the version number. Alternatively you can also use an external LED or (zener)-diode. The picture below shows both the added resistor as well as an external LED/resistor circuit. /media/uploads/maxint/mbuino_powermeter.jpg

Serial USB

The picture above shows a USB cable connected to mBuino. When mBuino is powered via USB and nothing else is connected to VCC, power comes from the 3v3 regulator. The VCC reading will then be around its maximum. USB is used to display the readings on the USB serial console. Note that for this the USB serial driver and a terminal application such as Putty is required. When using the console the spacebar can be hit to switch between pin14+LED6 and pin15 with an external LED/resistor via pin4.

See the code comments for more information.

main.cpp

Committer:
maxint
Date:
2015-08-07
Revision:
4:853634e0d433
Parent:
3:9e93f14db34d

File content as of revision 4:853634e0d433:

/*
** mBuino_PowerMeter
** 
** This program shows how to read the current voltage on the 3V3 (VCC) pin of mBuino using the blink of a LED.
**
** Since mBuino uses the VCC voltage as reference for analog reads, all readings are relative.
** To get an absolute reading an external reference voltage is needed.
** An easy way to get an external reference is by using a zener-diode with proper characteristics.
** Not having such a zener around made me look for alternatives. I found some on this page:
**   http://www.talkingelectronics.com/projects/200TrCcts/200TrCcts.html#52
** The most interesting method I found was to use the LEDs on mBuino
** See the code comments and this programs homepage for more information.
**   https://developer.mbed.org/users/maxint/code/mBuino_PowerMeter/ 
**
** 0.1.150801 First version made for mBuino on mbed.org by maxint on 1-aug-2015
**
** Feel free to use this code anyway you want, but please acknowledge my work by mentioning maxint as creator.
**
*/


#include "mbed.h"
#include "USBSerial.h"

// The USB virtual serial port is used for debugging. Note: driver required, see http://developer.mbed.org/handbook/USBSerial
USBSerial serialUSB(0x1f00, 0x2012, 0x0001, false); // Virtual serial port over USB, set connect_blocking to false to allow starting without PC
#define USBSERIAL_DEBUG 1

DigitalOut LED[] = {(LED1), (LED2), (LED3), (LED4), (LED5), (LED6), (LED7)};// declare 7 LEDs

float readVCC(PinName nAnalogIn=P0_14, PinName nLed=LED6, int nBlinkDelayMsec=0, float ftVrefInit=1.91)
{   // Unlike Arduino, mBuino v1.5 has no internal reference voltage for analog reads.
    // As all analog reads are relative to VCC, it would be nice to be able to tell the VCC
    // Additionally knowing the current VCC is critical for battery operated applications.
    
    // Please note that the voltage on the 3v3 pin is always lower than 3.3V as diodes D10 and D11
    // are between the actual VCC of the 3.3V regulator and that of the CR2032 battery.
    // Measured voltages are 2.85V when powered by USB or lower when powered by battery.
    // When mBuino has its LEDs on, or when an external device is using VCC, the voltage will be lower too.

    // The current VCC voltage can be determined by using analogIn to read a known reference voltage.
    // One way to get a reference voltage is by connecting a led via a resistor between VCC and GND.
    // The voltage over the led is (more or less) constant (about 1.7V, depending on the kind of LED).
    // This constant voltage can be measured by using a multimeter.
    // Measured voltages are e.g. 1,77V  for a yellow led, 1,66 for a red led and 1,73 for a green led (and 1K resistor).
    // See http://www.talkingelectronics.com/projects/200TrCcts/200TrCcts.html#52 for more methods.
    //
    // As mBuino already has LEDs we can use them as a reference by connecting P0_14 to the left leg of LED7 or LED6.
    // To avoid potential damage and to allow PWM on LED7 a 100R resistor can be used to make the connection.
    // Note: P0_14 is the tiny dot right above the 1 of the printed version number.
    // See https://developer.mbed.org/platforms/Outrageous-Circuits-mBuino/ for pin-outs and schematics
    //
    AnalogIn ana(nAnalogIn);      // TODO: can be made static for better speed?
    DigitalOut ledBlink(nLed);      // TODO: can be made static for better speed?
    float ftVoltage, ftVref;
    bool fLedStatus;
    
    fLedStatus=ledBlink;   // remember status of the led so we can set it back
    ledBlink=1;   // put a high voltage on LED7 so we can measure it as a reference voltage and use that to calculate the VCC
    if(nBlinkDelayMsec!=0)
        wait_ms(nBlinkDelayMsec);     // allow some time to reach full voltage level (when using no delay the reading can be a bit lower)
    ftVoltage=ana.read();
    ftVref=ftVrefInit;                       // assume reference voltage on ana14 to be 1.91V, e.g. by connecting is via a 100R resistor to the left leg of LED7
#ifdef USBSERIAL_DEBUG
    serialUSB.printf("Vref %1.2f / Vread %1.2f%% ~~> ", ftVref, ftVoltage*100);
#endif

    // Some silly trial and error compensations. Note: this compensation is not very precise!
    // TODO: improve the compensation to get more accurate readings. Now final readings may differ up to 0.10V from 
    // actual values, depending on the reference voltage, length of the blink, resistor and LED used, VCC-level and probably temperature.
    if(!fLedStatus && nBlinkDelayMsec<=10)
        ftVref+=(0.05-nBlinkDelayMsec/200.0); //compensate for lower voltage due to short delays.
    ftVref-=(0.04+(ftVoltage-0.66)/4);   // compensation: Vref is slightly lower on low voltages.

    // Calculate the VCC by dividing the Vref by the analog reading
    ftVoltage=ftVref/ftVoltage;

    ledBlink=fLedStatus;             // restore led7 to its original status
    return(ftVoltage);
}


void ledFlash(uint8_t nLed, float ftDelay=0.005)
{
    if(nLed>6) nLed=6;
    LED[nLed]=!LED[nLed];
    wait(ftDelay);
    LED[nLed]=!LED[nLed];
}

void SweepAllLeds(uint8_t nMaxLeds=7, bool fLeftToRight=true, float flDelay=0.1)
{   // light all leds up from left to rigth or vise-versa and then switch them off from reverse direction
    // leds on, left to right
    if(nMaxLeds>7) nMaxLeds=7;
    for(int n=0; n<nMaxLeds; n++)
    {
        LED[fLeftToRight?n:nMaxLeds-1-n] = 1; // turn on
        wait(flDelay); // delay
    }
    // leds off, right to left
    for(int n=0; n<7; n++)
    {
        LED[!fLeftToRight?n:nMaxLeds-1-n] = 0; // turn off
        wait(flDelay); // delay
    }
}


main()
{
    // Minimal voltage on 3v3 pin is about 1.8V. Below this mBuino may react weird or stop working.
    // Default reference for voltage on mBuino's LEDs is 1.91v, measurement pin is P0_14.
    // Press any key on USB-serial to switch to 1.85v and pin P0_15 for external (green) LED.
    float ftVmin=1.80, ftVled=1.91, ftVcc;
    PinName pinAnalog=P0_14, pinLed=LED6;
    Timer t;

    // small initial delay to show all leds are working and to allow USB connection
    SweepAllLeds();
    SweepAllLeds(7, false,0.02);
    SweepAllLeds();

    // flash a led while waiting for console response as we 
    t.start();
    while(!serialUSB.readable() && (t.read_ms() <= 3000))
    {
        serialUSB.printf("."); 
        ledFlash(0);
        wait(0.1);
    }

    // show status on console and give instructions
    serialUSB.printf("\r\n=== mBuino PowerMeter ====  \r\n");
    serialUSB.printf("Measuring LED pin %d [%1.2fV] using pin %d\r\n", pinLed, ftVled, pinAnalog);
    serialUSB.printf("Hit spacebar to change to LED pin 4 [1.85V] using pin 15.\r\n");

    // perform voltage readings, indicate voltage level and process serial console input
    while(true)
    {
        //ftVCC=readVCC();  // float readVCC(PinName nAnalogIn=P0_14, PinName nLed=LED6, int nBlinkDelayMsec=0, float ftVrefInit=1.91)
        ftVcc=readVCC(pinAnalog, pinLed, 0, ftVled);
        serialUSB.printf("VCC: %1.2fV\r\n", ftVcc);

        // indicate voltage level using all 7 LEDs on mBuino
        if(ftVcc>ftVmin)
        {
            uint8_t uLed=0;
            if(ftVcc>=1.90) uLed=1;
            if(ftVcc>=2.00) uLed=2;
            if(ftVcc>=2.20) uLed=3;
            if(ftVcc>=2.40) uLed=4;
            if(ftVcc>=2.60) uLed=5;
            if(ftVcc>=2.75) uLed=6;
            SweepAllLeds(uLed+1);
            ledFlash(uLed);
            wait(1);
        }
        else
        {   // battery is very low: show rapid flashing
            ledFlash(0);
            wait(0.25);
        }
        
        // process serial console input
        if(serialUSB.readable())
        {   // switch pins when is spacebar is hit
            if(serialUSB.getc()==' ')
            {
               pinLed=(pinLed==P0_4?LED6:P0_4);
               //ftVled=(ftVled!=1.85?1.85f:1.91f);   // hmm, weird bug; no conditional assignment on floats?
               if(ftVled!=1.91f) ftVled=1.91f; else ftVled=1.85f;
               pinAnalog=(pinAnalog==P0_15?P0_14:P0_15);
               serialUSB.printf("Switched to measuring LED pin %d [%1.2fV] using pin %d\r\n", pinLed, ftVled, pinAnalog);
            }
            else
            {
               serialUSB.printf("Hit spacebar to switch between pins (4/15 or LED6/14).\r\n");
            }
        }
    }    
}