[Gelöst] Wie Speicher sparen bei I2C Display und u8glib?

Hallo!

Bin gerade am programmieren eines Höhenmessers mit dem BMP280 und dem SH1106 Display.
Es klappt alles soweit, den Code habe ich aus einem fertigen Projekt (bin Anfänger) und passe ihn für mich gerade an. Auf der Startseite steht nun die Höhe in Fuß, das geschieht mit großen Zahlen und ich bin erschrocken dass nur das schon fast den kompletten Speicher gefressen hat. :o
Jetzt bin ich auf das Tool "bdfconv" gestoßen mit dem man wohl nur die genutzten Zeichen angeben kann um Platz zu sparen.
Das wären bei mir der Befehl
-m '32,46,48-58,72,78,81,102,116'

Leider aber reicht mein Verständnis für das Tool und der u8glib nicht aus. Welche Datei aus der u8glib muss ich nehmen und verändern? Muss ich das für jede Schriftart einzeln machen? Kann vielleicht jemand ein Beispiel der Befehlszeile nennen? Bin ich auf dem Holzweg?

Das Programm soll am Schluss erst einmal groß die Höhe in Fuß anzeigen und darunter klein den Druck P0 zum abgleichen den ich mit einem Encoder verstellen möchte.
Leider sind aber ohne Verstellung schon 92% vom Nano belegt und die Schriftgröße reicht mir eigentlich noch gar nicht.
Schriftart ist: u8g_font_fub20

Hi,

bei Deiner Anzeige kann ich Dir nicht helfen.
Wäre es eine SSD1306 (128x64 oder 128x32) und Du sagen würdest, daß Du nur ASCII brauchst, dann würde ich Dir diese Lib. empfehlen:

Die benutze ich aus diesem Grund selbst, benötigt nur 2 oder 3% RAM, dafür ist man eingeschränkt auf ASCII, aber den jeweils verwendeten Font kann man mit ASCII-Zeichen erweitern, die man braucht, bei mir als Beispiel µ und Ω.

Gruß André

Wenn Du nur Text brauchst, nimm die U8x8, ansonsten die u8g2lib. Genauere Infos findest Du im Wiki dazu.
Genaueres kann man ohne Sketch (bitte in Codetags) nicht sagen.

Gruß Tommy

was wird knapp, Programmspeicher oder RAM?

Also es wird der Programmspeicher knapp. Hatte mal die u8g2lib installiert aber beim kompilieren von einem Beispiel zeigt es immer dass es die Dateien in der lib nicht findet.
Mein Programm bekomme ich übrigens jetzt auch nicht mehr auf den Uno, der Nano ist zu 97% belegt, es funktioniert aber. Ich benötige tatsächlich nur die Zeichen 0-9, Q, N, H, f, t, ., : und Leerzeichen.

Hier mal der Sketch:

#include "U8glib.h"
#include "BMP280.h"
#include "Wire.h"
#define P0 1018.00                // Luftdruck (soll später einstellbar sein)
BMP280 bmp;

U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_NO_ACK);

char sA[9];                       // Was bedeutet [9]?
char sP0[9];

void draw(double A) {
  u8g.setFont(u8g_font_fub20);    //Soll eigentlich noch größer sein

  dtostrf(A, 4, 0, sA);
  dtostrf(P0, 4, 2, sP0);

  u8g.drawStr( 5, 20, sA);
  u8g.drawStr( 80, 20, " ft");

  u8g.setFont(u8g_font_9x18B);
  u8g.drawStr( 0, 63, "QNH: ");
  u8g.drawStr( 40 , 63, sP0);
}


void setup() {
  Serial.begin(9600);
  if (!bmp.begin()) {
    Serial.println("BMP init failed!");
    while (1);
  }
  else Serial.println("BMP init success!");

  bmp.setOversampling(4);

  u8g.setColorIndex(1);
  u8g.setFont(u8g_font_unifont);
}


