hobby-elektroniker.de

...selbst gebastelt macht einfach mehr Spass!

ESP8266: eine WiFi zu RS232 Bridge-Lösung

25.3.2018


Dieser Artikel ist mit dem Zusatz „aktuell“ markiert, was nichts anderes bedeutet, als dass ich an diesem Projekt und dem zugehörigen Ein kleines Projekt mit der NodeMCU V3 von Lolin, welches sich aus einer Notwendigkeit heraus ergab. Bei meinem derzeitigen Arbeitgeber habe ich (unter anderem) die Aufgaben des Energiemanagers inne und betreue somit die Zählererfassungssysteme. Unsere Zähler laufen auf Summenstationen des Typs U160x der Firma Gossen Metrawatt zusammen, diese benutzen eine serielle Schnittstelle zur Kommunikation mit übergeordneten Leitsystemen. Wir haben hierfür einen Adapter RS232 zu Ethernet angeschafft, um die Summenstationen ins Firmennetzwerk zu integrieren, was bisher auch ausreichte. Nun war es aber Zeit für einen Zählerabgleich und der gestaltet sich schwierig, wenn die Zähler im Keller, die nächste Arbeitsstation jedoch im ersten Stock liegt. Daher habe ich mir dieses kleine Tool gebastelt, um mit selbigem direkt an die Summenstationen zu gehen und dann mit einem Tablet ausgerüstet an die Zähler. Anleitungen hierfür gibt es so einige im Netz, meine schließt jedoch die Online-Parametrierung der RS232 Schnittstelle mit ein (für Systeme Abseits der 8N1).

Schritt 1: Die Vorbereitung


Im Internet kann man sich schnell anlesen, dass so eine NodeMCU nicht ohne weiteres einsetzbar ist. Zuerst muss auf den ESP8266 eine Firmware aufgespielt werden und sofern nicht vorher schon geschehen, muss als aller erstes der passende Treiber für die virtuelle COM-Schnittstelle installiert werden. Also erkläre ich mal kurz alles der Reihe nach …

Zuerst ist zu prüfen, welchen USB-UART Chip ihr auf dem Board habt. Möglich sind CP2102 oder CH340G (Es gibt verschiedene Clone der Lolin NodeMCU, jeder macht da seine eigene Sache).

Den jeweils aktuellen Treiber findet ihr im Internet auf den Herstellerseiten, wo er in der jeweils aktuellsten Version kostenlos heruntergeladen werden kann (siehe Box Links). Nach der Installation sollte der angeschlossene ESP8266 vom Rechner als USB-Serial Gerät erkannt werden. Merkt euch die COM Zuordnung.

Die ESP8266 hat nur einen begrenzten Flash-Speicher. Um sowohl die ESP selbst als auch den Programmspeicher effektiv nutzen zu können sollte zu Beginn eines jeden Projekts geprüft werden, welche Funktionalität der ESP eigen sein soll. Es sind über 40 Module verfügbar, darunter auch welche für die gängigsten Sensoren und Displayunterstützung. Was benötigt wird, kann in die Firmware hinein generiert werden. Alles andere sollte jedoch nicht unnötigerweise den Flash-Speicher belasten, um möglichst viel Platz für das eigene Nutzerprogramm zu behalten. Die Generierung der Firmware geschieht online in einen Cloud-Build-Service (siehe Links).

Gebt eine E-Mail-Adresse an, an welche, nach Fertigstellung der Firmware, der Downloadlink geschickt wird. Im Auswahldialog neben den bereits gewählten Komponenten noch jene hinzuwählen, welche ihr für euer Projekt benötigt. Ggf. zusätzlichen Auswahldialogen folgen und auf „Build“ klicken. Wartet auf die E-Mail mit dem Fertigstellungshinweis, dann kann die Firmware über einen Link heruntergeladen werden.

Für dieses Projekt habe ich die mit den Pfeilen gekennzeichneten Komponenten zusätzlich in die Firmware generiert, das U8G-Modul (Display) hat zusätzliche Auswahlfelder in denen ich Schriftart und Displaytyp ausgewählt habe (OLED Display mit 128x64 Pixel und SPI-Schnittstelle).

Ihr bekommt über die E-Mail zwei Links zu zwei Dateien, ich beschränke mich in aller Regel auf die float-Variante der Firmware-Datei, welche nun mit einem Flash-Tool (siehe Links) auf die NodeMCU gebrannt wird. Hier nun erst einmal eine kurze Bildergeschichte, danach die Erklärungen …

