Go Down

Topic: map Funktion; Darstellung der Wertebereiche auf einem OLED mittels u8g2 (Read 271 times) previous topic - next topic

Bolle_SC

Apr 19, 2019, 06:47 pm Last Edit: Apr 19, 2019, 06:54 pm by Bolle_SC Reason: just found out how to post code
Servus,
bin Anfänger und komme mit dem Schreiben eines Sketches für den Arduino (Nano) nicht weiter und bitte höflich um Hilfe.

Es handelt sich um den Code für ein simples Kfz-Voltmeter für meinen Oldtimer, welches den Spannungsabfall über dem Öltemperatur-NTC zunächst nur messen und auf einem OLED anzeigen soll.
Der Abgriff der - mit steigender Temperatur fallenden - Meßspannung geschieht über einen OpAmp als Buffer und wird mit einem Vorteiler auf die max. möglichen 5V an AØ begrenzt (daher der krude Teilfaktor 514,05 für den DA-Wandler).
(Nicht nur) weil die NTC-Kennlinie "krumm" ist, reichen mir Anzeigeschritte in Stufen von 5°C.

Zur Aufnahme einer "Eichkurve" lasse ich mir spannungsabhängig (1) den Wert des DA-Wandlers (DAC) anzeigen und habe dessen Ausgabe auf 20 Bereiche (range (2)) "gemapped" um letztere dann mittels "Switch/Case" als Temperaturwert - im Code noch Platzhalter "aa", "bb" usw. - anzeigen zu lassen.  Nebenbei wird auch die zugehörige Spannung (3) angezeigt.

Zur Frage: Im seriellen Monitor klappt die Anzeige der "Ranges" mit Serial.print(range); tadellos, die Ausgabe auf dem OLED will mir jedoch nicht gelingen.  "u8g2.print(range);" zeigt eine bleibende 0, die Library kann vermutl. die (Int?)Werte 0-19 nicht erkennen.

Leider funktioniert auch die itoa-Funktion nicht weiter, denn "u8g2.print(str); u8g2.setCursor(33, 36);u8g2.print("Range");" zeigt erfreulich passend die Bereiche 0-9 an, aber oberhalb davon werden es Kleinbuchstaben a-h…

Habe mich festgegrübelt und weiß einfach nicht weiter, was bei meinen völligen C++ Unkenntnissen ja auch kein Wunder ist :-(

Code: [Select]
/*
   DAC => Temp-Anzeige (Beispiel für switch/case und Mapping)
   <https://programmingelectronics.com/tutorial-14-5-switch-case-statement-old-version/>
   hier zur Zuordnung der NTC-Spannung des Ölthermometers vom Oldie zu Temperaturwerten in 5° Schritten
   
   1.3" 128x64 OLED mit HW-SPI
   Display Pin  ->  Arduino Pin
     SCL, D0    ->      13
       RST      ->      12
     SDA, D1    ->      11
   Chip Select  ->      10
      AØ, DC    ->       9
*/

#include <SPI.h>
#include <U8g2lib.h>
U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, 10, 9, 12); // u8g2 object to match SH1106 HW-SPI-OLED 
 // <https://github.com/olikraus/u8g2/wiki/u8g2setupcpp> und <https://forum.arduino.cc/index.php?topic=461220.0>

#define blue_led 5       // Über-/Untertemp., an wenn im Sink-Mode
#define red_led 6        // RGB-Led mit gem. Anode zur Anzeige von
#define NUM_SAMPLES 10   // number of analog samples to take per reading

unsigned int sum = 0;            // sum of samples taken
byte sample_count = 0;      // current sample number; "byte" = Zahl 0-255
unsigned int DAC = 0.0;     // unsigned int = keine Nachkommastellen, keine neg. Werte
unsigned int piezoPin = 7;  // digital pin 7 für den Alarmton
byte range = 0;             // Range entspr. dem zu "mappenden" Wertebereich
char str[20];               // Array für U8G2_druckbare Integer-Werte <http://forum.arduino.cc/index.php?topic=314684.0>

