ESP32 BMS JIKONG 8S OUT 2 PARAMETER MAC MANUAL INPUT PLUS RESET LCD TAMPIL IP , AP ,SSID , STATUS BLUTUT

 esphome:

  name: "bms-monitoring"

  min_version: 2025.9.1

  build_path: build/dara-tes-esp

  on_boot:

    priority: -10

    then:

      - lambda: |-

          id(apply_hysteresis).execute();


esp32:

  board: esp32dev

  framework:

    type: esp-idf


logger:


api:


ota:

  - platform: esphome


# =========================

#       KONFIGURASI WIFI

# =========================

wifi:

  ap:

    ssid: "PUTU@TEKNO"

    password: "rejeki88"


captive_portal:


# Web Server aktif SETELAH perangkat terhubung ke WiFi.

web_server:

  port: 80


# =========================

#  PENGATURAN MAC ADDRESS MANUAL

# =========================

text:

  - platform: template

    name: "BMS MAC Address"

    id: bms_mac_address

    mode: text

    pattern: "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$"

    optimistic: true

    restore_value: true

    on_value:

      then:

        - lambda: |-

            auto mac_str_to_uint64 = [](std::string mac_str) -> uint64_t {

              mac_str.erase(std::remove(mac_str.begin(), mac_str.end(), ':'), mac_str.end());

              mac_str.erase(std::remove(mac_str.begin(), mac_str.end(), '-'), mac_str.end());

              if (mac_str.length() != 12) {

                ESP_LOGW("custom", "Format MAC address tidak valid (panjang salah): %s", mac_str.c_str());

                return 0;

              }

              char *end;

              const char *start = mac_str.c_str();

              uint64_t mac_addr = strtoull(start, &end, 16);

              if (end != start + 12) {

                ESP_LOGW("custom", "Karakter tidak valid di MAC address: %s", mac_str.c_str());

                return 0;

              }

              return mac_addr;

            };


            uint64_t new_mac = mac_str_to_uint64(x);

            if (new_mac != 0) {

              ESP_LOGD("custom", "Mengatur MAC address BLE baru ke: %s", x.c_str());

              id(client0).set_address(new_mac);

            } else {

              ESP_LOGE("custom", "Gagal mengatur MAC address baru karena format salah.");

            }


# =========================

#         BUTTONS (SATU BLOK)

# =========================

button:

  # Tombol SAVE MAC

  - platform: template

    name: "Save and Apply MAC Address"

    on_press:

      then:

        - logger.log: "Menyimpan dan menerapkan MAC address yang tersimpan..."

        - lambda: |-

            id(bms_mac_address).publish_state(id(bms_mac_address).state);


  # Factory Reset (hapus preferences: WiFi & nilai restore_value)

  - platform: factory_reset

    id: factory_reset_btn

    name: "Factory Reset (Erase Prefs)"


  # Opsional: Restart

  - platform: restart

    id: restart_device

    name: "Restart ESP"


# =========================

#       RESET PIN FISIK (LONG-PRESS 5s)

# =========================

binary_sensor:

  - platform: gpio

    id: reset_pin

    name: "Reset Pin (Hold ≥5s)"

    pin:

      number: GPIO25

      mode:

        input: true

        pullup: true

      inverted: true

    filters:

      - delayed_on: 50ms

      - delayed_off: 50ms

    on_press:

      - logger.log: "Reset pin: PRESS (start 5s timer)"

      - script.execute: reset_longpress

    on_release:

      - logger.log: "Reset pin: RELEASE (cancel timer)"

      - script.stop: reset_longpress


# Komponen JK-BMS

external_components:

  - source: github://syssi/esphome-jk-bms@main

    refresh: 0s


esp32_ble_tracker:

  scan_parameters:

    active: false


# Placeholder; atur via web

