BMS JIKONG ESP32 DEV kIT V4 TAMPILAN LCD KOMPLIT

 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-BMS-JK"
    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    # event-driven dari ble_client triggers
    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"

    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:
      name: "JK Cell V9"
    cell_voltage_10:
      name: "JK Cell V10"
    cell_voltage_11:
      name: "JK Cell V11"
    cell_voltage_12:
      name: "JK Cell V12"
    cell_voltage_13:
      name: "JK Cell V13"
    cell_voltage_14:
      name: "JK Cell V14"
    cell_voltage_15:
      name: "JK Cell V15"

# =========================
#         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