[Gelöst] Arduino Yún - Adventure-Spiel mit I2C-Display

Hallo!

Ich habe mal wieder ein Problem. Wieder mit dem Yún.

Nachdem ich ja mit eurer Hilfe rausbekommen habe, wie man einen USB-Stick in Kombination mit FileIO nutzen kann, habe ich mich direkt ans Werk gemacht und ein Adventure-Spiel für den seriellen Monitor programmiert. Den Code würde ich gerne posten, aber würde damit die maximale Länge von 9000 Zeichen überschreiten. Ihr findet das Programm im Anhang.
Dem Spiel habe ich den vielsagenden Namen “Test” verpasst.
Die Dateien auf dem USB-Stick sehen so aus:

arduino/
  games/
    txtbased/
      test/
        logo.txt //Ein Ascii-Logo, bestehend aus  '|', '/', '\', '_'

        system/
          aktionen.txt //Datei zum Erkennen der gewählten Aktion
          dinge.txt     //Datei zum Erkennen der ausgewählten Gegenstände

        text/
          text0.txt //Texte für das Spiel
          text1.txt
          ...
          text10.txt

Es wird außer dem Yún und dem Stick nichts an Hardware benötigt. Im Anhang findet ihr nochmal Code und Dateien für den Stick. Ich bin auf dem Gebiet Yún noch nicht so weit, da ihr mir aber gesagt hattet, dass ich “sda1” statt “sdb1” benutzen soll, kann es sein, dass das in der obersten Funktion “openFile” geändert werden muss.
Das alles funktioniert mit dem seriellen Monitor bei baud 9600 ziemlich gut, “Befehle” wie “taschenlampe untersuchen” (Groß-/Kleinschreibung ist egal, toUpperCase macht eh alles zu Großbuchstaben) funktionieren einwandfrei.
Aber wenn alles klappt, warum habe ch dann ein Problem?
Da man ja nicht überall einfach so einen PC mit Arduino IDE rumstehen hat, dachte ich mir, dass es auch ganz nett wäre, das Spiel auch über ein Display und 2 Knöpfe spielen zu können. Mit dem 1. Button soll man zwischen Gegenstand 1, Aktion, gegebenenfalls Gegenstand 2 und einem Bestätigungsbutton wechseln können. Mit dem Anderen soll man entweder bei den Gegenständen und der Aktion diesen bzw. diese verändern, oder beim OK-Button soll eine Reaktion ausgelöst werden.
Der Aufbau besteht aus einem I2C-Display namens SSD1306 (Ist das so korrekt?) mit 128x64 Pixeln an den Pins 2 und 3, den I2C-Pins eines Leonardos, und 2 Buttons, auf der einen Seite verbunden mit dem Minuspol, auf der anderen Seite mit Pin 4 bzw. 5. Hier nocheinmal der Aufbau:

  D2(SDA)  D3(SCL)   D4       D5      GND      3V3
  |        |         |        |        |        |
  |________|___      |        |        |        |
           |   |     |        |        |        |
    _______|___|_____|______o_|______o_|        |
   |       |   |     |      | |      |          |
   |    ___|___|_____|______|_|______|__________|
   |   |   |   |     |      | |      |
   |   |   |   |     |      | |      |
   |   |   |   |     |      | |      |
  _|___|___|___|_    |      | |      |
 |GND VCC SCL SDA|   \    | | \    | |
 |               |    \---| |  \---| |
 |SSD1306(128x64)|     \  | |   \  | |
 |_______________|   o      | o      |
                     |______| |______|

Ich habe hier leider kein Fritzing, ich hoffe, man erkennt was, das hat 'ne halbe Ewigkeit gedauert.
Den Sketch kann ich leider auch hier nicht posten.
Ihr müsst beachten, dass vielleicht euer Display eine andere Adresse hat. Das muss in den setup-Zeilen geändert werden.
Es werden die nicht automatisch enthaltenen Libraries “Adafruit_SSD1306” und “Adafruit_GFX” benötigt.
Wie man vielleicht sieht, habe ich schon mit #define DEBUG und so 'nem Kram versucht, dem Fehler auf den Grund zu gehen, erfolglos. Die Dateien aus dem Anhang bleiben die Gleichen. Allerdings werden hier die text21_x.txt-Dateien verwendet, die nicht nach 64 Zeichen wie die anderen Texte einen Umbruch haben, sondern nach 21, so viele Zeichen, wie auf das Display passen.
Meine Displays haben dank einem Fehlkauf die obersten 16 Pixelreihen gelbe Pixel, darunter dann Blaue. Deswegen sind die obersten 2 Textzeilen für die Eingabe, die Zeilen darunter für die “Antwort”.