ble_client:

  - mac_address: "00:00:00:00:00:00"

    id: client0


    # === Tambahan: publish status BLE ke text_sensor ===

    on_connect:

      then:

        - logger.log: "BLE connected"

        - text_sensor.template.publish:

            id: ble_status

            state: "Connected"

    on_disconnect:

      then:

        - logger.log: "BLE disconnected"

        - text_sensor.template.publish:

            id: ble_status

            state: "Not Connected"


jk_bms_ble:

  - ble_client_id: client0

    protocol_version: JK02_32S

    throttle: 8s

    id: bms0


# --- I2C / OLED 0.96" + FONT ---

i2c:

  sda: GPIO21

  scl: GPIO22

  scan: true


font:

  - id: font_14

    file: "gfonts://Roboto"

    size: 14


# Text sensor Wi-Fi + BLE untuk OLED

text_sensor:

  - platform: wifi_info

    ip_address:

      name: "Device IP"

      id: device_ip

    ssid:

      name: "Connected SSID"

      id: connected_ssid


  # === Tambahan: text sensor template untuk status BLE ===

  - platform: template

    name: "BLE Status"

    id: ble_status

    update_interval: never

    icon: "mdi:bluetooth"


# =========================

#         SENSORS

# =========================

sensor:

  - platform: jk_bms_ble

    jk_bms_ble_id: bms0


    total_voltage:

      name: "JK Total Voltage"

      id: jk_total_voltage


    capacity_remaining:

      name: "JK SOC"

      id: jk_soc


    min_cell_voltage:

      name: "JK Min Cell V"

    max_cell_voltage:

      name: "JK Max Cell V"

    min_voltage_cell:

      name: "JK Min Voltage Cell #"

    max_voltage_cell:

      name: "JK Max Voltage Cell #"

    average_cell_voltage:

      name: "JK Avg Cell V"

    delta_cell_voltage:

      name: "JK Delta Cell V"


    # ====== 8S ONLY ======

    cell_voltage_1:

      name: "JK Cell V1"

    cell_voltage_2:

      name: "JK Cell V2"

    cell_voltage_3:

      name: "JK Cell V3"

    cell_voltage_4:

      name: "JK Cell V4"

    cell_voltage_5:

      name: "JK Cell V5"

    cell_voltage_6:

      name: "JK Cell V6"

    cell_voltage_7:

      name: "JK Cell V7"

    cell_voltage_8:

      name: "JK Cell V8"

    # (cell_voltage_9..15 dihapus untuk konfigurasi 8S)


# =========================

#         Kontrol GPIO

# =========================

output:

  - platform: gpio

    pin: GPIO32

    id: out_gpio32

  - platform: gpio

    pin: GPIO33

    id: out_gpio33


switch:

  # Output fisik

  - platform: output

    name: "SOC Output GPIO32"

    id: soc_output_32

    output: out_gpio32

    restore_mode: RESTORE_DEFAULT_OFF


  - platform: output

    name: "SOC Output GPIO33"

    id: soc_output_33

    output: out_gpio33

    restore_mode: RESTORE_DEFAULT_OFF


  # Toggle Auto Control per pin

  - platform: template

    name: "Auto Control by SOC (GPIO32)"

    id: auto_soc_enable_32

    optimistic: true

    restore_mode: RESTORE_DEFAULT_ON

    turn_on_action: []

    turn_off_action: []


  - platform: template

    name: "Auto Control by SOC (GPIO33)"

    id: auto_soc_enable_33

    optimistic: true

    restore_mode: RESTORE_DEFAULT_ON

    turn_on_action: []

    turn_off_action: []


