ADS1115 Meßwerte auf OLED-Display anzeigen

benötige nochmals Eure Hilfe...
Beim zusammenfügen zweier Sketche komme ich nicht weiter.
Der Compiler verweigert die Funktion.
Beim Testaufbau an einem UNO (alles über den I2C-Port verbunden) funktionieren die einzelnen Sketche jedoch der gemeinsame Sketch nicht. Wobei der ADS-Wandler-Sketch den seriellen Monitor nutzte und jetzt das OLED-Display beschreiben soll.
Was hab ich übersehen?

/* ADS1115 Datenausgabe auf OLED
 * 4-Kanaliges Spannungsmessgerät 0-5V
 * 
 * ADS1115 + OLED-Display mit Arduino über SCK-5, SDA-4, +5V, GND verbinden
*/
#include<ADS1115_WE.h>  // für AD-Wandler 
#include<Arduino.h>
#include<Wire.h>
#include <U8g2lib.h>  // für OLED-Display 
#define I2C_ADDRESS 0x48

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); // OLED-Display einrichten

ADS1115_WE adc = ADS1115_WE(I2C_ADDRESS);

void setup(void) {
  Wire.begin();
  adc.setVoltageRange_mV(ADS1115_RANGE_6144); // Eingang AD-Wandler auf 6,1V einstellen
  u8g2.begin();
}

void loop() {
  float voltage = 0.0;
  u8g2.clearBuffer();					        // den internen Speicher löschen
  u8g2.setFont(u8g2_font_ncenB08_tr);	// Schriftart

  u8g2.drawStr(10,20,"ws = ");	// Display Position 1 (von links, von oben) festlegen
  voltage = readChannel(ADS1115_COMP_0_GND); // AD-Wandler Abfrage Kanal 0
  u8g2.drawStr(20,20,voltage);

  u8g2.drawStr(70,20,"ge = ");	// Display Position 2 
  voltage = readChannel(ADS1115_COMP_1_GND); // AD-Wandler Abfrage Kanal 1
  u8g2.drawStr(90,20,voltage);

  u8g2.drawStr(10,40,"gn = ");	// Display Position 3 
  voltage = readChannel(ADS1115_COMP_2_GND); // AD-Wandler Abfrage Kanal 2
  u8g2.drawStr(20,40,voltage);

  u8g2.drawStr(70,40,"sw = ");	// Display Position 4 
  voltage = readChannel(ADS1115_COMP_3_GND); // AD-Wandler Abfrage Kanal 3
  u8g2.drawStr(90,40,voltage);

  u8g2.sendBuffer();					// das Display beschreiben

}

float readChannel(ADS1115_MUX channel) {
  float voltage = 0.0;
  adc.setCompareChannels(channel);
  adc.startSingleMeasurement();
  while(adc.isBusy()){}
  voltage = adc.getResult_V(); // alternative: getResult_mV for Millivolt
  return voltage;
}
  1. adc.init() fehlt in der setup() Routine. Da musst Du nochmal auf Wolles Webseite nachschauen...
  2. Dein Compiler meckert, weil du der Methode u8g2.drawStr() keinen String, sondern einen float Wert übergibst. Das Messergebnis vom Datentyp float muss also erst einmal in einen String umgewandelt werden.

Das kann mit dtostrf() erfolgen:

Eine Alternative wäre auch, andere u8g2 Methoden zu verwenden:

  u8g2.setCursor(10, 20); u8g2.print("ws = ");
  u8g2.setCursor(20, 20); u8g2.print(voltage);

Danke, jetzt hab ich schon einmal eine Anzeige. Aber der ADC bekommt den Verstärkerwert nicht mitgeteilt und zeigt max.2,05V an. Wolle sei dank geht zwar der ADC am Monitor doch die Umsetzung zum Display ist leider nicht so einfach...
Was ist mir passiert?

/* ADS1115 Datenausgabe auf OLED
 * 4-Kanaliges Spannungsmessgerät 0-5V
 * 
 * ADS1115 + OLED-Display mit Arduino über SCK-5, SDA-4, +5V, GND verbinden
*/
#include<ADS1115_WE.h>  // für AD-Wandler 
#include<Arduino.h>
#include<Wire.h>
#include <U8g2lib.h>  // für OLED-Display 
#define I2C_ADDRESS 0x48

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); // OLED-Display einrichten

ADS1115_WE adc = ADS1115_WE(I2C_ADDRESS);

void setup(void) {
  Wire.begin();
  if(!adc.init()){
  u8g2.drawStr(10,20,"ADS1115 nicht erkannt");
  u8g2.begin();
}

adc.setVoltageRange_mV(ADS1115_RANGE_6144); // Eingang AD-Wandler auf 6,1V einstellen

}