Auf der ersten Karteikarte (Operation) wählt ihr den COM-Port aus, welcher der NodeMCU zugeordnet ist. In der Karteikarte „Config“ sucht ihr über das kleine Zahnrad eure Datei vom Cloud-Build-Service aus (ich habe die float.bin genommen). In der Karteikarte „Advanced“ passt ihr die Übertragungsgeschwindigkeit an (etwas langsamer, um Fehler zu vermeiden). Zurück auf der Karteikarte „Operation“ klickt ihr auf „Flash(F)“. Zusätzliche Informationen zur NodeMCU werden angezeigt (MAC-Adressen). Wartet bis der Fortschrittsbalken das rechte Ende erreicht. Der kleine blaue Strudel unten links in der Ecke sollte ein grünes Häkchen werden. Die Firmware wurde nun erfolgreich auf die NodeMCU übertragen.

Ich habe das Projekt über die Arduino IDE umgesetzt, der ESP kann aber auch in LUA programmiert werden. Die Arduino IDE gibt es in der jeweils aktuellsten Version auf der Arduino Homepage (siehe Links). Diese unterstützt von Haus aus aber die ESP-Boards nicht, das kann aber leicht geändert werden.

  • ▶ startet die IDE und geht zu Menü „Datei“ -> „Voreinstellungen“. Tragt dort unter „Boardverwalter-URL“ folgendes ein: http://arduino.esp8266.com/stable/package_esp8266com_index.json
  • ▶ nun geht zum Menü „Werkzeuge“ -> „Board: …“ -> „Boardverwalter“ und gebt in der Suchzeile „ESP8266“ ein. Es sollte ein Treffer angezeigt werden, welchen ihr jetzt installieren solltet.
  • ▶ geht wieder ins Menü „Werkzeuge“ -> „Board: …“ und wählt die „NodeMCU V1.0 (ESP12E-Module)“.
  • ▶ geht wieder ins Menü „Werkzeuge“ -> „Port“ und wählt den COM-Port eurer NodeMCU aus.
  • ▶ Fertig. Nun sind alle Vorbereitungen abgeschlossen und wir können an den Aufbau und die Programmierung gehen.

Schritt 2: Hardwareaufbau