void loop(void) {
  double T, P;
  char result = bmp.startMeasurment();

  if (result != 0) {
    delay(result);
    result = bmp.getTemperatureAndPressure(T, P);

    if (result != 0) {
      double A = (bmp.altitude(P, P0) * 3.28084);


      u8g.firstPage();
      do {
        draw(A);
      } while ( u8g.nextPage() );
      u8g.firstPage();

      delay(1000);
    }
  }
}

Da Du ja anscheinend nur Text brauchst - hast Du die U8x8 getestet?

Gruß Tommy

Werde es mal ausprobieren. Bin noch nicht so der schnellste :stuck_out_tongue_closed_eyes:
Bisher hab ich nur die LED zum blinken gebracht und mal zur Reinigung Einspritzdüsen mit MOSFET getaktet.

Habe nun die Library installiert und mir das Beispiel geladen:

#include <Arduino.h>
#include <SPI.h>
#include <U8x8lib.h>

/* Constructor */
U8X8_SSD1306_128X64_NONAME_4W_SW_SPI u8x8(/* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);

/* u8x8.begin() is required and will sent the setup/init sequence to the display */
void setup(void)
{
  u8x8.begin();
}

void loop(void)
{
  u8x8.setFont(u8x8_font_chroma48medium8_r);
  u8x8.drawString(0, 0, "Hello World!");
  delay(1000);
}

In der Liste der Displays nehme ich an dass es eines der folgenden ist:
SH1106 128X64_NONAME
Controller “sh1106”, Display “128x64_noname”
U8X8_SH1106_128X64_NONAME_SW_I2C(clock, data [, reset])
U8X8_SH1106_128X64_NONAME_HW_I2C([reset [, clock, data]])
U8X8_SH1106_128X64_NONAME_2ND_HW_I2C([reset])

Leider weiß ich nicht genau wie ich mein Display einfügen bzw welche Zahlen ich reinschreiben muss :confused:
Kommen da die Ausgangspins rein? Was ist dieser Reset?

Probiere mal den letzten (HardwareI2C) und lasse reset leer (ist optional).

Ich kann es nicht testen, da ich das Display nicht habe.

Gruß Tommy

U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE);

Das ist die Lösung!
Display zeigt den Text an. Wenn ich Zeit habe versuche ich das alles auf mein Projekt zu übertragen und mein Glück zu versuchen. Mal gespannt ob mir dann etwas speicher übrig bleibt.

Grüße

So das war mein Versuch:

#include <SPI.h>
#include <U8x8lib.h>
#include "BMP280.h"
#include "Wire.h"
#define P0 1018.00
BMP280 bmp;

U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE);

char sA[9];
char sP0[9];

void draw(double A) {
  u8x8.setFont(u8x8_font_inb46_4x8_n);

  dtostrf(A, 4, 0, sA);
  dtostrf(P0, 4, 2, sP0);

  u8x8.drawString( 0, 0, sA);

  u8x8.setFont(u8x8_font_amstrad_cpc_extended_r);
  u8x8.drawString( 0, 63, "QNH: ");
  u8x8.drawString( 40 , 63, sP0);
}

void setup(void)
{
  u8x8.begin();
}


void loop(void) {
  double T, P;
  char result = bmp.startMeasurment();

  if (result != 0) {
    delay(result);
    result = bmp.getTemperatureAndPressure(T, P);

    if (result != 0) {
      double A = (bmp.altitude(P, P0) * 3.28084); //Umrechnung in feet

      delay(1000);
    }
  }
}

Leider aber bleibt das Display schwarz. Mit dem Example lief es, nur als ich eine Zeile mit anderer Schriftart eingefügt habe flackerte diese mit dem delay…

result dürfte auch nicht das sein, was Du ausgeben willst.
Gib Dir die Werte mal zusätzlich auf Serial aus, am besten als HEX, damit Du siehst, was da steht.

Gruß Tommy

