Maybe someone can help, I’m struggling to get this right. I want to use Adafruit’s MAX17048 LiPo battery gauge:
I tried to take inspiration from this thread:
Maybe someone can help, I’m struggling to get this right. I want to use Adafruit’s MAX17048 LiPo battery gauge:
I tried to take inspiration from this thread:
Ok so I tried modifying a-marcel’s code and got it to at least produce some output but the numbers seem wildly wrong:
[18:28:14][D][sensor:127]: 'Voltage': Sending state 3616.25000 with 0 decimals of accuracy
[18:28:14][D][sensor:127]: 'Percentage': Sending state 3.10937 with 0 decimals of accuracy
I confess that I really don’t understand what this is doing or why we need all these bit shift operators.
void update() override {
float voltage = 1.25f * (float)(read16(MAX17043_VCELL) >> 4);
voltage_sensor->publish_state(voltage);
uint16_t percentage_tmp = read16(MAX17043_SOC);
float percentage = (float)((percentage_tmp >> 8) + 0.003906f * (percentage_tmp & 0x00ff));
percentage_sensor->publish_state(percentage);
}
Why all the complicated formulas? Does the sensor not produce a simple voltage number and percentage so the data has to be massaged like this?
Ok…so I think I have this solved but maybe someone else can check my work. I copied these formulas from the source code of the Adafruit driver:
So now my code looks like this:
void update() override {
//float voltage = 1.25f * (float)(read16(MAX17048_VCELL) >> 4);
float voltage = (float)(read16(MAX17048_VCELL)) * 78.125 / 1000000;
voltage_sensor->publish_state(voltage);
uint16_t percentage_tmp = read16(MAX17048_SOC);
//float percentage = (float)((percentage_tmp >> 8) + 0.003906f * (percentage_tmp & 0x00ff));
float percentage = (float)(percentage_tmp) / 256;
percentage_sensor->publish_state(percentage);
}
Now it produces outputs that look like a voltage and percentage:
[06:24:58][D][sensor:127]: 'Voltage': Sending state 3.56000 with 0 decimals of accuracy
[06:24:58][D][sensor:127]: 'Percentage': Sending state 93.23047 with 0 decimals of accuracy
Does it look right to you? Am I missing anything?
The values are a bit odd, a fully charged battery started at 103% and 4.2V. Methinks my formulas aren’t quite optimized correctly.
but at least I can watch the values change as the battery discharges.
This is working great, if anyone else needs a solution for monitoring battery life. Here’s the full code in case anyone else needs it.
MAX17048_component.h - put this file in your esphome folder:
#include "esphome.h"
#define MAX17048_ADDRESS 0x36
#define MAX17048_VCELL 0x02 // voltage
#define MAX17048_SOC 0x04 // percentage
#define MAX17048_MODE 0x06
#define MAX17048_VERSION 0x08
#define MAX17048_CONFIG 0x0c
#define MAX17048_COMMAND 0xfe
class MAX17048Sensor : public PollingComponent, public Sensor {
public:
Sensor *voltage_sensor = new Sensor();
Sensor *percentage_sensor = new Sensor();
MAX17048Sensor() : PollingComponent(10000) {}
void setup() override {
// Initialize the device here. Usually Wire.begin() will be called in here,
// though that call is unnecessary if you have an 'i2c:' entry in your config
ESP_LOGD("custom", "Starting up MAX17048 sensor");
Wire.begin();
}
uint16_t read16(uint8_t reg) {
uint16_t temp;
Wire.begin();
Wire.beginTransmission(MAX17048_ADDRESS);
Wire.write(reg);
Wire.endTransmission();
Wire.requestFrom(MAX17048_ADDRESS, 2);
temp = (uint16_t)Wire.read() << 8;
temp |= (uint16_t)Wire.read();
Wire.endTransmission();
return temp;
}
void update() override {
float voltage = (float)(read16(MAX17048_VCELL)) * 78.125 / 1000000;
voltage_sensor->publish_state(voltage);
uint16_t percentage_tmp = read16(MAX17048_SOC);
float percentage = (float)(percentage_tmp) / 256;
percentage_sensor->publish_state(percentage);
}
};
Put this in your ‘includes’:
esphome:
includes:
- MAX17048_component.h
and this in your sensor section:
- platform: custom
lambda: |-
auto max17048_sensor = new MAX17048Sensor();
App.register_component(max17048_sensor);
return {max17048_sensor->voltage_sensor, max17048_sensor->percentage_sensor};
sensors:
- name: "Voltage"
unit_of_measurement: V
accuracy_decimals: 2
- name: "Percentage"
unit_of_measurement: '%'
Thanks, @greenleaf Appreciate you providing this code.
I tried it with my setup and the voltage is fine, but I need a way to adjust the percent range. In my case I’m using 3 AA batteries, so 100% is 4.75 V. Is there a place to adjust this in the code or is this hard coded into the sensor?
hmm, best guess would be to try tweaking this line and see if your readings line up closer to 4.75 on a full charge
float voltage = (float)(read16(MAX17048_VCELL)) * 78.125 / 1000000;
Has anyone done anything similar for the LC709203F
Hello,
I trying to build this ESPHome 2024.3.0 and I am facing an issue. Is that the case for you too?
INFO ESPHome 2024.3.0
INFO Reading configuration /config/esphome/adafruit-qualia-esp32-s3.yaml...
INFO Generating C++ source...
INFO Compiling app...
Processing adafruit-qualia-esp32-s3 (board: esp32-s3-devkitc-1; framework: espidf; platform: platformio/[email protected])
--------------------------------------------------------------------------------
HARDWARE: ESP32S3 240MHz, 320KB RAM, 8MB Flash
- framework-espidf @ 3.40406.240122 (4.4.6)
- tool-cmake @ 3.16.4
- tool-ninja @ 1.7.1
- toolchain-esp32ulp @ 2.35.0-20220830
- toolchain-riscv32-esp @ 8.4.0+2021r2-patch5
- toolchain-xtensa-esp32s3 @ 8.4.0+2021r2-patch5
Reading CMake configuration...
Dependency Graph
|-- noise-c @ 0.1.4
Compiling .pioenvs/adafruit-qualia-esp32-s3/src/main.o
/config/esphome/adafruit-qualia-esp32-s3.yaml: In lambda function:
/config/esphome/adafruit-qualia-esp32-s3.yaml:94:34: error: expected type-specifier before 'MAX17048Sensor'
auto max17048_sensor = new MAX17048Sensor();
^~~~~~~~~~~~~~
/config/esphome/adafruit-qualia-esp32-s3.yaml:96:82: error: could not convert '{<expression error>, <expression error>}' from '<brace-enclosed initializer list>' to 'std::vector<esphome::sensor::Sensor*>'
return {max17048_sensor->voltage_sensor, max17048_sensor->percentage_sensor};
^
*** [.pioenvs/adafruit-qualia-esp32-s3/src/main.o] Error 1
When I try to use @greenleaf 's code I get the error: src/MAX17048_component.h:23:5: error: 'Wire' was not declared in this scope
. Some poking around showed me that most ESPHome components are not using Wire
. Any thoughts, anyone?
Apparently custom components like this one are now deprecated: Custom I²C Device — ESPHome
So maybe one of us will get a chance to create an external component for the max17048 and share it here!
If anyone returns to this thread, I threw together a new version of the driver, which seems to work currently. I used it in this recipe:
https://community.home-assistant.io/t/recipe-esphome-adafruit-feather-esp32-s2-thinkink-e-paper-display-battery-monitor/
tl;dr:
external_components:
- source: github://Option-Zero/esphome-components@max17048
components: [max17048]
sensor:
- platform: max17048
battery_voltage:
name: Battery voltage
battery_level:
name: Battery level
rate:
name: Battery discharge rate
Lmk if you have success with this @lboue .
This works perfectly, thanks! Do you have plans to make a PR and get it natively integrated on ESPHome?
Thanks for this. Seems to be working! I’m getting readings, will have to see how accurate they are.