Dashboard theo dõi hiệu suất tấm pin mặt trời
Trung cấp1/8/2026- Tác giả: IoTlabs Maker

Dashboard theo dõi hiệu suất tấm pin mặt trời

ESP32 đo điện áp và dòng điện ra của tấm pin mặt trời bằng cảm biến INA219, hiển thị realtime trên OLED cục bộ và một dashboard web đơn giản chạy ngay trên ESP32.

ESP32INA219Năng lượng mặt trờiDashboardSmart Energy

Dự án xây dựng một dashboard theo dõi hiệu suất tấm pin mặt trời dùng ESP32. Cảm biến INA219 mắc nối tiếp trên đường ra của tấm pin (hoặc bộ điều khiển sạc) đo điện áp bus và dòng điện qua shunt resistor tích hợp, từ đó ESP32 tính công suất tức thời.

Dữ liệu được hiển thị cục bộ trên màn hình OLED và đồng thời publish qua một web server nhỏ chạy ngay trên ESP32 (WiFi STA, tự động chuyển sang chế độ SoftAP cục bộ nếu không kết nối được mạng nhà) để xem realtime từ trình duyệt trong cùng mạng LAN, không cần dịch vụ cloud bên ngoài.

Hướng dẫn chi tiết

ESP32 + INA219 đo V/I tấm pin mặt trời, hiển thị OLED và dashboard web cục bộ realtime.

1. Giới thiệu

Dự án xây dựng một dashboard theo dõi hiệu suất tấm pin mặt trời dùng ESP32. Cảm biến INA219 mắc nối tiếp trên đường ra của tấm pin (hoặc bộ điều khiển sạc) đo điện áp bus và dòng điện qua shunt resistor tích hợp, từ đó ESP32 tính công suất tức thời.

Dữ liệu được hiển thị cục bộ trên màn hình OLED và đồng thời publish qua một web server nhỏ chạy ngay trên ESP32 (WiFi STA, tự động chuyển sang chế độ SoftAP cục bộ nếu không kết nối được mạng nhà) để xem realtime từ trình duyệt trong cùng mạng LAN — không cần dịch vụ cloud bên ngoài.

Ghi chú thông số: INA219 đo dòng ±3.2A và điện áp bus 0–26V theo datasheet TI, phù hợp với tấm pin mặt trời nhỏ/vừa dùng cho giáo dục và thử nghiệm (dưới 3A). Với tấm pin công suất lớn hơn nên chuyển sang INA226 (dải rộng hơn) hoặc thêm shunt resistor ngoài phù hợp dòng tải thực tế.

2. Linh kiện cần có

Linh kiện

Số lượng

Ghi chú

ESP32 DevKit V4

1

Board chính, chạy WebServer nội bộ

INA219 – Đo dòng điện & Công suất (I2C)

1

Đo V/I ra của tấm pin mặt trời

OLED 0.96" 128×64 I2C (SSD1306)

1

Hiển thị cục bộ

LED Xanh lá 5mm

1

Báo trạng thái WiFi/dashboard sẵn sàng

Điện trở 220Ω

1

Hạn dòng LED

3. Sơ đồ kết nối

INA219 / OLED (chung bus I2C)

ESP32

VCC (cả 2 module)

3V3

GND (cả 2 module)

GND

SDA (cả 2 module)

GPIO21

SCL (cả 2 module)

GPIO22

LED xanh

ESP32

Qua điện trở 220Ω

GPIO26

INA219 VIN+/VIN- mắc nối tiếp trên đường ra thực tế của tấm pin mặt trời (không nối vào ESP32) — địa chỉ I2C mặc định 0x40 không trùng OLED (0x3C) nên dùng chung bus an toàn.

4. Code ví dụ #1 — Đọc INA219 cơ bản qua Serial

// Vi du 1: Doc INA219 co ban qua Serial (chua co OLED/Web dashboard)
#include <Wire.h>
#include <Adafruit_INA219.h>

Adafruit_INA219 ina219;
bool ina219Available = false;

void setup() {
  Serial.begin(115200);
  delay(300);
  Serial.println("=== INA219 basic reader - Boot OK ===");
  ina219Available = ina219.begin();
  if (!ina219Available) {
    Serial.println("[INA219] Khong tim thay cam bien.");
  }
}

void loop() {
  if (ina219Available) {
    float v = ina219.getBusVoltage_V();
    float mA = ina219.getCurrent_mA();
    Serial.print("Panel: ");
    Serial.print(v, 2);
    Serial.print(" V, ");
    Serial.print(mA, 1);
    Serial.println(" mA");
  } else {
    Serial.println("Cam bien loi - du lieu null");
  }
  delay(1500);
}

5. Code ví dụ #2 — Hiển thị lên OLED cục bộ

// Vi du 2: Hien thi INA219 len OLED cuc bo (chua co Web dashboard)
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_INA219.h>

Adafruit_SSD1306 display(128, 64, &Wire, -1);
Adafruit_INA219 ina219;
bool oledAvailable = false;
bool ina219Available = false;

void setup() {
  Serial.begin(115200);
  delay(300);
  Serial.println("=== INA219 + OLED local - Boot OK ===");
  Wire.begin(21, 22);
  oledAvailable = display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  ina219Available = ina219.begin();
  if (oledAvailable) {
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0, 0);
    display.println("Solar Panel");
    display.display();
  }
}

void loop() {
  if (ina219Available) {
    float v = ina219.getBusVoltage_V();
    float mA = ina219.getCurrent_mA();
    Serial.printf("%.2fV %.1fmA\n", v, mA);
    if (oledAvailable) {
      display.clearDisplay();
      display.setCursor(0, 0);
      display.setTextSize(2);
      display.printf("%.2fV\n", v);
      display.setTextSize(1);
      display.printf("%.0f mA\n", mA);
      display.display();
    }
  } else {
    Serial.println("Cam bien loi - du lieu null");
  }
  delay(1500);
}