void loop() {
  float voltage = 0.0;
  u8g2.clearBuffer();					        // den internen Speicher löschen
  u8g2.setFont(u8g2_font_ncenB08_tr);	// Schriftart

  u8g2.setCursor(10,20); u8g2.print("ws = ");	// Display Position 1 (von links, von oben) festlegen
  voltage = readChannel(ADS1115_COMP_0_GND); // AD-Wandler Abfrage Kanal 0
  u8g2.print(voltage);

  u8g2.setCursor(70,20); u8g2.print("ge = ");		// Display Position 2 
  voltage = readChannel(ADS1115_COMP_1_GND); // AD-Wandler Abfrage Kanal 1
  u8g2.print(voltage);

  u8g2.setCursor(10,40); u8g2.print("gn = ");	;	// Display Position 3 
  voltage = readChannel(ADS1115_COMP_2_GND); // AD-Wandler Abfrage Kanal 2
  u8g2.print(voltage);

  u8g2.setCursor(70,40); u8g2.print("sw = ");		// Display Position 4 
  voltage = readChannel(ADS1115_COMP_3_GND); // AD-Wandler Abfrage Kanal 3
  u8g2.print(voltage);

  u8g2.sendBuffer();					// das Display beschreiben

}

float readChannel(ADS1115_MUX channel) {
  float voltage = 0.0;
  adc.setCompareChannels(channel);
  adc.startSingleMeasurement();
  while(adc.isBusy()){}
  voltage = adc.getResult_V(); // alternative: getResult_mV for Millivolt
  return voltage;
}

Bist Du wirklich nicht in der Lage, oder einfach nur zu faul die Unterschiede zwischen dem ADC Code auf Wolles Webseite und dem was Du gebaut hast heraus zu finden?

Du solltest in der loop() am Ende noch ein delay einbauen ...
Und u8g2.drawStr(10,20,"ADS1115 nicht erkannt"); beim init() vom AD-Wandler kann so nicht funktionieren. Es muss zuerst das Display initialisiert werden, bevor Du da etwas drauf schreiben kannst.

Die Funktion des AD-Wandlers hat rein gar nichts mit der späteren Anzeige auf dem OLED zu tun. Also kannst Du doch den funktionierenden AD-Wandler Sketch von Wolles Seite nehmen und Stück für Stück den Code für die OLED Ausgabe da rein bauen. Dabei kannst Du die Ausgabe auf der Seriellen Konsole parallel laufen lassen.

Als Hilfe zur Selbsthilfe hier ein Link auf die Referenz der u8g2 Bibliothek:

Hier ist noch ein Beispiel. Da kannst Du dir Anregungen holen:

Hab keine Ahnung wieso, doch jetzt gehts...
Die Initialisierung des Wandlers erfolgt doch in Zeile 25

  u8g2.begin();

der Einrichtungsbefehl steht jedoch erst in Zeile32

adc.setVoltageRange_mV(ADS1115_RANGE_6144);
  • oder bin ich so daneben?
    Das Delay zwingt das Programm doch nur in eine Pause, die nicht erwünscht ist ? ? ?
    Entschuldige das ich Dich zum verzweifeln bringe, doch meine Grauen Zellen sind bereits etwas in die Jahre gekommen, ich sollte das Basteln vielleicht doch den Jüngeren überlassen...
    Und die engl. Hinweis-Erklärungen versuche ich möglichst zu umgehen und suche deutsche Seiten. Wir mußten uns in der Schule noch mit russisch quälen, haben davon auch nichts mehr...
/* ADS1115 Datenausgabe auf OLED
 * 4-Kanaliges Spannungsmessgerät 0-5V
 * 
 * ADS1115 + OLED-Display mit Arduino über SCK-5, SDA-4, +5V, GND verbinden
*/

#include<Arduino.h>
#include <SPI.h>
#include <U8g2lib.h>  // für OLED-Display 
#include<Wire.h>
#define I2C_ADDRESS 0x48
#include<ADS1115_WE.h>  // für AD-Wandler 
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); // OLED-Display einrichten

ADS1115_WE adc = ADS1115_WE(I2C_ADDRESS);

void setup(void) {
  u8g2.begin();
  Wire.begin();
  if(!adc.init()){
  u8g2.drawStr(10,20,"ADS1115 nicht erkannt");
  u8g2.begin();
}

adc.setVoltageRange_mV(ADS1115_RANGE_6144); // Eingang AD-Wandler auf 6,1V einstellen

}

