Jura Kaffeevollautomat per NewAskSin und Arduino fernsteuerbar

Begonnen von Actronx, 07 Dezember 2015, 22:11:33

Vorheriges Thema - Nächstes Thema

Actronx

Hallo zusammen,

das ist mein erstes Thema und ich wüsste gerne ob Interesse an meinem kleinen Projekt hier im Forum besteht.

Ich habe meinen Jura Z5 Kaffeevollautomaten per Arduino in meine FHEM / SelbstbauCUL Konfiguration eingebunden. Folgende Funktionen funktionieren dabei bisher:
-Pairen an den CUL (peeren sollte auch gehen)
-Maschine ein- und ausschalten per FHEM webcmd
-Kaffee beziehen per FHEM webcmd

Das ganze läuft per Arduino Nano mit ATmega328 , dem normalen CC1101 und der NewAskSin Libary
Als Basis hab ich das Beispiel HM_LC_SW1_BA_PCB aus der Libary genommen und auf 4 Kanäle erweitert.
Die Kommunikation mit der Kaffee-Maschine läuft über den Diagnoseport von Jura, da haben findige Leute die entsprechende Kommunikation entschlüsselt, so dass sie per RS232 ansteuerbar ist. Stromversorgung läuft praktischerweise auch direkt über den Diagnoseport.

Da an der Libary keine Veränderungen vorgenommen worden sind müssten die Channels auch normal mit allen anderen Geräten gepeered werden können, das kann ich aber leider nicht testen.

Ich würde gerne wissen wie das Interesse hier besteht, dann dokumentiere ich auch mehr :)

Mein herzlichen Dank geht an alle findigen Köpfe hier und da draußen im Internet ohne die das Projekt nicht möglich gewesen wäre!!

VG
Tris

Wuppi68

die Jura API wäre bestimmt super cool :-)

nen ESP8266 dran und der Drops ist gelutscht :-)
Jetzt auf nem I3 und primär Homematic - kein Support für cfg Editierer

Support heißt nicht wenn die Frau zu Ihrem Mann sagt: Geh mal bitte zum Frauenarzt, ich habe Bauchschmerzen

Actronx

Hey,
jo! Leider sind die Maschinen etwas unterschiedlich was die Befehle angeht...
Unter https://github.com/oliverk71/Coffeemaker-Payment-System (vielen Dank an OliverK71!) sind ganz viele Infos.

Für die Z5 gelten folgende Pinouts an dem Diagnoseport:
Jura 7-pin interface
(pin 8 - not used)
pin 7 - not used
pin 6 - +5V
pin 5 - not used
pin 4 - RX
pin 3 - GND
pin 2 - TX
pin 1 - not used
(pin 0 - not used)

Allerdings muss man ein bischen Bits schieben, weil Jura quasi ne Verschlüsselung eingebaut hat.
Mit den beiden Funktionen hier kann man aber mit der Jura kommunizieren:

Die Funktion übersetzt in das Jura Protokoll:

