Skip to content

Commit d6d21ea

Browse files
committed
Smoke testing release - But should work :-)
1 parent b26f49e commit d6d21ea

File tree

13 files changed

+490
-0
lines changed

13 files changed

+490
-0
lines changed

README.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,103 @@
11
# pet_ros2_currentsensor_ina219_pkg
22
ROS2-publisher for the Current/Voltage sensor INA219. Publish measurement as ROS2-topics.
3+
4+
**Input:** Current&Voltage shount on a I2C-INA219 sensor-breakout-board. \
5+
**Output:** ROS node (ROS2) that publish topics with voltage & current values.
6+
7+
<table>
8+
<td>
9+
<img src="./doc/pet_ros2_currentsensor(INA219)_wiring.png" height="350px">
10+
</td>
11+
<td>
12+
..
13+
</td>
14+
</table>
15+
16+
# ROS2 Package/Module Behaviour
17+
18+
# Prerequisite: Hardware
19+
* Single Board Computer(SBC): Raspberry Pi 3/4
20+
* Sensor: INA2129 Current Sensor via default I2C adr.=<code>0x4o</code>
21+
22+
# Prerequisite: Software
23+
* Ubuntu 20.04 (64bit) or newer
24+
* Robot Operating System 2, ROS2 (Version Galathic)
25+
<blockquote>...do the ROS2-installation stuff...</blockquote>
26+
27+
## Prerequisite: I2C-interface Raspberry Pi 4 / Ubuntu
28+
Prepared by adding additional, i2c communication, Linux-software-packages <br/>
29+
`Ubuntu Shell`
30+
```
31+
~$ sudo apt install i2c-tools
32+
~$ sudo apt install python3-pip
33+
~$ sudo pip3 install adafruit-blinka
34+
~$ sudo pip3 install adafruit-circuitpython-ina219
35+
~$ sudo i2cdetect -y 1
36+
0 1 2 3 4 5 6 7 8 9 a b c d e f
37+
00: -- -- -- -- -- -- -- -- -- -- -- -- --
38+
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
39+
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40+
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
41+
40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
42+
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
43+
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
44+
70: -- -- -- -- -- -- -- --
45+
$ sudo chmod a+rw /dev/i2c-1
46+
```
47+
48+
## Dowload and install this ROS2 packages
49+
Create a ROS2 workspace (in my exampel '~/ws_ros2/') \
50+
Dowload ROS2 package by using 'git clone'
51+
<ul><blockquote>🤔There is probably better tutorials how to do this...<br/>
52+
...but here is how I made it.<br/>
53+
</blockquote></ul>
54+
55+
`Ubuntu Shell`
56+
```
57+
~$ mkdir -p ~/ws_ros2/src
58+
~$ cd ~/ws_ros2/src
59+
~/ws_ros2/src$ git clone https://github.com/Pet-Series/pet_ros2_currentsensor_ina219_pkg.git
60+
~/ws_ros2/src$ cd ..
61+
~/ws_ros2$ colcon build --symlink-install
62+
~/ws_ros2$ source /opt/ros/galactic/setup.bash
63+
~/ws_ros2$ source ./install/setup.bash
64+
```
65+
66+
# ROS2 Launch sequence
67+
`Ubuntu Shell #1`
68+
```
69+
$ ros2 run pet_ros2_battery_state_pkg pet_battery_state_ina219_node
70+
[INFO] [1649019010.401689937] [pet_current_sensor_node]: INA219 Current/Voltage sensor. Config register:
71+
[INFO] [1649019010.404738606] [pet_current_sensor_node]: - bus_voltage_range: 0x1
72+
[INFO] [1649019010.407764240] [pet_current_sensor_node]: - gain: 0x3
73+
[INFO] [1649019010.410825520] [pet_current_sensor_node]: - bus_adc_resolution: 0x3
74+
[INFO] [1649019010.413920782] [pet_current_sensor_node]: - shunt_adc_resolution: 0x3
75+
[INFO] [1649019010.417161487] [pet_current_sensor_node]: - mode: 0x7
76+
[INFO] [1649019010.420058696] [pet_current_sensor_node]: ....
77+
```
78+
79+
`Ubuntu Shell #2`
80+
```
81+
$ ros2 topic echo /battery_status
82+
header:
83+
stamp:
84+
sec: 1649019055
85+
nanosec: 523441553
86+
frame_id: 18650 3S x1P main battery
87+
voltage: 11.143999981880188
88+
temperature: .nan
89+
current: 0.820000000040233135
90+
charge: .nan
91+
capacity: .nan
92+
design_capacity: .nan
93+
percentage: .nan
94+
power_supply_status: 0
95+
power_supply_health: 0
96+
power_supply_technology: 2
97+
present: true
98+
cell_voltage: []
99+
cell_temperature: []
100+
location: Think "Inside the box"
101+
serial_number: '0000000'
102+
---
103+
```
3.39 MB
Loading