void loop() {
  float voltage = 0.0;
  u8g2.clearBuffer();					        // den internen Speicher löschen
  u8g2.setFont(u8g2_font_ncenB08_tr);	// Schriftart

  u8g2.setCursor(10,20); u8g2.print("ws = ");	// Display Position 1 (von links, von oben) festlegen
  voltage = readChannel(ADS1115_COMP_0_GND); // AD-Wandler Abfrage Kanal 0
  u8g2.print(voltage);

  u8g2.setCursor(70,20); u8g2.print("ge = ");		// Display Position 2 
  voltage = readChannel(ADS1115_COMP_1_GND); // AD-Wandler Abfrage Kanal 1
  u8g2.print(voltage);

  u8g2.setCursor(10,40); u8g2.print("gn = ");	;	// Display Position 3 
  voltage = readChannel(ADS1115_COMP_2_GND); // AD-Wandler Abfrage Kanal 2
  u8g2.print(voltage);

  u8g2.setCursor(70,40); u8g2.print("sw = ");		// Display Position 4 
  voltage = readChannel(ADS1115_COMP_3_GND); // AD-Wandler Abfrage Kanal 3
  u8g2.print(voltage);

  u8g2.sendBuffer();					// das Display beschreiben

  delay(10);
}

float readChannel(ADS1115_MUX channel) {
  float voltage = 0.0;
  adc.setCompareChannels(channel);
  adc.startSingleMeasurement();
  while(adc.isBusy()){}
  voltage = adc.getResult_V(); // alternative: getResult_mV for Millivolt
  return voltage;
}

Nun, da das Programm im der Loop nichts anderes macht als Daten vom ADC abzufragen und diese darzustellen, frage mich mich natürlich, wieviel tausend Messungen pro Sekunde Du denn brauchst und was für einen Sinn das macht. Eine Pause könnte da vielleicht doch sinnvoll sein. Du hast ja eine eingefügt.

Aber das ist ja „Dein“ Programm . Von daher, wenn es Dir nicht gefällt, dann lass es weg… mir ist das ziemlich wurscht. :wink:
Nur Du kannst wissen, was Du erreichen willst.

Und wenn Du der Meinung bist, dass in setup() alles passt. Ok. Nur probiere es mal aus …. dann kannst Du dir auch sicher sein.

habs gemerkt und das 2. u8g2.begin entfernt, was ja in der Schleife steckte...
Bin eben noch lange nicht in der Materie, sehe noch nicht klar.
Danke für die u8g2referenz. Wird eine menge Zeit kosten die Anweisungen ins deutsche zu übertragen...
Mit dem Simulator komme ich auch nicht so schnell klar - wie füge ich den AD_Wandler hinzu ? ? ? Super ist das Teil schon.
Hatte auch gehofft den Include Block etwas zu erleichtern, das ifdef für die SPI brauch ich doch auch nicht ? ? ?
Die Wandlerrate begrenzt ja bereits die Umsetzfrequenz, ein zusätzliches Delay bringt doch nur Zeitverzug. Hatte die Absicht in kleinen OPV-Schaltungen die Spannung zu kontrollieren bzw. Mosfets zu analysieren. Da brauch ich die sofortige Anzeige bei einer Änderung. Vielfachmesser brauchen ewig um Spannungsänderungen anzuzeigen, deshalb nutzte ich bisher immer kleine Drehspulenmeßwerke. Komme noch aus der Zeit der Analogelektronik und bin nur in die TTL ... C-Mos Technik hineingewachsen.
Für Deine Hinweise erst einmal herzlichen Dank.
Roland

Den AD-Wandler gibt es bei der Simulation (so ohne Weiteres ) nicht. Darum wurde eine Dummyfunktion hinzugefügt, die den AD- Wandler simuliert um Werte für die Darstellung auf dem OLED zu erzeugen.

Die Simulation soll nur eine Variante zeigen, mit der Daten auf dem OLED Dargestellt werden können.

Danke, eine Frage hab ich noch,
eine analoge Balkenanzeige des Meßwertes bzw. reverse Zeilenbreite, ist das ohne weiteres machbar? Könnte mir vorstellen das damit die Aufmerksamkeit wesentlich beschleunigt wird...
hab mich auch schon gefragt ob es nicht sinnvoller ist den Arduino-Wandler zu nutzen und den ADS weg zu lassen...
Doch da ich den ADS1220 als besseres Meßwerk nutzen möchte, wollte ich mit den ADS1115 erste Erfahrungen sammeln.

Ist möglich…

Hilfst Du mir?

Nein… das kriegst Du schon selber hin…