Das Problem: Anstelle eines einleitenden Textes kommen nur komische Zeichen, die wohl einen Fehler darstellen. Eigentlich ist der Pfad korrekt, die Datei existiert, aber trotzdem kommen Fehler. Warum?

Dateien.zip (21.4 KB)

Hallo,

der Code geht sich eh aus, ich poste ihn mal.

// _________  _______    _____ _________
//|___   ___||  _____|  / ___/|___   ___|
//    | |    | |_____  / /__      | |
//    | |    |  _____| \___ \     | |
//    | |    | |_____  ___/ /     | |
//    |_|    |_______|/____/      |_|
//     D I S P L A Y   V E R S I O N
#include <FileIO.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define DEBUG         true

#define taschenlampe  0
#define schatzkarte   1
#define raum          2
#define wand          3

#define untersuchen   0
#define benutzen_mit  1
#define benutzen      2

bool taschenlampeAn, wandGesehen, zeichenGesehen;
byte thing1, action, thing2, select;
Adafruit_SSD1306 display(128, 64, &Wire);

String things[] = {
  "Taschenlampe", 
  "Schatzkarte",
  "Raum",
  "Wand"
};
String actions[] = {
  "\nuntersuchen",
  "\nbenutzen",
  "benutzen\nmit"
};

File openFile(String path, uint8_t mode = FILE_READ){
  String fileName = "/mnt/sdb1/arduino/games/txtbased/test/" + path;
  #if DEBUG
    SerialUSB.println(fileName);
  #endif
  fileName.trim();
  return FileSystem.open(fileName.c_str(), mode);
}

void printFile(String path){
  File file = openFile(path);
  #if DEBUG
    SerialUSB.println(file ? "gut" : "schlecht");
  #endif
  while(file.available()){
    byte line = 0;
    display.fillRect(0, 16, 128, 48, BLACK);
    display.setTextColor(WHITE);
    display.setCursor(0, 16);
    while(line < 6 && file.available()){
      char c = file.read();
      display.print(c);
      #if DEBUG
        Serial.write(c);
      #endif
      if(c == '\n'){
        line++;
      }
    }
    display.display();
    if(file.available()){
      while(digitalRead(4) && digitalRead(5));
      delay(5);
      while(!(digitalRead(4) && digitalRead(5)));
      delay(5);
    }
  }
}
void printText(byte number){
  printFile("text/text" + String(number) + ".txt");
}

void restart(){
  taschenlampeAn = wandGesehen = zeichenGesehen = false;
  thing1 = action = thing2 = select = 0;
  printText(0);
}

void react(){
  if(action == benutzen_mit){
    if(thing1 == thing2){
      printText(5);
    }
    if(min(thing1, thing2) == taschenlampe && max(thing1, thing2) == schatzkarte && wandGesehen){
      printText(9);
      zeichenGesehen = true;
    }
    else{
      printText(5);
    }
    return;
  }
  switch(action){
    case untersuchen:
      switch(thing1){
        case taschenlampe:
          printText(1);
          return;
        case schatzkarte:
          if(taschenlampeAn){
            printText(6);
          }
          else{
            printText(2);
          }
          return;
        case raum:
          if(taschenlampeAn){
            printText(7);
            wandGesehen = true;
          }
          else{
            printText(2);
          }
          return;
        case wand:
          printText(8);
          return;
      }
    case benutzen:
      switch(thing1){
        case taschenlampe:
          if(taschenlampeAn){
            printText(4);
          }
          else{
            printText(3);
            taschenlampeAn = true;
          }
          return;
        case schatzkarte:
          printText(4);
          return;
        case raum:
          printText(5);
          return;
        case wand:
          if(zeichenGesehen){
            printText(10);
            while(true);
          }
          else{
            printText(5);
          }
          return;
    }
  }
}
void setTextInverse(bool b){
  if(b){
    display.setTextColor(BLACK, WHITE);
  }
  else{
    display.setTextColor(WHITE);
  }
}
void reloadInput(){
  display.fillRect(0, 0, 128, 16, BLACK);
  display.setCursor(0, 0);
  setTextInverse(select == 0);
  display.print(things[thing1]);
  setTextInverse(false);
  display.print(" ");
  setTextInverse(select == 1);
  display.print(actions[action]);
  if(action == benutzen_mit){
    setTextInverse(select == 2);
    display.print(things[thing2]);
    setTextInverse(false);
    display.print(" ");
  }
  if(action == untersuchen){
    setTextInverse(false);
    display.print(" ");
  }
  setTextInverse(select == 3);
  display.print("OK");
  display.display();
}
void setup() {
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  while(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C));
  display.clearDisplay();
  display.setCursor(0, 16);
  display.setTextColor(WHITE);
  display.setTextSize(2);
  display.print("Laden ...");
  display.display();
  Bridge.begin();
  FileSystem.begin();
  delay(250);
  display.setTextSize(1);
  display.clearDisplay();
  display.display();
  display.setTextWrap(false);
  #if DEBUG 
    SerialUSB.begin(9600);
    while(!SerialUSB);
    SerialUSB.println("TEST\nDISPLAY VERSION");
  #endif
  restart();
}