package.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0"?>
2+
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
3+
<package format="3">
4+
<name>pet_ros2_battery_state_pkg</name>
5+
<version>0.0.1</version>
6+
<description>ROS2-publisher for the Current/Voltage sensor INA219. Publish measurement as ROS2-topics.</description>
7+
<maintainer email="stefan.kull@gmail.com">pi</maintainer>
8+
<license>MIT</license>
9+
10+
<test_depend>ament_copyright</test_depend>
11+
<test_depend>ament_flake8</test_depend>
12+
<test_depend>ament_pep257</test_depend>
13+
<test_depend>python3-pytest</test_depend>
14+
15+
<export>
16+
<build_type>ament_python</build_type>
17+
</export>
18+
</package>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
2+
# SPDX-License-Identifier: MIT
3+
#
4+
# $ sudo i2cdetect -y 1
5+
# 0 1 2 3 4 5 6 7 8 9 a b c d e f
6+
# 00: -- -- -- -- -- -- -- --
7+
# 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
8+
# 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
9+
# 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10+
# 40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
11+
# 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
12+
# 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
13+
# 70: -- -- -- -- -- -- -- --
14+
# $
15+
# $ sudo pip3 install adafruit-circuitpython-ina219
16+
# $ sudo pip3 install adafruit-blinka
17+
#
18+
# $ sudo python3 INA219.py
19+
# $ python3 INA219.py
20+
#
21+
"""Sample code and test for adafruit_ina219"""
22+
23+
import time
24+
import board
25+
from adafruit_ina219 import ADCResolution, BusVoltageRange, INA219
26+
27+
28+
i2c_bus = board.I2C()
29+
30+
ina219 = INA219(i2c_bus)
31+
32+
print("ina219 test")
33+
34+
# display some of the advanced field (just to test)
35+
print("Config register:")
36+
print(" bus_voltage_range: 0x%1X" % ina219.bus_voltage_range)
37+
print(" gain: 0x%1X" % ina219.gain)
38+
print(" bus_adc_resolution: 0x%1X" % ina219.bus_adc_resolution)
39+
print(" shunt_adc_resolution: 0x%1X" % ina219.shunt_adc_resolution)
40+
print(" mode: 0x%1X" % ina219.mode)
41+
print("")
42+
43+
# optional : change configuration to use 32 samples averaging for both bus voltage and shunt voltage
44+
ina219.bus_adc_resolution = ADCResolution.ADCRES_12BIT_32S
45+
ina219.shunt_adc_resolution = ADCResolution.ADCRES_12BIT_32S
46+
47+
# optional : change voltage range to 16V
48+
ina219.bus_voltage_range = BusVoltageRange.RANGE_16V
49+
50+
# measure and display loop
51+
while True:
52+
bus_voltage = ina219.bus_voltage # voltage on V- (load side)
53+
shunt_voltage = ina219.shunt_voltage # voltage between V+ and V- across the shunt
54+
current = ina219.current # current in mA
55+
power = ina219.power # power in watts
56+
57+
# INA219 measure bus voltage on the load side. So PSU voltage = bus_voltage + shunt_voltage
58+
print("Voltage (VIN+) : {:6.3f} V".format(bus_voltage + shunt_voltage))
59+
print("Voltage (VIN-) : {:6.3f} V".format(bus_voltage))
60+
print("Shunt Voltage : {:8.5f} V".format(shunt_voltage))
61+
print("Shunt Current : {:7.4f} A".format(current / 1000))
62+
print("Power Calc. : {:8.5f} W".format(bus_voltage * (current / 1000)))
63+
print("Power Register : {:6.3f} W".format(power))
64+
print("")
65+
66+
# Check internal calculations haven't overflowed (doesn't detect ADC overflows)
67+
if ina219.overflow:
68+
print("Internal Math Overflow Detected!")
69+
print("")
70+
71+
time.sleep(2)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env python3'
2+
# coding = utf-8
3+
####################################################################################
4+
# System test for Adafruit BLINKA.
5+
# Make sure that all Python3 package are installed and working.
6+
# Adjust access rights for the I2C and/or GPIO-pins.
7+
#
8+
# ---- install
9+
# $ sudo apt-get install python3-pip
10+
# $ sudo apt-get install i2c-tools
11+
# $ sudo apt-get install libgpiod-dev
12+
# $ sudo apt-get install RPi.GPIO
13+
# $ sudo pip3 install board
14+
# $ sudo pip3 install smbus2
15+
# $ sudo apt-get install adafruit-blinka
16+
# ($ sudo pip3 install --force-reinstall adafruit-blinka )
17+
#
18+
# ---- Access to i2c and GPIO
19+
# $ sudo chmod a+rw /dev/i2c-1
20+
# $ sudo groupadd i2c
21+
# $ sudo usermod -aG i2c pi
22+
# $ sudo usermod -a -G gpio pi
23+
#
24+
# ---- Run the script
25+
# $ sudo python3 blinkatest.py
26+
# $ python3 blinkatest.py # Might work...
27+
#
28+
import board
29+
import digitalio
30+
import busio
31+
32+
print("Hello blinka!")
33+
34+
# Try to create a Digital GPIO-input object.
35+
pin = digitalio.DigitalInOut(board.D4)
36+
print("Digital IO ok!")
37+
38+
# Try to create an I2C object.
39+
i2c = busio.I2C(board.SCL, board.SDA)
40+
print("I2C ok!")
41+
42+
# Try to create an SPI object.
43+
spi = busio.SPI(board.SCLK, board.MOSI, board.MISO)
44+
print("SPI ok!")
45+
46+
print("done!")

