ShineLAN-X: Ethernet + MQTT funktioniert
- Korrekte SPI2-Pins (PB12-PB15, RST=PC8) aus Referenz-Firmware übernommen - LEDs eingebunden (PC7/PB0/PB1/PC5), Startup-Blink - DHCP aktiviert, MQTT-Broker auf Heimnetz (<MQTT-BROKER-IP>) - Modbus UART auf USART3 (PB10/PB11, 115200 Baud) vorkonfiguriert - Kein RS485 DE/RE Pin — Wechselrichter nutzt direkten UART - UART-Port ausgelötet, muss nach Messung der korrekten Pins angepasst werden Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,22 +1,35 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// NETZWERK — ENC28J60 Ethernet (Bitbang-SPI auf Port C)
|
// Pin-Belegung — Quelle: https://github.com/mwalle/shinelanx-modbus
|
||||||
|
// Gleiche Platine, verifizierte Pins
|
||||||
// STM32F103RBT6, LQFP-64
|
// STM32F103RBT6, LQFP-64
|
||||||
// Hardware-SPI nicht nutzbar — alle SPI-Pins liegen auf Port C
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
// Bitbang-SPI Pins (alle gemessen)
|
// ENC28J60 — SPI2 (Hardware-SPI)
|
||||||
#define ETH_CS_PIN PC7 // ENC28J60 Pin 7 /CS → STM32 Pin 36 (gemessen)
|
// LQFP-64: PB12=33, PB13=34, PB14=35, PB15=36, PC6=37, PC8=39
|
||||||
#define ETH_SCK_PIN PC6 // ENC28J60 Pin 6 SCK → STM32 Pin 35 (gemessen)
|
#define ETH_CS_PIN PB12 // ENC28J60 /CS (SPI2 NSS)
|
||||||
#define ETH_MISO_PIN PC8 // ENC28J60 Pin 4 SO → STM32 Pin 37 (gemessen)
|
#define ETH_SCK_PIN PB13 // ENC28J60 SCK (SPI2 SCK)
|
||||||
#define ETH_MOSI_PIN PC9 // ENC28J60 Pin 5 SI → STM32 Pin ?? (noch unbekannt, Scan läuft)
|
#define ETH_MISO_PIN PB14 // ENC28J60 SO (SPI2 MISO)
|
||||||
|
#define ETH_MOSI_PIN PB15 // ENC28J60 SI (SPI2 MOSI)
|
||||||
|
#define ETH_RST_PIN PC8 // ENC28J60 /RESET
|
||||||
|
#define ETH_INT_PIN PC6 // ENC28J60 INT# (optional, polling reicht)
|
||||||
|
|
||||||
// ENC28J60 Reset
|
// LEDs
|
||||||
#define ETH_RST_PIN PB13 // ENC28J60 Pin 18 /RESET → STM32 Pin 32, 500Ω
|
#define LED_DEBUG PC7 // Debug-LED (grün o.ä.)
|
||||||
|
#define LED_RED PB1 // RGB Rot
|
||||||
|
#define LED_GREEN PB0 // RGB Grün
|
||||||
|
#define LED_BLUE PC5 // RGB Blau
|
||||||
|
|
||||||
|
// Taster
|
||||||
|
#define BTN_USER PA3 // User-Taster (low-aktiv)
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// NETZWERK
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
// 0 = DHCP, 1 = Statische IP
|
// 0 = DHCP, 1 = Statische IP
|
||||||
#define USE_DHCP 0
|
#define USE_DHCP 1
|
||||||
|
|
||||||
// Nur relevant wenn USE_DHCP = 0
|
// Nur relevant wenn USE_DHCP = 0
|
||||||
#define STATIC_IP 192,168,2,15
|
#define STATIC_IP 192,168,2,15
|
||||||
@@ -30,19 +43,23 @@
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
// MQTT
|
// MQTT
|
||||||
// ============================================================
|
// ============================================================
|
||||||
#define MQTT_BROKER "192.168.2.84"
|
#define MQTT_BROKER "192.168.1.1"
|
||||||
#define MQTT_PORT 1883
|
#define MQTT_PORT 1883
|
||||||
#define MQTT_USER "" // Leer lassen wenn kein Auth
|
#define MQTT_USER "mqtt"
|
||||||
#define MQTT_PASSWORD ""
|
#define MQTT_PASSWORD "HIER_MQTT_PASSWORT_EINTRAGEN"
|
||||||
#define MQTT_CLIENT "growatt-shinelan"
|
#define MQTT_CLIENT "growatt-shinelan"
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// RS485 / MODBUS
|
// MODBUS / WECHSELRICHTER-UART
|
||||||
// RS485 DE/RE → STM32 Pin 25 = PB1 (LQFP-64 neu gemessen)
|
// Growatt kommuniziert über USB-CDC (virtueller COM-Port) bei 115200 Baud —
|
||||||
// USART1: TX=PA9 (Pin 40), RX=PA10 (Pin 41)
|
// kein klassisches RS485, kein DE/RE-Pin nötig.
|
||||||
|
// Bestätigt durch ESPHome-Configs (tx=1, rx=3, baud=115200, kein flow_control_pin).
|
||||||
|
// Kandidat für STM32: USART3 (PB10=TX, PB11=RX) — auf Platine nachmessen!
|
||||||
|
// PA3 = Taster → USART2 (PA2/PA3) scheidet aus.
|
||||||
// ============================================================
|
// ============================================================
|
||||||
#define RS485_DE_PIN PB1 // RE/DE Steuerpin → STM32 Pin 25
|
#define MODBUS_TX_PIN PB10 // USART3 TX — TODO: auf Platine bestätigen
|
||||||
#define MODBUS_BAUD 9600 // Growatt Standard-Baudrate
|
#define MODBUS_RX_PIN PB11 // USART3 RX — TODO: auf Platine bestätigen
|
||||||
|
#define MODBUS_BAUD 115200 // Growatt USB-CDC Baudrate (nicht 9600 RS485!)
|
||||||
#define MODBUS_ADDR 1 // Modbus Slave-Adresse des Wechselrichters
|
#define MODBUS_ADDR 1 // Modbus Slave-Adresse des Wechselrichters
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
@@ -5,11 +5,12 @@
|
|||||||
#include <ModbusMaster.h>
|
#include <ModbusMaster.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
// Debug-UART: USART1, TX=PA9, RX=PA10 (Testpunkt auf Platine)
|
// Debug-UART: USART1 TX=PA9, RX=PA10
|
||||||
// HINWEIS: Modbus ist temporär deaktiviert — erst Ethernet/MQTT bestätigen,
|
|
||||||
// dann Debug entfernen und Modbus wieder aktivieren.
|
|
||||||
HardwareSerial DebugSerial(PA10, PA9);
|
HardwareSerial DebugSerial(PA10, PA9);
|
||||||
|
|
||||||
|
// Modbus-UART: USART3 TX=PB10, RX=PB11 (Growatt USB-CDC bei 115200 Baud)
|
||||||
|
HardwareSerial ModbusSerial(MODBUS_RX_PIN, MODBUS_TX_PIN);
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Sensor-Definition
|
// Sensor-Definition
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -17,7 +18,7 @@ struct Sensor {
|
|||||||
const char* id;
|
const char* id;
|
||||||
const char* name;
|
const char* name;
|
||||||
uint16_t address;
|
uint16_t address;
|
||||||
bool isDword; // true = 2 Register (32 bit), false = 1 Register (16 bit)
|
bool isDword;
|
||||||
float scale;
|
float scale;
|
||||||
const char* unit;
|
const char* unit;
|
||||||
const char* deviceClass;
|
const char* deviceClass;
|
||||||
@@ -25,65 +26,58 @@ struct Sensor {
|
|||||||
const char* icon;
|
const char* icon;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sensor-Liste — entspricht den Modbus-Registern des SPH 5000 TL3-BH-UP
|
|
||||||
// Für andere Modelle nicht zutreffende Sensoren auskommentieren.
|
|
||||||
const Sensor SENSORS[] = {
|
const Sensor SENSORS[] = {
|
||||||
// --- PV Eingang ---
|
// --- PV Eingang ---
|
||||||
{"pv1_voltage", "PV1 Voltage", 3, false, 0.1f, "V", "voltage", "measurement", "mdi:solar-panel"},
|
{"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_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"},
|
{"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_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_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"},
|
{"pv2_power", "PV2 Power", 9, true, 0.1f, "W", "power", "measurement", "mdi:solar-panel"},
|
||||||
// --- AC Ausgang / Netz ---
|
// --- AC Ausgang / Netz ---
|
||||||
{"ac_power_total", "AC Output Power Total", 35, true, 0.1f, "W", "power", "measurement", "mdi:flash"},
|
{"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_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_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_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_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_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_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"},
|
{"grid_current_l3", "Grid Current L3", 47, false, 0.1f, "A", "current", "measurement", "mdi:flash"},
|
||||||
// --- Energie PV ---
|
// --- Energie PV ---
|
||||||
{"energy_today", "Energy Today", 53, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:solar-power"},
|
{"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"},
|
{"energy_total", "Energy Total", 55, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:solar-power"},
|
||||||
// --- Temperatur ---
|
// --- Temperatur ---
|
||||||
{"inverter_temp", "Inverter Temperature", 93, false, 0.1f, "°C", "temperature", "measurement", "mdi:thermometer"},
|
{"inverter_temp", "Inverter Temperature", 93, false, 0.1f, "°C", "temperature", "measurement", "mdi:thermometer"},
|
||||||
// --- Batterie ---
|
// --- Batterie ---
|
||||||
{"bat_discharge_power", "Battery Discharge Power", 1009, true, 0.1f, "W", "power", "measurement", "mdi:battery-minus"},
|
{"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_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_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_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"},
|
{"bat_temperature", "Battery Temperature", 1040, false, 0.1f, "°C", "temperature", "measurement", "mdi:thermometer"},
|
||||||
// --- Netz- und Batterie-Energiezähler (für Energie-Dashboard) ---
|
// --- Energiezähler ---
|
||||||
{"energy_import_total", "Energy Import Total", 1046, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:transmission-tower-import"},
|
{"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"},
|
{"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_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"},
|
{"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]);
|
const uint8_t SENSOR_COUNT = sizeof(SENSORS) / sizeof(SENSORS[0]);
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Globale Objekte
|
// Globale Objekte
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// HardwareSerial modbusSerial(PA10, PA9); // USART1 — temporär deaktiviert (teilt sich UART mit DebugSerial)
|
|
||||||
|
|
||||||
byte mac[] = {MAC_ADDRESS};
|
byte mac[] = {MAC_ADDRESS};
|
||||||
EthernetClient ethClient;
|
EthernetClient ethClient;
|
||||||
PubSubClient mqtt(ethClient);
|
PubSubClient mqtt(ethClient);
|
||||||
// ModbusMaster modbus; // temporär deaktiviert
|
ModbusMaster modbus;
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// RS485 Richtungssteuerung (Callbacks für ModbusMaster)
|
// LED-Hilfsfunktionen
|
||||||
// ============================================================
|
// ============================================================
|
||||||
void preTransmission() { digitalWrite(RS485_DE_PIN, HIGH); }
|
void ledSet(uint8_t pin, bool on) { digitalWrite(pin, on ? LOW : HIGH); } // aktiv LOW
|
||||||
void postTransmission() { digitalWrite(RS485_DE_PIN, LOW); }
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// MQTT Hilfsfunktionen
|
// MQTT
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
// Veröffentlicht alle Sensor-Discovery-Pakete für Home Assistant
|
|
||||||
void publishDiscovery() {
|
void publishDiscovery() {
|
||||||
char topic[128];
|
char topic[128];
|
||||||
char payload[640];
|
char payload[640];
|
||||||
@@ -113,27 +107,23 @@ void publishDiscovery() {
|
|||||||
s.name,
|
s.name,
|
||||||
DEVICE_ID, s.id,
|
DEVICE_ID, s.id,
|
||||||
s.id,
|
s.id,
|
||||||
s.unit,
|
s.unit, s.deviceClass, s.stateClass, s.icon,
|
||||||
s.deviceClass,
|
|
||||||
s.stateClass,
|
|
||||||
s.icon,
|
|
||||||
DEVICE_ID, DEVICE_NAME, DEVICE_MODEL, DEVICE_MFR);
|
DEVICE_ID, DEVICE_NAME, DEVICE_MODEL, DEVICE_MFR);
|
||||||
|
|
||||||
mqtt.publish(topic, payload, true); // retained = true
|
mqtt.publish(topic, payload, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mqttReconnect() {
|
bool mqttReconnect() {
|
||||||
DebugSerial.print("MQTT connecting... ");
|
DebugSerial.print("MQTT connecting... ");
|
||||||
bool ok;
|
ledSet(LED_RED, true);
|
||||||
if (strlen(MQTT_USER) > 0) {
|
bool ok = (strlen(MQTT_USER) > 0)
|
||||||
ok = mqtt.connect(MQTT_CLIENT, MQTT_USER, MQTT_PASSWORD);
|
? mqtt.connect(MQTT_CLIENT, MQTT_USER, MQTT_PASSWORD)
|
||||||
} else {
|
: mqtt.connect(MQTT_CLIENT);
|
||||||
ok = mqtt.connect(MQTT_CLIENT);
|
|
||||||
}
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
DebugSerial.println("OK");
|
DebugSerial.println("OK");
|
||||||
|
ledSet(LED_RED, false);
|
||||||
|
ledSet(LED_GREEN, true);
|
||||||
publishDiscovery();
|
publishDiscovery();
|
||||||
} else {
|
} else {
|
||||||
DebugSerial.print("FAIL rc=");
|
DebugSerial.print("FAIL rc=");
|
||||||
@@ -146,122 +136,62 @@ bool mqttReconnect() {
|
|||||||
// Setup
|
// Setup
|
||||||
// ============================================================
|
// ============================================================
|
||||||
void setup() {
|
void setup() {
|
||||||
// Debug-UART zuerst starten (USART1: TX=PA9 = Testpunkt auf Platine)
|
|
||||||
DebugSerial.begin(115200);
|
DebugSerial.begin(115200);
|
||||||
delay(10);
|
delay(10);
|
||||||
DebugSerial.println("\r\n=== Growatt ShineLAN-X ===");
|
DebugSerial.println("\r\n=== Growatt ShineLAN-X ===");
|
||||||
DebugSerial.println("Build: " __DATE__ " " __TIME__);
|
DebugSerial.println("Build: " __DATE__ " " __TIME__);
|
||||||
|
|
||||||
// RS485 DE/RE Pin
|
// LEDs initialisieren (aktiv LOW laut Referenz)
|
||||||
pinMode(RS485_DE_PIN, OUTPUT);
|
pinMode(LED_DEBUG, OUTPUT); ledSet(LED_DEBUG, false);
|
||||||
digitalWrite(RS485_DE_PIN, LOW); // Empfangsmodus
|
pinMode(LED_RED, OUTPUT); ledSet(LED_RED, false);
|
||||||
|
pinMode(LED_GREEN, OUTPUT); ledSet(LED_GREEN, false);
|
||||||
|
pinMode(LED_BLUE, OUTPUT); ledSet(LED_BLUE, false);
|
||||||
|
|
||||||
// ENC28J60 Reset (Hardware-Reset vor init)
|
// 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...");
|
DebugSerial.println("ETH: reset...");
|
||||||
pinMode(ETH_RST_PIN, OUTPUT);
|
pinMode(ETH_RST_PIN, OUTPUT);
|
||||||
digitalWrite(ETH_RST_PIN, LOW);
|
|
||||||
delay(50);
|
|
||||||
digitalWrite(ETH_RST_PIN, HIGH);
|
|
||||||
delay(200);
|
|
||||||
|
|
||||||
// SO-Aktivitätstest: Ist der ENC28J60 überhaupt am Leben?
|
|
||||||
// PC8 mit Pull-Down → wenn ENC28J60 SO aktiv HIGH treibt = Chip antwortet
|
|
||||||
// Wenn PC8 bleibt LOW = SO ist High-Z = Chip tot/kein Takt/keine Power
|
|
||||||
pinMode(ETH_CS_PIN, OUTPUT); digitalWrite(ETH_CS_PIN, HIGH);
|
|
||||||
pinMode(ETH_SCK_PIN, OUTPUT); digitalWrite(ETH_SCK_PIN, LOW);
|
|
||||||
pinMode(PC8, INPUT_PULLDOWN);
|
|
||||||
|
|
||||||
DebugSerial.println("SO-Aktivitaetstest (PC8 = INPUT_PULLDOWN):");
|
|
||||||
|
|
||||||
// Test 1: CS hoch (SO sollte High-Z sein → LOW erwartet)
|
|
||||||
uint8_t so_cs_high = digitalRead(PC8);
|
|
||||||
DebugSerial.print(" CS=H SO="); DebugSerial.print(so_cs_high);
|
|
||||||
DebugSerial.println(so_cs_high == 0 ? " (High-Z, erwartet)" : " (getrieben! unerwartet)");
|
|
||||||
|
|
||||||
// Test 2: CS runter (ENC28J60 soll SO aktivieren)
|
|
||||||
digitalWrite(ETH_CS_PIN, LOW);
|
|
||||||
delayMicroseconds(50);
|
|
||||||
uint8_t so_cs_low = digitalRead(PC8);
|
|
||||||
digitalWrite(ETH_CS_PIN, HIGH);
|
|
||||||
DebugSerial.print(" CS=L SO="); DebugSerial.print(so_cs_low);
|
|
||||||
if (so_cs_low == 1) DebugSerial.println(" --> Chip treibt SO! Chip ist am Leben.");
|
|
||||||
else DebugSerial.println(" --> SO bleibt LOW = Chip antwortet nicht (kein Takt? kein Strom?)");
|
|
||||||
|
|
||||||
// Test 3: Reset-Zyklus, dann CS low
|
|
||||||
digitalWrite(ETH_RST_PIN, LOW); delay(10);
|
|
||||||
digitalWrite(ETH_RST_PIN, HIGH); delay(100);
|
|
||||||
digitalWrite(ETH_CS_PIN, LOW);
|
|
||||||
delayMicroseconds(50);
|
|
||||||
uint8_t so_after_reset = digitalRead(PC8);
|
|
||||||
digitalWrite(ETH_CS_PIN, HIGH);
|
|
||||||
DebugSerial.print(" nach Reset, CS=L SO="); DebugSerial.print(so_after_reset);
|
|
||||||
if (so_after_reset == 1) DebugSerial.println(" --> Chip lebt!");
|
|
||||||
else DebugSerial.println(" --> immer noch kein Leben");
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Roher SPI-Test: ESTAT-Register lesen (kein EthernetENC)
|
|
||||||
// ENC28J60 ESTAT = Bank0, Addr 0x1D
|
|
||||||
// Read Control Register: opcode 000 | addr 11101 = 0x1D
|
|
||||||
// Erwarteter Wert nach Reset: 0x01 (CLKRDY-Bit gesetzt)
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
// Langer Reset-Zyklus und Wartezeit für CLKRDY
|
|
||||||
pinMode(ETH_RST_PIN, OUTPUT);
|
|
||||||
digitalWrite(ETH_RST_PIN, LOW); delay(20);
|
digitalWrite(ETH_RST_PIN, LOW); delay(20);
|
||||||
digitalWrite(ETH_RST_PIN, HIGH); delay(800); // 25 MHz Quarz braucht <1 ms
|
digitalWrite(ETH_RST_PIN, HIGH); delay(200);
|
||||||
|
|
||||||
pinMode(ETH_CS_PIN, OUTPUT); digitalWrite(ETH_CS_PIN, HIGH);
|
Ethernet.init(ETH_CS_PIN);
|
||||||
pinMode(ETH_SCK_PIN, OUTPUT); digitalWrite(ETH_SCK_PIN, LOW);
|
DebugSerial.println("ETH: begin...");
|
||||||
pinMode(ETH_MISO_PIN, INPUT); // kein Pull — Chip treibt SO direkt
|
|
||||||
|
|
||||||
// Inline-Hilfsfunktion: 1 Byte über SPI senden und empfangen
|
#if USE_DHCP
|
||||||
// MOSI-Pin wird als Parameter übergeben (Kandidaten-Scan)
|
if (Ethernet.begin(mac) == 0) {
|
||||||
auto spiXfer = [](uint8_t mosiPin, uint8_t out) -> uint8_t {
|
DebugSerial.println("ETH: DHCP failed, reboot");
|
||||||
uint8_t in = 0;
|
ledSet(LED_RED, true);
|
||||||
for (int8_t i = 7; i >= 0; i--) {
|
delay(3000);
|
||||||
digitalWrite(mosiPin, (out >> i) & 1);
|
NVIC_SystemReset();
|
||||||
digitalWrite(ETH_SCK_PIN, HIGH);
|
|
||||||
in = (in << 1) | digitalRead(ETH_MISO_PIN);
|
|
||||||
digitalWrite(ETH_SCK_PIN, LOW);
|
|
||||||
}
|
|
||||||
return in;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto readESTAT = [&](uint8_t mosiPin) -> uint8_t {
|
|
||||||
digitalWrite(ETH_CS_PIN, LOW);
|
|
||||||
spiXfer(mosiPin, 0x1D); // RCR ESTAT
|
|
||||||
uint8_t val = spiXfer(mosiPin, 0x00);
|
|
||||||
digitalWrite(ETH_CS_PIN, HIGH);
|
|
||||||
return val;
|
|
||||||
};
|
|
||||||
|
|
||||||
// MOSI-Kandidaten
|
|
||||||
const uint8_t MOSI_CANDIDATES[] = { PC9, PB14, PB15, PB10, PB11, PA5, PA7 };
|
|
||||||
const char* MOSI_NAMES[] = {"PC9","PB14","PB15","PB10","PB11","PA5","PA7"};
|
|
||||||
const uint8_t NUM_CAND = sizeof(MOSI_CANDIDATES) / sizeof(MOSI_CANDIDATES[0]);
|
|
||||||
|
|
||||||
DebugSerial.println("SPI ESTAT-Scan (Erwartung: 0x01):");
|
|
||||||
for (uint8_t c = 0; c < NUM_CAND; c++) {
|
|
||||||
uint8_t pin = MOSI_CANDIDATES[c];
|
|
||||||
pinMode(pin, OUTPUT); digitalWrite(pin, LOW);
|
|
||||||
|
|
||||||
// Chip-Reset zwischen Kandidaten
|
|
||||||
digitalWrite(ETH_RST_PIN, LOW); delay(5);
|
|
||||||
digitalWrite(ETH_RST_PIN, HIGH); delay(50);
|
|
||||||
|
|
||||||
uint8_t estat = readESTAT(pin);
|
|
||||||
|
|
||||||
DebugSerial.print(" MOSI="); DebugSerial.print(MOSI_NAMES[c]);
|
|
||||||
DebugSerial.print(" -> ESTAT=0x"); DebugSerial.print(estat, HEX);
|
|
||||||
if (estat == 0x01) DebugSerial.println(" <-- TREFFER! SPI OK");
|
|
||||||
else if (estat == 0xFF) DebugSerial.println(" (kein Kontakt)");
|
|
||||||
else DebugSerial.println(" (unbekannt)");
|
|
||||||
|
|
||||||
pinMode(pin, INPUT); // danach wieder floating lassen
|
|
||||||
}
|
}
|
||||||
|
#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.println("Scan fertig.");
|
DebugSerial.print("ETH: IP=");
|
||||||
DebugSerial.println("Setup done (Ethernet deaktiviert bis MOSI gefunden).");
|
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 UART
|
||||||
|
ModbusSerial.begin(MODBUS_BAUD);
|
||||||
|
modbus.begin(MODBUS_ADDR, ModbusSerial);
|
||||||
|
|
||||||
|
DebugSerial.println("Setup done.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -270,7 +200,6 @@ void setup() {
|
|||||||
unsigned long lastUpdate = 0;
|
unsigned long lastUpdate = 0;
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
// MQTT Verbindung halten
|
|
||||||
if (!mqtt.connected()) {
|
if (!mqtt.connected()) {
|
||||||
if (!mqttReconnect()) {
|
if (!mqttReconnect()) {
|
||||||
delay(5000);
|
delay(5000);
|
||||||
@@ -282,11 +211,43 @@ void loop() {
|
|||||||
if (millis() - lastUpdate < UPDATE_INTERVAL) return;
|
if (millis() - lastUpdate < UPDATE_INTERVAL) return;
|
||||||
lastUpdate = millis();
|
lastUpdate = millis();
|
||||||
|
|
||||||
|
ledSet(LED_BLUE, true);
|
||||||
|
|
||||||
char stateTopic[64];
|
char stateTopic[64];
|
||||||
char valueStr[16];
|
char valueStr[16];
|
||||||
|
|
||||||
// Modbus temporär deaktiviert — Sensor-Loop übersprungen
|
for (uint8_t i = 0; i < SENSOR_COUNT; i++) {
|
||||||
DebugSerial.println("loop: MQTT ok, Modbus disabled");
|
const Sensor& s = SENSORS[i];
|
||||||
(void)stateTopic;
|
|
||||||
(void)valueStr;
|
uint8_t result;
|
||||||
|
uint32_t raw = 0;
|
||||||
|
|
||||||
|
if (s.isDword) {
|
||||||
|
result = modbus.readInputRegisters(s.address - 1, 2);
|
||||||
|
if (result == ModbusMaster::ku8MBSuccess)
|
||||||
|
raw = ((uint32_t)modbus.getResponseBuffer(0) << 16)
|
||||||
|
| modbus.getResponseBuffer(1);
|
||||||
|
} else {
|
||||||
|
result = modbus.readInputRegisters(s.address - 1, 1);
|
||||||
|
if (result == ModbusMaster::ku8MBSuccess)
|
||||||
|
raw = modbus.getResponseBuffer(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != ModbusMaster::ku8MBSuccess) {
|
||||||
|
DebugSerial.print("Modbus ERR ");
|
||||||
|
DebugSerial.print(s.id);
|
||||||
|
DebugSerial.print(" rc=");
|
||||||
|
DebugSerial.println(result, HEX);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user