Neben der NodeMCU V3 von Lolin werden noch ein paar LEDs für die Anzeige von Power, Rx und Tx benötigt. Dazu ein paar Widerstände und Dioden für die Pegelanpassung von 3,3V (NodeMCU) auf 5V (MAX232) und natürlich den MAX232 nebst Außenbeschaltung selbst. Des Weiteren habe ich ein Display (OLED 0,96" mit 128x64 Pixeln) verwendet um die IP, SSID und Passwort nach außen anzuzeigen. Das Display ist jedoch optional. Dazu noch einen 9poligen Sub-D Stecker. Das war es eigentlich schon an Bauteilen.

Das 0.96" OLED Display hat eine Betriebsspannung von 3,3V und kann somit direkt mit der NodeMCU verbunden werden. Hierfür sind nachstehende Verbindungen herzustellen:

  • Display GND ▶ NodeMCU GND
  • Display VCC ▶ NodeMCU 3.3V
  • Display SDA ▶ NodeMCU GPIO13(D7)
  • Display SCL ▶ NodeMCU GPIO14(D5)
  • Display CS ▶ NodeMCU GPIO15(D8) *
  • Display RES ▶ NodeMCU GPIO16(D0) *
  • Display DC ▶ NodeMCU GPIO2(D4) *

Das Sternchen(*) bedeutet in dem Fall, dass ihr die GPIOs hier frei wählen könnt, sie werden später im SPI Init übergeben.

Der MAX232 hat eine Betriebsspannung von 5V und kann somit nicht durch die 3,3V der MCU versorgt werden, jedoch wird in der aktuellen Version 3 der NodeMCU wird die Spannung der USB Versorgung über einen früher als „Reserviert“ markierten Pin nach außen geführt (VU / VUSB). Auch gilt es zu beachten, dass die TTL-Pegel des MAX232 bei 5V und damit höher als es die NodeMCU erlaubt liegen. Hier müssen die Pegel angepasst werden. So stellt ihr die Verbindungen her:

  • MAX232 GND (Pin15) ▶ NodeMCU GND
  • Sub-D Pin5 ▶ NodeMCU GND, MAX2323 GND
  • MAX232 VCC (Pin16) ▶ NodeMCU VU(VUSB)
  • MAX232 R1Out (Pin12) ▶ 4,7k..10k ▶ NodeMCU RX **
  • NodeMCU Rx ▶ 4,7k..10k ▶ NodeMCU GND **
  • MAX232 T1In (Pin11) ▶ + 1N4049 - ▶ NodeMCU TX **
  • MAX232 T1In (Pin11) ▶ 4,7k..10k ▶ MAX232 VCC (Pin16) **
  • MAX232 R1In (Pin13) ▶ Sub-D Pin3
  • MAX232 T1Out (Pin14) ▶ Sub-D Pin2
  • MAX232 RC1- (Pin3) ▶ - 10µF + ▶ MAX232 RC1+ (Pin1)
  • MAX232 RC2- (Pin5) ▶ - 10µF + ▶ MAX232 RC2+ (Pin4)
  • MAX232 V- (Pin6) ▶ - 10µF + ▶ MAX232 GND (Pin15)
  • MAX232 VCC (Pin16) ▶ - 10µF + ▶ MAX232 V+ (Pin2)
  • MAX232 VCC (Pin16) ▶ 100n ▶ MAX232 GND (Pin15)
  • MAX232 T2In (Pin10) ▶ MAX232 GND
  • Sub-D Pin4 ▶ Sub-D Pin6
  • Sub-D Pin7 ▶ Sub-D Pin8

Das Doppelsternchen(**) steht für die Bauteile der Pegelanpassung von 3,3V auf 5V (Tx) und andersherung (Rx).

Wer jetzt noch die LEDs verbauen möchte um die Daten auch optisch „fließen“ zu sehn, der schließt sie wie folgt an:

  • MAX232 VCC (Pin16) ▶ + LED rot - ▶ 470Ohm ▶ MAX232 GND (Pin15)
  • MAX232 VCC (Pin16) ▶ + LED gelb - ▶ 470Ohm ▶ MAX232 T1In (Pin11)
  • MAX232 VCC (Pin16) ▶ + LED gelb - ▶ 470Ohm ▶ MAX232 R1Out (Pin12)

Und das war es eigentlich auch schon. Die gesamte Schaltung wird über den USB-Anschluß der NodeMCU mit Spannung versorgt.
Gesamtschaltbild:

Schritt 3: Das Programm


Nach dem Start der Arduino IDE erstellen wir einen neuen Sketch. Achtet darauf, dass ihr das richtige Board und den korrekten Port gewählt habt (siehe Schritt 1).

Als Erstes binden wir die nötigen Libraries für das Projekt ein:

#include <ESP8266WiFi.h>   // Findet ihr im Bibliotheksverwalter
#include <ESP8266WebServer.h>
#include <ESP8266WebServerSecure.h>
#include <SPI.h>           // für Kommunikation mit Display 
#include "SSD1306Spi.h"    // Lib im Projektordner (Downlaod)


Festlegen der Netzwerkparameter und erzeugen einer Server-Instanz:

const char* ssid      = "Netzwerkname"; 	// WiFi Name
const char* password  = "Passwort";  	    // Wifi Password
IPAddress apIP(100,100,100,100); 			// Default 192.168.4.1
ESP8266WebServer server(80);


Für das Display legen wir noch die verwendeten GPIOs fest und erzeugen eine Instanz der SSD1306Spi:

uint8_t RES = 16;
uint8_t DC = 2;
uint8_t CS = 15;
SSD1306Spi display(GEOMETRY_128_64, RES, DC, CS);


Parameter/ Variablen für RS232:

uint8_t  ser_Config        = 28;          // Standard 8N1
uint32_t ser_Baud          = 9600;          // Standard 9600Bps
String   ser_BitsPerSend   = "8";
String   ser_Parity        = "keine";
String   ser_StoppBits     = "1";
String   ser_LineBreak     = "CR+LF";
String   ser_lb            = "\r\n"; 


Das Setup für Soft-Access-Point, Display und Serial-Verbindung:
Mit server.on(String,Handle); legen wir einzelne Handles für die Seitenaufrufe durch den Browser fest. Ich werde sie nachher noch erklären.

void setup() {
  // SoftAP einrichten
  WiFi.mode(WIFI_AP_STA);
  WiFi.softAPConfig(apIP, apIP, IPAddress(255,255,255,0));
  WiFi.softAP (ssid, password);
  server.on ("/", handleRoot);
  server.on ("/[~CMD]", handleCommand);
  server.on ("/[~DATA]", handleData);
  server.on ("/setup", handleBrowserSetup);
  server.on ("/index", hanldeBrowserRxTx);
  server.onNotFound (handleNotFound);
  server.begin();
  
  // Display einrichten
  display.init();
  display.flipScreenVertically();
  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.clear();
  
  // RS232 einrichten
  Serial.begin(ser_Baud,(SerialConfig)ser_Config); 
  
  // Ausgabe der Netzwerkparameter auf dem Display
  display.drawString(1,0,  "SSID: " );
  display.drawString(35,0, String(ssid));
  display.drawString(1,10, "PASS: " );
  display.drawString(35,10, String(password));
  display.drawString(1,25, "IP: ");
  display.drawString(35,25, "100.100.100.100");
  display.drawString(1,40, "MAC: ");
  display.drawString(35,40, WiFi.macAddress());
}


Die Hauptschleife:

void loop() {
  server.handleClient();
  delay(10);  
}


Der Großteil des Programms spielt sich jetzt in Subroutinen ab, für welche wir teilweise bereits Handles angelegt haben. Nach dem Start des Servers kann sich ein Nutzer via WLAN verbinden. Folgende Aufrufe sind nun vom Browser aus möglich:

- 100.100.100.100 und ▶ führt zur handleRoot().
- 100.100.100.100/index ▶ führt zu handleBrowserRxTx().
- 100.100.100.100/setup ▶ führt zu handleBrowserSetup().

Zusätzlich gibt es zwei Aufrufe mit Text-Response um Apps oder Programmen (die kein Browser sind) die Kommunikation zu ermöglichen und zu erleichtern:
- 100.100.100.100/[~CMD] ▶ führt zu handleCommand(). und
- 100.100.100.100/[~DATA] ▶ führt zu handleData(). und

Alle anderen Aufrufe führen zu handleNotFound().

Die einzelnen Subroutinen:



handleNotFound
gibt dem Benutzer eine Text-Response aus mit der Fehlermeldung „File not found“ der URI und den übergebenen GET und Post Parametern.

void handleNotFound() {
  String message = "File not found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";

  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName (i) + ": " + server.arg(i) + "\n";
  }
  server.send (404, "text/plain", message);
}