Moin!
Nachdem ich mich mit der u8x8 ein bisschen schwer getan habe bin ich nochmal an die u8g2 gegangen.
Es klappt jetzt mit größerer Shriftart mit 47% Speicherbelegung. :smiley:
Das bedeutet ich kann die Schrift richtig groß machen und die Einheit auf das Gehäuse drucken!
So sieht der code aus:

#include <U8g2lib.h>
#include <SPI.h>
#include "BMP280.h"
#include "Wire.h"
#define P0 1020.00
BMP280 bmp;

U8G2_SH1106_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0,U8X8_PIN_NONE);

char sA[9];
char sP0[9];

void draw(double A) {

  // Höhe
  u8g2.setFont(u8g2_font_fub30_tn);
  dtostrf(A, 4, 0, sA);
  u8g2.drawStr(15, 35, sA);

  // QNH
  u8g2.setFont(u8g2_font_9x18B_tr);
  dtostrf(P0, 4, 2, sP0);
  u8g2.drawStr(40 , 63, sP0);

  // Einheiten
  u8g2.drawStr( 100, 35, " ft");
  u8g2.drawStr(0, 63, "QNH: ");

}


void setup() {
  u8g2.begin();
  if (!bmp.begin());

}


void loop(void) {
  double T, P;
  char result = bmp.startMeasurment();

  if (result != 0) {
    delay(result);
    result = bmp.getTemperatureAndPressure(T, P);

    if (result != 0) {
      double A = (bmp.altitude(P, P0) * 3.28084);


      u8g2.firstPage();
      do {
        draw(A);
      } while ( u8g2.nextPage() );

      delay(1000);
    }
  }
}

Jetzt versuche ich den wert P0 zur Kalibrierung (hier 1020.00) mit dem Drehencoder in 0.01 Schritten zu ändern. Das example für den Encoder funktioniert schon mal. Wie würdet ihr das am einfachsten machen? Hatte die Idee mit dem Knopfdruck am Encoder das eigentliche Programm anzuhalten um den Wert zu ändern und mit erneutem Knopfdruck fortzusetzen. Geht das überhaupt so? Oder gibt es vielleicht eine bessere Lösung?

Grüße

Warum willst Du das Programm anhalten? Der Taster im Encoder sollte bei Betätigung den Wert in die entsprechende Variable übernehmen.

Gruß Tommy

Okay. Naja werde mich da wohl etwas mit auseinander setzen müssen. Möchte mit Linksdrehung den Wert um 0.01 senken und mit Rechtsdrehung um 0.01 erhöhen. Hatte bisschen herumprobiert, da konnte ich den Wert erhöhen, Sogar der Höhenwert im Display hat durch die Druckanpassung reagiert. Allerdings startete P0 bei 0 und erhöhte sich nur um 2 und das in beide Richtungen. Immerhin hat er sich bewegt :sweat_smile:

Warum willst Du ihn bei 0 starten lassen und nimmst nicht den aktuellen Wert, um den zu erhöhen/verringern?

Gruß Tommy

Leider bekomme ich es nicht hin.
Hier ist der Code für den Encoder. Mit ihm kann der veränderte Wert mit dem Switch im eeprom gespeichert werden, laut Monitor funktioniert das prima:

#include <EEPROM.h>
int reclk = 6;
int redt = 7;
int resw = 8;
int pos, alast, aval;
int eeadr = 14;

void setup() {
  pinMode (reclk, INPUT);
  pinMode (redt, INPUT);
  pinMode(resw, INPUT);
  digitalWrite(resw, HIGH);
  EEPROM.get(eeadr, pos);
  alast = digitalRead(reclk);
}


void loop() {
  if ( digitalRead(resw) == 0) {
    EEPROM.put(eeadr, pos);
    while (digitalRead(resw) == 0);
  }
  aval = digitalRead(reclk);
  if (aval != alast) {
    if (digitalRead(redt) != aval) {
      pos++;
    }
    else {
      pos--;
    }
  }
  alast = aval;
}

