Im letzten Tutorial habe ich erläutert wie man eine Verbindung zu einem ESP Controller aufbaut und Daten sendet & empfängt.
Nun möchte ich gerne erläutern wie man den Fotowiderstand ausliest und an die Android App sendet.
Als Mikrocontroller habe ich den WittyCloud verwendet, dieser bietet sich an da der Controller neben einem ESP12 Chip auch über einen Fotowiderstand und einem NeoPixel verfügt.
Als Grundlage möchte ich die Android-App aus dem letzten Tutorial Android App Entwicklung: ESP Controller ansprechen #1 verwenden.
Wie auch im letzten Tutorial möchte ich den Aufbau Schritt für Schritt erläutern und zum Schluss haben wir eine lauffähige Android-App.
Solltest du im nachfolgenden Tutorial Fehler finden, oder Fragen haben, so kannst du dich gerne per E-Mail oder über das Kontaktformular an mich wenden.
Schaltung
Für dieses Tutorial benötigst du einen ESPx mit einem Fotowiderstand, wie bereits erwähnt verwende, ich den WittyCloud dieser hat bereits einen Fotowiderstand verbaut.
Wenn du diesen nicht besitzt ist das nicht schlimm, es kann jeder beliebige ESPx Controller verwendet werden welcher über min. einen analogen Pin verfügt.
Aufbau der Schaltung für einen NodeMCU
In diesem Abschnitt möchte ich nun beschreiben, wie du eine Schaltung mit einem NodeMCU und einem Fotowiderstand aufbaust.
Du benötigst für diese Schaltung folgende Bauteile:
- 1x NodeMCU,
- 1x Fotowiderstand,
- 1x 10kOhm Widerstand,
- 4x Breadboardkabel,
- 1x Breadboard mit 400 Pins
Anschluß & Schaltung
Der Fotowiderstand verfügt über 2 Beinchen, eines von diesen wird mit dem 10kOhm Widerstand und das andere mit 3,3V verbunden. Des Weiteren wird zwischen dem Fotowiderstand und dem Widerstand ein Breadboardkabel mit dem analogen Pin A0 verbunden.
Quellcode
Hier nun ein einfacher Sketch um die Funktionalität der Schaltung zu testen.
#define FOTORESISTOR A0 void setup() { Serial.begin(9600); } void loop() { int value = analogRead(FOTORESISTOR); //Ließt den Wert des Sensors vom analogen PIN A0 Serial.println(value); delay(750); }
Schritt 1 – Auslesen der Werte des Fotowiderstandes
Als Erstes wollen wir nun die Werte des Fotowiderstandes auslesen. Dazu erzeugen wir uns eine neue Funktion in unserem bestehenden Sketch aus dem ersten Tutorial.
void callFotoresistor(){ //Ließt den Wert des Fotowiderstandes vom analogen PIN A0 int value = analogRead(FOTORESISTOR); //absenden des Wertes an den verbundenen Client sendResult("{\"msg\": \""+String(value)+"\"}"); }
Zusätzlich müssen wir diese Methode dem Server bekannt geben,
damit wir aus der Android App die Adresse „http://<<IP-Adresse>>/fotoresistor“ aufrufen können.
Dazu erweitern wir die Setup Funktion um folgenden Eintrag:
... server.on("/greeting", callGreeting); server.on("/fotoresistor", callFotoresistor); server.begin(); // Starten des Servers. ...
Schritt 2 – Auswerten der JSON Daten
Nachdem wir nun die Werte senden können, müssen wir diese noch auswerten. Die Daten empfangen wir im JSON Format, um aus diesem Datenformat die Schlüssel/Wertepaare auszulesen, verwende ich die Bibliothek Google Gson.
Ein einfacher JUnitTest
Wie diese Bibliothek funktioniert möchte ich in einem kurzen und knackigen JUnit Testcase erläutern.
Es wird ein JSON String mit {„msg“:“Hello from ESP8266!“} der Gson Bibliothek übergeben und in die Klasse „SimpleData“ transformiert.
package de.draegerit.simpleGsonProject; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import org.junit.Test; import com.google.gson.Gson; public class ReadJsonData { //Beispiel JSON mit einem Schlüssel "msg" und dem Wert "Hello from ESP8266!" private static final String JSON_DATA = "{\"msg\":\"Hello from ESP8266!\"}"; @Test public void readSimpleData() { //erzeugen einer Gson Instanz Gson gson = new Gson(); //Mit der Methode "fromJson" wird das JSON Datenformat in die übergebene Klasse umgewandelt. //Es ist wichtig das für jeden Schlüssel im JSON ein Feld in der Klasse existiert und das der Datentyp stimmt. SimpleData data = gson.fromJson(JSON_DATA, SimpleData.class); //Testen ob die Daten erfolgreich umgewandelt wurden. assertNotNull(data); //Der Wert in unserer Klasse "SimpleData" sollte "Hello from ESP8266!" sein. assertEquals("Hello from ESP8266!", data.getMsg()); } //Eine einfache Klasse zum testen des JSON Formates. class SimpleData { //Feld zum speichern der Nachricht. private String msg; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } } }
Einbinden der Bibliothek in das Android Projekt
Bevor wir diese Bibliothek verwenden können, müssen wir diese in unser BuildScript einbauen. Dazu öffnen wir die Datei „build.gradle (Module: app)“. Im unteren Abschnitt der Datei finden wir die „dependencies“ dieses sind die Abhängigkeiten für unser Projekt.
Dort fügen wir die nachfolgende Zeile hinzu und klicken danach auf den Link „Sync Now“ (gelber Balken, oben links).
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
Es wird nun die Bibliothek aus dem Internet geladen und das Projekt gebaut.
Wenn dieser Schritt erfolgreich ausgeführt wurde, kann mit der Implementation begonnen werden.
Umwandeln des JSON Formates
Wir erhalten vom ESP ein JSON mit eine Schlüssel / Wertepaar, der Schlüssel lautet in unserem Beispiel „msg“. D.h. wir erzeugen uns zunächst eine Klasse, welche später unser JSON repräsentieren soll.
package de.draegerit.esptutorialapp; public class EspMessage { private String msg; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
Da wir bereits eine Funktion haben um unsere Daten an den ESP zu senden wollen wir diese nutzen und auch auf das Empfangen reagieren. Wir ja nun zwei Anwendungsfälle einmal das eine Willkommensnachricht abgesendet wird und einmal nun unseren Fotowiderstand (und später wollen wir noch unseren NeoPixel steuern).
Hier bietet es sich an ein ENUM zu erzeugen welcher Werte für unsere „Actions“ enthält. Wir bauen hier nun noch mit ein das die Adresse enthalten ist und ob die Aktionen Parameter übergeben kann.
package de.draegerit.esptutorialapp; public enum EAction { GREETING("greeting?text=", true), FOTORESISTOR("/fotoresistor", false); private String adress; private boolean hasParameter; EAction(String adress, boolean hasParameter) { this.adress = adress; this.hasParameter = hasParameter; } public String getAdress() { return adress; } public boolean isHasParameter() { return hasParameter; } }
Und dieses ENUM übergeben wir unserem Konstruktor der Klasse „RequestAsyncTask“. Da wir vom ESP Controller einen Wert erwarten und keinen senden wollen schreiben wir uns einen neuen Konstruktor welcher keinen zusätzlichen Text erwartet. (Wir könnten auch ein „null“ anstelle des Textes setzen.)
private EAction action; public RequestAsyncTask(String ipAdresse, String text, EAction action) { this.ipAdresse = ipAdresse; this.text = text; this.action = action; } public RequestAsyncTask(String ipAdresse, EAction action) { this(ipAdresse,null,action); }
Zusätzlich müssen wir unsere bestehende Funktion für den Aufruf der Willkommensnachricht anpassen. In diesem Zuge benennen wir diese um von „sendRequest“ in „sendWellcomeGreetingRequest“.
private void sendWellcomeGreetingRequest(String ipAdresse, String text) { RequestAsyncTask connectionAsyncTask = new RequestAsyncTask(ipAdresse, text, EAction.GREETING); connectionAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }
Nun passen wir unseren Aufruf in der Funktion „doInBackground“ an, damit wir unterscheiden können, ob wir eine Willkommensnachricht absenden oder aber einen Wert des Fotowiderstandes erhalten wollen.
StringBuffer urlBuffer = new StringBuffer(); urlBuffer.append("http://"); urlBuffer.append(ipAdresse); urlBuffer.append("/"); urlBuffer.append(action.getAdress()); if(action.isHasParameter()){ switch(action){ case GREETING: urlBuffer.append(text); break; } }
Layout anpassen
Nun wollen wir einen zweiten Button erzeugen und diesem in der Funktion „onClick“ die Aktion zuweisen einen Request an den ESP Controller zu senden, einen Wert des Fotowiderstandes auszulesen und zurückzuliefern.
Unser Layout „activity_main.xml“ erweitern wir wiefolgt:
<TableRow android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="15dp"> <Button android:id="@+id/wellcomeBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_span="2" android:text="@string/btnWellcomeMsg" /> </TableRow> <TableRow android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="15dp"> <Button android:id="@+id/fotoresistorBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_span="2" android:text="@string/btnFotoresistorMsg" /> </TableRow>
Die Texte für die Schaltflächen werden in der Datei „strings.xml“ verwaltet, hier müssen wir nun die beiden Texte hinzufügen.
<resources> ... <string name="btnWellcomeMsg">Willkommensnachricht absenden</string> <string name="btnFotoresistorMsg">Fotowiderstand auslesen</string> ... </resources>
Des Weiteren müssen wir (natürlich) unserem neuen Button eine Aktion zuweisen.
private Button fotoresistorBtn; @Override protected void onCreate(Bundle savedInstanceState) { ... fotoresistorBtn = findViewById(R.id.fotoresistorBtn); fotoresistorBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (isNetworkAvailable()) { sendFotoresistorRequest(ipAdressEditText.getText().toString()); } } }); ... }
Da wir unsere Funktion „doInBackground“ bereits multifunktional umgeschrieben haben brauchen wir nichts weiter tun.
Ergebnis vom ESP Controller transformieren
Im nächsten Schritt wollen wir nun das Ergebnis (der Respond) vom ESP Controller in unser „EspMessage“ Objekt transformieren / umwandeln. Dazu nutzen wir wie eingangs erwähnt die Bibliothek Google Gson.
@Override protected void onPostExecute(String result) { super.onPostExecute(result); Log.i("ESPTutorialApp", result); if (errorMsg == null && result.trim().length() > 0) { Gson gson = new Gson(); EspMessage message = gson.fromJson(result, EspMessage.class); if (message != null) { textView.setText(message.getMsg()); } } else if (errorMsg != null) { showErrorMessage(errorMsg); } }
Nun wird uns der Wert des Fotowiderstandes in der App angezeigt, wenn wir den Button „FOTOWIDERSTAND AUSLESEN“ betätigen.
Download
Ausblick
Nun nachdem wir den Wert lesen können, wollen wir uns diesen in einem Liniendiagramm anzeigen lassen. Dazu müssen wir jedoch unser Layout (fast) komplett umstellen. Im nächsten Tutorial werden wir also eine Navigationsleiste hinzufügen und für jede bestehende Funktion eine Extraseite einrichten.