pet_ros2_battery_state_pkg/__init__.py

Whitespace-only changes.
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#!/usr/bin/env python3'
2+
# coding = utf-8
3+
########################################################################################
4+
##
5+
## Maintainer: stefan.kull@gmail.com
6+
##
7+
## Input: INA219 Current/Voltage-sensor (a.k.a "Battery State")
8+
## Output: ROS2 node that publish a BatteryState.msg topic
9+
##
10+
## Prerequisite:
11+
## Software
12+
## $
13+
## $ sudo pip3 install adafruit-circuitpython-ina219
14+
##
15+
## Hardware: Power circuit via INA219 break aout board (Power at VIn+, Drain/Source at VIn- )
16+
## Host: Raspberry Pi 4(Ubuntu) via I2C
17+
##
18+
## Launch sequence:
19+
## 1) $ ros2 run pet_ros2_currentsensor_ina219_pkg pet_current_sensor_ina219_node.py
20+
## 2) $ ros2 topic echo /xyz
21+
## $ ros2 topic echo /zyx
22+
##
23+
24+
# Import the ROS2-stuff
25+
import rclpy
26+
from rclpy.node import Node
27+
from sensor_msgs.msg import BatteryState
28+
29+
# Import the Ubuntu/Linux-hardware stuff
30+
import time
31+
import board
32+
from adafruit_ina219 import ADCResolution, BusVoltageRange, INA219
33+
34+
# Import the common Ubuntu/Linux stuff
35+
import sys
36+
from time import sleep
37+
from math import modf
38+
39+
#Set Button pin(GPIO no.) and ROS2-topic-name
40+
BATTERY_STATE_TOPIC = 'battery_state'
41+
42+
43+
class BatteryStatePublisher(Node):
44+
'''
45+
ROS2 current & voltage sensor publisher node
46+
Create a BatteryStatePublisher class, which is a subclass of the Node class.
47+
The class publishes the battery state of an object at a specific time interval.
48+
'''
49+
def __init__(self):
50+
# Initiate the Node class's constructor and give it a name
51+
super().__init__("pet_current_sensor_node")
52+
53+
i2c_bus = board.I2C()
54+
self.ina219 = INA219(i2c_bus)
55+
56+
# optional : change configuration to use 32 samples averaging for both bus voltage and shunt voltage
57+
self.ina219.bus_adc_resolution = ADCResolution.ADCRES_12BIT_32S
58+
self.ina219.shunt_adc_resolution = ADCResolution.ADCRES_12BIT_32S
59+
60+
# optional : change voltage range to 16V
61+
self.ina219.bus_voltage_range = BusVoltageRange.RANGE_16V
62+
63+
# display some of the advanced field (just to test)
64+
self.get_logger().info("INA219 Current/Voltage sensor. Config register:")
65+
self.get_logger().info(" - bus_voltage_range: 0x%1X" % self.ina219.bus_voltage_range)
66+
self.get_logger().info(" - gain: 0x%1X" % self.ina219.gain)
67+
self.get_logger().info(" - bus_adc_resolution: 0x%1X" % self.ina219.bus_adc_resolution)
68+
self.get_logger().info(" - shunt_adc_resolution: 0x%1X" % self.ina219.shunt_adc_resolution)
69+
self.get_logger().info(" - mode: 0x%1X" % self.ina219.mode)
70+
self.get_logger().info("")
71+
72+
# Create Message <https://github.com/ros2/common_interfaces/blob/master/sensor_msgs/msg/BatteryState.msg>
73+
current_time = modf(time.time())
74+
self.msg_battery = BatteryState()
75+
self.msg_battery.header.stamp.sec = int(current_time[1])
76+
self.msg_battery.header.stamp.nanosec = int(current_time[0] * 1000000000) & 0xffffffff
77+
self.msg_battery.header.frame_id = "18650 3S x1P main battery"
78+
79+
self.msg_battery.voltage = float('NaN') # Voltage in Volts (Mandatory)
80+
self.msg_battery.current = float('NaN') # Negative when discharging (A) (If unmeasured NaN)
81+
self.msg_battery.temperature = float('NaN') # Temperature in Degrees Celsius (If unmeasured NaN)
82+
self.msg_battery.charge = float('NaN') # Current charge in Ah (If unmeasured NaN)
83+
self.msg_battery.capacity = float('NaN') # Capacity in Ah (last full capacity) (If unmeasured NaN)
84+
self.msg_battery.design_capacity = float('NaN') # Capacity in Ah (design capacity) (If unmeasured NaN)
85+
self.msg_battery.percentage = float('NaN') # Charge percentage on 0 to 1 range (If unmeasured NaN
86+
87+
self.msg_battery.power_supply_status = 0 # The charging status as reported. [uint8 POWER_SUPPLY_STATUS_UNKNOWN = 0]
88+
self.msg_battery.power_supply_health = 0 # The battery health metric. [uint8 POWER_SUPPLY_HEALTH_UNKNOWN = 0]
89+
self.msg_battery.power_supply_technology = 2 # The battery chemistry. [uint8 POWER_SUPPLY_TECHNOLOGY_LION = 2]
90+
self.msg_battery.present = True # True if the battery is present
91+
92+
self.msg_battery.location = 'Think "Inside the box"' # The location into which the battery is inserted. (slot number or plug)
93+
self.msg_battery.serial_number = '0000000' # The best approximation of the battery serial number
94+
95+
# Create publisher(s)
96+
self.publisher_battery_state = self.create_publisher(BatteryState, '/battery_status', 10)
97+
98+
# Setup time interval in seconds... for the callback
99+
timer_period = 5.0
100+
self.timer = self.create_timer(timer_period, self.get_battery_state_callback)
101+
102+
# print("----------------------------------------")
103+
# print(self.msg_battery)
104+
# print("----------------------------------------")
105+
106+
107+
108+
def get_battery_state_callback(self):
109+
"""
110+
Callback function.
111+
This function gets called at the specific time interval.
112+
"""
113+
# Update the message header
114+
current_time = modf(time.time())
115+
self.msg_battery.header.stamp.sec = int(current_time[1])
116+
self.msg_battery.header.stamp.nanosec = int(current_time[0] * 1000000000) & 0xffffffff
117+
118+
self.msg_battery.voltage = self.ina219.bus_voltage # voltage on V- (load side)
119+
self.msg_battery.current = self.ina219.current /1000.0 # current in mA->A
120+
# print(self.msg_battery.voltage)
121+
122+
# Publish BatteryState message
123+
self.publisher_battery_state.publish(self.msg_battery)
124+
125+
126+
def main(args=None):
127+
rclpy.init(args=args)
128+
129+
# Create the node
130+
battery_state_pub = BatteryStatePublisher()
131+
132+
try:
133+
# Spin the node so the callback function is called.
134+
# Publish any pending messages to the topics.
135+
rclpy.spin(battery_state_pub)
136+
137+
except KeyboardInterrupt:
138+
print("**** * 💀 Ctrl-C detected...")
139+
140+
finally:
141+
print("**** 🪦 battery_state_pub ending... ")
142+
print( str(sys.exc_info()[1]) ) # Need ´import sys´
143+
144+
# Time to clean up stuff... - Destroy the node explicitly
145+
# (optional - otherwise it will be done automatically
146+
# when the garbage collector destroys the node object)
147+
battery_state_pub.destroy_node()
148+
149+
# Time to clean up stuff... Shutdown the ROS client library for Python
150+
rclpy.shutdown()
151+
152+
if __name__ == "__main__":
153+
main()

resource/pet_ros2_battery_state_pkg

Whitespace-only changes.

0 commit comments

Comments
 (0)