const unsigned int min = 0;     // Lowest reading at analog pin
const unsigned int max = 1024;  // Highest reading at analog pin


void setup() {
  pinMode(red_led, OUTPUT);
  pinMode(blue_led, OUTPUT);
  u8g2.begin();
  Serial.begin(9600);
}

 
void loop () {
  while (sample_count < NUM_SAMPLES) {  // Routine f. Mittelwert der Öltemp.
    sum += analogRead(A0);              // Meßeingang Pin A0
    sample_count++;
    delay(20);
  }

  DAC = ((float)sum / (float)NUM_SAMPLES); // DAC => D/A-Converter
  sample_count = 0;
  sum = 0;

  if ((DAC) >= 825) {                      // Bedingung f. Warn-LEDs
    digitalWrite(red_led, HIGH); digitalWrite(blue_led, LOW);
  }
  else if ((DAC) <= 100) {              // 100 entspr. 1V
    digitalWrite(red_led, LOW); digitalWrite(blue_led, HIGH);
  }
  else if ((DAC) >100 && (DAC) <825) {  // 825 entspr. 8V
    digitalWrite(red_led, HIGH); digitalWrite(blue_led, HIGH);
  }
  Serial.print(DAC); Serial.print("\t");
  Serial.print((DAC * 5.00) /514.05, 2); Serial.print(" V""\t"); // 1024:Faktor des Vorteilers

  byte range = map(DAC, min, max, 0, 19);           // Umsetzen DAC 0<->1024 zu Range 0<->19
  delay(20);  // map(Variable_to_be_Mapped, Low_Initial_Range, High_Initial_Range, New_Low, New_High)
  Serial.print(range); Serial.print("\t");
  itoa( range, str, 20 );  // iota-Funktion zur Umwandlung 'byte range' zu U8G2-"printbarem" String
 

  switch (range) {
    case 0:   // DAC 0-51
      Serial.println("hot!""\t");
      tone(piezoPin, 6, 3000);  // tone(Pin, Freq, Dauer)
      break;
    case 1:   // DAC 52-102
      Serial.println("hot""\t");
      tone(piezoPin, 6, 3000);  // tone(Pin, Freq, Dauer)
      break;
    case 2:   // DAC 103-154
      Serial.print("bb"); Serial.println("°C""\t");
      break;     
    case 3:   // DAC 155-205
      Serial.print("cc"); Serial.println("°C""\t");
      break;
    case 4:   // DAC 206-256
      Serial.print("dd"); Serial.println("°C""\t");
      break;
    case 5:   // DAC 257-307
      Serial.print("ee"); Serial.println("°C""\t");
      break;
    case 6:   // DAC 308-358
      Serial.print("ff"); Serial.println("°C""\t");
      break;
    case 7:   // DAC 359-410
      Serial.print("gg"); Serial.println("°C""\t");
      break;
    case 8:   // DAC 411-461
      Serial.print("hh"); Serial.println("°C""\t");
      break;
    case 9:   // DAC 462-512
      Serial.print("ii"); Serial.println("°C""\t");
      break;
    case 10:   // DAC 513-563
      Serial.print("jj"); Serial.println("°C""\t");
      break;
    case 11:   // DAC 564-614
      Serial.print("kk"); Serial.println("°C""\t");
      break;
    case 12:   // DAC 615-666
      Serial.print("ll"); Serial.println("°C""\t");
      break;
    case 13:   // DAC 667-717
      Serial.print("mm"); Serial.println("°C""\t");
      break;
    case 14:   // DAC 718-768
      Serial.print("nn"); Serial.println("°C""\t");
      break;
    case 15:   // DAC 769-819
      Serial.print("oo"); Serial.println("°C""\t");
      break;
    case 16:   // DAC 820-870
      Serial.print("pp"); Serial.println("°C""\t");
      break;
    case 17:   // DAC 871-922
      Serial.print("qq"); Serial.println("°C""\t");
      break;
    case 18:  // DAC 923-973
      Serial.println("kalt""\t");
      break;
    case 19:  // DAC 974-1024
      Serial.println("kalt!""\t");
      break;
  }

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


void draw() {
  u8g2.clearBuffer();  // Display Memory "aufräumen"
  u8g2.setFont(u8g2_font_helvR10_tf);     // Font auswählen
  u8g2.setCursor(2, 14); u8g2.print(DAC); u8g2.drawStr(33, 14,"DAC");
  u8g2.setCursor(2, 36); u8g2.print(str); u8g2.setCursor(33, 36);u8g2.print("Range");  // zeigt 0-9, dann Kleinbuchstaben?
  u8g2.setCursor(2, 62);  u8g2.print((DAC * 5.00) /514.05, 2); u8g2.drawStr(33, 62,"V"); // 1024:Vorteiler
}


ps.: Eine Variante mit else if Statements mit der Zuordnung von ((DAC) <= x && (DAC) < y) funktioniert, mich reizt als Anfänger aber der - wie mir scheint eleganter aussehende - Weg über ein Array mit den switch Cases.

gregorss

Hi!

Ohne Deinen Sketch angesehen zu haben: Was Du beschreibst lässt mich an ein „Problem mit ASCII-Codes" denken. Vermutlich „denkt" die von Dir verwendete Ausgabefunktion, dass Du ein Zeichen mit dem entsprechenden ASCII-Code anzeigen möchtest. Du möchtest allerdings einen „Text" angezeigt bekommen. Es gibt eine Funktion, mit der Du aus einem Zahlenwert eine entsprechende Zeichenkette erzeugen kannst - itoa() vermutlich, sicher bin ich mir da nicht.

Du musst also etwas wie

u8g2.print(itoa(DAC));

schreiben. Oder Du benutzt die Funktion zur Ausgabe eines Strings

u8g2.drawStr(x, y,DAC);

beachte die fehlenden Anführungszeichen.

Wie gesagt, ich vermute ... ich kenne die von Dir verwendete Bibliothek nicht.

Gruß

Gregor
Bei Vollmond ist Neuerde.

Bolle_SC

Servus, und erst einmal Dank für die Antwort.

Die verwendete u8g2.lib von Oli Kraus <https://github.com/olikraus/u8g2/wiki> hat sich zu einem Standard bei der Ansteuerung von OLED-Displays entwickelt.  Von daher dachte ich sind eigentlich keine Hindernisse zu erwarten.

Nachdem „meine Ranges" zunächst nicht auf dem Display „druckbar" waren - es kam immer nur der Wert „0" - wohl aber im Serial Monitor - bin ich bei der Sucherei auch auf die itoa-Funktion gestoßen.  Das führt immerhin dazu, daß - wie in der Frage beschrieben - wenigstens die Bereiche 0 bis 9 und a bis h angezeigt werden.

Ich habe die korrekte Anwendung leider überhaupt nicht verstanden und sie vermutlich falsch im Sketch implementiert?
Wäre sehr lieb, wenn Du da mal drüber gucken könntest.

gregorss

Wäre sehr lieb, wenn Du da mal drüber gucken könntest.
Mache ich. Allerdings müsstest Du vorher noch ein bisschen aktiv werden.

Ich arbeite gerne auf Papier. Dein Code erzeugt allerdings einen besonders hässlichen Ausdruck. PDF im Anhang. Lies mal mein kurzes Geschreibsel hier, damit ich das Ergebnis Deiner Aktivitäten mit einer Zigarette auf den Balkon lesen kann.

Vor heute Abend komme ich allerdings nicht zum Lesen.

Gruß

Gregor
Bei Vollmond ist Neuerde.

agmue

Habe mich festgegrübelt und weiß einfach nicht weiter, was bei meinen völligen C++ Unkenntnissen ja auch kein Wunder ist :-(
In ca. Zeile 67 hast Du eine globale Variable erneut als lokale Variable deklariert. Die lokale Variable ist in der Funktion nicht bekannt, weshalb die globale verwendet wird, die immer den Wert 0 hat. So geht es:

Code: [Select]
  range = map(DAC, min, max, 0, 19);           // Umsetzen DAC 0<->1024 zu Range 0<->19

... damit ich das Ergebnis Deiner Aktivitäten mit einer Zigarette auf den Balkon lesen kann.
Ich hoffe, Dir nicht den Abend verdorben zu haben. Ergänze den Qualm mit einem Single Malt Scotch, dann paßt es wieder :smiley-roll-blue:
Wahnsinn und Verstand trennt nur eine dünne Wand. (Daniel Düsentrieb)

gregorss

Ich hoffe, Dir nicht den Abend verdorben zu haben. Ergänze den Qualm mit einem Single Malt Scotch, dann paßt es wieder
Mir ausgerechnet heute den Abend verderben zu wollen ist ein aussichtsloses Unterfangen. Zumal Du Code zitierst, der nicht von mir stammt :-)

Aber das mit den Drogen ist eine Idee ...

Gruß

Gregor

BTW: Ist das mit den globalen und lokalen Variablen nicht umgekehrt? Eine lokale Variable „überdeckt" doch eine entsprechende globale, oder nicht?
Bei Vollmond ist Neuerde.

postmaster-ino

Hi

Anmerkung: 0...1023 (=1024 Schritte) --> 0...19 (20 Schritte)

MfG
anscheinend ist Es nicht erwünscht, einen Foren-internen Link als 'Homepage' einzubinden, damit JEDER nur einen Klick von combie's Liste zum Thema State-Maschine entfernt ist.
... dann eben nicht ...

agmue

Aber das mit den Drogen ist eine Idee ...
Genuß verlängert das Leben, zu viel davon zerstört es, der Mittelweg ist golden!

BTW: Ist das mit den globalen und lokalen Variablen nicht umgekehrt? Eine lokale Variable „überdeckt" doch eine entsprechende globale, oder nicht?
Ja, genau so. Deshalb wird in loop auch die lokale Variable für loop verwendet, die dann in der Funktion nicht bekannt ist.

Übrigens hätte dies funktioniert:

Code: [Select]
itoa( range, str, 10 ); "10 means decimal base"
Wahnsinn und Verstand trennt nur eine dünne Wand. (Daniel Düsentrieb)

gregorss

Genuß verlängert das Leben, zu viel davon zerstört es, der Mittelweg ist golden!
Da ich noch lebe, scheine ich mit dem, was ich so anstelle, nicht ganz daneben zu liegen :-)

