#include #include #include #include #include #include "config.h" // Debug-UART: USART1 TX=PA9, RX=PA10 HardwareSerial DebugSerial(PA10, PA9); // Modbus über USB-CDC (SerialUSB = PA11/PA12, Wechselrichter ist USB-Host) // ============================================================ // Sensor-Definition // ============================================================ struct Sensor { const char* id; const char* name; uint16_t address; bool isDword; float scale; const char* unit; const char* deviceClass; const char* stateClass; const char* icon; }; const Sensor SENSORS[] = { // --- PV Eingang --- {"pv1_voltage", "PV1 Voltage", 3, false, 0.1f, "V", "voltage", "measurement", "mdi:solar-panel"}, {"pv1_current", "PV1 Current", 4, false, 0.1f, "A", "current", "measurement", "mdi:solar-panel"}, {"pv1_power", "PV1 Power", 5, true, 0.1f, "W", "power", "measurement", "mdi:solar-panel"}, {"pv2_voltage", "PV2 Voltage", 7, false, 0.1f, "V", "voltage", "measurement", "mdi:solar-panel"}, {"pv2_current", "PV2 Current", 8, false, 0.1f, "A", "current", "measurement", "mdi:solar-panel"}, {"pv2_power", "PV2 Power", 9, true, 0.1f, "W", "power", "measurement", "mdi:solar-panel"}, // --- AC Ausgang / Netz --- {"ac_power_total", "AC Output Power Total", 35, true, 0.1f, "W", "power", "measurement", "mdi:flash"}, {"grid_frequency", "Grid Frequency", 37, false, 0.01f, "Hz", "frequency", "measurement", "mdi:sine-wave"}, {"grid_voltage_l1", "Grid Voltage L1", 38, false, 0.1f, "V", "voltage", "measurement", "mdi:flash"}, {"grid_current_l1", "Grid Current L1", 39, false, 0.1f, "A", "current", "measurement", "mdi:flash"}, {"grid_voltage_l2", "Grid Voltage L2", 42, false, 0.1f, "V", "voltage", "measurement", "mdi:flash"}, {"grid_current_l2", "Grid Current L2", 43, false, 0.1f, "A", "current", "measurement", "mdi:flash"}, {"grid_voltage_l3", "Grid Voltage L3", 46, false, 0.1f, "V", "voltage", "measurement", "mdi:flash"}, {"grid_current_l3", "Grid Current L3", 47, false, 0.1f, "A", "current", "measurement", "mdi:flash"}, // --- Energie PV --- {"energy_today", "Energy Today", 53, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:solar-power"}, {"energy_total", "Energy Total", 55, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:solar-power"}, // --- Temperatur --- {"inverter_temp", "Inverter Temperature", 93, false, 0.1f, "°C", "temperature", "measurement", "mdi:thermometer"}, // --- Batterie --- {"bat_discharge_power", "Battery Discharge Power", 1009, true, 0.1f, "W", "power", "measurement", "mdi:battery-minus"}, {"bat_charge_power", "Battery Charge Power", 1011, true, 0.1f, "W", "power", "measurement", "mdi:battery-plus"}, {"bat_voltage", "Battery Voltage", 1013, false, 0.1f, "V", "voltage", "measurement", "mdi:battery"}, {"bat_soc", "Battery State of Charge", 1014, false, 1.0f, "%", "battery", "measurement", "mdi:battery"}, {"bat_temperature", "Battery Temperature", 1040, false, 0.1f, "°C", "temperature", "measurement", "mdi:thermometer"}, // --- Energiezähler --- {"energy_import_total", "Energy Import Total", 1046, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:transmission-tower-import"}, {"energy_export_total", "Energy Export Total", 1050, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:transmission-tower-export"}, {"bat_discharge_total", "Battery Discharge Total", 1054, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:battery-minus"}, {"bat_charge_total", "Battery Charge Total", 1058, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:battery-plus"}, }; const uint8_t SENSOR_COUNT = sizeof(SENSORS) / sizeof(SENSORS[0]); // ============================================================ // Globale Objekte // ============================================================ byte mac[] = {MAC_ADDRESS}; EthernetClient ethClient; PubSubClient mqtt(ethClient); ModbusMaster modbus; // ============================================================ // LED-Hilfsfunktionen // ============================================================ void ledSet(uint8_t pin, bool on) { digitalWrite(pin, on ? LOW : HIGH); } // aktiv LOW // ============================================================ // MQTT // ============================================================ void publishDiscovery() { char topic[128]; char payload[640]; for (uint8_t i = 0; i < SENSOR_COUNT; i++) { const Sensor& s = SENSORS[i]; snprintf(topic, sizeof(topic), "homeassistant/sensor/%s_%s/config", DEVICE_ID, s.id); snprintf(payload, sizeof(payload), "{" "\"name\":\"%s\"," "\"unique_id\":\"%s_%s\"," "\"state_topic\":\"growatt/shinelan/%s\"," "\"unit_of_measurement\":\"%s\"," "\"device_class\":\"%s\"," "\"state_class\":\"%s\"," "\"icon\":\"%s\"," "\"device\":{" "\"identifiers\":[\"%s\"]," "\"name\":\"%s\"," "\"model\":\"%s\"," "\"manufacturer\":\"%s\"" "}}", s.name, DEVICE_ID, s.id, s.id, s.unit, s.deviceClass, s.stateClass, s.icon, DEVICE_ID, DEVICE_NAME, DEVICE_MODEL, DEVICE_MFR); mqtt.publish(topic, payload, true); } } bool mqttReconnect() { DebugSerial.print("MQTT connecting... "); ledSet(LED_RED, true); bool ok = (strlen(MQTT_USER) > 0) ? mqtt.connect(MQTT_CLIENT, MQTT_USER, MQTT_PASSWORD) : mqtt.connect(MQTT_CLIENT); if (ok) { DebugSerial.println("OK"); ledSet(LED_RED, false); ledSet(LED_GREEN, true); publishDiscovery(); } else { DebugSerial.print("FAIL rc="); DebugSerial.println(mqtt.state()); } return ok; } // ============================================================ // Setup // ============================================================ void setup() { DebugSerial.begin(115200); delay(10); DebugSerial.println("\r\n=== Growatt ShineLAN-X ==="); DebugSerial.println("Build: " __DATE__ " " __TIME__); // LEDs initialisieren (aktiv LOW laut Referenz) pinMode(LED_DEBUG, OUTPUT); ledSet(LED_DEBUG, false); pinMode(LED_RED, OUTPUT); ledSet(LED_RED, false); pinMode(LED_GREEN, OUTPUT); ledSet(LED_GREEN, false); pinMode(LED_BLUE, OUTPUT); ledSet(LED_BLUE, false); // Startup-Blink: alle LEDs kurz an ledSet(LED_RED, true); ledSet(LED_GREEN, true); ledSet(LED_BLUE, true); delay(300); ledSet(LED_RED, false); ledSet(LED_GREEN, false); ledSet(LED_BLUE, false); // ENC28J60 Reset DebugSerial.println("ETH: reset..."); pinMode(ETH_RST_PIN, OUTPUT); digitalWrite(ETH_RST_PIN, LOW); delay(20); digitalWrite(ETH_RST_PIN, HIGH); delay(200); Ethernet.init(ETH_CS_PIN); DebugSerial.println("ETH: begin..."); #if USE_DHCP if (Ethernet.begin(mac) == 0) { DebugSerial.println("ETH: DHCP failed, reboot"); ledSet(LED_RED, true); delay(3000); NVIC_SystemReset(); } #else IPAddress ip(STATIC_IP); IPAddress gw(STATIC_GW); IPAddress sn(STATIC_SUBNET); IPAddress dns(STATIC_DNS); Ethernet.begin(mac, ip, dns, gw, sn); #endif DebugSerial.print("ETH: IP="); DebugSerial.println(Ethernet.localIP()); DebugSerial.print("ETH: link="); DebugSerial.println(Ethernet.linkStatus() == LinkON ? "UP" : "DOWN"); if (Ethernet.linkStatus() == LinkON) ledSet(LED_DEBUG, true); // MQTT mqtt.setServer(MQTT_BROKER, MQTT_PORT); mqtt.setBufferSize(768); // Modbus: USB-CDC (PA11/PA12) — wird aktiviert sobald Wechselrichter angeschlossen // SerialUSB.begin(MODBUS_BAUD); // modbus.begin(MODBUS_ADDR, SerialUSB); DebugSerial.println("Setup done."); } // ============================================================ // Loop // ============================================================ unsigned long lastUpdate = 0; void loop() { if (!mqtt.connected()) { if (!mqttReconnect()) { delay(5000); return; } } mqtt.loop(); if (millis() - lastUpdate < UPDATE_INTERVAL) return; lastUpdate = millis(); ledSet(LED_BLUE, true); char stateTopic[64]; char valueStr[16]; for (uint8_t i = 0; i < SENSOR_COUNT; i++) { const Sensor& s = SENSORS[i]; uint8_t result; uint32_t raw = 0; // TODO: Modbus aktivieren sobald USB-CDC (Wechselrichter) angeschlossen result = ModbusMaster::ku8MBSuccess; // Platzhalter raw = 0; (void)result; float value = raw * s.scale; dtostrf(value, 1, (s.scale < 0.1f) ? 2 : 1, valueStr); snprintf(stateTopic, sizeof(stateTopic), "growatt/shinelan/%s", s.id); mqtt.publish(stateTopic, valueStr); } ledSet(LED_BLUE, false); DebugSerial.println("Update done."); }