number:

  # Ambang untuk GPIO32

  - platform: template

    name: "SOC Ah Min (ON) - GPIO32"

    id: soc_ah_min_on_32

    unit_of_measurement: "Ah"

    min_value: 0

    max_value: 100

    step: 0.1

    optimistic: true

    restore_value: true

    initial_value: 19.9


  - platform: template

    name: "SOC Ah Max (OFF) - GPIO32"

    id: soc_ah_max_off_32

    unit_of_measurement: "Ah"

    min_value: 0

    max_value: 100

    step: 0.1

    optimistic: true

    restore_value: true

    initial_value: 25.5


  # Ambang untuk GPIO33

  - platform: template

    name: "SOC Ah Min (ON) - GPIO33"

    id: soc_ah_min_on_33

    unit_of_measurement: "Ah"

    min_value: 0

    max_value: 100

    step: 0.1

    optimistic: true

    restore_value: true

    initial_value: 19.2


  - platform: template

    name: "SOC Ah Max (OFF) - GPIO33"

    id: soc_ah_max_off_33

    unit_of_measurement: "Ah"

    min_value: 0

    max_value: 100

    step: 0.1

    optimistic: true

    restore_value: true

    initial_value: 25.5


# =========================

#           SCRIPTS

# =========================

script:

  # Histeresis SOC → kontrol GPIO32/33

  - id: apply_hysteresis

    mode: queued

    then:

      # Kontrol untuk GPIO32

      - if:

          condition:

            lambda: |-

              return id(auto_soc_enable_32).state &&

                     (id(jk_soc).state <= id(soc_ah_min_on_32).state);

          then:

            - switch.turn_on: soc_output_32

      - if:

          condition:

            lambda: |-

              return id(auto_soc_enable_32).state &&

                     (id(jk_soc).state >= id(soc_ah_max_off_32).state);

          then:

            - switch.turn_off: soc_output_32


      # Kontrol untuk GPIO33

      - if:

          condition:

            lambda: |-

              return id(auto_soc_enable_33).state &&

                     (id(jk_soc).state <= id(soc_ah_min_on_33).state);

          then:

            - switch.turn_on: soc_output_33

      - if:

          condition:

            lambda: |-

              return id(auto_soc_enable_33).state &&

                     (id(jk_soc).state >= id(soc_ah_max_off_33).state);

          then:

            - switch.turn_off: soc_output_33


  # Timer long-press reset (tanpa menunggu tombol dilepas)

  - id: reset_longpress

    mode: restart

    then:

      - delay: 5s

      - if:

          condition:

            binary_sensor.is_on: reset_pin

          then:

            - logger.log: "Long press ≥5s terdeteksi → factory reset!"

            - text.set:

                id: bms_mac_address

                value: ""

            - lambda: |-

                id(client0).set_address(0);

            - button.press: factory_reset_btn

          else:

            - logger.log: "Tombol dilepas <5s → batal reset."


# Evaluasi berkala

interval:

  - interval: 5s

    then:

      - script.execute: apply_hysteresis


# --- OLED 0.96" (SSD1306 128x64) ---

display:

  - platform: ssd1306_i2c

    model: "SSD1306 128x64"

    address: 0x3C

    update_interval: 5s

    lambda: |-

      it.clear();

      // Jika sudah terhubung Wi-Fi (punya IP) → tampilkan SSID terhubung & IP

      if (id(device_ip).has_state()) {

        if (id(connected_ssid).has_state()) {

          it.printf(0, 0, id(font_14), "WiFi: %s", id(connected_ssid).state.c_str());

        } else {

          it.printf(0, 0, id(font_14), "WiFi: -");

        }

        it.printf(0, 20, id(font_14), "IP: %s", id(device_ip).state.c_str());

      } else {

        // Mode AP aktif → tampilkan SSID AP & password

        it.printf(0, 0, id(font_14), "AP: %s", "PUTU@TEKNO-BMS-JK");

        it.printf(0, 20, id(font_14), "PASS: %s", "rejeki88");

      }

      // SOC (Ah)

      if (id(jk_soc).has_state()) {

        it.printf(0, 40, id(font_14), "SOC: %.2f Ah", id(jk_soc).state);

      } else {

        it.printf(0, 40, id(font_14), "SOC: -");

      }

      // === Tambahan: Status Bluetooth ===

      if (id(ble_status).has_state()) {

        it.printf(0, 56, id(font_14), "BT: %s", id(ble_status).state.c_str());

      } else {

        it.printf(0, 56, id(font_14), "BT: -");

      }


Comments