Tarantino im O-Ton und Drogen ... ein Osterfest „mit Eiern" für Genießer ...

Gruß

Gregor
Bei Vollmond ist Neuerde.

Bolle_SC

oh, ich muß wohl Hausaufgaben machen!  ... und darf erst mal nicht, weil die nächsten Tage die Familie Vorrang haben soll/darf/muß ;-)

Vielen Dank Euch erst mal, ich werde berichten.

gregorss

oh, ich muß wohl Hausaufgaben machen!  ... und darf erst mal nicht, weil die nächsten Tage die Familie Vorrang haben soll/darf/muß ;-)
Vielen Dank Euch erst mal, ich werde berichten.
Umso besser - dann bin ich erst nächste Woche wieder dran. Ostern hat sozusagen sogar zwei Eier :-)

Wenn es mehrere Variablen gibt, die gleich heißen, je nach Kontext jedoch andere Bedeutung haben, ist das IMO ein Designfehler. Das geht zwar oft gut, ist aber übel zu debuggen. Ausnahmen sind so Sachen wie „temp".

Gruß

Gregor
Bei Vollmond ist Neuerde.

Bolle_SC

[quote
Ich arbeite gerne auf Papier. Dein Code erzeugt allerdings einen besonders hässlichen Ausdruck.
[/quote]

Das soll natürlich nicht sein! Denn ich schreibe in einem Texteditor (Mac) erst einmal vorab um selber besser klar zu kommen.  Ästhetik ist die halbe Miete ;-)

