Integration Husquarna Automower G2 mit TASMOTA in FHEM

Begonnen von e-t-h, 24 Juli 2020, 15:50:30

Vorheriges Thema - Nächstes Thema

e-t-h

Hier geht es um eine Anbindung eines Husquarna G2 Modells an FHEM.
Die hier vorgestellte Lösung ist bei mir an einem Husqvarna ACX 230 eingesetzt und sollte auch an allen anderen G2-Typen funktionieren.

Viele Informationen zum Mower habe ich mir aus verschiedensten Foren zusammengetragen, besonders hilfreich war dieses hier aus dem HomeMatic-Forum (https://homematic-forum.de/forum/viewtopic.php?f=31&t=7295), wo auch viele Verweise auf andere Foren zu finden sind. In der PDF dort gleich am Anfang ist die Anschlussbelegung des Steckers zu finden. Vielen Dank an dieser Stelle an Mule!
Auch das Forum https://www.roboternetz.de/community/threads/1330-AutoMower-von-Elektrolux-und-Husqvarna/page9 war sehr hilfreich, hier habe ich Basis für die Excel-Tabelle her. Danke an Vogon!
Und natürlich der Klassiker: http://automowerfans.free.fr, Merci beaucoup @poildecarotte!

Zwei wesentliche Komponenten sind meine Basis:

die Einbindung über WLAN an MQTT
die Anbindung von MQTT an FHEM


Einbindung Mower über WLAN und MQTT


Die Einbindung des Mowers funktioniert über ein ESP01 Board, welches hardwareseitig an die Diagnoseschnittstelle des Mowers angeschlossen ist. Natürlich sind auch alle anderen, größeren Vertreter der ESP-Boards möglich.

Ich habe eine kleine Lochrasterplatine im Bedienteil untergebracht, auf der der ESP und ein Stützkondensator mit ca. 100µF untergebracht sind. Da an der Diagnoseschnittstelle des Mowers alles über 3,3V läuft ist der Aufwand sehr übersichtlich. Stecker Pin 1->3,3V an ESP-3,3V, Stecker Pin2->Masse an ESP-Masse, Stecker Pin3-> Tx an ESP-Rx und Stecker Pin4(quadratisch)-> Rx an ESP-Tx.

Seit vorsichtig, wenn hier dran baut! Messt zur Orientierung vorher am Stecker wo + und – ist. Zieht die Akkustecker beim Hantieren ab und prüft vor dem wieder reinstecken besser 3 Mal ob alles richtig ist. Und Bitte: alles auf eigene Gefahr!

Ich habe selbst ein Motherboard für knapp 400,-€ auf dem Gewissen. Zugegebenermaßen ist das aber bei ersten Tests passiert, als der Mower auf dem Weg unter die Schrankwand den Kabelbaum der FTDI-Adapters vom Schreibtisch mit sich riss...  >:(

Der Diagnosestecker ist schwer zu beschaffen und müsste von außen gesteckt sein. Es gibt je Model auch innen eine weitere Kontaktleiste die man nutzen könnte. Ich habe mich entschieden die Lochrasterplatine direkt an die Leitungen zum Stecker zulöten. Sollte der Mower zum Service müssen, ist es auf alle Fälle ratsam den ESP von der Platine zu ziehen!

Bilder findet Ihr im Anhang..

Das ESP01 Board habe ich mit TASMOTA geflasht. Ich verwende immer die Script-Erweiterung von GEMU (Doku unter https://tasmota.github.io/docs/Scripting-Language/), die eine relativ einfache und geniale Programmierumgebung innerhalb TASMOTA zur Verfügung stellt. Mit den letzten Erweiterungen (Stand Juli 20) ist es möglich den Script-Speicher auf ca. 4000 Zeichen ,,aufzubohren", was es ermöglichte die Statustexte alle auch hier mit unterzubringen. So könnte man das Ganze auch ohne weitere übergeordnete HA-Lösungen nutzen.

Das Script für den ESP macht im Grunde nur folgendes:

Innerhalb der TELEPERIOD werden 10 interessante Registerwerte (siehe Excel-Tabelle) zyklisch abgefragt, konvertiert und über den MQTT-Sensor-String an den MQTT-Server published.

Aus den bekannten Statusmeldungen aus Register 01F1 und den Quittungen des Beschreibens von 812C wird zusätzlich ein ,,Substatus" mit den folgenden 5 Zuständen gebildet: 1:charge 2:wait 3:error 4:move 5:mow. Die TELEPERIOD wird in Abhängigkeit dieser Substatis angepasst, für 1..3 beträgt sie 30 sec, ansonsten 10 sec. Das soll für zügige Updates in FEHM sorgen.

Weiterhin wird ein MQTT Pfad ,,%topic%/cmnd/MOW" subscribed, über den die Kommandos an den Mower gesendet werden können. Da es eigentlich nur darum geht ihn zur Arbeit oder wieder nach Hause zu schicken, reichen die zwei Kommandos ,,MODE_HOME" und ,,MODE_AUTO" aus. Eine 0 an /MOW stoppt, eine 1 startet den Mäher.

Das Binary zum Flashen des EPS01 muss selbst und mit folgenden Compilerschaltern erstellt werden:

#define EEP_SCRIPT_SIZE 4096 // mit Speichererweiterung
#define USE_EEPROM
#undef USE_RULES // Add support for rules (+8k code)
#define USE_SCRIPT // Add support for script (+17k code)
#define SUPPORT_MQTT_EVENT // enables support for subscribe unsubscribe
#define USE_SCRIPT_WEB_DISPLAY
#define USE_SCRIPT_STATUS
#define USE_SCRIPT_JSON_EXPORT //enable >J section (publish JSON payload on TelePeriod)
#define USE_SCRIPT_WEB_DISPLAY //enable >W section (modify web UI)
#define SCRIPT_FULL_WEBPAGE //enable >w section (seperate full web page and webserver)


Der Quelltext des Scriptes ist kommentiert. Ich empfehle den von GEMU entwickelten Editor zu nutzen, dieser entfernt die Kommentare beim Übertragen an den ESP und spart den knappen Platz. Der Workflow ist dabei so:

- Bearbeiten des Quelltextes aus einen *.txt Datei (ein schönes Backup)
- Senden per Tastendruck an den ESP
Den Download für den Editor und seine Doku findet Ihr für Win und Mac, wenn Ihr in der Script-Doku (s.o.) nach ,,Optional external editor" sucht. Ihr müsst dafür noch die IP-Adresse Eures ESP-Boards im Quelltext unter ,,IP=xxxx" anpassen.

Nehmt Ihr den Editor nicht, entfernt auf alle Fälle die Zeilen mit dem ,,SB=.." und ,,IP=.." und vielleicht die Kommentare.
Hier der Quellcode in Tasmota-Script:


>D 45
SB=4096
IP=192.168.xxx.xxx
;input string via serialreceive
is=""
;send string
ss=""
;current command
ac=""
tt=""
MOW=0
lo=0
rt=0
;mqtt status values, see >J for description
mt=0
ak=0
ct=0
cb=0
st=0
s=6
bt=0
tc=0
uc=0
bv=0
nc=0

>B
;SetOption64 1
=>Baudrate 9600

>E
is=SerialReceived
;print is-> %is%
if sl(is)%10==0
;otherwhise there must be an communication error, if so discard packet
{
for lo 1 int(sl(is)/10) 1
;split packet in single register information and separate register in ac
ac=sb(is ((lo-1)*10)+2 4)
;convert hex return into value in rt
rt=hd(sb(is ((lo-1)*10)+8 2)+sb(is ((lo-1)*10)+6 2))
print lo %lo% ac %ac% rt %rt%
;assign to mqtt values
switch ac
case "01F1"
st=rt
case "01EB"
ak=rt
;this is signed integer
if rt>32767
{
ak=rt-65536
}
case "01EF"
cb=rt
case "0038"
mt=rt
case "01EC"
ct=rt
case "2EE0"
uc=rt
case "0233"
bt=rt
case "2EF4"
bv=rt
case "0234"
tc=rt
case "00B1"
nc=rt
case "812C"
;return of HOME and STOP cmd's
st=rt

ends
next
}

>S
if mqttc>0
{
;for start/stop via MQTT
+>Subscribe MOW,%topic%/cmnd/MOW
}

if upd[MOW]>0
{
if MOW>0
{
print Start mowing
=#sd("0F812C0001")
tper=10
} else {
print Move Home
=#sd("0F812C0003")
tper=10
}
}

;ask every cycle for only 4 registers in maximum, because of limited size of string vars
;timing depends on teleperiode to reduce traffic

switch upsecs%tper
case tper-3
=#sd("0F01F100000F01EB00000F003800000F01EF0000")
case tper-2
=#sd("0F01EC00000F2EE000000F2EF400000F02330000")
case tper-1
=#sd("0F023400000F00B10000")
ends
;s stores aggretated substates 1:charge 2:wait 3:error 4:move 5:mow
s=3
switch st
case 0
=#pt("Mode Manual quittiert ")
s=4
case 1
;1..3 initiated by cmd 812C!
=#pt("Mode Auto quittiert")
s=4
case 3
=#pt("Mode HOME quittiert")
s=4
case 6
=#pt("Radmotor blockiert")
case 12
=#pt("Kein Schleifensig.")
case 16
=#pt("Außerhalb")
case 18
=#pt("Niedrige Batteriesp.")
case 26
=#pt("Ladestation block.")
case 34
=#pt("Mäher gehoben")
case 52
=#pt("Kein Kontakt zur LS")
case 54
=#pt("Pin abgelaufen")
case 1000
=#pt("aus LS fahren")
s=4
case 1002
=#pt("Mähen")
s=5
case 1006
=#pt("Mähwerk start")
s=5
case 1008
=#pt("Mähwerk gestartet")
s=5
case 1012
=#pt("Signal starte Mähwerk")
s=5
case 1014
=#pt("Laden")
s=1
case 1016
=#pt("in LS wartend")
s=2
case 1024
=#pt("aus LS einfahren")
s=4
case 1036
=#pt("Viereckmodus")
s=5
case 1038
=#pt("Festgefahren")
case 1040
=#pt("Kollision")
s=4
case 1042
=#pt("Suchen")
s=4
case 1044
=#pt("Stop")
case 1048
=#pt("Andocken")
s=2
case 1050
=#pt("aus LS ausfahren")
s=4
case 1052
=#pt("Fehler")
case 1056
=#pt("Wartet Man./Home")
s=2
case 1058
=#pt("Begrenzung folg.")
s=4
case 1060
=#pt("N-Signal gefunden")
s=4
case 1062
=#pt("Festgefahren")
case 1064
=#pt("Suchen")
s=4
case 1066
=#pt("Fernverfolgungsproblem")
s=3
case 1070
=#pt("Suchschleife folgen")
s=4
case 1072
=#pt("Schleife folgen")
s=4
ends
;reduce traffic if mower is not in action
if s<4
{
tper=30
} else {
tper=10
}

#pt(tt)
print Status: %tt% %0tper%

#sd(ss)
;sends data to robo
=>SerialSend5 %ss%

>J
;add data to MQTT sensor publish
,"status":%0st%,"sstate":%0s%,"mow time":%0mt%,"time since chg":%0tc%,"batt current":%0ak%,"batt charge time":%0ct%,"batt charged":%0cb%,"batt temp":%0bt%,"batt used":%0uc%,"batt voltage":%0bv%,"batt capacity":%0nc%




Habt Ihr das Script geladen lohnt sich ein Blick in die Tasmota-Konsole: dort gibt es hoffentlich das erste Erfolgserlebnis in Form einer funktionierenden Kommunikation.
Für die, die mit Tasmota noch nicht so viel anfangen können, schaut mal hier:
https://forum.creationx.de/index.php?tagged/21-tasmota/&objectType=com.woltlab.wbb.thread
dort habe ich das hier auch noch mal gepostet.

Anbindung über MQTT an FHEM

Basis dafür ist das MQTT2_DEVICE. Ich persönlich nutze es im Zusammenhang mit einem MOSQUITTO-MQTT-Server.

Hier die wichtigsten Aufrufe beim Anlegen des Devices:


defmod <name> MQTT2_DEVICE
attr <name>IODev <yourBroker>
attr <name>icon scene_robo_lawnmower
attr <name>readingList <your topic>/tele/LWT:.* LWT\
  <your topic>/tele/STATE:.* { json2nameValue($EVENT) }\
  <your topic>/tele/SENSOR:.* { json2nameValue($EVENT) }\
  <your topic>/tele/INFO.:.* { json2nameValue($EVENT) }\
  <your topic>/stat/RESULT:.* { json2nameValue($EVENT) }
attr <name>room Garten
attr <name>setList off:noArg <your topic>/cmnd/MOW 0\
  on:noArg <your topic>/cmnd/MOW 1
attr <name> stateFormat sstate


<your topic> ist dabei durch das eigene Topic zu ersetzen, <name> mit dem gewünschten Gerätenamen und <yourBroker> mit eurer MQTT Serverinstanz in FEHM.

Nach erfolgreichem Laden wird man nach ein paar Sekunden die Readings sehen können.
Das entscheidende Attribut für die Visualisierung ist das devStateIcon. Hierin befindet sich ein etwas größerer PERL-Codeblock, den ich zum besseren Verständnis hier extra aufführe. Einfach alles inklusive der beiden geschweiften Klammern kopieren und in das Attribut einfügen!


{
use v5.10;
my $st = 0;
my $bsp = 0.0;
my $pic = 'scene_robo_lawnmower@red';
my $picb = 'measure_battery_0@red';
my $RSt = -1;
my $stattxt ='unknown';
$st=ReadingsVal($name,"sstate",0);
$bsp=ReadingsVal($name,"batt_used",0)/ReadingsVal($name,"batt_capacity",0);
$RSt = ReadingsVal($name,"status",-1);
given($st)
{
when (1) {$pic='hue_filled_plug';}
when (2) {$pic='measure_battery_100@green';}
when (3) {$pic='scene_robo_lawnmower@red';}
when (4) {$pic='scene_robo_lawnmower@orange';}
when (5) {$pic='scene_robo_lawnmower@green';}
}
if ($bsp>=0.12) {$picb='measure_battery_25@red';}
if ($bsp>=0.37) {$picb='measure_battery_50@red';}
if ($bsp>=0.65) {$picb='measure_battery_75@red';}
if ($bsp==1) {$picb='measure_battery_100@green';}
given($RSt)
{
when (0) {$stattxt='Mode Manual quittiert';}
when (1) {$stattxt='Mode Auto quittiert';}
when (3) {$stattxt='Mode HOME quittiert';}
when (6) {$stattxt='Linker Radmotor blockiert';}
when (12) {$stattxt='Kein Schleifensignal';}
when (16) {$stattxt='Außerhalb';}
when (18) {$stattxt='Niedrige Batteriespannung';}
when (26) {$stattxt='Ladestation blockiert';}
when (34) {$stattxt='Mäher hochgehoben';}
when (52) {$stattxt='Kein Kontakt zur Ladestation';}
when (54) {$stattxt='Pin abgelaufen';}
when (1000) {$stattxt='aus LS ausfahren';}
when (1002) {$stattxt='Mähen';}
when (1006) {$stattxt='Mähwerk starten';}
when (1008) {$stattxt='Mähwerk gestartet';}
when (1012) {$stattxt='Signal starte Mähwerk';}
when (1014) {$stattxt=sprintf("Laden %.01fA, %.01fV, %dmin",ReadingsVal($name,"batt_current",0)/1000,ReadingsVal($name,"batt_voltage",0)/1000,ReadingsVal($name,"batt_charge_time",0));}
when (1016) {$stattxt='in LS wartend';}
when (1024) {$stattxt='aus LS einfahren';}
when (1036) {$stattxt='Viereckmodus';}
when (1038) {$stattxt='Festgefahren';}
when (1040) {$stattxt='Kollision';}
when (1042) {$stattxt='Suchen';}
when (1044) {$stattxt='Stop';}
when (1048) {$stattxt='Andocken';}
when (1050) {$stattxt='aus LS ausfahren';}
when (1052) {$stattxt='Fehler';}
when (1056) {$stattxt=' Wartet (Modus Manuell/Home)';}
when (1058) {$stattxt='Begrenzung folgen';}
when (1060) {$stattxt='N-Signal gefunden';}
when (1062) {$stattxt='Festgefahren';}
when (1064) {$stattxt='Suchen';}
when (1066) {$stattxt='Fernverfolgungsproblem';}
when (1070) {$stattxt='Suchschleife folgen';}
when (1072) {$stattxt='Schleife folgen';}
default {$stattxt='??? unbekannt ???';}
}
"<div>". sprintf(" (%d%, %d°C)",$bsp*100,ReadingsVal($name,"batt_temp",0)) . FW_makeImage("$picb") ." ". FW_makeImage("$pic") . "</br>" . sprintf(" (%d) ",$RSt) . sprintf($stattxt) . "</div>"
}


Am Ende sollte das dabei herauskommen:

(siehe Bild unten)

Man sieht die zwei (bunten) devStateIcons, die sich in Abhängigkeit des Batteriezustandes und des Substatuses ändern. Zur Batterie gibt es einen Ladezustand und die Temperatur. In der unteren Zeile wird der aktuelle Status mit seiner Nummer in Klammern angezeigt, beim Laden mit Ladestrom und Spannung. ,,On" schickt den Mower zur Arbeit, ,,Off" holt ihn zurück an die Station. Die Readings kann man ja bekanntermaßen aus anderen Elementen in FHEM abfragen und für die Steuerung anderer Komponenten (Beregnung usw.) auswerten.

Zur Erklärung des Perl-Codes:

Nach der Deklaration der Variablen werden verschiedene Werte aus den Readings errechnet. Im ersten given Element (was in anderen Sprachen so etwas wie ein Switch/case ist) wird das Icon für den Substatus festgelegt. Die if Anweisungen legen das Icon für den Batteriezustand fest. Das zweite given-Konstrukt bildet den Statustext in Abhängigkeit des Gerätestatus und ergänzt ggf. um Readingwerte (Strom/Spannung).
Zum Schluss wir das Ganze formatiert und ausgegeben.

Excel Tabelle:

Ich füge noch eine Excel-Tabelle bei, die ich aus o.g. Forum bekommen und adaptiert habe.

Im Reiter ,,Register" sind alle Register aufgeführt. Ich hatte teilweise widersprüchliche Infos in verschiedenen Foren gefunden, diese findet man ab Spalte F. Da dies für mich nicht relevant war bin ich dem nicht nachgegangen. In der Spalte MQTT finden sich die zugeordneten Variablennamen für das Script.
Im Reiter Kommando finden sich die Register für die Kommandos (stammt von Mule aus dem von Ihm geschriebenen Deamon). Ich verwende davon nur zwei.
In Status findet man die bekannten Statis, meist auch aus Mule's Daemon entnommen. Hier findet sich die Zuweisung zu den Sub-Statis, in H wird der PERL-Code für die when-Zweige zum rauskopieren gebildet.

Ich habe gelegentlich Statis war genommen, die nicht in dieser Tabelle aufgeführt sind. Wenn jemand hierzu noch etwas beitragen kann: bitte gerne!


Viel Spass!

Ekkehard.
e-t-h

cpramhofer

Lieber e-t-h

ich habe einen alten AC 220 geerbt der seinen Dienst tadellos verrichtet aber irgendwie kann ich mit einem Roboter der nicht im smart home hängt nichts anfangen.
Ich habe mir daher deine Anleitung durchgesehen und mich in Tasmota, Scripting und Visual Code eingearbeitet.

Die selbst compilierte Tasmota Bin läuft auf meinem WEMOS D1 mini, auch das Script konnte ich erfolgreich starten.
Mein Problem ist dass ich als Ergebnis immer noch ein {"Command":"Unknown"} bekomme wenn ich MOW 0 oder 1 schicken. (Habe alle Varianten von Topic/Prefix versucht)

Ausserdem ist das Skript sehr gesprächig über MQTT auch wenn ich das Logging auf 0 stelle.

Hast du inzwischen am Projekt weitergearbeitet und neue Versionen entwickelt? Vielen Dank und Liebe Grüsse
Christoph

e-t-h

Hallo Christoph,

willkommen als erster hier ;-), ist ja schon ne Weile her..

Ich habe daran nichts mehr geändert, mein R2D2 steht draussen und ist nach wie vor aktiv. Ich hatte in einem anderen Forum auch schon mal die Frage nach dem "Command unknown". Dort war es das Problem, dass ich bei mir die Einstellungen für Präfix/Suffix immer genau anders herum mache als Tasmota das im Standart tut. Aber das hast Du schon ausprobiert? Ich meine full topic auf %topic%/%prefix%/ stellen.

Letztendlich nur diese zwei MQTT Kommandos: (bei mir):

XYZ/GT/R2D2/cmnd/MOW 0 zum Start
XYZ/GT/R2D2/cmnd/MOW 1 zum Stop

Müsste sonst
cmnd/XYZ/GT/R2D2/MOW 0
cmnd/XYZ/GT/R2D2/MOW 1

heißen.

Kann auch nur den MQTT-Explorer empfehlen, mit dem kann man sowas erstmal schön "zu Fuß" machen. Kennst Du den?

Was ich nicht verstehe ist was Du mit gesprächig meinst. Die Frequenz mit der die Daten published werden? Die stellst Du mit der Teleperiode ein.
Habe mich lange nicht mehr damit beschäftigt, hilft das weiter?
e-t-h

cpramhofer

Hallo e-t-h,

Was mir sehr hilft ist dass du dich noch mit dem Thema beschäftigst!

Ich habe die Umstellung des Topics versucht und es läuft immer auch MQTT-FX mit um den gesamten Traffic im Detail und an externer Stelle zu prüfen.

Auch die Teleperiode habe ich umgestellt, aber hier muss ich wohl nochmal checken ob sich da etwas geändert hat beim letzten flashen.

Was ja komisch ist ist dass er überhaupt Command unknown schreibt weil das ja bereits indiziert dass er ein Kommando empfangen hat und damit das Subscribe funktioniert.

Aja: eine Anmerkung noch: das Ding läuft derzeit ,,trocken" also ohne an den AC 220 angeschlossen zu sein, könnte das ein Problem sein?

Ich spiele mich hier mal weiter und gib Bescheid sobald ich was habe.

e-t-h

Das mit dem "Trocken" sehe ich nicht als Problem. Command Unknown kommt vom Tasmota, da ist also was in der Kommunikation faul. Setze Dir einfach Debugausgaben wie "print Start mowing" an Stellen um zu verfolgen was wo ankommt.
Das mit dem Gesprächig habe ich immer noch nicht richtig verstanden.

Poste einfach hier was Du hast.
e-t-h

cpramhofer

Hallo e-t-h,

ich habe es gestern noch geschafft! Der Wemos läuft und nimmt auch meine MQTT Befehle an.
Die Statusanzeige ist noch etwas langweilig was aber natürlich daran liegt dass er nicht nicht an der Seriellen Schnittstelle des AC220 hängt.
Vielen Dank aber für deine Hilfe und vor allem auch dafür dass du deine Umsetzung mit allen geteilt hast!!

Liebe Grüsse
Christoph

e-t-h

Na denn! Glückwunsch! Viel Spass beim Einbauen!
Darf man fragen woran es gelegen hat?

Viele Grüße!

P.S. Und immer auf die Igel achten!
e-t-h

cpramhofer

Lieber e-t-h

ich weiss leider selber nicht woran es gelegen ist, habe das script nochmal bereinigt und direkt in der Konsole bearbeitet - danach hat es funktioniert.
Habe den WEMOS D1 inzwischen auch in meinen AutoMower 220 AC eingebaut und es funktioniert alles tadellos!

Die Akku Anzeige ist noch etwas kryptisch und ein paar andere Werte würden mich interessieren aber die wesentlichen Funktionen laufen.

Vielen Dank für deine tolle Arbeit und vor allem auch für die präzise Dokumentation!!

cpramhofer

Ich denke ich habe den Fehler gefunden warum einige Werte nicht bzw. nicht korrekt angezeigt werden:

json2nameValueErrorText
json2nameValue: no closing } found


Der JSON Blob sieht (im FHEM Reading) so aus:

{"Time":"2022-05-19T12:31:33","Uptime":"0T00:27:40","UptimeSec":1660,"Heap":13,"SleepMode":"0,Husqvarna/stat/RESULT{"SerialSend":"Done"}06Husqvarna/tele/RESULT{"SerialReceived":"0F01F1EA03"}0,Husqvarna/stat/RESULT{"SerialSend":"Done"}06Husqvarna/tele/RESULT{"SerialReceived":"0F01EC4200"}0�Husqv

lg
Christoph