void ByteToJuraByte(char input) {
  byte maske = B01011011;
  byte tmp;
  boolean bitx,bity = 0;

  for (int i = 0; i <=3; i++) {
      bitx = (input & (1 << i*2))!=0; // test if lsb of input is 1
      bity = (input & (1 << 1+i*2))!=0; // test if lsb+1 of input is 1
     
      tmp = (maske | (bitx << 2) | (bity << 5) );
      //printbyte_msbfirst(tmp); //inserted for debugging purpose
      Serial.write(tmp);
  }
  _delay_ms(8); // after each 4 bytes send wait at least 8ms



Und mit der hier kriegt man die Rückantwort übersetzt:
char translatejurareturn(char input[4]) {
char tmp = 0;
  // 11011011 , 00100100 als Masken
  tmp = tmp | (input[3] & B00100000) << 2 ;
  tmp = tmp | (input[3] & B00000100) << 4 ;
  tmp = tmp | (input[2] & B00100000) << 0 ;
  tmp = tmp | (input[2] & B00000100) << 2 ; 
  tmp = tmp | (input[1] & B00100000) >> 2 ;
  tmp = tmp | (input[1] & B00000100) << 0 ;
  tmp = tmp | (input[0] & B00100000) >> 4 ;
  tmp = tmp | (input[0] & B00000100) >> 2 ;
  //Serial.println(tmp);
  return tmp;
}


Anschalten zB ist AN:01\r\n
Ausschalten: AN:02\r\n
Einen Spezialkaffee beziehen: FA:06\r\n

Die Befehler für die Kaffeesorten muss man aber per Maschinentyp herausfinden....

Was ich gerne noch hätte, woran ich aber im Moment scheitere ist eine vernünftige Statusabfrage.
Mir IC:\r\n kriegt man die Register oder Eingänge von der Maschine zurück. Ich erkenne aber kein richtiges Muster um etwa: Eingeschaltet und alles ok oder Kaffee wird gebrüht zu erkennnen :(

VG
Tris

hoods

Hallo Tris,

ich habe ne Z5 allerdings keine Ahnung wie ich die Jura Register ausgelesen und in Fhem dargestellt bekomme.

Nutzt Du diese Integration noch und könntest Du mir in Stichpunkten sagen was zu tun ist?

Danke & Gruss Sven
Odroid C2, FHEM 5.8, HMUSB, Jeelink, Rademacher DuoFern Stick, Benning WR über HTTPMOD

thorschtn

Auch wenn sich hier schon eine Weile nichts mehr tut, ich fand das Thema schon immer interessant - alleine um Morgens an meiner S90 gleich meinen Kaffee ziehen zu können, ohne zu warten auf "Kaffeekreislauf heizen", "Spülen", "Dampfkreislauf heizen".

Drei Tastendrücke und Wartezeiten, das macht ab sofort FHEM!

- einfach nach der Anleitung von "wintermute" aus dem KNX-Forum (https://knx-user-forum.de/forum/projektforen/edomi/1010532-kaffeeautomaten-jura-bremer-aehnliche) einen Wemos D1 Mini mit nem Pegelwandler versehen und den Sketch von wintermute draufgespielt
- das ganze gemäß der Belegung aus http://protocoljura.wiki-site.com/index.php/Serial_interfaces#four-pin_interface an die Service-Schnittstelle angeschlossen, damit hat dann jede alte Jura Maschine ein Webinterface über welches sie über WLAN gesteuert werden kann und Statusinfos ausgibt
- in FHEM ein Notify angelegt, welches nacheinander einschaltet, spült und den Dampfkreislauf aufheizt.

"Alexa schalte die Kaffeemaschine ein" - und bis ich in der Küche bin ist die gute alte Jura S90 aufgeheizt und ich kann meinen Kaffee ziehen. Materialaufwand: 1 Wemos D1 mini, 1 Pegelwandler, d.h. nicht viel mehr als 5€!

NUC - FHEM & HA
MapleCUN, Homematic, 433MHz, AB440, 1-Wire Bewässerung & Pool, Jarolift (Signalduino), Signal Messenger, Denon AVR, LG WebOS, AmazonEcho, Jura S90 (ESP8266), Sonoff, Xiaomi Mii Sauger, Worx SO500i

warcraft123

Hallo,

habe den Thread endeckt und gleich meine Jura s90 angeschlossen.
Das hat soweit geklappt, per Web ist sie erreichbar,
aber wie kann ich die per Fhem ansprechen?
Könntest du mir das bitte genauer erklären, bzw. dein Notify hier posten? Danke

Gruss Marcus

thorschtn

AN (Kaffeekreislauf heizen, Milchkreislauf heizen, spülen):
defmod on_Jura notify du_JURA_Control:on {GetFileFromURL("http://192.168.178.66/power?param=1") };; { sleep 2 };; {GetFileFromURL("http://192.168.178.66/proffer?param=8") } ;; { sleep 120 };; {GetFileFromURL("http://192.168.178.66/proffer?param=2") }
attr on_Jura room Kaffeemaschine


AUS:
defmod off_Jura notify du_JURA_Control:off {GetFileFromURL("http://192.168.178.66/power?param=0") }
attr off_Jura room Kaffeemaschine
NUC - FHEM & HA
MapleCUN, Homematic, 433MHz, AB440, 1-Wire Bewässerung & Pool, Jarolift (Signalduino), Signal Messenger, Denon AVR, LG WebOS, AmazonEcho, Jura S90 (ESP8266), Sonoff, Xiaomi Mii Sauger, Worx SO500i

killah78

Hi thorschtn,
habe mal kurzerhand nachgebaut und in eine Jura S9 eingebaut. Funktioniert auf anhieb. Danke für diesen Tip.
Kannst du mir vielleicht mit deinem notify helfen? Wenn ich diesen so einbaue, blockiert der Notify für die zwei Minuten das komplette FHEM-Webinterface. Wie kann ich das non-blocking aufrufen?
Danke und Gruss
killah78

thorschtn

Ich nutz den nicht über das Webinterface, daher ist mir das bislang noch nicht aufgefallen.

Grundsätzlich sind die HttpUtils wohl nicht vollständig nonblocking, wenn ich https://wiki.fhem.de/wiki/HttpUtils richtig verstehe - da kenn ich mich aber auch nicht wirklich gut aus. Rufst Du die Kaffeemaschine über die IP oder über den Hostnamen auf?

Vielleicht versuchst Du statt GetFileFromURL auch mal HttpUtils_NonblockingGet?

Viele Grüße

Thorsten
NUC - FHEM & HA
MapleCUN, Homematic, 433MHz, AB440, 1-Wire Bewässerung & Pool, Jarolift (Signalduino), Signal Messenger, Denon AVR, LG WebOS, AmazonEcho, Jura S90 (ESP8266), Sonoff, Xiaomi Mii Sauger, Worx SO500i

killah78

Hi thorschtn,
ja, habe jetzt mal auf HttpUtils_NonblockingGet umgebaut, aber auch das blockiert. Denke, das sleep blockiert. Ich nutze das auch nicht über die Weboberfläche, sondern extern über Alexa per Sprache. Aber trotzdem friert FHEM da komplett ein und verarbeitet keine weiteren Events.
Werde das demnächst mal mit einem (non-)BlockingCall versuchen, das sollte ja dann funktionieren.
Gruss
killah78

det.

Hallo,
Hänge mich hier mal mit rein. Das Blockieren bekommt Ihr weg, wenn Ihr die Befehle zeitgesteuert absetzt, wie in meinem Beispiel zum Start vom Sonosplayer nach Strom einschalten:
SONOS:on.* {
fhem "define SonosStart at +00:01:00 set Sonos_Wohnzimmer LoadRadio Radioc%20Paradise";
fhem "define SonosStart1 at +00:01:01 set Sonos_Wohnzimmer Volume 15";
fhem "define SonosStart2 at +00:01:02 set Sonos_Wohnzimmer AddMember Sonos_Fernseher";
fhem "define SonosStart3 at +00:01:03 set Sonos_Wohnzimmer Play";
fhem "define SonosStart8 at +00:01:04 set Sonos_Wohnzimmer Mute 0"
}

Ich habe bisher vergeblich versucht den Sketch aus dem Link auf einen ESP8266 zu spielen. Da kommen immer Fehler bei D1 ,2,5 und 6? Liegt das daran, das der Sketch von einem Wemos D1 mini ausgeht?
LG
det.

eszych

Hallo Zusammen,
die Feiertage haben mir etwas Zeit beschert und ich habe mich mal daran gemacht die Anleitung von hier
1. https://knx-user-forum.de/forum/projektforen/edomi/1010532-kaffeeautomaten-jura-bremer-aehnliche/page2
und von hier
2. http://www.instructables.com/id/IoT-Enabled-Coffee-Machine/ umzusetzen.

Ich habe die Webseite von 1. und die Möglichkeiten von 2. alles auch per MQTT zu schalten umgesetzt.

Was ich aber gar nicht hinbekomme ist, meine Impressa F50 classic zu schalten...
Außer "Ausschalten" geht gar nichts und ich habe keine Idee, was nicht stimmen könnte.

Hier ist der Code vom Sketch:

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>
#include <PubSubClient.h>
#include <Nextion.h>
// used for UDP communication
#include <WiFiUdp.h>
// used for general time related stuff
#include <Time.h>
// used to schedule actions at specific times
#include <TimeAlarms.h>
// SoftwareSerial for communicating with the machine
#include <SoftwareSerial.h>
enum machine {NONE,UNSUPPORTED,S90,E50,C5};

/*
* Configuration START
*/

// set machine type, or set to "NONE" to disable communication with machine (for test mode)
// set to "UNSUPPORTED" if your machine is not listed and not compatible with listed types
machine type=UNSUPPORTED;
// Hostname
const char* host="hostname";
// mDNS name (leave empty if not needed)
const char* mdns="jura";
// SSID
const char* ssid="WLANSSID";
// Passphrase
const char* passphrase="WLANPW";
// IP (leave empty if DHCP is used)
// IPAddress ip(192,168,100,200);
IPAddress ip;
// Netmask (can be ignored if DHCP is used)
IPAddress mask(255,255,255,0);
// Gateway (set to (0,0,0,0) if no gateway should be configured or DHCP is used)
IPAddress gateway(0,0,0,0);
// DNS (can be ignored if DHCP is used)
IPAddress dns;
// TimeServer IP (leave empty to use hostname, defined below)
IPAddress NTPserverIP;
// TimeServer Hostname (will be overriden by above configuration)
const char* NTPserverName="1.de.pool.ntp.org";
// username and password for web access (leave at least one empty if no authentication should be used)
const char* user="admin";
const char* pass="password";
/* UDP receiver IP and port, if both is provided, status messages of the machine will be send to that host */
const char* udpRemoteAddress="";
const unsigned int udpRemotePort=0;
int i;
// machine gets queried every "cycle" seconds
unsigned int cycle=60;

const char *mqtt_server = "192.168.1.123";
const int   mqtt_port   = 1883;
const char *mqtt_client_name = "JuraF50"; // Client connections cant have the same connection name

WiFiClient espClient;                //Declares a WifiClient Object using ESP8266WiFi
PubSubClient MQTTClient(espClient);  //instanciates client object

/*
* Configuration END
*/

/*
* Variables & constants
*/
// Version
const String fw_version="0.1 - 2016112801";
// connection to machine
SoftwareSerial mySerial(D5,D6);
// Webserver & Update
ESP8266WebServer server(80);
ESP8266HTTPUpdateServer httpUpdater;
// NTP stuff
const int NTP_PACKET_SIZE=48;
byte packetBuffer[NTP_PACKET_SIZE];
const int timeZone=1; // CET
// UDP instance for NTP communication
WiFiUDP udp;
// UDP instance for state updates
WiFiUDP Sudp;
// web response
String html="";
String head_main="<html>\
<head>\
<link rel='shortcut icon' type='image/x-icon' href='data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEREDwAAAAAmJiYtR0lJgCYmJi1AREQPAAAAAAAAAAAAAAAAAAAAAEBERA8AAAAAQEREDwAAAABNTU3/TU1N/01NTf9MTEz/TU1N/01NTf9NTU3/TU1N/0xMTP9NTU3/TU1N/wAAAAAAAAAAQEREDwAAAABMTEz/TU1N/01NTf9NTU3/AAAAAEBERA8nJyd/AAAAAAAAAABNTU3/TExM/01NTf9NTU3/AAAAAAAAAAAAAAAAAAAAAEBERA9NTU3/AAAAAE1NTf9NTU3/TExM/01NTf9NTU3/AAAAAE1NTf9AREQPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQERED01NTf9LTExwTU1N/01NTf9MTEz/TU1N/01NTf8AAAAAAAAAAEBERA8AAAAAAAAAAAAAAAAAAAAAAAAAAE1NTf9AREQPTU1N/01NTf9NTU3/TU1N/0xMTP9NTU3/TU1N/01NTf9NTU3/QEREDwAAAABAREQPAAAAAAAAAABNTU3/AAAAAExMTP9NTU3/TU1N/01NTf9NTU3/TExM/01NTf8AAAAATU1N/wAAAABAREQPAAAAAEBERA8AAAAATU1N/wAAAABNTU3/TExM/01NTf9NTU3/TU1N/01NTf9MTEz/AAAAAExMTIxGSEh5AAAAAAAAAAAAAAAARkhIPU1NTf9NTU3/TU1N/01NTf9MTEz/TU1N/01NTf9NTU3/TU1N/0xMTP9NTU3/AAAAAAAAAAAAAAAAAAAAACcnJxVMTEz/AAAAAAAAAAAAAAAAAAAAAEBERA8AAAAAAAAAAE1NTf8mJiYmQEREDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExMTP9NTU3/TU1N/01NTf9NTU3/TExM/01NTf8AAAAAAAAAAAAAAABAREQPAAAAAEBERA8AAAAAAAAAAAAAAAAAAAAAQEREDwAAAAAAAAAAAAAAAAAAAABAREQPAAAAAAAAAAAAAAAAAAAAAEBERA8AAAAAQEREDwAAAAAAAAAAAAAAAE1NTf9AREQPTU1NpwAAAABNTU3/AAAAAEBERA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAREQPAAAAAAAAAABNTU3/AAAAAExMTP8AAAAATU1N/wAAAAAAAAAAQEREDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBERA8AAAAAAAAAAAAAAAAAAAAAQEREDwAAAAAAAAAAAAAAAAAAAABAREQPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEREDwAAAAAAAAAAAAAAAAAAAABAREQPAAAAAAAAAAAAAAAAAAAAAEBERA8AAAAA/v8AAMAHAACHwwAA6C8AAPQfAADoAwAA6AsAAOgLAADgAwAA7+8AAPAfAAD//wAA+r8AAPq/AAD//wAA//8AAA=='/> \
<title>"+String(host)+"</title>\
<meta http-equiv='refresh' content='60'>\
<style>\
  body,a { background-color: #000000; font-family: Arial, Helvetica, Sans-Serif; Color: #d0d0d0; }\
  a:link { text-decoration: none; }\
  a:visited { text-decoration: none; }\
  a:hover { text-decoration: underline; }\
  a:active { text-decoration: underline; }\
</style>\
</head>\
<body>\
";
String head="<html>\
<head>\
<link rel='shortcut icon' type='image/x-icon' href='data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEREDwAAAAAmJiYtR0lJgCYmJi1AREQPAAAAAAAAAAAAAAAAAAAAAEBERA8AAAAAQEREDwAAAABNTU3/TU1N/01NTf9MTEz/TU1N/01NTf9NTU3/TU1N/0xMTP9NTU3/TU1N/wAAAAAAAAAAQEREDwAAAABMTEz/TU1N/01NTf9NTU3/AAAAAEBERA8nJyd/AAAAAAAAAABNTU3/TExM/01NTf9NTU3/AAAAAAAAAAAAAAAAAAAAAEBERA9NTU3/AAAAAE1NTf9NTU3/TExM/01NTf9NTU3/AAAAAE1NTf9AREQPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQERED01NTf9LTExwTU1N/01NTf9MTEz/TU1N/01NTf8AAAAAAAAAAEBERA8AAAAAAAAAAAAAAAAAAAAAAAAAAE1NTf9AREQPTU1N/01NTf9NTU3/TU1N/0xMTP9NTU3/TU1N/01NTf9NTU3/QEREDwAAAABAREQPAAAAAAAAAABNTU3/AAAAAExMTP9NTU3/TU1N/01NTf9NTU3/TExM/01NTf8AAAAATU1N/wAAAABAREQPAAAAAEBERA8AAAAATU1N/wAAAABNTU3/TExM/01NTf9NTU3/TU1N/01NTf9MTEz/AAAAAExMTIxGSEh5AAAAAAAAAAAAAAAARkhIPU1NTf9NTU3/TU1N/01NTf9MTEz/TU1N/01NTf9NTU3/TU1N/0xMTP9NTU3/AAAAAAAAAAAAAAAAAAAAACcnJxVMTEz/AAAAAAAAAAAAAAAAAAAAAEBERA8AAAAAAAAAAE1NTf8mJiYmQEREDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExMTP9NTU3/TU1N/01NTf9NTU3/TExM/01NTf8AAAAAAAAAAAAAAABAREQPAAAAAEBERA8AAAAAAAAAAAAAAAAAAAAAQEREDwAAAAAAAAAAAAAAAAAAAABAREQPAAAAAAAAAAAAAAAAAAAAAEBERA8AAAAAQEREDwAAAAAAAAAAAAAAAE1NTf9AREQPTU1NpwAAAABNTU3/AAAAAEBERA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAREQPAAAAAAAAAABNTU3/AAAAAExMTP8AAAAATU1N/wAAAAAAAAAAQEREDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBERA8AAAAAAAAAAAAAAAAAAAAAQEREDwAAAAAAAAAAAAAAAAAAAABAREQPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEREDwAAAAAAAAAAAAAAAAAAAABAREQPAAAAAAAAAAAAAAAAAAAAAEBERA8AAAAA/v8AAMAHAACHwwAA6C8AAPQfAADoAwAA6AsAAOgLAADgAwAA7+8AAPAfAAD//wAA+r8AAPq/AAD//wAA//8AAA=='/> \
<title>"+String(host)+"</title>\
<style>\
  body,a { background-color: #000000; font-family: Arial, Helvetica, Sans-Serif; Color: #d0d0d0; }\
  a:link { text-decoration: none; }\
  a:visited { text-decoration: none; }\
  a:hover { text-decoration: underline; }\
  a:active { text-decoration: underline; }\
</style>\
</head>\
<body>\
";
// machine stuff
String mState="";
// misc stuff
unsigned long lastRun=0;
const int led = LED_BUILTIN;
// Debug stuff
unsigned int debug_pointer=0;
char debug_0[100]="";
char debug_1[100]="";
char debug_2[100]="";
char debug_3[100]="";
char debug_4[100]="";
char debug_5[100]="";
char debug_6[100]="";
char debug_7[100]="";
char debug_8[100]="";
char debug_9[100]="";
char debug_10[100]="";
char debug_11[100]="";
char debug_12[100]="";
char debug_13[100]="";
char debug_14[100]="";
char debug_15[100]="";
char debug_16[100]="";
char debug_17[100]="";
char debug_18[100]="";
char debug_19[100]="";
String debug_table[]={
  debug_0,
  debug_1,
  debug_2,
  debug_3,
  debug_4,
  debug_5,
  debug_6,
  debug_7,
  debug_8,
  debug_9,
  debug_10,
  debug_11,
  debug_12,
  debug_13,
  debug_14,
  debug_15,
  debug_16,
  debug_17,
  debug_18,
  debug_19
};
unsigned int debug_count=20;
char debug_buffer[100];
boolean debug_lastnewline=false;
const char* pubchar = "";

bool valueChanged = false;

// Pins for 2-Way-RELAY
int relay_IN1 = D1;
int relay_IN2 = D2;

// Time settings
String date2text() {
  char buf[11]="";
  if (timeStatus()==timeSet) sprintf(buf,"%02d.%02d.%04d",day(),month(),year());
  return String(buf);
}

String time2text() {
  char buf[8]="";
  if (timeStatus()==timeSet) sprintf(buf, "%02d:%02d:%02d",hour(),minute(),second());
  return String(buf);
}

void mqttConnect() {
  if (!MQTTClient.connected()) {
    //Serial.println("Connecting to broker");
    if (MQTTClient.connect("mqtt_client_name")){
      //Serial.print("Subscribing to :");
      //Serial.println(subPath);
      // MQTTClient.subscribe(subPath);
      MQTTClient.subscribe("/FHEM/Kaffeemaschine/Power");
      MQTTClient.subscribe("/FHEM/Kaffeemaschine/Action");
    }else{
      //Serial.println("Failed to connect!");
    }
  }
}

void debug(String text="", boolean newline=true) {
  if (debug_lastnewline && timeStatus()==timeSet && text.length()) text=time2text()+" :: "+text;
  Serial.print(text);
  pubchar = text.c_str();
  MQTTClient.publish("/Kaffee/Debug/Text/", pubchar);
  if (debug_lastnewline) {
    if (text.length()) {
      debug_pointer++;
      if (debug_pointer>=debug_count) debug_pointer=0;
    }
  } else {
    text=debug_table[debug_pointer]+text;
  }

  if (newline) {
    Serial.println();
  }
  debug_lastnewline=newline;
  // add debug message to debug array
  if (text.length()) {
    text.toCharArray(debug_buffer,100);
    debug_table[debug_pointer]=debug_buffer;
  }
}

void http_debug(String txt, boolean newline=true) {
  debug(txt + String(" [") + server.client().remoteIP().toString() + String("]"), newline);
}

void restart() {
  ESP.restart();
}

void reset() {
  ESP.reset();
}

/*
* NTP Functions BEGIN
*/
unsigned long sendNTPpacket(IPAddress& ip) {
  // build and send a NTP query packet
  memset(packetBuffer,0,NTP_PACKET_SIZE);
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;            // Stratum, or type of clock
  packetBuffer[2] = 6;            // Polling Interval
  packetBuffer[3] = 0xEC;         // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]= 49;
  packetBuffer[13]= 0x4E;
  packetBuffer[14]= 49;
  packetBuffer[15]= 52;
  udp.beginPacket(ip, 123);
  udp.write(packetBuffer,NTP_PACKET_SIZE);
  udp.endPacket();
}

time_t getNTPtime() {
  int pp=0;
  while (pp==0) {
    sendNTPpacket(NTPserverIP);
    delay(1000);
    pp=udp.parsePacket();
    delay(1000);
  }

  udp.read(packetBuffer,NTP_PACKET_SIZE);
  unsigned long secsSince1900;
  // convert four bytes starting at location 40 to a long integer
  secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
  secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
  secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
  secsSince1900 |= (unsigned long)packetBuffer[43];
  return secsSince1900-2208988800UL+timeZone*SECS_PER_HOUR;
}

/*
* NTP functions END
*/

/*
* Webserver functions BEGIN
*/
void auth() {
  digitalWrite(led,LOW);
  if(user!="" && pass!="") {
    if (!server.authenticate(user,pass)) server.requestAuthentication();
  }
  delay(50);
  digitalWrite(led,HIGH);
}

boolean ARGplain() {
  for (uint8_t i=0; i<server.args(); i++) {
    if (server.argName(i)=="plain") return true;
  }
  return false;
}

String ARGcommand() {
  for (uint8_t i=0; i<server.args(); i++) {
    if (server.argName(i)=="cmd") return server.arg(i);
  }
  return "";
}

String ARGparam() {
  for (uint8_t i=0; i<server.args(); i++) {
    if (server.argName(i)=="param") return server.arg(i);
  }
  return("");
}

void web_root() {
  auth();
  http_debug("/ accessed");
  int s=WiFi.RSSI();
  if (ARGplain()) {
    html ="Status: "+readState()+"\n";
    switch (type) {
      case NONE:
        html+="Machine: none\n";
        break;
      case UNSUPPORTED:
        html+="Machine: unsupported\n";
        break;
      case C5:
        html+="Machine: C5\n";
        break;
      case S90:
        html+="Machine: S90\n";
        break;
      case E50:
        html+="Machine: E50\n";
        break;
      default:
        html+="Machine: none\n";
        break;
    }
    html+="Controller uptime: "+String(millis())+"\n";
    html+="SSID: "+String(ssid)+"\n";
    html+="Signal: "+String(s)+"\n";
    html+="Time: "+time2text()+"\n";
    html+="Date: "+date2text()+"\n";
    html+="Firmware: "+fw_version+"\n";
    server.send(200,"text/plain",html);
  } else {
    html=head_main;
    html+="<h3>Status: "+readState()+"</h3>";
    html+="<a href='/power?param=0'>Abschalten</a><br/>";
    html+="<a href='/power?param=1'>Anschalten</a><br/>";
    html+="<a href='/flush'>Sp&uuml;len</a><br/>";
    html+="<hr/>";
    html+="<a href='/proffer?param=1'>Produkt 1 beziehen</a><br/>";
    html+="<a href='/proffer?param=2'>Produkt 2 beziehen (Ger&auml;t sp&uuml;len)</a><br/>";
    html+="<a href='/proffer?param=3'>Produkt 3 beziehen (Pulverkaffee)</a><br/>";
    html+="<a href='/proffer?param=4'>Produkt 4 beziehen (1 kleine Tasse)</a><br/>";
    html+="<a href='/proffer?param=5'>Produkt 5 beziehen (2 kleine Tassen)</a><br/>";
    html+="<a href='/proffer?param=6'>Produkt 6 beziehen (1 gro&szlig;e Tasse)</a><br/>";
    html+="<a href='/proffer?param=7'>Produkt 7 beziehen (2 gro&szlig;e Tassen)</a><br/>";
    html+="<a href='/proffer?param=8'>Produkt 8 beziehen (Dampf Portion)</a><br/>";
    html+="<a href='/proffer?param=9'>Produkt 9 beziehen (Dampf)</a><br/>";
    html+="<a href='/proffer?param=10'>Produkt 10 beziehen</a><br/>";
    html+="<a href='/proffer?param=11'>Produkt 11 beziehen</a><br/>";
    html+="<a href='/proffer?param=12'>Produkt 12 beziehen (Kaffee Spezial)</a><br/>";
    html+="<a href='/proffer?param=13'>Produkt 13 beziehen</a><br/>";
    html+="<hr/>";
    html+="<a href='/reset'>Controller Reset</a><br/>";
    html+="<a href='/restart'>Controller Restart</a><br/>";
    html+="<a href='/update'>Controller Update</a><br/>";
    html+="<a href='/debug'>Debug Log</a><br/>";
    html+="<hr/>";
    html+=String(host)+", verbunden mit "+String(ssid)+", Signalst&auml;rke "+String(s)+"dBm&nbsp;-&nbsp;";
    html+="Systemzeit "+date2text()+", "+time2text()+"<br/>";
    html+="<small>Controller uptime "+String(millis())+"ms&nbsp;-&nbsp;";
    html+="Firmwareversion "+fw_version;
    html+="</small></body></html>";
    server.send(200,"text/html",html);
  }
  delay(100);
}

void web_reset() {
  auth();
  http_debug("/reset accessed");
  debug("Reset requested");
  if (ARGplain()) {
    server.send(200,"text/plain","ok");
  } else {
    html=head;
    html+="<h3>Controller is resetting</h3><a href='/'>back</a></body</html>";
    server.send(200,"text/html",html);
  }
  reset();
}

void web_restart() {
  auth();
  http_debug("/restart accessed");
  debug("Restart requested");
  if (ARGplain()) {
    server.send(200,"text/plain","ok");
  } else {
    html=head;
    html+="<h3>Controller is restarting</h3><a href='/'>back</a></body</html>";
    server.send(200,"text/html",html);
  }
  restart();
}

void web_send() {
  auth();
  if (ARGplain()) {
    server.send(200,"text/plain","ok");
  } else {
    html=head;
    html+="<h3>Controller is resetting</h3><a href='/'>back</a></body</html>";
    server.send(200,"text/html",html);
  }
  restart();
}

void web_power() {
  auth();
  http_debug("/power accessed");
  unsigned int p=ARGparam().toInt();
  if (ARGparam()=="") {
    if (mState=="off") {
      server.send(200,"text/plain","0");
    } else {
      server.send(200,"text/plain","1");
    }
    return;
  }
  String command="AN:02";
  String answer="";
  switch (p) {
    case 1:
      MQTTClient.publish("/Kaffee/Power/PowerOnOff/", "ON");
      digitalWrite(relay_IN1, LOW);
      digitalWrite(relay_IN2, LOW);

      debug("Power On");
      command="AN:01";
      break;
    default:
      MQTTClient.publish("/Kaffee/Power/PowerOnOff/", "OFF");
      debug("Power Off");
      command="AN:02";
      digitalWrite(relay_IN1, HIGH);
      digitalWrite(relay_IN2, HIGH);
      break;
  }
  toMachine(command);
  delay(10);
  answer=fromMachine();
  if (ARGplain()) {
    server.send(200,"text/plain","Answer: "+answer);
  } else {
    html=head;
    html+="<h3>Maschine an/abschalten</h3>R&uuml;ckmeldung: "+answer+"<hr/><a href='/'>back</a></body</html>";
    server.send(200,"text/html",html);
  }
}

void web_flush() {
  auth();
  http_debug("/flush accessed");
  String answer="";
  toMachine("FA:0B");
  delay(10);
  answer=fromMachine();
  if (ARGplain()) {
    server.send(200,"text/plain","Answer: "+answer);
  } else {
    html=head;
    html+="<h3>Sp&uuml;len</h3>R&uuml;ckmeldung: "+answer+"<hr/><a href='/'>back</a></body</html>";
    server.send(200,"text/html",html);
  }

}

void web_proffer() {
  auth();
  http_debug("/proffer accessed");
  unsigned int p=ARGparam().toInt();
  String command="";
  String answer="";
  switch (p) {
    case 1:
      debug("Product 1 requested"); command="FA:01"; break;
    case 2:
      debug("Product 2 requested"); command="FA:02"; break;
    case 3:
      debug("Product 3 requested"); command="FA:03"; break;
    case 4:
      debug("Product 4 requested - small cup???"); command="FA:04"; break;
    case 5:
      debug("Product 5 requested - two small cups???"); command="FA:05"; break;
    case 6:
      debug("Product 6 requested - large cup???"); command="FA:06"; break;
    case 7:
      debug("Product 7 requested - two large cups???"); command="FA:07"; break;
    case 8:
      debug("Product 8 requested - hot water???"); command="FA:08"; break;
    case 9:
      debug("Product 9 requested - steam???"); command="FA:09"; break;
    case 10:
      debug("Product 10 requested - steam???"); command="FA:0A"; break;
    case 11:
      debug("Product 11 requested - steam???"); command="FA:0B"; break;
    case 12:
      debug("Product 12 requested - XXL"); command="FA:0C"; break;
    case 13:
      debug("Product 13 requested - steam???"); command="FA:0D"; break;
    default:
      debug("Unknown product "+String(p)+" requested, aborting"); return;
  }
  toMachine(command);
  delay(10);
  answer=fromMachine();
  if (ARGplain()) {
    server.send(200,"text/plain","Answer: "+answer);
  } else {
    html=head;
    html+="<h3>Produkt "+String(p)+" angefordert</h3>R&uuml;ckmeldung: "+answer+"<hr/><a href='/'>back</a></body</html>";
    server.send(200,"text/html",html);
  }
}

void web_debug() {
  auth();
  //http_debug("/debug accessed");
  uint8_t p=debug_pointer;
  if (ARGplain()) {
    html="";
    for (uint8_t i=1; i<=debug_count; i++) p++;
    if (p>=debug_count) p-=debug_count;
    html+=String(debug_table[p]) + String("\n");
    server.send(200,"text/plain",html);
  } else {
    html=head+String("<h3>Debug Ausgaben</h3><pre>");
    for (uint8_t i=1; i<=debug_count; i++) {
      p++;
      if (p>=debug_count) p-=debug_count;
      html+=String(debug_table[p]) + String("\n");
    }
    html+=String("</pre><hr/><a href='/'>back</a></body></html>");
    server.send(200,"text/html",html);
  }
}

/*
* Webserver functions END
*/

/*
* Communication with machine BEGIN
* Parts of this code (especially the essential communicaton with the machine) are taken from
* or are inspired by code published by Oliver Krohn and the c't sharepresso project
*/

String fromMachine() {
  if (type==NONE) {
    debug("FROM machine: N/A - no machine type set");
    return("");
  }
  delay(10);
  String inputString="";
  char d4=255;
  while (mySerial.available()) {
    byte d0=mySerial.read();
    delay(1);
    byte d1=mySerial.read();
    delay(1);
    byte d2=mySerial.read();
    delay(1);
    byte d3=mySerial.read();
    delay(1);
    bitWrite(d4,0,bitRead(d0,2));
    bitWrite(d4,1,bitRead(d0,5));
    bitWrite(d4,2,bitRead(d1,2));
    bitWrite(d4,3,bitRead(d1,5));
    bitWrite(d4,4,bitRead(d2,2));
    bitWrite(d4,5,bitRead(d2,5));
    bitWrite(d4,6,bitRead(d3,2));
    bitWrite(d4,7,bitRead(d3,5));
    if (d4!=10) inputString+=d4;
    yield();
  }
  inputString.trim();
  debug("FROM machine: "+inputString);
  pubchar = inputString.c_str();
  MQTTClient.publish("/Kaffee/FromMachine/RAW/", pubchar);
  return(inputString);
}

void toMachine(String outputString) {
  outputString.toUpperCase();
  if (type==NONE) {
    debug("TO machine: "+outputString+" - nothing sent, machine type not set");
    return;
  }
  debug("TO machine: "+outputString);
  pubchar = outputString.c_str();
  MQTTClient.publish("/Kaffee/ToMachine/RAW/", pubchar);
  outputString+="\r\n";
  for (byte a=0; a<outputString.length(); a++) {
    byte d0=255;
    byte d1=255;
    byte d2=255;
    byte d3=255;
    bitWrite(d0,2,bitRead(outputString.charAt(a),0));
    bitWrite(d0,5,bitRead(outputString.charAt(a),1));
    bitWrite(d1,2,bitRead(outputString.charAt(a),2));
    bitWrite(d1,5,bitRead(outputString.charAt(a),3));
    bitWrite(d2,2,bitRead(outputString.charAt(a),4));
    bitWrite(d2,5,bitRead(outputString.charAt(a),5));
    bitWrite(d3,2,bitRead(outputString.charAt(a),6));
    bitWrite(d3,5,bitRead(outputString.charAt(a),7));
    mySerial.write(d0);
    delay(1);
    mySerial.write(d1);
    delay(1);
    mySerial.write(d2);
    delay(1);
    mySerial.write(d3);
    delay(1);
  }
}

String readState() {
  String answer="";
  String power="";
  String state="";
  switch (type) {
    case NONE:
      return("unknown");
      break;
    case UNSUPPORTED:
      toMachine("RE:31");
      delay(10);
      answer=fromMachine();
      debug("Answer from Machine: " + answer);
      mState="unknown :: "+state+"/" + answer;
      return(answer);
      break;
    case C5:
      toMachine("RR:BB");
      delay(10);
      answer=fromMachine().substring(3,7);
      if (answer=="0104") {
        debug("STATE: on");
        mState="on";
        /* sendState(); */
        return(mState);
      } else if (answer=="0100") {
        debug("STATE: off");
        mState="off";
        /* sendState(); */
        return(mState);
      } else {
        debug("STATE UNKNOWN: "+state+"/"+answer);
        mState="unknown :: "+state+"/"+answer;
        /* sendState(); */
        return(mState);
      }
      break;
    case S90:
      toMachine("RR:03");
      delay(10);
      answer=fromMachine();
      power=answer.substring(4,5);
      state=answer.substring(6,7)+answer.substring(25,28);
      if (power=="0") {
        debug("STATE: off");
        mState="off";
        /* sendState(); */
        return(mState);
      } else if (state=="C404") {
        debug("STATE: out of water");
        mState="no_water";
        /* sendState(); */
        return(mState);
      } else if (state=="4805") {
        debug("STATE: out of beans");
        mState="no_beans";
        /* sendState(); */
        return(mState);
      } else if (state=="C045") {
        debug("STATE: tray is missing");
        mState="no_tray";
        /* sendState(); */
        return(mState);
      } else if (state=="C444") {
        debug("STATE: tray is missing, out of water");
        mState="no_tray|no_water";
        /* sendState(); */
        return(mState);
      } else if (state=="4205") {
        debug("STATE: pomace full");
        mState="pomace_full";
        /* sendState(); */
        return(mState);
      } else if (state=="C105") {
        debug("STATE: tray is full");
        mState="tray_full";
        /* sendState(); */
        return(mState);
      } else if (state=="4005") {
        debug("STATE: on");
        mState="on";
        /* sendState(); */
        return(mState);
      } else {
        debug("STATE UNKNOWN: "+state+"/"+answer);
        mState="unknown :: "+state+"/"+answer;
        /* sendState(); */
        return(mState);
      }
      break;
    case E50:
      toMachine("RR:20");
      delay(10);
      answer=fromMachine().substring(3,11);
      state=answer.substring(0,2)+answer.substring(6,8);
      if (state=="0000") {
        debug("STATE: off");
        return("off");
      } else if (state=="0101") {
        debug("STATE: ready");
        return("ready");
      } else if (state=="1111") {
        debug("STATE: cleaning");
        return("cleaning");
      } else if (state=="4000") {
        debug("STATE: flushing");
        return("flushing");
      } else if (state=="8180") {
        debug("STATE: out of water");
        return("no_water");
      } else if (state=="9190") {
        debug("STATE: out of water");
        return("no_water");
      } else if (state=="4040") {
        debug("STATE: need flushing");
        return("need_flushing");
      } else if (state=="0505") {
        debug("STATE: need powder");
        return("no_powder");
      } else if (state=="2120") {
        debug("STATE: ??? full (2120)");
        return("??2_full");
      } else if (state=="3130") {
        debug("STATE: ??? full (3130)");
        return("??3_full");
       } else {
        debug("STATE UNKNOWN: "+state+"/"+answer);
        return("unknown");
      }
      break;
    default:
      return("unknown");
      break;
  }
}

void testCommand() {
  String outputString = server.arg(0);
  toMachine(outputString);
  delay(50);  // wait for answer?
  String inputString = fromMachine();
  delay(100);
  server.send(200, "text/plain", inputString);//a html_message);
}


/*
* Communication with machine END
*/

void ConfigureWifi() {
  // establish WLAN connection
  if (ip!=0) {
    debug("Static IP configuration");
    if (dns!=0) {
      WiFi.config(ip,gateway,mask,dns);
    } else {
      WiFi.config(ip,gateway,mask);
    }
  } else {
    debug("DHCP configured");
  }
  debug("Connecting to " + String(ssid),false);
  WiFi.begin(ssid,passphrase);
  while (WiFi.status()!=WL_CONNECTED) {
    delay(100);
    debug(".",false);
  }
  debug();
  debug("Connected...");

  ip=WiFi.localIP();
  mask=WiFi.subnetMask();
  gateway=WiFi.gatewayIP();
  debug();
  debug("IP     : " + ip.toString());
  debug("Netmask: " + mask.toString());
  debug("Gateway: " + gateway.toString());

}

void mqttcallback(char* topic, byte* payload, unsigned int length) {
  String sTopic = String(topic);
 
  // Workaround to get int from payload
  payload[length] = '\0';
  String payloadchar = String((char*)payload);

  uint32_t payloadvalue = String((char*)payload).toInt();

  unsigned int p=ARGparam().toInt();
  String command="";
  String answer="";

  // MQTTClient.subscribe("/FHEM/Kaffeemaschine/Power");
  // MQTTClient.subscribe("/FHEM/Kaffeemaschine/Action");

  if (sTopic == "/FHEM/Kaffeemaschine/Power") {
    if (payloadchar == "IN1ON") {
      debug("Received Power IN1 ON via MQTT");
      digitalWrite(relay_IN1, LOW);
    } else if (payloadchar == "IN2ON") {
      debug("Received Power IN2 ON via MQTT");
      digitalWrite(relay_IN2, LOW);
    } else if (payloadchar == "IN1OFF") {
      debug("Received Power IN1 OFF via MQTT");
      digitalWrite(relay_IN1, HIGH);
    } else if (payloadchar == "IN2OFF") {
      debug("Received Power IN2 OFF via MQTT");
      digitalWrite(relay_IN2, HIGH);
    }
  } else if (sTopic == "/FHEM/Kaffeemaschine/Action") {
    debug("Received Action "+ payloadchar +" via MQTT");
    if (payloadchar == "SPUELEN") {
      debug("Product 2 requested");
      command="FA:02";
    } else if (payloadchar == "1xESPRESSO") {
      debug("Product 4 - 1 x Espresso requested");
      command="FA:04";
    } else if (payloadchar == "2xESPRESSO") {
      debug("Product 5 - 2 x Espresso requested");
      command="FA:05";
    } else if (payloadchar == "1xKAFFEE") {
      debug("Product 6 - 1 x Kaffee requested");
      command="FA:06";
    } else if (payloadchar == "2xKAFFEE") {
      debug("Product 7 - 2 x Kaffee requested");
      command="FA:07";
    }
  }
  if (command != "") {
    toMachine(command);
    delay(100);
    answer=fromMachine();
    pubchar = answer.c_str();
    MQTTClient.publish("/Kaffee/Kaffemaschine/ProductReply/", pubchar);
  }

  valueChanged = true;
}

void setup() {
  // serial communication with the machine
  mySerial.begin(9600);
  // serial communication for debugging
  Serial.begin(115200);

  debug("Controller is starting...");
  debug("ChipID is "+String(ESP.getChipId()));

  //Init the relay pins
  debug("Initializing relay pins...");
  pinMode(relay_IN1, OUTPUT);
  delay(200);
  pinMode(relay_IN2, OUTPUT);
  delay(200);
  digitalWrite(relay_IN1, HIGH); // HIGH = AUS
  delay(200);
  digitalWrite(relay_IN2, HIGH); // HIGH = AUS
  delay(200);

  WiFi.mode(WIFI_STA); // Client Mode
  ConfigureWifi();

  if (NTPserverIP.toString()=="0.0.0.0") {
    debug("NTPname: " + String(NTPserverName) + " (resolving)");
    WiFi.hostByName(NTPserverName,NTPserverIP);
  }
  debug("NTP IP : " + NTPserverIP.toString());
  debug();
  debug("Establishing UDP socket for NTP communication, syncing time");
  udp.begin(1230);
  setSyncProvider(getNTPtime);
  // debug("Will perform a new NTP query every hour/2");
  // setSyncInterval(1800000); SYNC Intervall auf 4h gesetzt EJS
  debug("Will perform a new NTP query every 4h");
  setSyncInterval(14400000);
  debug("Local time is now: ",false);
  debug(date2text()+String(", ")+time2text());

  debug();
  if (mdns!="" && MDNS.begin(mdns)) {
    debug("Established mDNS responder for ("+String(mdns)+")");
    MDNS.addService("http","tcp",80);
  } else {
    debug("mDNS responder not established");
  }

  debug("Establishing connection to MQTT broker");
  MQTTClient.setServer(mqtt_server, 1883);
  MQTTClient.setCallback(mqttcallback);
  mqttConnect();
  if (!MQTTClient.connected()) {
    debug("Not connected to MQTT broker");
  }else{
    debug("Connected to MQTT broker");
  }
 
  debug("Starting Web server");
  httpUpdater.setup(&server);
  server.begin();
  server.on("/",web_root);
  server.on("/reset",web_reset);
  server.on("/restart",web_restart);
  server.on("/debug",web_debug);
  server.on("/proffer",web_proffer);
  server.on("/flush",web_flush);
  server.on("/power",web_power);
  server.on("/command",testCommand);

  debug();
  if (udpRemotePort==0 || udpRemoteAddress=="") {
    debug("No UDP listener configured");
  } else {
    debug("Establishing socket for UDP listener");
    Sudp.begin(1231);
  }

  debug();
  debug("Restarting controller every night at 01:30");
  Alarm.alarmRepeat(01,30,0,restart);

  pinMode(led,OUTPUT);
  debug();
  debug("Setup done...");

  switch (type) {
    case NONE:
      debug("CONTROLLER IS CURRENTLY RUNNING IN TESTMODE - communication with machine disabled");
      for(uint8_t i=0; i<10; i++) {
        digitalWrite(led,LOW); // Turn ON internal LED
        delay(50);
        digitalWrite(led,HIGH); // Turn OFF internal LED
        delay(50);
      }
      break;
    case UNSUPPORTED:
      debug("Unsupported machine type, won't read machine status");
      break;
    case C5:
      debug("Machine type set to C5");
      break;
    case S90:
      debug("Machine type set to S90");
      break;
    case E50:
      debug("Machine type set to E50");
      break;
  }
}

void loop() {
  //status=fromMachine();
  if (millis()-lastRun>cycle*1000) {
    lastRun=millis();
    // debug(String(lastRun));
    readState();

    // Make sure we are connected
    if (WiFi.status() != WL_CONNECTED) {
      ConfigureWifi();
    }
    mqttConnect();
  }
  server.handleClient();
  Alarm.delay(0);
  MQTTClient.loop();
}



Falls jemand eine Idee hat - ich bin für jeden Vorschlag dankbar!

Gruss
Elmar
Raspberry Pi 2 - FHEM 5.7
HM-LAN, HM-CFG-USB-2
HM-Sec-SCo, HM-Sec-SC-2, HM-TC-IT-WM-W-EU,
HM-LC-SW4-DR, HM-LC-Sw1-DR, HM-ES-PMSw1-DR,    
HM-ES-PMSw1-Pl - Rademacher Hompilot DuoFern

irqnet

#12
Ich versuche das Ganze gerade mit meiner Jura Ena Micro 90 und bekomme sie überhaupt nicht überredet irgendwas zu machen. Habe den Sketch mit diversen ESP8266 Modulen getestet, aber die Maschine reagiert auf keinen einzigen "Befehl".

Gibt es eine Möglichkeit den Serviceport der Maschine irgendwie zu debuggen? Oder kann ich im Sketch sehen ob da überhaupt was an Infos an die Maschine geht oder von der Maschine kommt?

@eszych: Habe versucht die mqtt Integration zu konfigurieren, nutze bei meinem lokalen mqtt server allerdings eine authentifzierung. Seltsamerweise funktioniert die Übergabe von User und Passwort beim Connect nicht. Hast Du evtl. einen Tipp?


@det. Du musst bei einem "normalen" ESP die GPIO Ports angeben. D5 / D6 usw. kennt der aus der Bibliothek dann nicht. Die PINOUTs für die ESPs sind leider nicht immer identisch, aber hier hast Du einen Anhaltspunkt:

http://www.mikrocontroller-elektronik.de/nodemcu-esp8266-tutorial-wlan-board-arduino-ide/

Tausch also einfach mal D5 gegen 14 und D6 gegen 12 etc. gemäß dem Pinout für dein entsprechendes ESP Modul.

det.

Hallo irqnet,

hatte es hinbekommen indem ich einfach einen Wemos mini gekauft habe. Mit dem ging es (Sketch aufspielen, Web Oberfläche sehen), allerdings meine F70 macht über den Service Port nichts weiter als den Wemos mit 5V zu versorgen. Schalten konnte ich nichts, nicht mal nur aus. Da habe ich es aufgegeben und beobachte hier ob jemand mehr Erfolg hat.
LG
det.

irqnet

Zitat von: det. am 14 Februar 2018, 17:35:44
Hallo irqnet,

hatte es hinbekommen indem ich einfach einen Wemos mini gekauft habe. Mit dem ging es (Sketch aufspielen, Web Oberfläche sehen), allerdings meine F70 macht über den Service Port nichts weiter als den Wemos mit 5V zu versorgen. Schalten konnte ich nichts, nicht mal nur aus. Da habe ich es aufgegeben und beobachte hier ob jemand mehr Erfolg hat.

Schade. Ich vermute das es vielleicht eine Änderung im Protokoll gab, oder aber die Ports doch nicht voll belegt sind. +5V und GND konnte ich auch identifzieren. Ob die beiden anderen Ports für RX TX wirklich was tun intern, weiß ich nicht.

hanswerner1

Hallo,

ich habe am meiner Z7 jetzt auch einen Wemos mini angeschlossen. Leider funktioniert auch das Einschalten nicht, da bei ausgeschalteter Maschine keine 5V auf den Interface sind.

Die meisten Sachen funktionieren. Nur sind die Menüpunkte anders belegt.:


P1 Abschalten
P2 -
P3 Milch
P4 Latte M.
P5 Cappucino
P6 Kaffee Spezial
P7 -
P8 Ristretto
P9 Esspresse
P10 Kaffee 1x
P11 -
P12 -
P13 -



irqnet

Was meinst Du mit "anders" belegt? Sind die Kommandos nicht AN:01 etc, sondern P1 - P13?

hanswerner1

Ich meine das die Menüpunkte anders belegt sind:
zb.: Produkt 4 beziehen (1 kleine Tasse) ist bei mir Latte M.

Dahinter sehen natürlich die zb: AN:01 befehle an die Maschine.

Wie kann man die verschiedenen Befehle an die Maschine herausfinden ?

Kann ich den Wemos auch extern mit 5V versorgen ? Dann nur RX, TX und GND mit der Jura verbinden um zu testen ob ich die Maschine dann auch einschalten kann ? Da ja ausgeschaltet die 5V fehlen.

irqnet

Ich bin ein Stück weiter mit dem Thema - allein mit dem ESP ging es gar nicht, egal welches Modul (NodeMCU oder Wemos). Es funktioniert bei mir nur mit einem Levelshifter 3,3V <-> 5V weil die Maschine scheinbar 5V benötigt an den RX/TX Pins.

Das Ganze funktioniert dann, wenn auch nicht so zuverlässig, aber auch bei mir ist die Belegung eine andere. Was ich raus finden konnte für meine Ena Micro 90:

FA:01 ohne Funktion
FA:02 ohne Funktion
FA:03 entspricht dem Druck auf die Menütaste (bei mir lange Betätigung des Knopfes in der Mitte des Drehrads)
FA:04 ohne Funktion
FA:05 entspricht dem drehen des Drehrads für Auswahl normal/starker Kaffee
FA:07 Espresso
FA:08 Cappucino
FA:09 Kaffee
FA:10 Aufheizen?
FA:11 Error 1

Nach den Tests bis FA11 habe ich nicht mehr weiter gemacht, mir fehlt also noch Dampf, heißes Wasser und Latte Macciato. Die Maschine wirft aber bei manchen Kommandos den Fehler 1 oder Fehler 10, da hab ich mich erstmal nicht getraut weiter zu machen.

Ich habe den Wemos auch mit 5V extern versogt da die Maschine im "Standby" kein Saft mehr liefert. Der Levelschifter wird von der 3.3 V Seite vom Wemos versorgt und von der 5V Seite von der Jura.

hanswerner1

Levelshifter hab ich auch am Wemos dran. Habe gerade mal mit externer 5V Versorgung versucht die Maschine einzuschalten, hat aber leider auch nicht funktioniert. Kann natürlich daran liegen das ich den falschen Code gesendet habe oder das es grundsätzlich nicht geht.

Wie hast Du die Codes gesendet ?

irqnet

Ich nutze diesen Sketch, der hat ein Webinterface (https://gitlab.com/Blueforcer/HA2JURA/snippets/1674496)

Einschalten der Maschine geht aber auch bei mir mit keiner Kombination.

hanswerner1

Muß bei dem Sketch noch etwas eingestell werden, hab auf anhieb nichts für wie wLan Zugangsdaten gefunden ?

irqnet

Je nach eingesetztem ESP musst du schauen ob die Werte hier passen:


#define GPIORX    12
#define GPIOTX    13

#define GPIOLEDIO     4
#define GPIOLEDPULSE  0


Teilweise sind die GPIOS gemapped in den Libaries, dann kann man sie direkt mit "Namen" ansprechen wie D6 D7 oder LED_BUILTIN.


Der Sketch macht nach der ersten Inbetriebnahme einen Hotspot "JURA" auf -> connecten -> WLAN Daten hinterlegen -> reboot -> ESP im WLAN

stratege-0815

Hallo zusammen,
bin eben zufällig hier drauf gestossen und würde gerne meine Jura auch "ins Netz bringen" und mit FHEM kommunizieren lassen.
Der Thread ist ja noch nicht sooo lang, trotzdem tu ich mich etwas schwer zu verfolgen was man jetzt aus welchem thread für Infos ziehen muss und welche Hardwarekomponenten man benötigt.
Ist jemand in der Lade das quasi "als Tutor" Step by step aufzulisten?
Gruß
Jan

det.

Hallo Jan,
Da wirst Du Dich wohl selbst einlesen müssen - beginne mit den Links in Antwort 5 - wenn Du die durch hast, kommt Dir der Treat nicht mehr so kurz vor. Ich habe leider nicht den Eindruck, das es jemand hier bis zum erfolgreichen Ende geschafft hat, seine Jura komplett über Fhem zu steuern. Dazu kommt das die div. Modelle offenbar sehr unterschiedliche Herausforderungen darstellen. Meine Jura F70 stellt zwar an der Serviceschnittstelle dauerhaft 5V zur Verfügung, reagiert aber auf keinen Befehl über den Wemos mit dem Sketch von der in 5 verlinken Seite. Andere Modelle reagieren anders, das kannst Du aber hier im Treat gut nachlesen.
LG
det.

hanswerner1

Zitat von: irqnet am 23 Februar 2018, 13:20:51
Je nach eingesetztem ESP musst du schauen ob die Werte hier passen:


#define GPIORX    12
#define GPIOTX    13

#define GPIOLEDIO     4
#define GPIOLEDPULSE  0


Teilweise sind die GPIOS gemapped in den Libaries, dann kann man sie direkt mit "Namen" ansprechen wie D6 D7 oder LED_BUILTIN.


Der Sketch macht nach der ersten Inbetriebnahme einen Hotspot "JURA" auf -> connecten -> WLAN Daten hinterlegen -> reboot -> ESP im WLAN

Er macht keinen Hotspot auf. Hab die Datei example.c runtergeladen umbenant in HA2jura.ino und geflasht.

stratege-0815

Zitat von: det. am 23 Februar 2018, 20:14:20
Hallo Jan,
Da wirst Du Dich wohl selbst einlesen müssen - beginne mit den Links in Antwort 5 - wenn Du die durch hast, kommt Dir der Treat nicht mehr so kurz vor. Ich habe leider nicht den Eindruck, das es jemand hier bis zum erfolgreichen Ende geschafft hat, seine Jura komplett über Fhem zu steuern. Dazu kommt das die div. Modelle offenbar sehr unterschiedliche Herausforderungen darstellen. Meine Jura F70 stellt zwar an der Serviceschnittstelle dauerhaft 5V zur Verfügung, reagiert aber auf keinen Befehl über den Wemos mit dem Sketch von der in 5 verlinken Seite. Andere Modelle reagieren anders, das kannst Du aber hier im Treat gut nachlesen.

Ach, manche Probleme sind doch eigentlich gar keine. Ich wundere mich warum in dem anderen Forum kein Link und kein Bild geht und immer nur eine Fehlermeldung kommt. Man muss sich zuerst anmelden!😊
Also diese "Hürde" ist hier schon einmal für die Nachwelt dokumentiert. Ich warte noch auf die Hardware und werde dann meiner Jura C5 zu Leibe rücken. Ich will versuchen dann meine Schritte in einer Anleitung zusammenzufassen.

hanswerner1

#27
Ich habe den Wemos jetzt mal vom Steckboard runter und mit Levelshifter eingeschrumpft. Passt so prima in die Klappe zum Serviceport.



thorschtn

Zitat von: det. am 23 Februar 2018, 20:14:20
Hallo Jan,
[..] - beginne mit den Links in Antwort 5 - [..] Ich habe leider nicht den Eindruck, das es jemand hier bis zum erfolgreichen Ende geschafft hat, seine Jura komplett über Fhem zu steuern. [..]

Doch, ich kann mit den verlinkten Anleitungen meine S90 vollständig steuern, d.h. die Anleitungen funktionieren Prinzipiell - nachgewiesen bislang aber eben nur für die S90 (bzw. impressa 5000).

Gruß, Thorsten
NUC - FHEM & HA
MapleCUN, Homematic, 433MHz, AB440, 1-Wire Bewässerung & Pool, Jarolift (Signalduino), Signal Messenger, Denon AVR, LG WebOS, AmazonEcho, Jura S90 (ESP8266), Sonoff, Xiaomi Mii Sauger, Worx SO500i

hanswerner1

Prinzipiell funktioniert meine Impressa Z7 auch, Das einschalten funktioniert nicht, da der Wemos dann keine 5V Versorgungsspannung hat aber auch mit externer Spannung geht's nicht. Wahrscheinlich weil die Steuerung der Maschine keine Standby Stromversorgung hat.

Sonst funktionieren aber die Steuercodes. Ich habe leider nur noch nicht alle rausgefunden, da ich den HA2jura Sketch den mir irqnet empfohlen hat, nicht ans laufen bekomme. Die Kommandos sind wohl bei den verschiedenen Jura Maschienen unterschiedlich.

Gruß HW1

stratege-0815

#30
Ich habe inzwischen auch die Hardware zusammen gesteckt und den Sketch aus dem knx Forum aufgespielt. Klappt soweit alles, IP Adresse, Web Interface. Nur schalten kann ich nichts. Ausschalten ging als einziges.
Ich habe eine Jura C5 und das auch im Sketch hinterlegt. Einschalten geht Mangels Strom auch nicht, dafür habe ich aber irgendwo schon einmal eine Anleitung zu einem Umbau gesehen.

Mir fehlt im Moment eine Idee welche Werte ich hier "ausprobieren" soll und woher ich diese bekomme.

Forve

Hallo zusammen,

Actronx, das ist eine tolle Idee :) Funktioniert es einwandfrei bei dir?

Cooper81

Hallo zusammen!
Haben uns nun auch endlich eine Jura zugelegt!  ;D

Natürlich war meine erste suche, ob ich das Teilchen auch in fhem einbinden kann (wegen Wecker und so xD)

Leider habe ich aber noch nichts dazu gefunden. Wir haben nun eine S8 mit 7-Pin service Port.
Hat jemand schon erfahrung damit? funktioniert das? habe schon den levelshifter und den wemos hier liegen.

Gruß
Cooper

bennebartsch

Hat's jemand schon geschafft bei der Z5 den Status auszulesen?

garfield2412

Hallo,

Hab an meiner E75 das ganze mit einem wemos D1 am laufen.
Soweit alles gut. Läuft...
FA:06 ist Kaffee normal, FA:07 ebenfalls (vermute Doppeltasse).
Aber ich trinke gerne stärken Kaffee (Also Kaffee Taste länger drücken).
Wie lautet der Befehl hierzu? Hab fast alles (FA:...) durchprobiert.

Jemand ne Idee?

buzzmain

Ich finde das sehr interessant.
leider bin ich nicht so der Bastler.
Ich habe dies gefunden. Vielleicht hilft es ja weiter.
https://community.home-assistant.io/t/control-your-jura-coffee-machine/26604
... And on the 8th Day God Created The Homeautomation.

Stonemuc

Hat jemand das ganze auch ein einer moderneren Maschine wie z.B. der Z Reihe betrieben?
FHEM aus Raspberry PI 3 B+, Haussteuerung auf EnOcean Basis, Tecalor THZ 404eco Wärmepumpe

hanswerner1

#37
ich habe es mal mit meiner Z7 versucht. Aber leider wird die nicht im Code unterstützt. Einige Befehle funktionieren aber nicht alle. Rückmeldungen von der Maschine funktionieren nicht.  Habe das Projekt aber auch schon längere Zeit nicht mehr weiter verfolgt. Hab den Adapter aber noch in der Maschine.

Stonemuc

Wie hast du das gelöst? Mit welcher Version? Der aus dem KNX Forum mit dem Wemos D1 mini? Oder der andere Weg über Arduino?
FHEM aus Raspberry PI 3 B+, Haussteuerung auf EnOcean Basis, Tecalor THZ 404eco Wärmepumpe


Stonemuc

Gut...wenn sie sich nicht remote einschalten lässt, macht das für mich keinen Sinn...denke nicht, dass meine Z9 da anders reagieren wird. Also muss ivh wohl doch auf eine Z8 umsteigen...
FHEM aus Raspberry PI 3 B+, Haussteuerung auf EnOcean Basis, Tecalor THZ 404eco Wärmepumpe

Microsuck

Hi,

auch wenn der Beitrag schon ein paar Tage alt ist wollte ich mal nachfragen ob es jemand hinbekommen hat eine Jura Impressa J9 zum laufen zu bringen? Habe es bis dato noch nicht geschafft.

Auf GitHub habe ich ein Projekt gefunden "Jura Coffee Machine Gateway" https://github.com/hn/jura-coffee-machine welches ich auf einen WEMOS geflasht habe. Mit diesem Projekt ist es möglich über curl -d 'AN:01' http://ip-wemos/api "hat aber auch ein Webinterface" die Befehle an die Maschine zu senden, leider bekomme ich immer die Rückmeldung "Service not available".

Vielleicht habe ich glück und jemand von euch ist schon ein bisschen weiter gekommen.

Danke

Microsuck

Hi,

auch wenn der Beitrag schon ein paar Tage alt ist wollte ich mal nachfragen ob es jemand hinbekommen hat eine Jura Impressa J9 zum laufen zu bringen? Habe es bis dato noch nicht geschafft.

Auf GitHub habe ich ein Projekt gefunden "Jura Coffee Machine Gateway" https://github.com/hn/jura-coffee-machine welches ich auf einen WEMOS geflasht habe. Mit diesem Projekt ist es möglich über curl -d 'AN:01' http://ip-wemos/api "hat aber auch ein Webinterface" die Befehle an die Maschine zu senden, leider bekomme ich immer die Rückmeldung "Service not available".

Vielleicht habe ich glück und jemand von euch ist schon ein bisschen weiter gekommen.

Danke