Angefügt eine pdf-Version. 

gregorss

Angefügt eine pdf-Version. 
Uh. Ich empfehle Dir dringend, den Editor der Arduino-IDE zu benutzen! Oder wenigstens eine Festbreitenschrift wie Courier. Die Probleme, die Du Dir mit der Tipperei in Proportionalschrift einhandelst, sind nicht ohne. Vom Komfort ganz abgesehen.

Gruß

Gregor
Bei Vollmond ist Neuerde.

Bolle_SC

@ agmue: You are my hero!!  Konnte nicht widerstehen und habe mich an den Mac geschlichen:

Code: [Select]
  range = map(DAC, min, max, 0, 19);
        // Umsetzen DAC 0<->1023 zu Range 0<->19 
        // map(Variable_to_be_Mapped, Low_Initial_Range, High_Initial_Range, New_Low, New_High)

  delay(20);

  Serial.print(range); Serial.print("\t");
  itoa( range, str, 10 ); 
        // iota-Funktion zur Umwandlung 'byte range' zu U8G2-"printbarem" String
        // [u]"10" bedeutet decimal base[/u]


hat's gebracht und jetzt funzt es.  Ich hatte gemeint mit meiner "20" Platz für 20 Wertebereiche zu reservieren und völlig übersehen, daß die 10 für die Ausgabe von Dezimalzahlen steht...

