Odroid Go mit Arduino programmieren: GPIO Pinout und Library Funktionen
Neulich stieß ich im Internet auf den Odroid Go, den der Hersteller Hardkernel aus Südkorea von etwa einem Jahr auf den Markt gebracht hat. Eigentlich beschäftigt sich die Firma ja eher mit ESP32 und anderen Entwicklungsboards für Mikrocontroller, aber zum Zehnten Jubiläum hat man den Odroid Pro als Do It yourself-Kit herausgebracht.Da der Odroid nicht nur so etwas wie ein handlicher Gameboy ist, sondern gleichzeitig auch eine Plattform für Mikrocontroller-Bastelprojekte, war das genau das richtige Spielzeug für mich und ich legte mir einen zu. Allerdings war es gar nicht so einfach, an einen heranzukommen. Bei amazon war er gerade ausverkauft, bei Pollin stand er zwar im (Papier)-Katalog, war aber zur Zeit nicht lieferbar. Und bei eBay gingen die Preise teilweise bis 100 Euro. Das Teil schien also sehr gefragt zu sein. Ich ging das Risiko einer Wartezeit ein und bestellte bei Pollin um 40 Euro und bekam den Odroid dann doch erfreulicherweise schnell nach etwas über einer Woche. Glück gehabt.
Über die Hardware und den Zusammenbau des Do-it-yourself-Kits habe ich bereits einen Artikel geschrieben. Dort wird auch beschrieben, wie man darauf seine alten Gameboy-Spiele zum Laufen bekommt.
Als ersten Schritt in Richtung Programmierung eigener Firmware habe ich erklärt, wie man die Arduino IDE für den Odroid-Go fit macht und seine Firmware kompiliert und auf die SD-Karte bringt.
Aber das war nur ein Hello-World-Progrämmchen. Diesmal will ich die 10 GPIO-Header-Pins und die wichtigsten Funktionen für die integrierte Hardware erklären.
Die Odroid Go Hardware
Warum man den Odroid-Go als Entwicklungsplattform hernehmen sollte statt ein "nacktes" ESP32-Entwicklungsboard, liegt auf der Hand. Der Odroid-Go bringt eine Menge an angebundener Hardware mit, die wir gut gebrauchen können:- zur Anzeige: TFT-Screen mit 320x240 Pixel (2.4"), über SPI angesprochen
- zum Loggen: µSD-Kartenleser (SPI)
- zur Tonausgabe: Lautsprecher mit 0.5W (8O)
- zur Eingabe: Steuerkreuz, 1 Ein/Ausschalter, 2 Feuerknöpfe, 4 kleine Menüknöpfe
- zum Debugging: Micro USB Port zum Laden und Programmieren (UART)
- für eigenen Sensoren: GPIO-Header-Anschluss mit 10 Pins für eigene Hardwareprojekte
- unabhängig: durch eigenne LiPo-Akku mit 3.7V / 1200 mAh (hält ca. 10 Stunden Spielen durch)
- 80...240 MHz Takt
- 16 MB Flash Speicher
- WLAN 802.11 b/g/n 2.4GHz
- Bluetooth v4.2 BR/EDR, BLE
Natürlich müssten wir unseren Sensor dafür an den Odroid-Go anschliessen. Dazu hat dieser an der Oberseite 10 Pins, die in einer kleinen, schwarzen Buchse herausgeführt sind. Hier kann man den mitgelieferten 10-poligen Header einstecken, an den man wiederum Jumperkabel anstecken kann, die man dann mit dem Sensor verbindet.
Die Dokumentation zum Odroid-Go gibt das Header-Pinout wie folgt an:
Unseren BMP280 mit 3.3V I2C-Anbindung würden wir also z. B. wie folgt verdrahten:
Odroid-Go Pin | BMP280 Pin |
---|---|
Pin 1 (GND, ganz rechts) | GND |
Pin 6 (P3V3, 3.3V) | VCC (3.3V) |
Pin 4 (IO15 in/out) | SCL (I2C) |
Pin 5 (IO4 in/out) | SDA (I2C) |
Würden wir den BMP280 über SPI anbinden, würden wir außer den Stromversorguns-Pins noch die Pins 2, 7 und 8 benutzen.
Die Library Funktionen
Springen zu: LED, Display, Buttons abfragen, Akkustand, Lautsprecher, Bluetooth, Wireless LAN, SD-KarteDie integrierte, blaue LED
In der Mitte zwischen und unterhalb der 4 Funktionsknöpfe besitzt der Odroid-Go eine blaue LED, die man steuern kann.Der IO-Pin zur Ansteuerung ist Pin 2. Der Pin beherrscht PWM (Pulse Width Modulation), das heißt, er kann durch wiederholtes schnelles An- und Abschalten die LED auch mit geringerer Geschwindigkeit leuchten lassen. Da das so dermaßen schnell geht, bekommt das träge menschliche Auge das nicht mit. Höchstens wenn man den Odroid im Dunkeln schnell hin und her schwenkt, fällt es auf.
LED blinken lassen:
#define PIN_BLUE_LED 2
void setup() {
pinMode(PIN_BLUE_LED, OUTPUT);
}
void loop() {
digitalWrite(PIN_BLUE_LED, HIGH);
delay(500);
digitalWrite(PIN_BLUE_LED, LOW);
delay(500);
}
Um die LED auf- / abschwellen zu lassen, brauchen wir noch einen PWM-Kanal
#define PIN_BLUE_LED 2
#define PWM_CHANNEL 1
unsigned char helligkeit = 0;
void setup() {
pinMode(PIN_BLUE_LED, OUTPUT);
digitalWrite(PIN_BLUE_LED, HIGH);
ledcAttachPin(PIN_BLUE_LED, PWM_CHANNEL);
ledcSetup(PWM_CHANNEL, 12000, 8);
}
void loop() {
for (helligkeit=0; helligkeit <= 255; helligkeit++) {
ledcWrite(PWM_CHANNEL, helligkeit);
delay(3);
}
for (helligkeit=255; helligkeit >= 0; helligkeit--) {
ledcWrite(PWM_CHANNEL, helligkeit);
delay(3);
}
}
Das Display
Das Display am Odroid-Go ist ein 320x240 Farb-TFT mit Hintergrundbeleuchtung. Die Hintergrundbeleuchtung und damit die Displayhelligkeit kann gesteuert werden.Initialisierung
- #include <odroid_go.h>: Header-File für Odroid-Go-Libraries einbinden
- GO.begin(): Go-Libary initialisieren
Allgemeine Funktionen
- setBrightness(uint8_t brightness): Stellt die Bildschirmheligkeit ein
- progressBar(int x, int y, int w, int h, uint8_t val): gibt einen Fortschrittsbalken aus
- clearDisplay(): Löscht den Screen.
Textausgabe
GO.lcd.{...}- print(String text): Schreibt Text an aktuelle Cursor-Position
- printf(String format, type par1, type par...): Schreibt formatierten Text mit Parametern (%d=Zahl, %s=String etc.)
- println(String text): Schreibt Text mit Zeilenumbruch
- setRotation(int rotation [0...7]): Dreht die Textausgabe
- setCursor(int x, int y): Setzt die Screen-Position, auf der die nächste Ausgabe erfolgt
- setTextFont(int fontnr [1|2|4|6|7|8]): Benutzt die Schriftart fontnr für die Textausgabe, die Fonts 6 bis 8 können nur Ziffern darstellen
- setTextSize(int fontsize [1...7]): Benutzt den Schriftgröße fontsize für die Textausgabe
- setTextColor(int color [int16_t color [0x0000...0xFFFF]);: Bestimmt die Farbe der Textausgabe
- Vordefinierte Farben: BLACK 0x0000 , NAVY 0x000F , DARKGREEN 0x03E0 , DARKCYAN 0x03EF , MAROON 0x7800 , PURPLE 0x780F , OLIVE 0x7BE0 , LIGHTGREY 0xC618 , DARKGREY 0x7BEF , BLUE 0x001F , GREEN 0x07E0 , CYAN 0x07FF , RED 0xF800 , MAGENTA 0xF81F , YELLOW 0xFFE0 , WHITE 0xFFFF , ORANGE 0xFD20 , GREENYELLOW 0xAFE5, PINK 0xF81F
- setTextWrap(boolean wrap): Schaltet Zeilenumbruch ein / aus
- setTextDatum(uint8_t datum): Setzt Textausrichtung (links, mittig, rechts)
- setTextPadding(uint16_t x_width): Setzt Text-Padding
- drawChar(unsigned int uniCode, int x, int y, int font): Gibt ein einzelnes Zeichen aus
- drawNumber(long long_num,int poX, int poY, int font),: Gibt eine Ganzzahl aus
- drawFloat(float floatNumber,int decimal,int poX, int poY, int font): Gibt eine Fließkommazahl aus
- drawString(const char *string, int poX, int poY, int font)Gibt eine Zeichenkette aus
Beispiel-Source-Code Schriftarten
// etwas auf dem Display ausgeben
// Schriftarten
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.setTextWrap(1);
GO.lcd.setTextFont(1); GO.lcd.println("Font 1: 8px Adafruit");
GO.lcd.setTextFont(2); GO.lcd.println("Font 2: 16px small");
GO.lcd.setTextFont(4); GO.lcd.println("Font 4: 26px medium");
GO.lcd.setTextFont(6); GO.lcd.println("6: 48px");
GO.lcd.setTextFont(7); GO.lcd.println("7: 48px");
GO.lcd.setTextFont(8); GO.lcd.println("8: 75");
Beispiel-Source-Code Schriftgrößen
// Schriftgrößen
GO.lcd.setTextFont(1);
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
for (int i=1; i<=7; i++) {
GO.lcd.setTextSize(i);
GO.lcd.print("Size ");
GO.lcd.println(i);
}
Beispiel-Source-Code Schriftfarben
//Schriftfarben
GO.lcd.setTextFont(4);
GO.lcd.setTextSize(1);
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.setTextColor (YELLOW); GO.lcd.println("YELLOW ");
GO.lcd.setTextColor (GREEN); GO.lcd.println("GREEN ");
GO.lcd.setTextColor (PURPLE ); GO.lcd.println("PURPLE ");
GO.lcd.setTextColor (OLIVE ); GO.lcd.println("OLIVE ");
GO.lcd.setTextColor (MAGENTA); GO.lcd.println("MAGENTA ");
GO.lcd.setTextColor (ORANGE); GO.lcd.println("ORANGE ");
GO.lcd.setTextColor (PINK); GO.lcd.println("PINK ");
GO.lcd.setTextColor (LIGHTGREY); GO.lcd.println("LIGHTGREY ");
GO.lcd.setTextColor (DARKGREY); GO.lcd.println("DARKGREY ");
Zeichenfunktionen
GO.lcd.{...}- invertDisplayInvertiert die Bildschrimausgabe
- drawPixel(uint32_t x, uint32_t y, uint32_t color): einen einzelnen Bildpunkt zeichnen
- fillScreen(uint32_t color): Setzt die Hintergrundfarbe auf color
- drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color): zeichnet eine Gerade
- drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color): zeichnet ein Rechteck
- fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color): zeichnet ein gefülltes Rechteck
- drawRoundRect(int32_t x0, int32_t y0, int32_t w, int32_t h, int32_t radius, uint32_t color): zeichnet ein abgerundetes Rechteck
- fillRoundRect(int32_t x0, int32_t y0, int32_t w, int32_t h, int32_t radius, uint32_t color): zeichnet ein abgerundetes,gefülltes Rechteck
- drawCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color): zeichnet einen Kreis
- fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color)
- drawEllipse(int16_t x0, int16_t y0, int16_t rx, int16_t ry, uint16_t color): zeichnet eine Ellipse
- fillEllipse(int16_t x0, int16_t y0, int16_t rx, int16_t ry, uint16_t color)
- drawTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color): zeichnet ein Dreieck
- fillTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color)
- drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color: Zeichnet ein Bild
- drawBitmap(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t *pcolors)
Beispiel-Source-Code Zeichenfunktionen
// Zeichnen
GO.lcd.clearDisplay();
GO.lcd.fillScreen(GREEN); // Rasen
GO.lcd.fillRect(0, 0, 320, 80, CYAN); // Himmel
GO.lcd.fillRect(20, 100, 120, 100, LIGHTGREY); // Haus
GO.lcd.fillRect(30, 140, 30, 55, DARKGREY); // Tür
GO.lcd.fillRect(70, 130, 45, 45, WHITE); // Fensterrahmen
GO.lcd.fillRect(75, 135, 15, 15, BLUE); // Fenster
GO.lcd.fillRect(95, 135, 15, 15, BLUE); // Fenster
GO.lcd.fillRect(75, 155, 15, 15, BLUE); // Fenster
GO.lcd.fillRect(95, 155, 15, 15, BLUE); // Fenster
GO.lcd.fillTriangle(20, 100, 80, 20, 140, 100, MAROON); // Dach
GO.lcd.fillCircle(280, 40, 30, YELLOW); // Sonne
Die Eingabe-Buttons
Die Einkopfe-Knöpfe und auch der Joystick des Odroid-Go ist komplett digital, das heißt, er kennt nur die Zustände "gedrückt" und "nicht gedrückt". Es können auch mehrere Buttons gleichzeitig gedrückt werden, allerdings nicht die einer Achse (links und rechts bzw. oben und unten).Nach einem GO.update(); werde die Zustände der Buttons festgestellt und können dasnn mit folgenden Funktionen abgefragt werden:
Die 4 Funktionsknöpfe:
- if (GO.BtnMenu.isPressed()): Ist der 1. Funktionsbutton (Menü) momentan gedrückt?
- if (GO.BtnVolume.isPressed()): Ist der 2. Funktionsbutton (Lautstärke) momentan gedrückt?
- if (GO.BtnSelect.isPressed()): Ist der 3. Funktionsbutton (Select) momentan gedrückt?
- if (GO.BtnStart.isPressed()): Ist der 4. Funktionsbutton (Start) momentan gedrückt?
- if (GO.BtnA.isPressed()): Ist der rechte Feuerbutton (A) momentan gedrückt?
- if (GO.BtnB.isPressed()): Ist der linke Feuerbutton (B) momentan gedrückt?
- if (GO.JOY_X.isAxisPressed() == 2): Wurde das D-Pad nach links momentan gedrückt?
- if (GO.JOY_X.isAxisPressed() == 1): Wurde das D-Pad nach rechts momentan gedrückt?
- if (GO.JOY_Y.isAxisPressed() == 2): Wurde das D-Pad nach oben momentan gedrückt?
- if (GO.JOY_Y.isAxisPressed() == 1): Wurde das D-Pad nach unten momentan gedrückt?
- if (GO.BtnXXX.wasPressed()): Wurde der Button in letzter Zeit gedrückt (wartet nicht aufs Loslassen)
Beispiel-Source-Code Button-Abfrage
void waitA() { // Auf Knopfdruck A warten
while (!GO.BtnA.isPressed()) {
GO.update(); // Knopfzustände holen
delay (10);
}
digitalWrite(PIN_BLUE_LED,HIGH);
// auf Loslassen warten
while (GO.BtnA.isPressed()) {
GO.update();
delay (10);
}
digitalWrite(PIN_BLUE_LED,LOW);
}
Die blaue LED leuchtet solange Button A gedrückt wird. Wird A wieder losgelassen, wird im Programm fortgefahren.Batterie-Zustand abfragen
Der Odroid-Go hat ja einen eingebaute Lithium-Polymer-Akku mit einer Nominalspannung von 3.7 Volt.Der Akku hängt auch an GPIO Pin 36 des ESP32 und kann damit mit dem Analog-Digital-Wandler (ADC1) ausgelesen werden. Da die ADCs im ESP32 12 bit breit sind, können sie 212 = 4096 Werte zurückgeben. Je nach anliegenden Spannungshöhe an einem ADC-Pin fällt der Wert höher oder niedriger aus.
Da 3.7V aber über den 3.3V liegen, die der ESP32 vertragen kann, ist hier ein Spannungsteiler vorgeschaltet, der die Spannung vorher halbiert. Damit wird die am ADC anliegende Spannung max. ca. 2.1V (entspr. 4.2V bei vollgeladenem Akku) und ist im sicheren Bereich.
Die Spannungsteilung müssen wir beim Berechnen der realen Spannung berücksichtigen (sprich: Ergebnis verdoppeln). Wenn wir den gemessenen Spannungswert durch die Maximalspannung von 4.2 V bei vollem Akku teilen, erhalten wir einen Prozentwert, wie sehr der Akku geladen ist. Da die Entladekurve eines LiPo-Akkus aber nicht linear ist, ist dies nur ein Schätzwert.
Beispiel-Source-Code Batteriestandsabfrage
// Batterie-Spannung ausgeben
GO.lcd.setTextFont(4);
GO.lcd.setTextSize(1);
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.setTextColor (YELLOW);
static esp_adc_cal_characteristics_t adc_chars;
double voltage;
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &adc_chars);
// warum 1100? -> (https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/adc.html)
// Per design the ADC reference voltage is 1100mV, however the true reference voltage can range from
// 1000mV to 1200mV amongst different ESP32s.
uint32_t adc_reading = 0;
for (int i = 0; i < 32; i++) {
adc_reading += adc1_get_raw((adc1_channel_t) ADC1_CHANNEL_0);
delay(1);
}
// voltage = adc_reading /32. / 4096. * 2. * 3.3; // 4096 Einheiten, max. messbar 3.3V, Verdopplung wegen Spannungsteiler
// genauer ist, da der ADC einen nicht linearen Messverlauf hat:
voltage = esp_adc_cal_raw_to_voltage (adc_reading / 32., &adc_chars) * 2. / 1000.;
int percent= voltage/4.2*100.; // max. Spannung bei vollgeladenem Akku = 4.2V
if (percent > 100) percent=100;
// Textausgabe
GO.lcd.printf("Akku: %1.3lf V (%d%%)\n", voltage, percent);
// und eine kleine Grafik
GO.lcd.drawRect(0, 30, 300, 200, WHITE); // Batterie-Korpus
GO.lcd.drawRect(1, 31, 300, 200, WHITE);
GO.lcd.drawRect(300, 110, 20, 40, WHITE);
GO.lcd.drawRect(301, 111, 18, 38, WHITE);
GO.lcd.fillRect(2, 32, percent*2.96, 198, GREEN); // Füllstand
Der Lautsprecher
Der interne Lautsprecher wird über einen Digital-zu-Analog-Wandler (DAC) angesprochen. Man kann Töne mit bestimmter Frequenz und Dauer ausgeben oder auch Samples abspielen. Außerdem kann man die Lautstärke einstellen.Die Funktionen sind:
- setVolume([0...11]): Setzt die Lautsträke von 0 bis 10. 11 bedeutet stumm.
- mute(): wie setVolume(11)
- setBeep(uint16_t frequency, uint16_t duration): Setzt Tonhöhe in Hertz und Tonlänge in ms
- beep(): Piept wie definiert.
- tone(uint16_t frequency, uint32_t duration): Spielt einen Tom mit Tonhöhe in Hertz und Tonlänge in ms.
- playMusic(const uint8_t* music_data, uint16_t sample_rate): Spielt einen digitaliserten Sample, der in 8 bit integers definiert wurde mit einer bestimmten Sample Rate ab.
Beispiel-Source-Code Melodie und Sample abspielen
// Sound abspielen
GO.lcd.setTextFont(4);
GO.lcd.setTextSize(1);
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.setTextColor (YELLOW);
GO.lcd.print("May the force will\nbe with you.\n\n");
GO.lcd.setTextColor (WHITE);
GO.lcd.print("Moege die Macht\n mit dir sein.\n");
GO.Speaker.setVolume(10); // bezieht sich nicht auf tone, nur auf playMusic !
const int fx_StarWars_melody[] = { 659, 659, 698, 932, 1397, 1245, 1175, 1047, 1865, 1397, 1245, 1175, 1047, 1865, 1397, 1245, 1175, 1245, 1047 };
const byte fx_StarWars_duras[] = { 4, 4, 4, 24, 24, 4, 4, 4, 24, 12, 4, 4, 4, 24, 12, 4, 4, 4, 16 };
int dur;
for (int i=0; i < sizeof(fx_StarWars_duras); i++) {
dur = fx_StarWars_duras[i]*40;
GO.Speaker.begin();
GO.Speaker.tone (fx_StarWars_melody[i], dur);
delay (dur);
GO.Speaker.end();
delay (100);
}
waitA(); // Auf Tastendruck warten ----------------------------------------------------------------------------------
GO.Speaker.playMusic(m5stack_startup_music, 25000);
Demonstrations-Video für die bisher besprochenen Funktionen:Bluetooth
Der Odroid-Go basiert auf einem ESP32 und der kann Bluetooth 4.2, das wir mittels der entsprechenden Libraries nutzen können.Im folgenden Beispiel realisieren wir eine serielle Kommunikation zwischen Odroid-Go und Android-Smartphone. Dazu benötigen wir die App "Serial Blutetooth Terminal" von Kai Morich aus dem Android Play Store, das wir uns installieren.
Nachdem wir das untenstehende Programm auf unseren Odroid-Go geflasht und gestartet haben, suchen wir auf dem Smartphone nach dem Bluetooth-Gerät "ODROID-GO" und koppelt uns mit ihm. Danach können wir im Terminalprogramm auf Verbinden klicken.
Nach einer kurzen Bestätigung wird dann alles, was wir auf dem Smartphone tippen über Bluetooth an den Odroid-Go geschickt und dort auf dem Bildschirm ausgegeben (wenn der Bildschirm voll ist, Start drücken).
Andersherum werden auf dem Smartphone die Knöpfe, die wir auf dem Odroid-Go drücken mit einem Buchstaben ausgegeben. Der Joystick besitzt dabei eine Wiederholfunktion.
Beispiel-Source-Code serielle Kommunikation über Bluetooth
// 2019 by Oliver Kuhlemann, http://cool-web.de
#include "BluetoothSerial.h"
#include <odroid_go.h>
BluetoothSerial serialBT;
void setup() {
GO.begin(); // Go-Libary initialisieren
GO.lcd.setTextWrap(1);
GO.lcd.setTextFont(2);
GO.lcd.setTextSize(1);
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.setTextColor (YELLOW);
GO.lcd.println("Bluetooth gestartet. Zum Pairing bereit.");
delay (500);
serialBT.begin("ODROID-GO");
}
void loop() {
if (serialBT.available()) {
GO.lcd.print((char)serialBT.read());
}
if (GO.BtnA.wasPressed()) serialBT.write('A');
if (GO.BtnB.wasPressed()) serialBT.write('B');
if (GO.BtnMenu.wasPressed()) serialBT.write('1');
if (GO.BtnVolume.wasPressed()) serialBT.write('2');
if (GO.BtnSelect.wasPressed()) serialBT.write('3');
if (GO.BtnStart.wasPressed()) {
serialBT.write('4');
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
}
if (GO.JOY_X.isAxisPressed() == 2) serialBT.write('L');
if (GO.JOY_X.isAxisPressed() == 1) serialBT.write('R');
if (GO.JOY_Y.isAxisPressed() == 2) serialBT.write('U');
if (GO.JOY_Y.isAxisPressed() == 1) serialBT.write('D');
delay(20);
GO.update();
}
Demonstrationsvideo für obiges Beispiel:WLAN
Der verbaute ESP32 verfügt auch über ein WLAN (802.11 b/g/n 2.4GHz). Damit kann er sich zum Beispiel ins heimische WLAN einloggen und Informationen über das Internet holen oder verbreiten.Oder aber er spannt einen eigenen Access Point auf. Dann kann man über seinen PC oder sein Smartphone diese AP suchen, sich damit verbinden und landet dann etwa auf einem auf dem Odroid-Go laufenden Webserver, mit dem man den Odroid-Go dann fernsteuern kann.
Dies tut auch der nachfolgende Beispiel-Code. Mit ihm kann man über das WLAN die blaue LED des Odroid-Go ein- und ausschalten.
Beispiel-Source-Code Webserver über WLAN Access Point
#include <WiFi.h>
#include <odroid_go.h>
#define PIN_STATUS_LED 2
const char *apSsid = "ODROID_GO_AP";
const char *apPasswd = "12345678";
WiFiServer server(80);
void setup() {
GO.begin(); // Go-Libary initialisieren
GO.lcd.setTextWrap(1);
GO.lcd.setTextFont(2);
GO.lcd.setTextSize(1);
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.setTextColor (YELLOW);
pinMode(PIN_STATUS_LED, OUTPUT);
digitalWrite(PIN_STATUS_LED, LOW);
IPAddress gateway(192, 168, 4, 1);
IPAddress subnet(255, 255, 255, 0);
if (WiFi.softAP(apSsid, apPasswd)) {
GO.lcd.println("WLAN Access Point gestartet.");
GO.lcd.print("AP IP: ");
GO.lcd.println(WiFi.softAPIP());
GO.lcd.print("AP SSID: ");
GO.lcd.println(apSsid);
GO.lcd.print("AP Password: ");
GO.lcd.println(apPasswd);
server.begin();
} else {
GO.lcd.println("WLAN Access Point konnte nicht gestartet werden.");
}
}
void loop() {
WiFiClient client = server.available();
if (client) {
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.println("\nEingehende Anfrage:");
String currentLine = "";
while (client.connected()) {
if (client.available()) {
char c = client.read();
GO.lcd.print(c);
if (c == '\n') {
if (currentLine.length() == 0) {
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
client.print("<html><body><div style=\"font-family:Arial;font-size:48pt;\">");
client.print("<a href=\"/H\">blaue LED einschalten</a><br><br>");
client.print("<a href=\"/L\">blaue LED ausschalten</a><br><br>");
client.print("</div></body></html>");
client.println();
break;
} else {
currentLine = "";
}
} else if (c != '\r') {
currentLine += c;
}
if (currentLine.endsWith("GET /H")) {
digitalWrite(PIN_STATUS_LED, HIGH);
}
if (currentLine.endsWith("GET /L")) {
digitalWrite(PIN_STATUS_LED, LOW);
}
}
}
client.stop();
}
}
Demonstrationsvideo für obiges Beispiel:SD-Karte
Der SD-Karten-Leser/Schreiber ist wie das Display über den SPI-Bus angeschlossen (siehe Blockdiagramm).
Leider bietet die Odroid-Library keine direkten Funktionen für den SD-Karten-Zugriff, so dass wir uns einer anderen Library bedienen müssen.
Hier hat sich die Library SD by Arduino, Sparkfun als funktional herausgestellt, die folgende Funktionen bietet:
SD-Karten-Funktionen:
- SD.begin(): Library initialisieren
- SD.cardType(): Kartentyp abfragen (CARD_MMC|MMC|SDSC|SDHC)
- SD.cardSize(): Kartengroesse in Bytes abfragen
- SD.totalBytes(): Patitionsgroesse in Bytes abfragen
- SD.usedBytes(): Auf der Karte benutzte Bytes abfragen
- SD.mkdir(path): Verzeichnis erstellen
- SD.rmdir(path): Verzeichnis löschen
- SD.remove(path): Datei löschen
- SD.remane(pathOld, PathNew): Datei umbenennen
- File f = SD.open(pathDirOrFilename): File öffnen und File-Objekt erzeugen; False, wenn es nicht geöffnet werden konnte
- f.close(): File schließen
- f.isDirectory(): ist das File/Dir ein Verzeichnis?
- f.openNextFile(): wenn f ein Verzeichnis, nächsten Verzeichniseintrag zuweisen.
- while(file.available()){ Serial.write(f.read());}: Textfile lesen und auf Serial ausgeben.
- File f = SD.open(path, FILE_WRITE): File zum schreiben öffnen / erzeugen, False, wenn es nicht geöffnet werden konnte
- File f = SD.open(path, APPEND): File zum anhängen öffnen / erzeugen, False, wenn es nicht geöffnet werden konnte
- f.print(text): in zuvor geöffnete Datei schreiben
- f.write(static uint8_t buf, size_t toWrite): toWrite Bytes vom Buffer buf in das File schreiben
- File f = SD.open(path): File zum binären lesen öffnen, False, wenn es nicht geöffnet werden konnte
- size_t flen = f.size(): Dateilänge ermitteln
- f.read(static uint8_t buf, size_t toRead): toRead Bytes in den vorher allokierten Buffer buf einlesen
Beispiel-Source-Code SD-Karten-Größe und Hauptverzeichnis der SD
// (C) 2019 by Oliver Kuhlemann, http://cool-web.de/arduino/
#include "FS.h" // Library für FileSystem
#include "SD.h" // Library für SD-Karten-Zugriff
#include "SPI.h" // SD-Karte ist über SPI angeschlossen
#include <odroid_go.h> // Header-File für Odroid-Go-Libraries einbinden
#define PIN_BLUE_LED 2 // IO-Pin der blauen LED
void setup() {
pinMode(PIN_BLUE_LED, OUTPUT);
GO.begin(); // Go-Libary initialisieren
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.setTextWrap(1);
GO.lcd.setTextFont(2);
GO.lcd.setTextColor (YELLOW);
if(!SD.begin()){
GO.lcd.println("SD Karte konnte nicht gemounted werden.");
return;
}
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE){
GO.lcd.println("Keine SD-Karte eingesteckt.");
return;
}
GO.lcd.print("SD Karten Typ: ");
if(cardType == CARD_MMC){
GO.lcd.println("MMC-Karte gefunden.");
} else if(cardType == CARD_SD){
GO.lcd.println("SDSC-Karte gefunden.");
} else if(cardType == CARD_SDHC){
GO.lcd.println("SDHC-Karte gefunden.");
} else {
GO.lcd.println("Karte mit unbekanntem Format.");
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
GO.lcd.printf("SD Kartengroesse: %llu MiB\n\n", cardSize);
GO.lcd.printf("Partitionsgroesse: %llu MiB\n", SD.totalBytes() / (1024 * 1024));
GO.lcd.printf("Davon benutzt: %llu MiB\n", SD.usedBytes() / (1024 * 1024));
waitA(); // Auf Taste A warten ---------------------------------------------------------------
//Hauptverzeichnis ausgeben
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.setTextWrap(1);
GO.lcd.setTextFont(2);
GO.lcd.setTextColor (YELLOW);
GO.lcd.println("Dateien im Hauptverzeichnis");
GO.lcd.setTextFont(1);
GO.lcd.setTextColor (WHITE);
File dir = SD.open("/");
if (!dir) {
GO.lcd.println("Konnte Hauptverzeichnis nicht oeffnen!");
}
File file = dir.openNextFile();
while(file){
if(file.isDirectory()){
GO.lcd.print(" DIR : ");
GO.lcd.println(file.name());
} else {
GO.lcd.print(" FILE: ");
GO.lcd.print(file.name());
GO.lcd.print(" SIZE: ");
GO.lcd.println(file.size());
}
file = dir.openNextFile();
}
file.close();
waitA(); // Auf Taste A warten ---------------------------------------------------------------
}
void waitA() {
while (!GO.BtnA.isPressed()) {
GO.update(); // Knopfzustände holen
delay (10);
}
digitalWrite(PIN_BLUE_LED,HIGH);
// auf Loslassen warten
while (GO.BtnA.isPressed()) {
GO.update();
delay (10);
}
digitalWrite(PIN_BLUE_LED,LOW);
}
void loop() {
}
Download der fertig kompilierten Firmware _sd-card-test.fw