handleRoot():
ist eine Weiterleitung zu handleBrowserRxTx().

void handleRoot() {
  hanldeBrowserRxTx();
}


handleBrowserRxTx
gibt dem Benutzer (seinem Browser) eine HTML-Response welche die Darstellung des Terminals beinhaltet. Der Screenshoot zeigt die Browserdarstellung. Des weiteren verarbeitet sie die Formulardaten, also einen Sendebefehl, und gibt die über RS232 erhaltene Antwort wiederum im entsprechenden Feld der HTML-Response auf den Browser aus.


Achtung!! Dieser Programmteil ist noch unvollendet!

void hanldeBrowserRxTx() {
  WiFiClient client = server.client();
  client.println("<HTML>");
  client.println("  <HEAD>");
  client.println("    <TITLE>WIFI-RS232-USB Bridge ~Webterminal V1.0~</TITLE>");
  client.println("    <meta charset=\"UTF-8\">");
  client.println("    <meta http-equiv=\"language\" content=\"deutsch, de\">");  
  client.println("    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
  client.println("    <meta http-equiv=\"imagetoolbar\" content=\"false\">");
  client.println("    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">");
  client.println("    <meta name=\"viewport\" content=\"initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">");
  client.println("    <meta name=\"msapplication-tap-highlight\" content=\"no\">");
  client.println("    <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">");
  client.println("    <meta name=\"theme-color\" content=\"#fff5e6\">");
  client.println("    <meta name=\"author\" content=\"Torsten Just\">");
  client.println("    <meta name=\"date\" content=\"Mar,2018\">");
  client.println("    <STYLE>");
  client.println("      html, body {");
  client.println("        width: 100%;");
  client.println("        margin:0;");
  client.println("        padding: 0;");
  client.println("        font-family: 'Verdana';");
  client.println("        font-size: 1em;");
  client.println("        background-color: #fff5e6;}");
  client.println("      .wt_head, .wt_setbaud, .wt_comm{");
  client.println("        width: 100%;");
  client.println("        max-width: 1000px;");
  client.println("        margin: 10px auto;");
  client.println("        padding: 10px;");
  client.println("        border-radius: 8px;");
  client.println("        text-align: center;}");
  client.println("      .wt_head          { background-color: #ffd699; border: 1px solid #ffa31a; }");
  client.println("      .wt_setbaud       { background-color: #ffad33; border: 1px solid #ff9933; }");
  client.println("      .wt_setbaud label { float: left; width: 10em; margin-right: 1em; text-align: right; }");
  client.println("      .wt_comm          { background-color: #cc7a00; border: 1px solid #804d00; }");
  client.println("      h1                { color: #cc7a00; text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; }");
  client.println("      a                 { color: black;text-decoration:none;}");
  client.println("      button {");
  client.println("        width: 200px;");
  client.println("        border-radius: 8px;");
  client.println("        padding: 6px 28px;");
  client.println("        color: black;");
  client.println("        border: 1px solid #000;");
  client.println("        margin-bottom: 4px;");
  client.println("        font-size: 1em;");
  client.println("        background-color: #fff5e6;}");
  client.println("      button:hover      { box-shadow: 0 3px 4px 0 rgba(0,0,0,0.24), 0 6px 20px 0 rgba(0,0,0,0.19);}"); 
  client.println("      select            { width: 7em; margin-right: 1em;}");
  client.println("      textarea, input[type='text']{");
  client.println("        font-family: 'Courier New';");
  client.println("        font-size: 1em;");
  client.println("        border: none;");
  client.println("        padding: 5px;");
  client.println("        margin-top: 8px;");
  client.println("        width: 95%;");
  client.println("        background-color: #ffebcc;");
  client.println("        -webkit-box-sizing: border-box;");
  client.println("        -moz-box-sizing: border-box;");
  client.println("        box-sizing: border-box;");
  client.println("        border-radius: 4px;");
  client.println("        border: 1px solid #804d00;}");
  client.println("    </STYLE>");
  client.println("  </HEAD>");
  client.println("  <BODY>");
  client.println("    <div class=\"wt_head\"><p><h1>WiFi-Terminal V1.0</h1>");
  client.println("      <font style=\"font-size:0.8em\">ESP8266 SoftAP - WIFI/RS232/USB Adapter von Torsten Just / www.hobby-elektroniker.de</font></p></div>");
  client.println("    <div class=\"wt_setbaud\">");
  client.println("      <p><b>RS232/USB:</b> ");  
  client.println(ser_Baud);
  client.println(" bps, " + ser_BitsPerSend + " Bits, " + ser_Parity + " Parität, " + ser_StoppBits + " Stopp-Bits, " + ser_LineBreak);
  client.println("      </p><button><a href=\"/setup\">Setup</a></button></div>");  

  client.println("    <div class=\"wt_comm\">");
  client.println("      <form action=\"\" id=\"wt_communicate\" method=\"post\">");
  client.println("        <p><b>Datenübertragung:</b></p>");
  client.println("        <p><label for=\"wt_receive\">Zuletzt empfangen:</label><textarea name=\"wt_receive\" rows=\"10\" cols=\"100\">");
  client.println("");
  client.println("        </textarea></p>");
  client.println("        <p><label for=\"wt_transmit\">Übermitteln:</label><input type=\"text\" name=\"wt_transmit\"></p>");
  client.println("        <p><button type=\"submit\">Senden</button><br></p>");
  client.println("      </form>");
  client.println("    </div>");
  client.println("  </BODY>");
  client.println("</HTML>");
  
  // TODO: Abarbeitung Post Parameter
  // Senden und Empfangen
  // Ausgabe des letzten Empfangenen Strings
}


handleBrowserSetup und doBrowserSetup
gibt dem Benutzer eine HTML-Response welche die Möglichkeiten zur Parametrierung der RS232 Schnittstelle beinhaltet. Der Screenshoot zeigt die Browserdarstellung. Des Weiteren registriert die Subroutine den Erhalt von Formulardaten, also die gewünschten Einstellungen, leitet zur Funktion doSetup() weiter, wo die serielle Schnittstelle gestoppt, und mit den geänderten Einstellungen neu gestartet wird.



void handleBrowserSetup() {
  if (server.args() > 0) {
    // Wenn Argumente übergeben wurden, wurde wohl das Formular ausgefüllt
    // und die Werte können übernommen werden.
    doBrowserSetup();
  } else {
    WiFiClient client = server.client();
    client.println("<HTML>");
    client.println("  <HEAD>");
    client.println("    <TITLE>WIFI-RS232-USB Bridge ~Webterminal V1.0~</TITLE>");
    client.println("    <meta charset=\"UTF-8\">");
    client.println("    <meta http-equiv=\"language\" content=\"deutsch, de\">");  
    client.println("    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
    client.println("    <meta http-equiv=\"imagetoolbar\" content=\"false\">");
    client.println("    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">");
    client.println("    <meta name=\"viewport\" content=\"initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">");
    client.println("    <meta name=\"msapplication-tap-highlight\" content=\"no\">");
    client.println("    <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">");
    client.println("    <meta name=\"theme-color\" content=\"#fff5e6\">");
    client.println("    <meta name=\"author\" content=\"Torsten Just\">");
    client.println("    <meta name=\"date\" content=\"Mar,2018\">");
    client.println("    <STYLE>");
    client.println("      html, body {");
    client.println("        width: 100%;");
    client.println("        margin:0;");
    client.println("        padding: 0;");
    client.println("        font-family: 'Verdana';");
    client.println("        font-size: 1em;");
    client.println("        background-color: #fff5e6;}");
    client.println("      .wt_head, .wt_setbaud, .wt_comm{");
    client.println("        width: 100%;");
    client.println("        max-width: 1000px;");
    client.println("        margin: 10px auto;");
    client.println("        padding: 10px;");
    client.println("        border-radius: 8px;");
    client.println("        text-align: center;}");
    client.println("      .wt_head          { background-color: #ffd699; border: 1px solid #ffa31a; }");
    client.println("      .wt_setbaud       { background-color: #ffad33; border: 1px solid #ff9933; }");
    client.println("      .wt_setbaud label { float: left; width: 10em; margin-right: 1em; text-align: right; }");
    client.println("      h1                { color: #cc7a00; text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; }");
    client.println("      a                 { color: black;text-decoration:none;}");
    client.println("      button {");
    client.println("        width: 200px;");
    client.println("        border-radius: 8px;");
    client.println("        padding: 6px 28px;");
    client.println("        color: black;");
    client.println("        border: 1px solid #000;");
    client.println("        margin-bottom: 4px;");
    client.println("        font-size: 1em;");
    client.println("        background-color: #fff5e6;}");
    client.println("      button:hover      { box-shadow: 0 3px 4px 0 rgba(0,0,0,0.24), 0 6px 20px 0 rgba(0,0,0,0.19);}"); 
    client.println("      select            { width: 7em; margin-right: 1em;}");
    client.println("    </STYLE>");
    client.println("  </HEAD>");
    client.println("  <BODY>");
    client.println("    <div class=\"wt_head\"><p><h1>WiFi-Terminal V1.0</h1>");
    client.println("      <font style=\"font-size:0.8em\">ESP8266 SoftAP - WIFI/RS232/USB Adapter von Torsten Just / www.hobby-elektroniker.de</font></p></div>");
    client.println("    <div class=\"wt_setbaud\"><br/>");
    client.println("      <form action=\"\" id=\"wt_defineport\" method=\"post\">");
    client.println("        <b>RS232 Verbindung definieren:</b> </br></br>");
    client.println("        <div style=\"text-align: left; display:inline-block;\">");
    client.println("          <p><label for=\"wt_baud\">Bits je Sekunde</label>"); 
    client.println("          <select name=\"wt_baud\" size=\"1\">");
    client.println("            <option>300</option>");
    client.println("            <option>1200</option>");
    client.println("            <option>2400</option>");
    client.println("            <option>4800</option>");
    client.println("            <option selected>9600</option>");
    client.println("            <option>14400</option>");
    client.println("            <option>19200</option>");
    client.println("            <option>28800</option>");
    client.println("            <option>38400</option>");
    client.println("            <option>57600</option>");
    client.println("            <option>115200</option>");
    client.println("            <option>230400</option>");
    client.println("          </select></p>");
    client.println("          <p><label for=\"wt_bits\">Daten Bits</label> ");
    client.println("          <select name=\"wt_bits\" size=\"1\">");
    client.println("            <option>5</option>");
    client.println("            <option>6</option>");
    client.println("            <option>7</option>");
    client.println("            <option selected>8</option>");
    client.println("          </select></p>");
    client.println("          <p><label for=\"wt_pari\">Parität</label>");
    client.println("          <select name=\"wt_pari\" size=\"1\">");
    client.println("            <option selected>keine</option>");
    client.println("            <option>gerade</option>");
    client.println("            <option>ungerade</option>");
    client.println("          </select></p>");
    client.println("          <p><label for=\"wt_stop\">Stopp-Bits</label>");
    client.println("          <select name=\"wt_stop\" size=\"1\">");
    client.println("            <option selected>1</option>");
    client.println("            <option>2</option>");
    client.println("          </select></p>");
    client.println("          <p><label for=\"wt_line\">Umbruchzeichen</label>");
    client.println("          <select name=\"wt_line\" size=\"1\">");
    client.println("            <option>CR</option>");
    client.println("            <option>LF</option>");
    client.println("            <option selected>CR+LF</option>");
    client.println("          </select></p>");
    client.println("        </div>");
    client.println("        <p><button type=\"reset\">Zurücksetzen</button> <button type=\"submit\">Übernehmen</button><br>");
    client.println("        <button style=\"width:404px\"><a href=\"/index\">Abbruch und zurück zur Rx/Tx Ansicht</a></button></p>");
    client.println("      </form>");
    client.println("    </div>");
    client.println("  </BODY>");
    client.println("</HTML>");
  }
}

void doBrowserSetup() {
  String str_BrowserOut = setRS232(1);
  // Bestätigung anzeigen (Weiterleitung zu Index nach 3sec.)
  WiFiClient client = server.client();
  client.println("<HTML>");
  client.println("  <HEAD>");
  client.println("    <TITLE>WIFI-RS232-USB Bridge ~Webterminal V1.0~</TITLE>");
  client.println("    <meta charset=\"UTF-8\">");
  client.println("    <meta http-equiv=\"language\" content=\"deutsch, de\">");  
  client.println("    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
  client.println("    <meta http-equiv=\"imagetoolbar\" content=\"false\">");
  client.println("    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">");
  client.println("    <meta name=\"viewport\" content=\"initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">");
  client.println("    <meta name=\"msapplication-tap-highlight\" content=\"no\">");
  client.println("    <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">");
  client.println("    <meta name=\"theme-color\" content=\"#fff5e6\">");
  client.println("    <meta name=\"author\" content=\"Torsten Just\">");
  client.println("    <meta name=\"date\" content=\"Mar,2018\">");
  client.println("    <meta http-equiv=\"refresh\" content=\"3; URL=index\">");
  client.println("    <STYLE>");
  client.println("      html, body {");
  client.println("        width: 100%;");
  client.println("        margin:0;");
  client.println("        padding: 0;");
  client.println("        font-family: 'Verdana';");
  client.println("        font-size: 1em;");
  client.println("        background-color: #fff5e6;}");
  client.println("      .wt_head, .wt_setbaud, .wt_comm{");
  client.println("        width: 100%;");
  client.println("        max-width: 1000px;");
  client.println("        margin: 10px auto;");
  client.println("        padding: 10px;");
  client.println("        border-radius: 8px;");
  client.println("        text-align: center;}");
  client.println("      .wt_head          { background-color: #ffd699; border: 1px solid #ffa31a; }");
  client.println("      .wt_setbaud       { background-color: #ffad33; border: 1px solid #ff9933; }");
  client.println("      .wt_setbaud label { float: left; width: 10em; margin-right: 1em; text-align: right; }");
  client.println("      h1                { color: #cc7a00; text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; }");
  client.println("      a                 { color: black;text-decoration:none;}");
  client.println("    </STYLE>");
  client.println("  </HEAD>");
  client.println("  <BODY>");
  client.println("    <div class=\"wt_head\"><p><h1>WiFi-Terminal V1.0</h1>");
  client.println("      <font style=\"font-size:0.8em\">ESP8266 SoftAP - WIFI/RS232/USB Adapter von Torsten Just / www.hobby-elektroniker.de</font></p></div>");
  client.println("    <div class=\"wt_setbaud\">");
  client.println("      <p><b>");  
  client.println(str_BrowserOut);
  client.println("      </b></p><p>(Automatische Weiterleitung nach 3 Sekunden)</p></div>");  
  client.println("  </BODY>");
  client.println("</HTML>");
}


setRS232
Diese Funktion beendet die serielle Verbindung (RS232) und startet sie mit veränderten Parametern neu. Sie wird von doBrowserSetup() und handleCommand() gerufen. Die Variable Sender wird von doBrowserSetup auf 1 und von handleCommand auf 2 gesetzt um eine lange(HTML) bzw. kurze Antwort(Text) anzufordern.

String setRS232(int caller) {
//  So ergibt sich das ConfigValue (links der Bezeichner, rechts der zugehörige Integer-Wert)  
//  {5,6,7,8 = 0,4,8,12} +              Anzahl Bits
//  {No,Even,Odd = 16,18,19} +          Parity
//  {1,2 = 0,32}                        Stopp-Bits

  uint8_t  ConfigValue       = 0;
  uint32_t int_Baud          = 0;
  String   str_BitsPerSend   = "";
  String   str_Parity        = "";
  String   str_StoppBits     = "";
  String   str_LineBreak     = "";
  String   str_lb            = "";

  String   str_BrowserOut    = "Es ist ein Fehler aufgetreten. Änderungen konnten nicht übernommen werden.";
  String   str_TxtOut        = "Fail.";
  
  for (uint8_t i = 0; i < server.args(); i++) {
    if (server.argName(i) == "wt_baud") {
      int_Baud = (server.arg(i)).toInt();
    }
    if (server.argName(i) == "wt_bits") {
      str_BitsPerSend = server.arg(i);
      if (str_BitsPerSend == "5") ConfigValue += 0;
      if (str_BitsPerSend == "6") ConfigValue += 4;
      if (str_BitsPerSend == "7") ConfigValue += 8;
      if (str_BitsPerSend == "8") ConfigValue += 12;
    }
    if (server.argName(i) == "wt_pari") {
      str_Parity = server.arg(i);
      if (str_Parity == "keine") ConfigValue += 16;
      if (str_Parity == "gerade") ConfigValue += 18;
      if (str_Parity == "ungerade") ConfigValue += 19;
    }
    if (server.argName(i) == "wt_stop") {
      str_StoppBits = server.arg(i);
      if (str_StoppBits == "1") ConfigValue += 0;
      if (str_StoppBits == "2") ConfigValue += 32;
    }
    if (server.argName(i) == "wt_line") {
      str_LineBreak = server.arg(i);
      if (str_LineBreak == "CR") str_lb = "\r";
      if (str_LineBreak == "LF") str_lb = "\n";
      if (str_LineBreak == "CR+LF") str_lb = "\r\n";
    }
  }
  if ((str_BitsPerSend != "") and (str_Parity != "") and (str_StoppBits != "") and (int_Baud != 0)) {
      // Alle Parameter erhalten, Serial kann neu gestartet werden
      
      ser_Config        = ConfigValue;
      ser_Baud          = int_Baud;
      ser_BitsPerSend   = str_BitsPerSend;
      ser_Parity        = str_Parity;
      ser_StoppBits     = str_StoppBits;
      ser_LineBreak     = str_LineBreak;
      ser_lb            = str_lb;

      str_BrowserOut    = "Änderungen übernommen.";
      str_TxtOut        = "OK";
      
      Serial.end();
      Serial.begin(ser_Baud,(SerialConfig)ser_Config); 
  }
  if (caller == 1) return str_BrowserOut;
  if (caller == 2) return str_TxtOut; 
}


handleCommand
Verarbeitet ein Kommando zur Einstellung der RS232 Schnittstelle, ähnlich doSetup() jedoch ohne HTML-Response. Es wird lediglich ein „OK“ oder „Fail“ zurückgegeben. Dieser Aufruf ist für Apps und Programme gedacht, die mit einer HTML-Response nichts anfangen könnten.

void handleCommand() {
  String TxtOut = setRS232(2);
  server.send (200, "text/plain", TxtOut); 
}

und so sieht ein beispielhafter Aufruf durch eine App oder ein Programm aus:

http:\\100.100.100.100\[~CMD]?wt_baud=9600&wt_bits=8&wt_pari=keine&wt_stop=1&wt_line=CR+LF


handleData
Sendet einen String an die RS232 Schnittstelle. Es wird lediglich die Antwort der Gegenstation oder „Fail“ zurückgegeben. Dieser Aufruf ist für Apps und Programme gedacht, die mit einer HTML-Response nichts anfangen könnten.
Achtung!! Dieser Programmteil ist noch unvollendet!

void handleData() {
  // TODO: Alles
}


Soweit der Stand der Dinge
In den nächsten Tagen und Wochen werden die fehlenden Programmteile noch ergänzt. Ich werde zudem noch einen Schaltplan erstellen und die Downloads online bringen. (Unter dem Titel des Artikels wird ein entsprechender Vermerk „Update“ ausgebracht bzw. der Zusatz „aktuell“ verschwindet.) Ich hoffe, ich konnte bis hierher schon etwas weiterhelfen und/oder habe euch neugierig gemacht.
Viel Spaß allen, die schon mit dem Nachbau beginnen wollen.

Kommentar verfassen: