Introduction: 1S-6S Battery Voltage Monitor (ROS)

About: Firmware Engineer / Hobbiest

The ability to remotely monitor the health of a battery provides the user with the convenience of knowing when it is time to bring their drone back for recharging. Currently, there are devices on the market such as the Hobby King LiPo Alarm or the more common 3DR Power Module used for drones/UAVs. However, neither product allows for real time monitoring of individual cell health over a serial connection. Not to mention that the 3DR PM cannot handle more than a 4S battery. This project will show how to construct a serial enabled power monitoring system that publishes messages over rosserial to a ROS master.

Note: This chip can be expanded to an 18S Cell LiPo battery by using three 8:1 analog multiplexers (MAX4617).

Step 1: Materials Required

Materials:

Tools:

  • Volt Meter
  • Soldering Iron
  • Solder
  • Heat Gun
  • Snips
  • Knife

Step 2: Understanding the Circuit

Since the Arduino analog pins operate in the 0-5V range and the voltage of a Lithium Polymer battery is compounding voltage with respect to ground as you advance pins, we need to use a voltage step down circuit using resistors. The equation for this circuit goes as follows:

Vout = (Vin * R2 ) / ( R1 + R2 )

Where,

Vin --> Supply Voltage

R1 --> Resistor One

R2 --> Resistor Two

Vout --> Scaled Voltage

By adjusting the ratio between R1 and R2 you can control the output voltage. Knowing that each consecutive pin will provide approximately ~3.7V more than the pin before it, you can design for the required voltage inputs to ensure that the Arduino pins will always get the required 0-5V. A demonstration design chart is HERE.

Step 3: Building & Testing the Circuit

Following the resistor values from the wiring schematic above, plug in the resistors on a bread board so they all share a common ground. Before wiring any of the wires to the Arduino Nano, use the multimeter to check that all the voltages fall within 0-5V. If not, check your circuit and repeat.

Step 4: Programming the Arduino

This tutorial assumes that you already have ROS installed and running on your system. If not, follow these links to get your system up to date:

Install from Source (Recommended)
Navigate to your ROS package directory (usually called your Sandbox) and create a new package for this project.

$ git clone git@github.com:djiglesias/ros-utilities.git
$ cd ros-utilities/

Navigate into the new package and clone the project from GitHub or download it HERE.

$ cd ros_battery_monitor/
$ make

That's it! Now let's upload the code to the Arduino. Open ros-utilities/ros_battery_monitor/src/main/main.ino in your Arduino IDE and select the board type and port. Click the check mark to verify that you have all required dependencies and upload to the board!

Install Manually
This code is an adaptation of the Hello World (publisher) example and this post from RC Groups. For this example, the cell_const[] are drawn from the scaling factors on the chart above.

#include <ros.h>
#include <sensor_msgs/BatteryState.h>

#define K           0.00472199
#define CELLS       6
#define MAX_CELLS   12

double cell_const[MAX_CELLS] = 
{
  1.0000,
  2.1915,
  2.6970,
  4.1111,
  4.7333,
  6.6000,
  6.6000,
  7.8293,
  8.4667,
  9.2353,
  11.0000,
  11.0000
};

ros::NodeHandle nh;
sensor_msgs::BatteryState batt_state;
ros::Publisher batteryState("battery_state", &batt_state);

void setup() 
{
  // Initialize the ROS node.
  nh.initNode();
  nh.advertise(batteryState);

  // Populate battery parameters.
  batt_state.design_capacity = 2200;      // mAh
  batt_state.power_supply_status = 2;     // discharging
  batt_state.power_supply_health = 0;     // unknown
  batt_state.power_supply_technology = 3; // LiPo
  batt_state.present = 1;                 // battery present

  batt_state.location = "Crawler";        // unit location
  batt_state.serial_number = "ABC_0001";  // unit serial number
  batt_state.cell_voltage = new float[CELLS];
  
}

void loop() 
{<br>  // Battery status.
  double battVoltage = 0.0;
  double prevVoltage = 0.0;
  
  // Populate battery state message.
  for (int i = 0; i < CELLS; i++)
  {
    // Read raw voltage from analog pin.
    double cellVoltage = analogRead(i) * K;
    
    // Scale reading to full voltage.
    cellVoltage *= cell_const[i];
    double tmp = cellVoltage;
    
    // Isolate current cell voltage.
    cellVoltage -= prevVoltage;
    battVoltage += cellVoltage;
    prevVoltage = tmp;

    // Set current cell voltage to message.
    batt_state.cell_voltage[i] = (float)cellVoltage;

    // Check if battery is attached.
    if (batt_state.cell_voltage[i] >= 2.0)
      batt_state.present = 1;
    else
      batt_state.present = 0;
  }

  // Update battery health.
  batt_state.voltage = (float)battVoltage;

  if (batt_state.voltage > CELLS * 4.2)
    batt_state.power_supply_health = 4; // overvoltage
  else if (batt_state.voltage < CELLS * 3.0)
    batt_state.power_supply_health = 3; // dead
  else
    batt_state.power_supply_health = 1; // good

  // Publish data to ROSSERIAL.
  batteryState.publish( &batt_state );
  nh.spinOnce();
  delay(1000);
  
}

Step 5: Reading From the Arduino

Installed from Source
If you install this project from GitHub, then you can run the following command to launch the battery monitor.

$ roslaunch ros_battery_monitor battery_monitor.launch &
$ rostopic echo /battery/info

Manual Installation

Once the code is uploaded to the Arduino, plug it into your computer (Linux system) without the battery. If not already running, instantiate an instance of ROS master.

$ roscore &

Run the serial_node.py node from the rosserial_python package passing in the appropriate USB port as an argument.

$ rosrun rosserial_python serial_node.py /dev/ttyUSB0 &

If you received errors warning about insufficient permissions, then run the following commands and try again.

$ sudo usermod -a -G dialout client
$ sudo chmod a+rw /dev/ttyUSB0

Now, to view the incoming data from the Arduino you can simply run the echo command to display all new messages being published on /battery_state.

$ rostopic echo /battery/info

At first, the messages should come in stating 0.00V and Present = False for the battery. Once you plug the battery in, these fields will populate accordingly. For more information on the sensor_msgs::BatteryState, please view the documentation.

Note: I have not included diodes, so if you plug the battery in the wrong way there is a chance you will fry your Arduino and entire computer.

Step 6: Assembling the Circuit

Once your circuit is confirmed working, you should mount it to a perforation board to make it a more permanent solution. My project has limited space so I made the board as small as I could without making a PCB board. Notice the common ground rail on the underside of the board, this allowed me to keep the board clean and organized.

Note for Improvement:

  • Use diodes to make the board polarity protected.
  • Use the proper JST socket to prevent short circuiting.

Step 7: Final Touches

To prevent short circuiting, use 3 inch heat shrink tubing to encase the circuit board. Label the polarity of the pinout accordingly, and cut a hole for the USB socket.

Boom! You are done. Plug it into your ROS Robot and enjoy!