Voltage Sensor

Hi there,

I am building a wireless node based on the atmega 328P inspired by maniacbug over here: Low-Power Wireless Sensor Node | maniacbug

It's working and allows me to send data via the rf24+ network he programmed (big thanks!). Most of the nodes run on battery (3V coin cell) so I need a way to monitor the battery. He suggest a voltage divider with 1M ohm and 470k Ohm + a capacitor, which I build into my pcb. Now I am trying to get correct voltage reading from the nodes, but all I get is fast changing numbers which cant be true. Only possible error I found is that I am using a digital pin as an input. I used a wire to an analog pin too though, and it did not seem to help. Is it possible to use the digital pin or do I have to change that?

Here ist my software (based on his examples):

/*
 Copyright (C) 2012 James Coliz, Jr. <maniacbug@ymail.com>

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 version 2 as published by the Free Software Foundation.
 
 Update 2014 - TMRh20
 */

/**
 * Simplest possible example of using RF24Network 
 *
 * TRANSMITTER NODE
 * Every 2 seconds, send a payload to the receiver node.
 */

#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>

RF24 radio(10,9);                    // nRF24L01(+) radio attached using Getting Started board 

RF24Network network(radio);          // Network uses that radio

const uint16_t this_node = 1;        // Address of our node
const uint16_t other_node = 0;       // Address of the other node

const unsigned long interval = 2000; //ms  // How often to send 'hello world to the other unit

unsigned long last_sent;             // When did we last send?
unsigned long packets_sent;          // How many have we sent already

const unsigned voltage_reference = 3.3 * 256; // 3.3V
const int voltage_pin = 5;

struct payload_t {                  // Structure of our payload
  unsigned long ms;
  unsigned long voltage;
};

void setup(void)
{
  Serial.begin(57600);
  Serial.println("RF24Network/examples/helloworld_tx/");
  
    // Sensors use the stable internal 1.1V voltage
#ifdef INTERNAL1V1
  analogReference(INTERNAL1V1);
#else
  analogReference(INTERNAL);
#endif

  pinMode(voltage_pin, INPUT);
  
  SPI.begin();
  radio.begin();
  network.begin(/*channel*/ 90, /*node address*/ this_node);
}

uint32_t measure_voltage()
{
    // Take the voltage reading 
    // Convert the voltage reading to volts*256
    return ( analogRead(voltage_pin) * voltage_reference ); 
}

void loop() {
  
  network.update();                          // Check the network regularly

  
  unsigned long now = millis();              // If it's time to send a message, send it!
  if ( now - last_sent >= interval  )
  {
    last_sent = now;

    Serial.print("Sending...");
    payload_t payload = { millis(), measure_voltage() };
    RF24NetworkHeader header(/*to node*/ other_node);
    bool ok = network.write(header,&payload,sizeof(payload));
    if (ok)
      Serial.println("ok.");
    else
      Serial.println("failed.");
  }
}

I played around with the voltage_reference part and changed it from 5v to 3.3 V (that what the coin cell gives me). I also added the analogReference(INTERNAL); part which I found online. I am somehow lost here because I dont even know if the error is software, hardware or me :wink:

Thanks alot, I knew I overlooked something. I just connected an analog pin and the old pin with a cable and I still get strange reading from the analog pin. Somewhat about 10000. What excactly has to be done with the values? Do you need the
analogRead(voltage_pin) * voltage_reference part really? And what should this voltage_reference be? 5V? 3.3V?
Some help here would be awesome. And/or perhaps a good source where I find stuff like this.

Edit:

Found this great link here explaining some of this: Measuring Voltage with Arduino

I can calculate the dividing factor for my voltage divider but what value do I use here instead of the 5V? The 3.3 from the coin cell? Or still 5V because thats what the arduino is able to use?

You should carefully read through this material, and the links therein analogRead() - Arduino Reference

I read through this and I feel like I understand it now. Still my programm isn't working. I only get 0.0 and 1.1 as an answer. Where's the error?

Sender (shortened)

...

const unsigned long interval = 1000;

unsigned long last_sent;             // When did we last send?
unsigned long packets_sent;          // How many have we sent already

const int voltage_pin = A0;


