Closed lumiror closed 1 year ago
I don't understand you question. All BMS measurements are exposed as sensor entities and are available at Home Assistant if you like. I recommend to use the native api
component to make the ESPHome node available/accessible at Home Assistant.
You could extend the YAML configuration to monitor the state of charge entity and control some LEDs to visualize the SoC.
I use something like this: https://de.aliexpress.com/item/4000877848238.html
The ESPHome code looks like this:
sensor:
- platform: ant_bms
soc:
name: "${name} soc"
id: soc
light:
- platform: neopixelbus
id: strip
name: "LED strip"
type: GRB
pin: GPIO3
num_leds: 24
method: ESP8266_DMA
variant: WS2812
restore_mode: ALWAYS_ON
internal: true
display:
- platform: addressable_light
id: gauge
addressable_light_id: strip
width: 24
height: 1
update_interval: 16ms
lambda: |-
float brightness = 0.40f;
Color color = Color(
brightness * 0,
brightness * 255,
brightness * 0
);
if(id(soc) && !isnan(id(soc).state)) {
int width = std::floor((it.get_width() / 100.0f) * id(soc).state);
it.line(0, 0, width, 0, color);
}
Thank you for your quick reply. To make it more precise, I would need to control the output PWM signal on the esp32 or ESP 8266 based on the value of the entity, i.e. the battery charge in % (sensor.antbms_soc). As I wrote, if the value of the entity is e.g. 95%, the battery is charged to 95% on the ESP 32 analog output will be 10% PWM signal, etc. I need to use this signal to control the PWM to volt converter from AliExpress. https://www.aliexpress.com/item/4000128019142.html
Is the ESP reading the ANT-BMS and the ESP with the PWM output the same device / ESP?
You could use these components to control the PWM of a ESP8266/ESP32:
https://esphome.io/components/output/esp8266_pwm.html https://esphome.io/components/output/ledc.html
Is the ESP reading the ANT-BMS and the ESP with the PWM output the same device / ESP?
No ANT-BMS has a separate ESP32
Do you prefer a MQTT or a Home Assistant connection?
home assistant I found something like this, but I don't know how I could replace that sensor with my own (sensor.antbms_soc)
output:
- platform: ledc
pin: GPIO14
frequency: 25000 Hz
id: fan_pwm
fan:
- platform: speed
output: fan_pwm
name: "Network Rack Exhaust Fan Speed"
sensor:
- platform: bmp280
i2c_id: bus_b
address: 0x77
temperature:
name: "Network Rack Temperature"
id: rack_temp
oversampling: 16x
on_value:
then:
lambda: |-
if (id(rack_temp).state < 23.0) {
id(fan_pwm).set_level(0.00);
ESP_LOGD("PWM", "PWM: 0%");
}
else if ((id(rack_temp).state >= 23.0) and (id(rack_temp).state <= 24.0)) {
id(fan_pwm).set_level(0.05);
ESP_LOGD("PWM", "PWM: 5%");
}
else if ((id(rack_temp).state > 24.0) and (id(rack_temp).state <= 25.0)) {
id(fan_pwm).set_level(0.1);
ESP_LOGD("PWM", "PWM: 10%");
}
else if ((id(rack_temp).state > 25.0) and (id(rack_temp).state <= 26.0)) {
id(fan_pwm).set_level(0.2);
ESP_LOGD("PWM", "PWM: 20%");
}
else if ((id(rack_temp).state > 26.0) and (id(rack_temp).state <= 27.0)) {
id(fan_pwm).set_level(0.3);
ESP_LOGD("PWM", "PWM: 30%");
}
else if ((id(rack_temp).state > 27.0) and (id(rack_temp).state <= 28.0)) {
id(fan_pwm).set_level(0.4);
ESP_LOGD("PWM", "PWM: 40%");
}
else if ((id(rack_temp).state > 28.0) and (id(rack_temp).state <= 29.0)) {
id(fan_pwm).set_level(0.5);
ESP_LOGD("PWM", "PWM: 50%");
}
else if ((id(rack_temp).state > 29.0) and (id(rack_temp).state <= 30.0)) {
id(fan_pwm).set_level(0.6);
ESP_LOGD("PWM", "PWM: 60%");
}
else if ((id(rack_temp).state > 30.0) and (id(rack_temp).state <= 32.0)) {
id(fan_pwm).set_level(0.7);
ESP_LOGD("PWM", "PWM: 70%");
}
else if ((id(rack_temp).state > 32.0) and (id(rack_temp).state <= 35.0)) {
id(fan_pwm).set_level(0.8);
ESP_LOGD("PWM", "PWM: 80%");
}
else {
id(fan_pwm).set_level(1.0);
ESP_LOGD("ALERT", "OVER 35C! Setting fan to 100%");
}
update_interval: 2s
Do you prefer a MQTT or a Home Assistant connection between the ESPs?
Home Assistant
You can subscribe to any Home Assistant sensor using the homeassistant
component. It should look like this:
api:
output:
- platform: ledc
pin: GPIO14
frequency: 25000 Hz
id: output_pwm
sensor:
- platform: homeassistant
internal: true
id: soc
name: "${name} soc"
entity_id: sensor.ant-bms_soc
on_value:
then:
lambda: |-
if (id(soc).state < 23.0) {
id(output_pwm).set_level(0.00);
ESP_LOGD("PWM", "PWM: 0%");
}
else if ((id(soc).state >= 23.0) and (id(soc).state <= 24.0)) {
id(output_pwm).set_level(0.05);
ESP_LOGD("PWM", "PWM: 5%");
}
else if ((id(soc).state > 24.0) and (id(soc).state <= 25.0)) {
id(output_pwm).set_level(0.1);
ESP_LOGD("PWM", "PWM: 10%");
}
else if ((id(soc).state > 25.0) and (id(soc).state <= 26.0)) {
id(output_pwm).set_level(0.2);
ESP_LOGD("PWM", "PWM: 20%");
}
else if ((id(soc).state > 26.0) and (id(soc).state <= 27.0)) {
id(output_pwm).set_level(0.3);
ESP_LOGD("PWM", "PWM: 30%");
}
else if ((id(soc).state > 27.0) and (id(soc).state <= 28.0)) {
id(output_pwm).set_level(0.4);
ESP_LOGD("PWM", "PWM: 40%");
}
else if ((id(soc).state > 28.0) and (id(soc).state <= 29.0)) {
id(output_pwm).set_level(0.5);
ESP_LOGD("PWM", "PWM: 50%");
}
else if ((id(soc).state > 29.0) and (id(soc).state <= 30.0)) {
id(output_pwm).set_level(0.6);
ESP_LOGD("PWM", "PWM: 60%");
}
else if ((id(soc).state > 30.0) and (id(soc).state <= 32.0)) {
id(output_pwm).set_level(0.7);
ESP_LOGD("PWM", "PWM: 70%");
}
else if ((id(soc).state > 32.0) and (id(soc).state <= 35.0)) {
id(output_pwm).set_level(0.8);
ESP_LOGD("PWM", "PWM: 80%");
}
else {
id(output_pwm).set_level(1.0);
ESP_LOGD("ALERT", "OVER 35C! Setting fan to 100%");
}
Thank you very much for your help, I'm going to try it
You are welcome! I've fixed a typo. Please use the most recent version of the example above.
Can you please look at it again, why it marked line 45 as an error. I didn't put the API: there, I already have it in the compilation, is that correct?
Please goto your Home Assistant instance, open the "Developer Tools -> States" page (/developer-tools/state
) and search for "soc". Please copy the full entity name of your state of charge entity. Use this entity name at the YAML. May be you have to add quotation marks.
Yes, there is a mistake, I have already fixed it. Thanks
Oh no, I just destroyed my ESP32. I'm an idiot. I also have an ESP 8266, I can use it or the Output would have to be reconfigured
How did you destroy the ESP?
If you use a ESP8266 you have to use the esp8266_pwm
component:
output:
- platform: esp8266_pwm
pin: GPIO4
frequency: 1000 Hz
id: output_pwm
I connected +a- the other way around
Okay. ;-) In this case, I also think it is dead.
Thank you very much, I'm going to try it again.
So it works perfectly, I just have one more request for you. Would it be possible to create an entity for the state of the PWM signal so that I can see it in homeassistant? I would be very grateful. And I did not destroy the ESP32, :) only the diode was destroyed.
Hmm... it looks like we cannot ask the output
for the current PWM. I assume you have to implement a workaround like this:
sensor:
- platform: template
name: "PWM"
id: pwm_sensor
- platform: homeassistant
internal: true
id: soc
name: "${name} soc"
entity_id: sensor.ant-bms_soc
on_value:
then:
lambda: |-
if (id(soc).state < 23.0) {
id(output_pwm).set_level(0.00);
id(pwm_sensor).publish_state(0.00);
}
else if ((id(soc).state >= 23.0) and (id(soc).state <= 24.0)) {
id(output_pwm).set_level(0.05);
id(pwm_sensor).publish_state(0.05);
}
else if ((id(soc).state > 24.0) and (id(soc).state <= 25.0)) {
id(output_pwm).set_level(0.1);
id(pwm_sensor).publish_state(0.1);
}
else if ((id(soc).state > 25.0) and (id(soc).state <= 26.0)) {
id(output_pwm).set_level(0.2);
id(pwm_sensor).publish_state(0.2);
}
else if ((id(soc).state > 26.0) and (id(soc).state <= 27.0)) {
id(output_pwm).set_level(0.3);
id(pwm_sensor).publish_state(0.3);
}
else {
id(output_pwm).set_level(1.0);
id(pwm_sensor).publish_state(1.0);
}
Thank you, it works perfectly
Can you explain the purpose of your setup in a few words? What do you drive with the output voltage of the converter? Thanks in advance!
Yes, of course I can. I need to control the water heating in the tank. That is, if the batteries are charged, the water will start to heat up. And in order to use as much energy as possible from the sun, the water heating will start when the battery is charged to approx. 95% and will gradually increase.
Can I ask one more question? Would it be possible to control the PWM signal linearly? That is, on the basis of some value, e.g. 0-20 from the entity and the Pwm signal output would be 0-100%?
Could you provide your current configuration / lambda function? I would like to understand which (SoC) value(s) should be translated into another one (PWM percentage).
The value is in Amp, it is the voltage on the solar panels. But it is not an installation on ESP... but on a raspberry and transferred via mqtt to home assistant. It's data from my inverter
I'm a bit confused. I need some more details to understand your goal. Yesterday we talked about the SoC and we tried to feed your heater depending on the state of charge in steps. You prefer a linear function instead of steps now. Right? How do you like to take the panel current into account?
What we dealt with yesterday is OK. This is something else. This is a value from another entity in Amp. Its range is from 0-20A and I would need to linearly convert it to a PWM signal to the ESP 32/2866. so that 0-20A = 0-100% PWM, if that is possible.
What do you control with the PWM signal this time? It's the heater again and both solutions should be combined or it's another device? :-)
Exactly, :) I would like to use them together and control the water heating according to them. I wanted to use a Hall current sensor, but this would be better.
It's pretty the same like last time. ;-) You can make the sensor state from HA available using this snippet:
- platform: homeassistant
internal: true
id: pv_current
name: "${name} pv current"
entity_id: sensor.panely_amp
The combination of both values is a bit tricky. Could you try to describe the logic in words? Something like:
This sensor will be separate, I don't need the values to be combined with the sensor that we solved yesterday. It would be better linearly, not in steps. So use the tenth place. If, for example, 10A=50%PWM, if 10.1A=50.5%PWM, if 10.2A = 50.1%PWM, etc. Just writing it like before was probably too long, isn't there a formula? I will compare the values from both sensors myself.
I assume you are looking for something like this:
sensor:
- platform: template
name: "PWM"
id: pwm_sensor
- platform: homeassistant
internal: true
id: pv_current
name: "${name} pv current"
entity_id: sensor.panely_amp
on_value:
then:
lambda: |-
if(id(pv_current) && !isnan(id(pv_current).state)) {
float percentage = max(min(id(pv_current).state * 5.0f, 100.0f), 0.0f);
id(output_pwm).set_level(percentage);
id(pwm_sensor).publish_state(percentage);
}
On each new "panely_amp" value the PWM is changed according the following formula:
pv_current
5: 0A 5 = 0% PWM, 10A 5 = 50% PWM, 20A 5 = 100% PWMIt looks great, I'll try it. I just found out that the testing is difficult, because waiting for the real value to change is difficult and I don't have any entities that I would use for the test and on which the value would be changed manually in homeassistant. But I'll manage somehow. Thanks
I still want to ask what happens to the PWM signal if the value in the entity is lost? Because sometimes I lose the connection to the ESP32/2866 and the data is unavailable. The outage lasts approx. 4-6 seconds.
The if condition
if(id(pv_current) && !isnan(id(pv_current).state)) {}
checks the sensor with the ID pv_current
exists and provides a value unequal "not a number". In other words the code at the brackets ({}
) gets executed only if the sensor value is numeric. At the ESPHome ecosystem the unavailable
state is encoded as NaN
. I assume the PWM doesn't get changed if your pv_current
sensor gets unavailable.
Well, it's not very good if the PWM remains at an unchanged value, especially in the case of permanent malfunctions. Is it not possible to insert some time condition that if the signal is not renewed after xy /sec., the signal will be switched off to 0?
What about:
sensor:
- platform: template
name: "PWM"
id: pwm_sensor
- platform: homeassistant
internal: true
id: pv_current
name: "${name} pv current"
entity_id: sensor.panely_amp
on_value:
then:
lambda: |-
if(id(pv_current) && !isnan(id(pv_current).state)) {
float percentage = max(min(id(pv_current).state * 5.0f, 100.0f), 0.0f);
id(output_pwm).set_level(percentage);
id(pwm_sensor).publish_state(percentage);
} else {
id(output_pwm).set_level(0.0f);
id(pwm_sensor).publish_state(0.0f);
}
Does this mean that if there is no value from the entity at the output, the PWM will be 0?
Yes!
And this time condition could not be added there, so that the PWM signal is not turned off unnecessarily if the outage is short / one-time
I cannot provide more advanced solutions without investing too much time.
I understand clearly, thank you very much for your help anyway. :)
Let's close this issue. It's solved! :-)
Yes Thanks.
Hi, could you help me one more time? I don't know why, but I still have 100% voltage on the "PWM1" output1_pwm of GPIO27 in my test circuit. The formula seems to be fine 0-20V=0-100%, but in reality the PWM signal on the GPIO27 output does not change and is still as I wrote 100% . "PWM" on the GPIO25 output works correctly. Thank you
output:
- platform: ledc
pin: GPIO25
frequency: 25000 Hz
id: output_pwm
- platform: ledc
pin: GPIO27
frequency: 25000 Hz
id: output1_pwm
sensor:
- platform: adc
pin: GPIO35
name: "voltage sensor"
id: voltage_sensor
update_interval: 2s
filters:
- multiply: 50
- platform: adc
pin: GPIO34
name: "voltage sensor1"
id: voltage_sensor1
update_interval: 2s
filters:
- multiply: 50
- platform: template
name: "PWM"
id: pwm_sensor
- platform: homeassistant
internal: true
id: soc
name: "${name} soc"
entity_id: sensor.voltage_sensor
on_value:
then:
lambda: |-
if (id(soc).state < 23.0) {
id(output_pwm).set_level(0.00);
id(pwm_sensor).publish_state(0.00);
}
else if ((id(soc).state >= 23.0) and (id(soc).state <= 24.0)) {
id(output_pwm).set_level(0.1);
id(pwm_sensor).publish_state(0.05);
}
else if ((id(soc).state > 24.0) and (id(soc).state <= 25.0)) {
id(output_pwm).set_level(0.2);
id(pwm_sensor).publish_state(0.2);
}
else if ((id(soc).state > 25.0) and (id(soc).state <= 26.0)) {
id(output_pwm).set_level(0.3);
id(pwm_sensor).publish_state(0.3);
}
else if ((id(soc).state > 26.0) and (id(soc).state <= 27.0)) {
id(output_pwm).set_level(0.4);
id(pwm_sensor).publish_state(0.4);
}
else if ((id(soc).state > 27.0) and (id(soc).state <= 28.0)) {
id(output_pwm).set_level(0.5);
id(pwm_sensor).publish_state(0.5);
}
else if ((id(soc).state > 28.0) and (id(soc).state <= 29.0)) {
id(output_pwm).set_level(0.6);
id(pwm_sensor).publish_state(0.6);
}
else {
id(output_pwm).set_level(1.0);
id(pwm_sensor).publish_state(1.0);
}
- platform: template
name: "PWM1"
id: pwm1_sensor
- platform: homeassistant
internal: true
id: pv_current
name: "${name} pv current"
entity_id: sensor.voltage_sensor1
on_value:
then:
lambda: |-
if(id(pv_current) && !isnan(id(pv_current).state)) {
float percentage = max(min(id(pv_current).state * 5.0f, 100.0f), 0.0f);
id(output1_pwm).set_level(percentage);
id(pwm1_sensor).publish_state(percentage);
}
I measured it and it looks like there is probably no signal there, only the output is high. Because there is a frequency of 25KHz on the output of GPIO25. But there is no frequency on the output of GPIO27.
Hello, I want to ask, would it be possible to use an entity from ANT BMS in homeassistant, specifically information about the battery charge in % (sensor.antbms_soc) on pwm signal control from ESP32? For example, if the battery is 95% charged at the ESP32 output, the PWM signal will be 20%, if it is 96% at the output, it will be 35%, 97%=50%, 98%=65%, 99%=85%. 100%=100. Thank you