ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ina219.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file ina219.cpp
6 * @brief TI INA219 current / power monitor codec - implementation. See ina219.h.
7 */
8
10#include "protocore_config.h"
11
12#if PC_ENABLE_INA219
13
14#if defined(ARDUINO)
16#include <Arduino.h>
17#include <Wire.h>
18#endif
19int32_t pc_ina219_bus_mv(uint16_t raw)
20{
21 return (int32_t)((raw >> 3) * 4); // value in bits [15:3], LSB 4 mV
22}
23
24int32_t pc_ina219_shunt_uv(int16_t raw)
25{
26 return (int32_t)raw * 10; // LSB 10 uV, signed
27}
28
29uint16_t pc_ina219_calibration(uint32_t current_lsb_ua, uint32_t shunt_mohm)
30{
31 uint32_t denom = current_lsb_ua * shunt_mohm;
32 if (denom == 0)
33 {
34 return 0;
35 }
36 // 0.04096 / (lsb[A] * R[ohm]) = 40960000 / (lsb_ua * shunt_mohm).
37 uint32_t cal = 40960000u / denom;
38 return (uint16_t)(cal > 0xFFFF ? 0xFFFF : cal);
39}
40
41int32_t pc_ina219_current_ua(int16_t raw, uint32_t current_lsb_ua)
42{
43 return (int32_t)((int64_t)raw * current_lsb_ua);
44}
45
46int32_t pc_ina219_power_uw(int16_t raw, uint32_t current_lsb_ua)
47{
48 return (int32_t)((int64_t)raw * 20 * current_lsb_ua); // power LSB = 20 * current LSB
49}
50
51// ---------------------------------------------------------------------------
52// I2C binding
53// ---------------------------------------------------------------------------
54
55#if defined(ARDUINO)
56
57namespace
58{
59// All INA219 I2C-binding state, owned by one instance (internal linkage): the device address
60// and the current LSB, grouped so it is one named owner, unreachable from any other TU.
61struct Ina219Ctx
62{
63 uint8_t addr = PC_INA219_I2C_ADDR;
64 uint32_t lsb_ua = PC_INA219_CURRENT_LSB_UA;
65};
66Ina219Ctx s_ina;
67
68bool wr16(uint8_t reg, uint16_t v)
69{
70 Wire.beginTransmission(s_ina.addr);
71 Wire.write(reg);
72 Wire.write((uint8_t)(v >> 8)); // INA219 registers are big-endian
73 Wire.write((uint8_t)(v & 0xFF));
74 return Wire.endTransmission() == 0;
75}
76
77bool rd16(uint8_t reg, uint16_t *v)
78{
79 Wire.beginTransmission(s_ina.addr);
80 Wire.write(reg);
81 if (Wire.endTransmission(false) != 0)
82 {
83 return false;
84 }
85 if (Wire.requestFrom((int)s_ina.addr, 2) != 2)
86 {
87 return false;
88 }
89 uint8_t hi = (uint8_t)Wire.read();
90 uint8_t lo = (uint8_t)Wire.read();
91 *v = (uint16_t)(((uint16_t)hi << 8) | lo);
92 return true;
93}
94} // namespace
95
96bool pc_ina219_begin(uint8_t addr, uint32_t current_lsb_ua, uint32_t shunt_mohm)
97{
98 s_ina.addr = addr ? addr : (uint8_t)PC_INA219_I2C_ADDR;
99 s_ina.lsb_ua = current_lsb_ua ? current_lsb_ua : (uint32_t)PC_INA219_CURRENT_LSB_UA;
100 pc_i2c_begin();
101 bool ok = true;
102 ok &= wr16(INA219_REG_CALIBRATION,
103 pc_ina219_calibration(s_ina.lsb_ua, shunt_mohm ? shunt_mohm : (uint32_t)PC_INA219_SHUNT_MOHM));
104 ok &= wr16(INA219_REG_CONFIG, 0x399F); // 32 V range, /8 gain (320 mV), 12-bit, continuous
105 return ok;
106}
107
108bool pc_ina219_read_bus_mv(int32_t *millivolts)
109{
110 uint16_t v = 0;
111 if (!rd16(INA219_REG_BUS, &v))
112 {
113 return false;
114 }
115 if (millivolts)
116 {
117 *millivolts = pc_ina219_bus_mv(v);
118 }
119 return true;
120}
121
122bool pc_ina219_read_shunt_uv(int32_t *microvolts)
123{
124 uint16_t v = 0;
125 if (!rd16(INA219_REG_SHUNT, &v))
126 {
127 return false;
128 }
129 if (microvolts)
130 {
131 *microvolts = pc_ina219_shunt_uv((int16_t)v);
132 }
133 return true;
134}
135
136bool pc_ina219_read_current_ua(int32_t *microamps)
137{
138 uint16_t v = 0;
139 if (!rd16(INA219_REG_CURRENT, &v))
140 {
141 return false;
142 }
143 if (microamps)
144 {
145 *microamps = pc_ina219_current_ua((int16_t)v, s_ina.lsb_ua);
146 }
147 return true;
148}
149
150bool pc_ina219_read_power_uw(int32_t *microwatts)
151{
152 uint16_t v = 0;
153 if (!rd16(INA219_REG_POWER, &v))
154 {
155 return false;
156 }
157 if (microwatts)
158 {
159 *microwatts = pc_ina219_power_uw((int16_t)v, s_ina.lsb_ua);
160 }
161 return true;
162}
163
164#else // host build: no I2C. The decode / calibration / scaling above are host-tested.
165
166bool pc_ina219_begin(uint8_t, uint32_t, uint32_t)
167{
168 return false;
169}
170bool pc_ina219_read_bus_mv(int32_t *)
171{
172 return false;
173}
174bool pc_ina219_read_shunt_uv(int32_t *)
175{
176 return false;
177}
178bool pc_ina219_read_current_ua(int32_t *)
179{
180 return false;
181}
182bool pc_ina219_read_power_uw(int32_t *)
183{
184 return false;
185}
186
187#endif // ARDUINO
188
189#endif // PC_ENABLE_INA219
The one owner of the shared I2C bus bring-up for the peripheral drivers.
void pc_i2c_begin()
Bring up the shared I2C bus on PC_I2C_SDA_PIN / PC_I2C_SCL_PIN (-1 = default).
Definition i2c.h:33
TI INA219 high-side current / power monitor codec (PC_ENABLE_INA219).
int32_t pc_ina219_power_uw(int16_t raw, uint32_t current_lsb_ua)
Scale the raw power register to microwatts (power LSB is 20 * current LSB).
bool pc_ina219_read_shunt_uv(int32_t *microvolts)
Read the shunt voltage into microvolts.
#define INA219_REG_SHUNT
shunt voltage
Definition ina219.h:31
bool pc_ina219_begin(uint8_t addr, uint32_t current_lsb_ua, uint32_t shunt_mohm)
Program the INA219 at addr: write the calibration for current_lsb_ua (uA/bit) and shunt_mohm (millioh...
uint16_t pc_ina219_calibration(uint32_t current_lsb_ua, uint32_t shunt_mohm)
Compute the calibration register from the current LSB (microamps per bit) and the shunt resistance (m...
#define INA219_REG_CALIBRATION
calibration
Definition ina219.h:35
int32_t pc_ina219_current_ua(int16_t raw, uint32_t current_lsb_ua)
Scale the raw current register to microamps (raw * current_lsb_ua).
int32_t pc_ina219_shunt_uv(int16_t raw)
Decode the shunt-voltage register to microvolts (signed, LSB 10 uV).
#define INA219_REG_CONFIG
configuration
Definition ina219.h:30
bool pc_ina219_read_bus_mv(int32_t *millivolts)
Read the bus voltage into millivolts.
bool pc_ina219_read_current_ua(int32_t *microamps)
Read the current into microamps (needs the calibration set by pc_ina219_begin).
#define INA219_REG_BUS
bus voltage
Definition ina219.h:32
int32_t pc_ina219_bus_mv(uint16_t raw)
Decode the bus-voltage register to millivolts (value is bits [15:3], LSB 4 mV).
bool pc_ina219_read_power_uw(int32_t *microwatts)
Read the power into microwatts (needs the calibration set by pc_ina219_begin).
#define INA219_REG_POWER
power
Definition ina219.h:33
#define INA219_REG_CURRENT
current
Definition ina219.h:34
User-facing configuration for ProtoCore.
#define PC_INA219_SHUNT_MOHM
Default INA219 shunt resistance in milliohms (calibration input). The fallback when pc_ina219_begin()...
#define PC_INA219_CURRENT_LSB_UA
Default INA219 current LSB in microamps per bit (calibration input). The fallback when pc_ina219_begi...
#define PC_INA219_I2C_ADDR
I2C address of the INA219 (0x40 default; the A0/A1 pins select 0x40..0x4F).