void loop() {
  reloadInput();
  if(!digitalRead(4)){
    select++;
    select &= B11;
    if(select == 2 && action != benutzen_mit){
      select = 3;
    }
    delay(5);
    while(!digitalRead(4));
    delay(5);
  }
  if(!digitalRead(5)){
    switch(select){
      case 0:
        thing1++;
        if(thing1 == wand && !wandGesehen){
          thing1++;
        }
        thing1 |= B11;
        break;
      case 1:
        action++;
        if(action > benutzen_mit){
          action = untersuchen;
        }
        break;
      case 2:
        thing2++;
        if(thing2 == wand && !wandGesehen){
          thing2++;
        }
        thing2 |= B11;
        break;
      case 3:
        react();
        break;
    }
    delay(5);
    while(!digitalRead(5));
    delay(5);
  }
}

Lass Dir doch mal den endgültigen Pfad über Serial.print() ausgeben und überprüfe ihn.

Grüße,
Donny

Meinst du das, was in der openFile-Funktion über meinen Debug-Kram rausgegeben wird?

Also ich weiß nicht ganz, was ich großartig verändert habe, als ich bei der Display-Version printFile, printText und openFile zu einer Funktion vereint habe, hat es auf einmal geklappt. Dateien sind leicht verändert, wieder im Anhang.
Trotzdem danke für die Antwort, dony.

Dateien.zip (8.26 KB)

HTML-Fan:
Also ich weiß nicht ganz, was ich großartig verändert habe, als ich bei der Display-Version printFile, printText und openFile zu einer Funktion vereint habe, hat es auf einmal geklappt.

Genau das meinte ich. Den halben Pfad aus der Funktion, der nächste Teil von wo anders. Da kann durchaus was schiefgehen und der Pfad stimmt nicht mehr. Ich kann das Sketch nicht testen, obwohl ich ein YUN hab, hab ich das entsprechende Display nicht und ich hab nicht Dein Dateisystem.

Bevor ich das mach, kannst Du ja mal ein Serial.print() das den kompletten Pfad, vor dem öffnen der Datei ausgeben (in der Funktion). Wenn der stimmt weiß ich leider auch nicht was Du tun könntest aber zumindest ist das Problem eingegrenzt und wer anderer kann helfen. :slight_smile:

Nachtrag:

Meinst du das, was in der openFile-Funktion über meinen Debug-Kram rausgegeben wird?

Ja, das meinte ich.

Hab ich das richtig verstanden? Du hast aus den 3 Funktionen, 1 Funktion gemacht und jetzt funktioniert es? Wenn ja dann liegt das Problem beim ursprünglichen Code wahrscheinlich in den Variablen bzw. dessen Übergabe.

Du solltest Dir den Pfad ausgeben lassen, daran erkennst Du gleich wo der Hund begraben ist. doppelter "/" zb.

  #if DEBUG
    SerialUSB.println(file);
  #endif

Grüße,
Donny

@dony mein Dateisystem findest du in dem zip.-Ordner. Der Ordner arduino ist der Kram, der auf den Stick muss und der Sketch ist auf im zip-Ordner. Und da der Adafruit-GFX-Kram ja relativ kompatibel mit allen anderen Displays ist, dürfte sich das doch leicht umschreiben lassen für andere Displays.

Hilfe zur Selbsthilfe. :wink:

Grüße,
Donny
PS: Hier im Urlaub in Südtirol hab ich gar kein Display. :wink:

Wo bist Du in Südtirol; ich bin nämlich auch da, fast immer.
Grüße Uwe

dony:
PS: Hier im Urlaub in Südtirol hab ich gar kein Display. :wink:

Ich könnte es zu einer Processing-Datei umbasteln.

uwefed:
Wo bist Du in Südtirol; ich bin nämlich auch da, fast immer.

Aso, von wo bist Du?
Ich bin immer wieder mal in der nähe von Sterzing (Familie). :wink:

@HTML-Fan: Wir wollen ja nicht umbasteln oder so, ich wollte einfach nur den Pfad wissen (bzw. Du solltest kontrollieren ob er richtig ist), den Du mit einem Befehl herausfinden kannst. Aber es funktioniert ja jetzt. :wink:
Vielleicht teste ich Dein Spiel mal richtig. :slight_smile:

Grüße,
Donny

dony:
Du solltest kontrollieren ob er richtig ist

Eigentlich ist bzw. war er korrekt. Aber ist jetzt auch egal, läuft ja.