// number of analog samples to take per reading
#define NUM_SAMPLES 10

void setup(void)
{
  Serial.begin(57600);
  
    // Sensors use the stable internal 1.1V voltage
#ifdef INTERNAL1V1
  analogReference(INTERNAL1V1);
#else
  analogReference(INTERNAL);
#endif
  
  SPI.begin();
  radio.begin();
  network.begin(/*channel*/ 90, /*node address*/ this_node);
}

float measure_voltage()
{
    int sum = 0;                    // sum of samples taken
    unsigned char sample_count = 0; // current sample number
    float voltage = 0.0;            // calculated voltage

    while (sample_count < NUM_SAMPLES) {
        sum += analogRead(voltage_pin);
        sample_count++;
        delay(10);
    }
    // calculate the voltage

    voltage = ((float)sum / (float)NUM_SAMPLES * 1.1) / 1024.0;
// 1.1 volt is the internal reference
// the first part gets the average reading and 1024 is the possible resolution

//1.47 is the voltage divider factor (1M and 470k)
    Serial.print(voltage * 1.47);
    return voltage * 1.47;
    Serial.println (" V");
}

void loop() {
  
  network.update();                          // Check the network regularly

  
  unsigned long now = millis();              // If it's time to send a message, send it!
  if ( now - last_sent >= interval  )
  {
    last_sent = now;

    Serial.print("Sending...");
    unsigned long payload = measure_voltage();
    RF24NetworkHeader header(/*to node*/ other_node);
    bool ok = network.write(header,&payload,sizeof(payload));
    if (ok)
      Serial.println("ok.");
    else
      Serial.println("failed.");
  }
}

The formular should be right as far as I can tell...

receiver:

...

void setup(void)
{
  Serial.begin(57600);
 
  SPI.begin();
  radio.begin();
  network.begin(/*channel*/ 90, /*node address*/ this_node);
}

void loop(void){
  
  network.update();                  // Check the network regularly

  
  while ( network.available() ) {     // Is there anything ready for us?
    
    RF24NetworkHeader header;        // If so, grab it and print it out
    float payload;
    network.read(header,&payload,sizeof(payload));
    Serial.print("Received Voltage #");
    Serial.println(payload);
  }
}

The problem seems to be with the voltage reading. However, your program has a lot of extraneous junk that confuses you and me and everyone else. Start with the very simple Arduino IDE example program ReadAnalogVoltage and get that working to your satisfaction before you move on.

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
}
void loop() {
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float voltage = sensorValue * (5.0 / 1023.0);
  // print out the value you read:
  Serial.println(voltage);
}

Well it's only the communication stuff. But sure I'll try out an easier method. Thing is I have to add the "analogReference(INTERNAL);" part and use 1.1 instead of 5?! Or do I miss something here? Because this "arduino" is running on ~3V not on the normal 5.
Btw what's the best way for a serial connection to my test pcb? I could connect an arduino (rx & tx) but does that not change the outcome (cause of the 5V?)?

At this point you can remove everything and just use the analogread code posted above. Using the 1v1 internal voltage may give you better precision eventually but first get it running with just analogread. If your VCC isn't 5v then you need to switch the 5.0 in

float voltage = sensorValue * (5.0 / 1023.0);

to whatever your VCC is.

Are you sure your circuit is laid out as listed in that article? Is your voltage divider actually dividing? Confirmation with a multimeter would be useful

So, if you need more accurate measurements, you can :

More simple :

  • Set the reference to internal
  • With a voltmeter measure the voltage at the pin Aref : it is exactly the same that Vref
    See datasheet, for Atmege 328P page 244 (last version Atmel-8271H-AVR 08 2014)