mal sehen ob ich es noch diesseits schaffe...
übrigens war meine Absicht die scheiß T88 zu nutzen und wollte deshalb Speicherplatz sparen - Programm ist jedoch auch zu groß...

Du hast ja zwar die schnellste, aber auch die am meisten RAM Speicher verbrauchende Variante der Darstellung auf dem OLED gewählt. Außerdem hat die Wahl des gewählten Fonts einen großen Einfluss auf dem RAM Verbrauch.

Diese Informationen finden sich im Detail in der oben verlinkten Referenz.

Eine kleine Änderung macht die Bildschimausgabe zwar etwas langsamer, speichert aber fast 50% RAM ein:

Oben gezeigte Variante
RAM:   [========= ]  89.6% (used 1835 bytes from 2048 bytes)
Flash: [====      ]  43.9% (used 14162 bytes from 32256 bytes)

Speicher sparende Variante
RAM:   [=====     ]  45.8% (used 939 bytes from 2048 bytes)
Flash: [====      ]  44.0% (used 14190 bytes from 32256 bytes)

Habe das Buch "Arduino Kompendium" raus gesucht und versuche mich in den Grundlagen der Programmierung weiter zu bilden...
Mit 20 hatte ich keine Probleme Nächte lang Bücher zu verschlingen - heute fallen mir nach 2 Seiten die Augen zu...
2 Stunden die Arduimo Programme zu manipulieren und zum Testen aufzuspielen ist machbar...
Der Simulator ist ein Buch mit 7 Siegeln - habe keine Möglichkeit etwas einzugeben

Ja, das Leben ist nicht einfach.

Hier ist ein Beispiel, wie Du eine Balkengrafik umsetzen kannst. Du solltest jetzt eigentlich alle Informationen haben die Du brauchst:

Vielen Dank, bin mit der Darstellung zufrieden. Hoffe das ich die Zeilen richtig kommentiert habe. Einiges ist mir zwar noch rätselhaft, doch es wird ...
Hier nochmals für Interessierte der Sketch:
ADS1115_Balkengrafik.ino

/*
 * Reference:
 * https://github.com/olikraus/u8g2/wiki
 *
 * ADS1115 + OLED-Display parallel mit Arduino (SCK->5, SDA->4, +5V, GND) verbinden
 *
 * zum testen 5 Stück 2k2 (o.ä.) Widerstände seriell zusammenlöten und mit + und GND
 * verbinden ergibt 1, 2, 3 und 4V für den AD-Wandler bei 5V Speisespannung
 * 
 */

#include <Arduino.h>      // für Variablen
#include <ADS1115_WE.h>   // für AD-Wandler
#include <Wire.h>         // für I2C Schnittstelle
#include <U8g2lib.h>      // für OLED-Display

#ifdef U8X8_HAVE_HW_SPI   // Schriftgruppenauswahl für OLED
#include <SPI.h>          // für serielle Kommunikation
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

#define SPANNUNG_5V   // Zeile aktiv 5V Anzeige - Zeile als Kommentar 12V Anzeige

constexpr byte I2C_ADDRESS {0x48};  // Adresse AD-Wandler
constexpr byte NUM_CHANNELS {4};    // 4 Kanäle aktiv

struct ValueStorage {          // Struktur Wertspeicher
  const ADS1115_MUX channel;   // ADC Kanal
  const unsigned int x;        // Display x-pos
  const unsigned int y;        // Display y-pos
  float voltage;               // Wert
};

// Auffüllen des Wertspeicher-Arrays
ValueStorage voltS[NUM_CHANNELS] {
    {ADS1115_COMP_0_GND, 22, 0,  0.0},
    {ADS1115_COMP_1_GND, 22, 12, 0.0},
    {ADS1115_COMP_2_GND, 22, 24, 0.0},
    {ADS1115_COMP_3_GND, 22, 36, 0.0}
};

// using OLED = U8G2_SH1106_128X64_NONAME_F_HW_I2C;
// using OLED = U8G2_SSD1306_128X64_NONAME_F_HW_I2C;

using OLED = U8G2_SH1106_128X64_NONAME_1_HW_I2C;   // 1,3 Zoll SH1106
// using OLED = U8G2_SSD1306_128X64_NONAME_1_HW_I2C;  // 0,96 Zoll SH1306
OLED u8g2(U8G2_R0, U8X8_PIN_NONE);   // OLED-Display einrichten

ADS1115_WE adc = ADS1115_WE(I2C_ADDRESS);

void dpPrintError(OLED &, char *);
void dpPrintItems(OLED &, const ValueStorage[], byte);
float readChannel(ADS1115_MUX channel);

