HomeAssistant Bluetooth BMS Monitoring Server Guide [ESP32]

Never Mind… The issue was that the TYPE should always be lower case.

1 Like

As using the internal Bluetooth from your server can cause issues with range and such I put together another solution.

It is very robust
Stuff needed:

ESP32 module
Lcd2004 display(optional)
Spare time

Instead of letting the server connect we use a ESP32 Arduino instead. These have Bluetooth and WiFi. So it can grab the battery data from the BMS for us over BLE and then send them over to homeassistant over WiFi.

The functionality is the same as the batmon addon, except it doesn’t use MQTT and works straight with ESPhome(.yaml at end of post)
To add something extra to it I added an LCD that shows your current battery data:





substitutions:
  name: 12s5pbattery
  device_description: "Monitor and control a Xiaoxiang Battery Management System (JBD-BMS) via BLE"
  external_components_source: github://syssi/esphome-jbd-bms@main
  mac_address: xx:xx:xx:xx:xx:xx

esphome:
  name: ${name}
  comment: ${device_description}
  project:
    name: "syssi.esphome-jbd-bms"
    version: 1.4.0

esp32:
  board: esp32dev
  framework:
    type: esp-idf

external_components:
  - source: ${external_components_source}
    refresh: 0s

wifi:
  ssid: "xxxxxxxx"
  password: "xxxxxxx"
  manual_ip:
    static_ip: 192.168.1.188
    gateway: 192.168.1.1
    subnet: 255.255.255.0

api:
  encryption:
    key: "xxxxxxxxxxxxxxxxxxxx="

ota:
  password: "xxxxxxxxxxxxxxxxx"

logger:
  level: DEBUG


i2c:
  sda: GPIO16
  scl: GPIO17


display:
  - platform: lcd_pcf8574
    dimensions: 16x2
    address: 0x27
    lambda: |-
          switch (id(page)){
            case 1:
              it.printf("Total:""%.3fV", id(total_voltage).state);              
              it.printf(0, 1, "Avg:""%.3fV", id(avgcell).state);                     
              break;
            case 2:
              it.printf("Charging:""%d", id(charge).state);
              it.printf(0, 1, "Delta:""%.3fV", id(deltacell).state);                            
              break;
            case 3:
              it.printf("Current:""%.3fA", id(current).state);
              it.printf(0, 1, "Temp:""%.1fC", id(temp2).state);                  
              break;
          }

globals:
- id: page
  type: int
  initial_value: "1"


interval:
- interval: 4s
  then:
    - lambda: |-
        id(page) = (id(page) + 1);
        if (id(page) > 3) {
          id(page) = 1;
        }











esp32_ble_tracker:
  on_ble_advertise:
    then:
      - lambda: |-
          if (x.get_name().rfind("xiaoxiang", 0) == 0) {
            ESP_LOGI("ble_adv", "New JBD-BMS found");
            ESP_LOGI("ble_adv", "  Name: %s", x.get_name().c_str());
            ESP_LOGI("ble_adv", "  MAC address: %s", x.address_str().c_str());
            ESP_LOGD("ble_adv", "  Advertised service UUIDs:");
            for (auto uuid : x.get_service_uuids()) {
              ESP_LOGD("ble_adv", "    - %s", uuid.to_string().c_str());
            }
          }

ble_client:
  - id: client0
    mac_address: ${mac_address}

jbd_bms_ble:
  - id: bms0
    ble_client_id: client0
    update_interval: 30s

binary_sensor:
  - platform: jbd_bms_ble
    jbd_bms_ble_id: bms0
    balancing:
      name: "${name} balancing"
    charging:
      name: "${name} charging"
    discharging:
      name: "${name} discharging"
    online_status:
      name: "${name} online status"


sensor:
  - platform: jbd_bms_ble
    jbd_bms_ble_id: bms0
    battery_strings:
      name: "${name} battery strings"
    current:
      name: "${name} current"
      id: current
    power:
      name: "${name} power"
    charging_power:
      name: "${name} charging power"
    discharging_power:
      name: "${name} discharging power"
    state_of_charge:
      name: "${name} state of charge"
    nominal_capacity:
      name: "${name} nominal capacity"
    charging_cycles:
      name: "${name} charging cycles"
    capacity_remaining:
      name: "${name} capacity remaining"
    battery_cycle_capacity:
      name: "${name} battery cycle capacity"
    total_voltage:
      name: "${name} total voltage"
      id: total_voltage
    average_cell_voltage:
      name: "${name} average cell voltage"
      id: avgcell
    delta_cell_voltage:
      name: "${name} delta cell voltage"
      id: deltacell
    min_cell_voltage:
      name: "${name} min cell voltage"
    max_cell_voltage:
      name: "${name} max cell voltage"
    min_voltage_cell:
      name: "${name} min voltage cell"
    max_voltage_cell:
      name: "${name} max voltage cell"
    temperature_1:
      name: "${name} temperature 1"
      id: temp1
    temperature_2:
      name: "${name} temperature 2"
      id: temp2
    temperature_3:
      name: "${name} temperature 3"
    cell_voltage_1:
      name: "${name} cell voltage 1"
    cell_voltage_2:
      name: "${name} cell voltage 2"
    cell_voltage_3:
      name: "${name} cell voltage 3"
    cell_voltage_4:
      name: "${name} cell voltage 4"
    cell_voltage_5:
      name: "${name} cell voltage 5"
    cell_voltage_6:
      name: "${name} cell voltage 6"
    cell_voltage_7:
      name: "${name} cell voltage 7"
    cell_voltage_8:
      name: "${name} cell voltage 8"
    cell_voltage_9:
      name: "${name} cell voltage 9"
    cell_voltage_10:
      name: "${name} cell voltage 10"
    cell_voltage_11:
      name: "${name} cell voltage 11"
    cell_voltage_12:
      name: "${name} cell voltage 12"

    operation_status_bitmask:
      name: "${name} operation status bitmask"
    errors_bitmask:
      name: "${name} errors bitmask"
    balancer_status_bitmask:
      name: "${name} balancer status bitmask"
    software_version:
      name: "${name} software version"