@ all:  1000 Dank für die spontane Hilfsbereitschaft, Ihr seid großartig!
 
Verstanden habe ich zwar immer noch nicht, warum ein Serial.print automatisch diese Umwandlung vornimmt, und die im Prinzip doch identische Anweisung zur Darstellung auf einem Display eben nicht.

...  und was die itoa Funktion tatsächlich wie macht natürlich auch nicht.... 

Liebe Grüße, Michael

agmue

@ agmue: You are my hero!!  Konnte nicht widerstehen und habe mich an den Mac geschlichen:
In diesem Fall freue ich mich, Dich verführt zu haben.

Verstanden habe ich zwar immer noch nicht, warum ein Serial.print automatisch diese Umwandlung vornimmt, und die im Prinzip doch identische Anweisung zur Darstellung auf einem Display eben nicht.
Dein Programm auf die wesentlichen Zeilen reduziert:

Code: [Select]
...
byte range = 0; // globale Variable
...
void loop () {
...
  byte range = map(DAC, min, max, 0, 19);  // lokale Variable in der Funktion loop
...
  Serial.print(range); Serial.print("\t");  // Ausgabe der lokalen Variablen
...
}

void draw() {
...
  u8g2.setCursor(2, 36); u8g2.print(range); u8g2.setCursor(33, 36);u8g2.print("Range");  // zeigt den Wert der globalen Variablen, der immer 0 ist.
...

Diese Variante würde funktionieren:

Code: [Select]
...
byte range = 0; // globale Variable
...
void loop () {
...
  range = map(DAC, min, max, 0, 19);  // globale Variable wird verändert
...
  Serial.print(range); Serial.print("\t");  // Ausgabe der globalen Variablen
...
}

void draw() {
...
  u8g2.setCursor(2, 36); u8g2.print(range); u8g2.setCursor(33, 36);u8g2.print("Range");  // zeigt den Wert der globalen Variablen.
...

itoa benötigst Du nicht.
Wahnsinn und Verstand trennt nur eine dünne Wand. (Daniel Düsentrieb)

Go Up