void setup(void) {
  Serial.begin(115200);   // serielle Übertragungsgeschwindigkeit
  Wire.begin();

  u8g2.begin();
  // u8g2.setFont(u8g2_font_ncenB08_tr);
  u8g2.setFont(u8g2_font_6x10_tr);      // OLED Schriftauswahl

  // bei angeschlossem AD-Wandler entfernen
  /*
    if (!adc.init()) {
     dpPrintError(u8g2,"ADS1115 nicht erkannt!");
     while(1) {}
    }
  */
  adc.setVoltageRange_mV(ADS1115_RANGE_6144);   // Eingang AD-Wandler auf 6,1V einstellen
  dpPrintItems(u8g2, voltS, NUM_CHANNELS);
}

void loop() {
  for (byte i = 0; i < NUM_CHANNELS; ++i) {
    float voltage = readChannel(voltS[i].channel);
    if (voltage != voltS[i].voltage) {
      voltS[i].voltage = voltage;
      dpPrintItems(u8g2, voltS, NUM_CHANNELS);
    }
  }
  delay(10);    // Pause bei Zufallszahlen auf 1000 verlängern
}

void dpPrintError(OLED &dp, char error[]) {
  u8g2.firstPage();
  do {
    dp.setCursor(1, 30);
    dp.print(error);
  } while (u8g2.nextPage());
}

//
// Die Schrift ist 6 Pixel Breit und 10 Pixel hoch.
//
void dpPrintItems(OLED &dp, const ValueStorage vs[], byte num) {
  constexpr byte FONT_WIDTH {6};
  constexpr byte FONT_HIGH {10};

  u8g2.firstPage();
  do {
    dp.setCursor(0, 6); dp.print("ws:");
    dp.setCursor(0, 18); dp.print("ge:");
    dp.setCursor(0, 30); dp.print("gn:");
    dp.setCursor(0, 42); dp.print("sw:");
#ifdef SPANNUNG_5V
    dp.setCursor(20, 55); dp.print("| | | | | |");
    dp.setCursor(20, 63); dp.print("0.1.2.3.4.5 V");
#else
    dp.setCursor(20, 55); dp.print("|  |  |  |  |");
    dp.setCursor(20, 63); dp.print("0..3..6..9..12 V");
#endif
    for (byte i = 0; i < num; ++i) {
#ifdef SPANNUNG_5V
      // Skalenschritte = 0,5V. Darum Zeichenbreite * 2
      unsigned int barLen = vs[i].voltage * FONT_WIDTH * 2;   // Berechne Balkenlänge für 5V
#else
      // Skalenschritte = 1V
      unsigned int barLen = vs[i].voltage * FONT_WIDTH;   // Berechne Balkenlänge für 12V
#endif
      dp.drawBox(vs[i].x, vs[i].y, barLen, 6);
      unsigned int offset {76};
      if (vs[i].voltage < 10) {
        offset += FONT_WIDTH;
      }   // Bei einstelligen Werten den Cursor eine Zeichenbreite weiter rechts setzen
      dp.setCursor(vs[i].x + offset, vs[i].y + (FONT_HIGH - 3));
      dp.print(vs[i].voltage, 2);
    }
  } while (u8g2.nextPage());
}

// Zufallswerte bei fehlendem AD-Wandler
/*
float readChannel(ADS1115_MUX channel) {
  randomSeed(millis());
#ifdef SPANNUNG_5V
  return static_cast<float>(random(0, 5001)) / 1000;
#else
  return static_cast<float>(random(0, 12001)) / 1000;
#endif
}
*/
// aktiv bei angeschlossem AD-Wandler
//
  float readChannel(ADS1115_MUX channel) {
  float voltage = 0.0;
  adc.setCompareChannels(channel);
  adc.startSingleMeasurement();
  while (adc.isBusy()) {}
  voltage = adc.getResult_V();   // alternativ: getResult_mV für Millivolt
  return voltage;
  }
//

als Tipp:
Mini Grabber
eignen sich ideal für den Spannungsabriff an den Widerständen. Als hochflexible Anschlußdrähte sind die Adern alter Telefonsteckdosen-Anschlußleitungen ideal.

Schön wenn es passt. Allerdings wird das Strukturarray, dort wo Du „// Auslesen des Wertespeichers“ hingeschrieben hast nicht ausgelesen, sondern mit Startwerten initialisiert.

so richtig ?
ich frage mich weshalb die Werte nur 3 Stellig dargestellt werden. 15 Bit ergeben doch 32767, also locker 4,5 Stellen, bei 16 Bit sogar 4 3/4 Stellen. So ist die Ausgabe wie vom Arduino-Wandler selbst mit seinen 10 Bit...

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.