24.5.2 ADC Voltage Reference
The reference voltage for the ADC (VREF) indicates the conversion range for the ADC. Single ended channels that exceed VREF will result in codes close to 0x3FF. VREF can be selected as either AVCC, internal 1.1V reference, or external AREF pin. AVCC is connected to the ADC through a passive switch. The internal 1.1V reference is generated from the internal bandgap reference (VBG) through an internal amplifier. In either case, the external AREF pin is directly connected to the ADC, and the reference voltage can be made more immune to noise by connecting a capacitor between the AREF pin and ground. VREF can also be measured at the AREF pin with a high impedance voltmeter. Note that VREF is a high impedance source, and only a capacitive load should be connected in a system. If the user has a fixed voltage source connected to the AREF pin, the user may not use the other reference voltage options in the application, as they will be shorted to the external voltage. If no external voltage is applied to the AREF pin, the user may switch between AVCC and 1.1V as reference selection. The first ADC conversion
result after switching reference voltage source may be inaccurate, and the user is advised to discard this result.

Made the correction in the code.

  • connect the MCU to a serial monitor
  • type in the measured voltage.

???????
You don't need a serial monitor nor other things

When your ADC reference is set to INTERNAL, just measure the voltage between GND and Aref, it's all.
A voltage reference is sure the best but you have to wire extra components : voltage reference with decoupling capacitor and often a resistor. You can improve drasticaly your measurement by using my method without any extra component.
You just have a multimeter but this unit is mandatory with our activity and a model which coast less than 10$ is sufficient .

I never program to release batches of 10 000 unit :grin:

Thanks again guys! With your help I got it to work and already build in the energy saving features I needed. Now I have a wierd (hopefully) last problem. Everything works fine while I send messages from node 1 to node 0. Now I wanted to start sending from 11 to 1 to 0 and I can't get this to work. I know these adresses are octal but afaik 0 - 01 - 011 - ... should be the right values?!
Or am I wrong here? Is there another setting you need?

(still using maniacbugs RF24Network) here is my sender code. I am only changing
const uint16_t this_node = 01;        // Address of our node to 011 on the second platform.

/*
 Copyright (C) 2012 James Coliz, Jr. <maniacbug@ymail.com>

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 version 2 as published by the Free Software Foundation.
 
 Update 2014 - TMRh20
 */

/**
 * Simplest possible example of using RF24Network 
 *
 * TRANSMITTER NODE
 * Every 2 seconds, send a payload to the receiver node.
 */

#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#include <LowPower.h>

RF24 radio(10,9);                    // nRF24L01(+) radio attached using Getting Started board 

RF24Network network(radio);          // Network uses that radio

const uint16_t channel = 90;         // Channel we use
const uint16_t this_node = 01;        // Address of our node
const uint16_t other_node = 0;       // Address of the other node

const unsigned long interval = 1000; //ms  // How often to send 'hello world to the other unit

unsigned long last_sent;             // When did we last send?
unsigned long packets_sent;          // How many have we sent already

const int voltage_pin = A0;
const float voltage_reference = 1.1;
const float voltage_divider_factor = 3.13;

// number of analog samples to take per reading
#define NUM_SAMPLES 10

void setup(void)
{
  
  // Sensors use the stable internal 1.1V voltage
  #ifdef INTERNAL1V1
    analogReference(INTERNAL1V1);
  #else
    analogReference(INTERNAL);
  #endif
  
  SPI.begin();
  radio.begin();
  network.begin(/*channel*/ channel, /*node address*/ this_node);
  radio.setDataRate(RF24_250KBPS);
}

float measure_voltage()
{
  int sum = 0;                    // sum of samples taken
  unsigned char sample_count = 0; // current sample number

  while (sample_count < NUM_SAMPLES) {
      sum += analogRead(voltage_pin);
      sample_count++;
      delay(10);
  }
  
  // average value
  int raw = sum / NUM_SAMPLES;
  // value at pin (1024 = possible resolution of the atmega328P
  float voltage_at_pin = (voltage_reference / 1024) * raw;
  float real_voltage = voltage_at_pin * voltage_divider_factor;
  
  return real_voltage;
}

void loop() {
  
  network.update();                          // Check the network regularly

  if (millis() - last_sent > interval) {
    last_sent = millis();
    float voltage = measure_voltage(); 
    radio.powerUp();   
    RF24NetworkHeader header(/*to node*/ other_node);
    bool ok = network.write(header,&voltage,sizeof(voltage));
    radio.powerDown();
  }
  
  //LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);
}

This has not much to do with the former topic of this thread so I made a new one here:
http://forum.arduino.cc/index.php?topic=272751.0