Ich hatte (pos) bei P0 in meinem Programm verwendet und es ließ sich tatsächlich bedingt der Wert verändern, speichern und wurde beim Neustart wieder aus dem eeprom ausgelesen.
Leider habe ich zwei Probleme:

  1. Muss ich denn nicht eine separate loop für den Encoder machen und das Hauptprogramm in der Zeit unterbrechen in welcher ich Änderungen am Wert vornehme? Die Aktualisierung der Höhenanzeige und somit die Loop des Hauptprogramms sollen ja auf 1000ms bleiben. So ganz weiß ich nicht wie man das bewerkstelligen soll.

  2. Der Wert wird immer in Ganzzahlen verändert, jedoch möchte ich 0.01 Schritte. Muss ich hier durch 100 dividieren oder geht das anders?

Vielleicht denke ich auch zu kompliziert.
Grüße

Hi

Da Dein loop() in minimaler Zeit durchlaufen wird, bist Du doch eh in jedem Durchlauf am Prüfen, was nun gemacht werden soll.
Wenn bei dieser Prüfung raus kommt, daß Da gerade Wer am Poti spielt, wirst Du wohl den aktuellen Wert anzeigen wollen, damit man nicht 'die Katze im Sack' kauft ... oder im EEprom speichert.

MALE Dir auf, was wann wie passieren soll - und programmiere den Kram dann.
Dein aktuelles Programm ist dann halt nur noch einer der vielen Status Deiner State-Maschine und wird dann halt nur abgearbeitet, wenn die Bedingungen dafür gegeben sind - z.B., wenn eben kein neuer Wert eingestellt wird.

MfG

PS: Du verwendest den EEprom, dort werden BYTES drin gespeichert, Du speicherst dort aber Variablen des Typ int (= 2 Byte lang) - vll. erklärt Das auch schon, warum Das nur 'so halbwegs' klappt.
PPS: Warum int? Auch im EEprom wird's keine negativen Adressen geben - also zumindest unsigned int für die Adressen.
PINs werden sich wohl nicht im Programmlauf ändert - also mach Diese const.
Ebenfalls keine negativen Nummern, also auch hier mindestens unsigned int.
Da wohl keine Pin-Nummern über 255 zu erwarten sind - reicht dafür also auch BYTE.
PPPS: Durch 00 Teilen ist schon Mal nicht der schlechteste Weg - ist nur die Frage, wie Du Das machst.
float braucht spezielle Rechen-Routinen, 'günstiger' dürfte % und / sein.

Hallo und danke postmaster-ino.

Habe mich mal an den encoder gemacht, vielleicht ist es ja so okay. Er tut zumindest was ich will, jedoch komme ich ja beim speichern um long nicht herum wenn ich durch dividieren zwei Nachkommastellen möchte oder liege ich falsch? Auf jeden Fall lässt sich die Position speichern und auf zwei Nachkommastellen ändern wie gewünscht.
Hier einmal der code.

#include <EEPROM.h>

const byte reclk = 6;
const byte redt = 7;
const byte resw = 8;

long pos;
byte alast, aval;
double pos1;

const byte eeadr = 14;

void setup() {
  pinMode (reclk, INPUT);
  pinMode (redt, INPUT);
  pinMode(resw, INPUT);
  digitalWrite(resw, HIGH);
  EEPROM.get(eeadr, pos);
  alast = digitalRead(reclk);
}
void loop() {
  if ( digitalRead(resw) == 0) {
    EEPROM.put(eeadr, pos);
    while (digitalRead(resw) == 0);
  }
  aval = digitalRead(reclk);
  if (aval != alast) {
    if (digitalRead(redt) != aval) {
      pos++;
    }
    else {
      pos--;
    }
    double pos1 = pos / 100.0;
  }
  alast = aval;
}

Kann man das so lassen?

Werde das ganze jetzt aber ändern und die Drehgeberabfrage über Interrupts machen. Jetzt ist es aber etwas OT geworden. Ich danke allen denn mein ursprüngliches Problem ist gelöst, mein Speicher macht keine Probleme mehr und ich habe bei dem Projekt schon einiges gelernt. 8)