text_sensor:
  - platform: jbd_bms_ble
    jbd_bms_ble_id: bms0
    errors:
      name: "${name} errors"
    operation_status:
      name: "${name} operation status"
    device_model:
      name: "${name} device model"

switch:
  - platform: ble_client
    ble_client_id: client0
    name: "${name} enable bluetooth connection"

  - platform: jbd_bms_ble
    jbd_bms_ble_id: bms0
    charging:
      name: "${name} charging"
      id: charge
    discharging:
      name: "${name} discharging"

Could be interesting for @rusins or @luastoned

3 Likes

Is there a way to save all the data and put it into a spread sheet? I’ve been thinking of doing this and probably will, but it would be sweet to save everything. I know it’s not needed but I have a data addiction and like to keep as much as I can.

Yes, you can export the data from home assistant, or even use the Grafana addon.
Home assistant already makes a graph from every data point though.

Using this system now to closely monitor my drowned battery… yesterday I crashed while riding a wet road and my board ended up in a creek. :frowning:

Drying it out now while closely monitoring with hassio

After crash pic, while I was dying from pain. Just used my last adrenaline to fish up my board from the creek and open the battery box for draining


1 Like

I was just about to do this exact same thing with esphome, so thanks for saving me the work! I have it running on a dev board. It sits out in my garage and when I bring a board within range, I get the nice BMS stats, and I use it to either trigger charge stops at voltage storage level, or charge up to like 80% the night before I do a ride, then have HA kick the charger back on in time for the ride.

Do you have any recommendations for a “production” esp32-based board or fully built-out hardware module? I’m looking for something off-the-shelf. Otherwise I’ll do my usual style (translation: ugly board enclosure and complete DIY aesthetic).


This one is cheap and reliable. Also has a nice wifi antenna.

2 Likes

So two things:

  1. The Waveshare esp32-s3-relay-6ch is definitely a nice unit. I picked up two (one for prod, one for testing) and they are pretty solid. I haven’t tested the relays yet, but I have a couple other projects that I could definitely use this with. I mean this is literally begging to be used as a garage light switch/garage door opener/bms monitor all in one :smiley:

  2. The esp32 implementation running on this thing seems to be pretty solid (hint for anyone stumbling across this, board model esp32-s3-devkitc-1 with framework arduino seems to work fine). The issue I’m running into seems to be that that the ESP can’t connect to any of my boards with manufacturer ID DGJBD. It connects fine to the one with manufacturer ID SZLLT. Those all seem to be JBD BMSs. I did make sure nothing was trying to connect to the DGJBD units at the same time and that they’re visible both in general BLE scans and connectable via Overkill Solar. None of them are set to require PINs.

The error is just a straightforward unable to connect message:

[23:36:19][C][esp32_ble:391]: ESP32 BLE:
[23:36:19][C][esp32_ble:393]:   MAC address: 30:30:F9:79:DD:9D
[23:36:19][C][esp32_ble:394]:   IO Capability: none
[23:36:19][C][esp32_ble_tracker:653]: BLE Tracker:
[23:36:19][C][esp32_ble_tracker:654]:   Scan Duration: 300 s
[23:36:19][C][esp32_ble_tracker:655]:   Scan Interval: 320.0 ms
[23:36:19][C][esp32_ble_tracker:656]:   Scan Window: 30.0 ms
[23:36:19][C][esp32_ble_tracker:657]:   Scan Type: ACTIVE
[23:36:19][C][esp32_ble_tracker:658]:   Continuous Scanning: True
[23:36:19][C][ble_client:027]: BLE Client:
[23:36:19][C][mdns:117]:   Hostname: batmon_dev0
[23:36:19][C][esphome.ota:073]: Over-The-Air updates:
[23:36:19][C][esphome.ota:074]:   Address: batmon_dev0.local:3232
[23:36:19][C][esphome.ota:075]:   Version: 2
[23:36:19][C][safe_mode:018]: Safe Mode:
[23:36:19][C][safe_mode:020]:   Boot considered successful after 60 seconds
[23:36:19][C][safe_mode:021]:   Invoke after 10 boot attempts
[23:36:19][C][safe_mode:023]:   Remain in safe mode for 300 seconds
[23:36:19][C][api:139]: API Server:
[23:36:19][C][api:140]:   Address: batmon_dev0.local:6053
[23:36:19][C][api:144]:   Using noise encryption: NO
[23:36:20][W][jbd_bms_ble:156]: [83:3D:05:37:C2:A5] Not connected
[23:36:39][W][jbd_bms_ble:156]: [19:73:06:37:C2:A5] Not connected
[23:36:47][W][jbd_bms_ble:156]: [F0:6F:13:37:C2:A5] Not connected
[23:36:50][W][jbd_bms_ble:156]: [83:3D:05:37:C2:A5] Not connected
[23:36:53][D][esp32_ble_tracker:270]: Starting scan...
[23:37:09][W][jbd_bms_ble:156]: [19:73:06:37:C2:A5] Not connected
[23:37:17][W][jbd_bms_ble:156]: [F0:6F:13:37:C2:A5] Not connected
[23:37:20][W][jbd_bms_ble:156]: [83:3D:05:37:C2:A5] Not connected