6. Code ví dụ #3 — Ứng dụng thực tế: OLED + Dashboard web nội bộ

/*
  Dashboard theo doi hieu suat tam pin mat troi (ESP32 + INA219 + OLED)
  Web server noi bo tren port 80, tu dong fallback SoftAP neu STA that bai.
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_INA219.h>
#include <WiFi.h>
#include <WebServer.h>

const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const unsigned long WIFI_CONNECT_TIMEOUT_MS = 15000;
const char* AP_SSID = "SolarDashboard-ESP32";
const char* AP_PASSWORD = "solar1234";

Adafruit_SSD1306 display(128, 64, &Wire, -1);
bool oledAvailable = false;
Adafruit_INA219 ina219;
bool ina219Available = false;
const int LED_STATUS_PIN = 26;
WebServer server(80);
bool apMode = false;

float lastVoltage = NAN, lastCurrentMA = NAN, lastPowerMW = NAN;

void handleRoot() {
  String html = "<!DOCTYPE html><html><head><meta charset='utf-8'>";
  html += "<meta http-equiv='refresh' content='3'><title>Solar Dashboard</title></head><body>";
  html += "<h2>Solar Panel Dashboard</h2>";
  if (!isnan(lastVoltage)) {
    html += "<p>Dien ap: " + String(lastVoltage, 2) + " V</p>";
    html += "<p>Dong dien: " + String(lastCurrentMA, 1) + " mA</p>";
    html += "<p>Cong suat: " + String(lastPowerMW, 1) + " mW</p>";
  } else {
    html += "<p>Cam bien INA219 khong san sang</p>";
  }
  html += "</body></html>";
  server.send(200, "text/html", html);
}

void handleJson() {
  String json = "{\"voltage_V\":";
  json += isnan(lastVoltage) ? "null" : String(lastVoltage, 3);
  json += ",\"current_mA\":";
  json += isnan(lastCurrentMA) ? "null" : String(lastCurrentMA, 2);
  json += ",\"mode\":\"" + String(apMode ? "ap" : "sta") + "\"}";
  server.send(200, "application/json", json);
}

bool connectWiFiOrFallbackToAP() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  unsigned long start = millis();
  while (WiFi.status() != WL_CONNECTED && millis() - start < WIFI_CONNECT_TIMEOUT_MS) delay(300);
  if (WiFi.status() == WL_CONNECTED) { apMode = false; return true; }
  WiFi.mode(WIFI_AP);
  WiFi.softAP(AP_SSID, AP_PASSWORD);
  apMode = true;
  return false;
}

void setup() {
  Serial.begin(115200);
  delay(300);
  Serial.println("=== Dashboard hieu suat tam pin mat troi - Boot OK ===");
  pinMode(LED_STATUS_PIN, OUTPUT);
  Wire.begin(21, 22);
  oledAvailable = display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  ina219Available = ina219.begin();
  connectWiFiOrFallbackToAP();
  digitalWrite(LED_STATUS_PIN, HIGH);
  server.on("/", handleRoot);
  server.on("/data.json", handleJson);
  server.begin();
}

void loop() {
  server.handleClient();
  static unsigned long lastTelemetryMs = 0;
  if (millis() - lastTelemetryMs >= 2000) {
    lastTelemetryMs = millis();
    if (ina219Available) {
      lastVoltage = ina219.getBusVoltage_V();
      lastCurrentMA = ina219.getCurrent_mA();
      lastPowerMW = ina219.getPower_mW();
    }
    Serial.print("{\"voltage_V\":");
    Serial.print(ina219Available ? String(lastVoltage, 3) : "null");
    Serial.println("}");
    if (oledAvailable) {
      display.clearDisplay();
      display.setCursor(0, 0);
      display.setTextSize(2);
      if (ina219Available) display.printf("%.2fV\n", lastVoltage); else display.println("--.--V");
      display.display();
    }
  }
}

7. Lỗi thường gặp

Lỗi

Nguyên nhân

Cách sửa

ina219.begin() trả về false

Sai địa chỉ I2C hoặc dây SDA/SCL lỏng

Kiểm tra dây, quét địa chỉ I2C (mặc định 0x40)

Không truy cập được dashboard qua trình duyệt

ESP32 rơi vào SoftAP nhưng máy tính vẫn ở mạng WiFi cũ

Kết nối máy tính/điện thoại vào SSID SolarDashboard-ESP32 rồi truy cập 192.168.4.1

Trang dashboard load nhưng số liệu là null

Cảm biến INA219 mất kết nối I2C giữa chừng

Kiểm tra lại dây/hàn nối, xem log Serial để xác nhận trạng thái cảm biến

OLED và dashboard web hiển thị số liệu lệch nhau

Bình thường do chu kỳ cập nhật khác nhau (OLED theo TELEMETRY_INTERVAL_MS, trình duyệt theo refresh 3s)

Không phải lỗi — có thể đồng bộ chu kỳ nếu cần độ chính xác cao hơn

8. Tổng kết

Bài viết trình bày cách dựng một dashboard theo dõi hiệu suất tấm pin mặt trời hoàn toàn cục bộ trên ESP32, không phụ thuộc dịch vụ cloud, dùng INA219 đo V/I và một web server nhỏ để xem realtime từ bất kỳ thiết bị nào trong mạng LAN (hoặc SoftAP khi không có WiFi nhà).

Kiến trúc dashboard-on-device này phù hợp cho các dự án giáo dục, thử nghiệm nhanh trước khi mở rộng lên nền tảng IoT cloud đầy đủ.