The other one connects without issue:

36:48][C][esp32_ble:391]: ESP32 BLE:
[23:36:48][C][esp32_ble:393]:   MAC address: 24:58:7C:EC:62:59
[23:36:48][C][esp32_ble:394]:   IO Capability: none
[23:36:48][C][esp32_ble_tracker:653]: BLE Tracker:
[23:36:48][C][esp32_ble_tracker:654]:   Scan Duration: 300 s
[23:36:48][C][esp32_ble_tracker:655]:   Scan Interval: 320.0 ms
[23:36:48][C][esp32_ble_tracker:656]:   Scan Window: 30.0 ms
[23:36:48][C][esp32_ble_tracker:657]:   Scan Type: ACTIVE
[23:36:48][C][esp32_ble_tracker:658]:   Continuous Scanning: True
[23:37:11][D][esp32_ble_client:110]: [0] [70:3E:97:FF:17:E6] ESP_GATTC_WRITE_CHAR_EVT
[23:37:12][D][esp32_ble_client:110]: [0] [70:3E:97:FF:17:E6] ESP_GATTC_NOTIFY_EVT
[23:37:12][D][esp32_ble_client:110]: [0] [70:3E:97:FF:17:E6] ESP_GATTC_NOTIFY_EVT
[23:37:12][I][jbd_bms_ble:242]: Hardware info frame (25 bytes) received
(etc etc)

Any suggestions?

Mmmmm sexy Grafana graphs

2 Likes

Hi @lidocaineusi I just put this together like a script kiddie. Can’t really give you support on why it doesnt work with the other type BMS.
I suggest you open a ticket at the GitHub repo.

Happy I was able to help !

1 Like

Sat down this weekend to debug, and it turns out the bluetooth MAC addresses I was using were backwards. That’s what I get for relying on Overkill Solar to pull the addresses, which on the JBD BMSs, it actually reverses. I have NO idea why. Guess this is what I get for not checking on it myself. Discovered that when I was doing a proper BT scan outside Overkill Solar. First shot shows Overkill. Second shot shows the scan.


rb6

After that, added all the boards with compatible Smart BMSs to HA and got the Grafana graphs going:


Will need to clean that up and probably coalesce a bunch of those into single graphs with selectable variables per board.

On the to do list:

  1. ble_client in esphome is limited to three clients total. Not sure if that’s a software or hardware limitation (probably hardware) but I need to verify. Three per ESP chip is ok but not great. Also curious if this limitation exists on the non-esp32 mqtt version running on something like a Raspberry Pi.

  2. The charging and balancing sensors need some debugging; they’re either not updating datapoints very often or they’re doing it incorrectly, or I’m running into an ESP bluetooth query timeout. Either way, they’re acting wonky and not showing state correctly.

But all in all, this is damn cool and WAY nicer than having to babysit a phone BMS app.

1 Like

Hey where’d you get the Davega numbers? I’d also like the rough SOC percentages for an 18s pack. Reason being I was asked to implement a “reach X percentage charge at Y time” automation for both 12s and 18s packs. Right now I have that about halfway there, in that I can set a board to charge to storage voltage levels OR full, with an optional delay start at a specified date/time:

With those Davega numbers I can do the calc for a 12s, but no idea what the 18s voltage/percentage mappings are.

I also have mobile notifications going for charge starts, charge stops (storage or full), and temperature alerts:

I got the voltage levels from a thread on the forum by @ janpom . But they’re expressed as a percentage of cell voltage, so it should work for both 12s and 18s as long as you have a cell voltage value :slight_smile:

Ah that makes sense. Brain was not working.

1 Like

hy sir, I need help can u pleas guide me regarding this project i want to interface JK-BMS with ESP-32

Sure but this thread is already a guide.

1 Like

Did anyone perhaps made a yaml for multiple bmss connected to 1 esp?

1 Like