ADS1115 – A/D-Wandler mit Verstärker

Über den Beitrag

Mit diesem Beitrag möchte ich den 16-Bit, 4-Kanal A/D-Wandler ADS1115 und seinen Bruder, den 12-Bit, 4-Kanal A/D-Wandler ADS1015 vorstellen. Ihr könnt beide könnt ihr mit meiner Bibliothek ADS1115_WE steuern.

Ich gehe zunächst auf die technischen Eigenschaften ein, dann möchte ich anhand von Beispielsketchen zeigen, wie ihr den ADS1115 (und den ADS1015) mit der Bibliothek nutzen könnt. Zum Schluss gibt es – für die Interessierten – noch einen tieferen Einblick in den ADS1115 und seine Register.

Genau betrachtet behandele ich in diesem Beitrag ADS1115 / ADS1015 Module. Wenn ihr im Löten von SMDs fit seid, könnt ihr natürlich auch den „nackten“ ADS1115 / ADS1015 verwenden. Meine Bibliothek sollte damit genauso funktionieren.

Wozu brauche ich den ADS1115 oder den ADS1015?

„Mein Arduino bzw. mein Microcontroller hat doch schon einen eingebauten A/D-Wandler. Wieso soll ich mich also mit diesem externen A/D-Wandler beschäftigen?“

Hier ein paar Gründe. Der ADS1115

  • hat eine Auflösung von 16 Bit gegenüber den 10 Bit des Arduino.
  • besitzt einen internen Verstärker, sodass er auch kleine Spannungen messen kann.
  • verfügt über eine Alarmfunktion, die ihr zur effizienten Überwachung von Spannungen einsetzen könnt, denn:
    • wenn euer Microcontroller nichts anderes zu tun hat, könntet ihr ihn schlafen schicken und den genügsamen ADS1115 arbeiten lassen.
    • ausgelagerte Prozesse, die ihr über Interrupts einbindet, können euren Code vereinfachen.

Der ADS1015 besitzt eine Auflösung von 12 Bit, er ist dafür aber erheblich schneller als der ADS1115.

Technische Eigenschaften des ADS1115 / ADS1015

Verschiedene ADS1115 Module
Verschiedene ADS1115 Module

Ein- und Ausgänge des ADS1115 / ADS1015

Der ADS1115 / ADS1015 beziehungsweise die Module besitzen die folgenden Ein- und Ausgänge:

  • VCC / GND: Anschluss von 2 bis 5.5 Volt.
  • SDA / SCL: Kommunikation über I2C. Ich habe keine extra Pull-Up Widerstände benötigt, aber sie sind nicht unbedingt in jedem Modul implementiert.
  • ADDR: Adresspin – vier I2C Adressen sind gemäß der unten abgebildeten Tabelle einstellbar.
  • ALRT: der Alert Pin wird bei Über- oder Unterschreitung festlegbarer Limits oder bei Abschluss einer A/D-Wandlung („Conversion Ready“) aktiv. In der Voreinstellung ist der Pin funktionslos.
  • A0 – A3: Anschlüsse für die vier Kanäle. Die angelegte Spannung darf VCC um nicht mehr als 0.3 Volt überschreiten.

Wie ihr seht, besitzt das oben abgebildete, mittlere Modul keinen Adress- und Alarmpin. Ihr seid damit auf die Adresse 0x48 festgelegt und könnt keine Alarme programmieren. Wenn ihr das nicht braucht, habt ihr damit aber eine sehr platzsparende Lösung.

Einstellung der I2C Adressen des ADS1115
Einstellung der I2C Adressen des ADS1115 / ADS1015

Wenn ihr die Adressen überprüfen wollt, dann könnt ihr diesen I2C Scanner benutzen.

Eigenschaften

Hier ein kurzer Überblick über die wichtigsten Eigenschaften:

  • 16 Bit Auflösung – vorzeichenbehaftet (+/- 215 Bit).
    • 12 Bit für den ADS1015
  • 4 Kanäle, die gegeneinander oder gegen GND gemessen werden können.
  • Multiplexverfahren, es kann also nur ein Kanal pro Zeit gewandelt werden.
  • 6 Spannungsbereiche (bzw. Verstärkungsbereiche) von +/- 256 mV bis +/- 6.144 V.
  • Alarmfunktion (Limitüberschreitung oder „Conversion ready“).
  • Wandlungsrate (Conversion Rate) von 8 s-1 bis 860 s-1 einstellbar.
    • Für den ADS1015: 128 s-1 bis 3300 s-1
  • Kontinuierlicher Modus oder Einfachmessungen („Single-Shot“).
  • Interne Spannungsreferenz.
  • Geringer Stromverbrauch: 150 µA im kontinuierlichen Modus, 2.5 µA im Power-Down Modus. Der ADS1115 / ADS1015 geht nach einer Single-Shot-Messung automatisch in den Power-Down Modus.

Weitere Informationen findet ihr im Datenblatt des ADS1115.

ADS1115/ADS1015 Module gibt es für wenige Euro in den meisten Online-Shops, z.B. hier bei Amazon.

Anscheinend gibt es auch ADS1115 Module, die keine 16 Bit Auflösung liefern, weil sie fälschlicherweise auf einem ADS1015 basieren. Auch gibt es ADS1015 Module, die fälschlicherweise aus ADS1115 Modulen basieren. Mit meinem Beispielsketch „Who_AM_I.ino“ könnt ihr prüfen, welcher Chip verbaut ist.

Typische Schaltung

Die unten abgebildete Schaltung habe ich für alle Beispielsketche verwendet. Anstelle der Potentiometer könnt ihr natürlich irgendetwas anderes an die Eingänge hängen. Zum Prüfen der Ergebnisse könntet ihr ein Multimeter verwenden oder ihr messt parallel mit den analogen Eingängen des Arduino.

Auf Pull-Ups an den I2C Leitungen konnte ich, wie schon erwähnt, verzichten. Falls ihr Probleme habt, dann ergänzt sie.

Beschaltung des ADS1115 - verwendet für alle Beispielsketche
Beschaltung des ADS1115 – verwendet für alle Beispielsketche

Ansteuerung mit der ADS1115_WE Bibliothek

Ihr könnt die Bibliothek über die Arduino Bibliotheksverwaltung oder hier von Github herunterladen.

Anhand von Beispielsketchen lässt sich der Umgang mit der ADS1115_WE Bibliothek am anschaulichsten erklären. Ich habe die Bibliothek mit vielen Beispielsketchen ausgestattet. Auf Single_Shot.ino gehe ich am intensivsten ein. Das meiste wiederholt sich dann in den anderen Sketchen. Die Sketche sind für den ADS1115 geschrieben. Die „Übersetzung“ für den ADS1015 ist einfach. Ihr findet dazu den Beispielsketch „Continuous_ADS1015.ino“ als Teil der Bibliothek.

Beispielsketch 1: Single_Shot.ino

Mit diesem Sketch messen wir die vier Eingänge im Single-Shot Modus (also auf Anfrage) nacheinander gegen GND. Die möglichen Parameter zu den Bibliotheksfunktionen sind in den Kommentaren im Sketch aufgelistet. Ein paar Anmerkungen zu den (hier relevanten) Funktionen:

  • ADS1115_WE adc = ADS1115_WE() erzeugt das ADS1115_WE Objekt. Ihr könnt alternative I2C Adressen und Busse übergeben. 
  • init() setzt die Einstellungen des Konfigurationsregisters des ADS1115 auf die Standardwerte zurück. Das hat den Vorteil, dass ihr den ADS1115 nicht vom Strom trennen müsst, wenn ihr Änderungen an eurem Sketch vornehmt. Das müsstet ihr sonst nämlich tun, um einen definierten Zustand der Register zu haben. init() prüft außerdem die Verbindung zum Modul. Die Funktion liefert false zurück, wenn der ADS115 nicht antwortet.
  • setVoltageRange_mV() bestimmt den Spannungsbereich in Millivolt. Je kleiner der Bereich, desto größer die Verstärkung und die Auflösung (= +/-range / 215).
  • setCompareChannels() legt die zu vergleichenden Kanäle fest.
  • setConversionRate() setzt die Wandlungsrate in Wandlungen pro Sekunde (SPS = Samples per second).
  • setMeasureMode() legt den Modus fest, also kontinuierlich oder Single-Shot. Letzteres ist die Voreinstellung.
  • startSingleMeasurement() startet eine Wandlung für die zuvor mit setCompareChannels() gewählten Kanäle.
  • isBusy() liefert true, solange die Wandlung noch nicht abgeschlossen ist. Ohne die Zeile while(adc.isBusy()){} würde der Wert aus der letzten Wandlung gelesen werden – und der stammt eventuell aus einem anderen Kanal. Es gibt nur ein Wandlungsregister (Conversion Register)!
  • getResult_V() liefert die Spannung in Volt. Für kleine Werte könnt ihr getResult_mV() verwenden.

Die anderen Funktionen werden erst relevant, wenn wir den Alarm Pin benutzen.

/***************************************************************************
* Example sketch for the ADS1115_WE library
*
* This sketch shows how to use the ADS1115 in single shot mode. 
*  
* Further information can be found on:
* https://wolles-elektronikkiste.de/ads1115 (German)
* https://wolles-elektronikkiste.de/en/ads1115-a-d-converter-with-amplifier (English)
* 
***************************************************************************/

#include<ADS1115_WE.h> 
#include<Wire.h>
#define I2C_ADDRESS 0x48

/* There are several ways to create your ADS1115_WE object:
 * ADS1115_WE adc = ADS1115_WE()             -> uses Wire / I2C Address = 0x48
 * ADS1115_WE adc = ADS1115_WE(I2C_ADDRESS)  -> uses Wire / I2C_ADDRESS
 * ADS1115_WE adc = ADS1115_WE(&wire2)       -> uses the TwoWire object wire2 / I2C_ADDRESS
 * ADS1115_WE adc = ADS1115_WE(&wire2, I2C_ADDRESS) -> all together
 * Successfully tested with two I2C busses on an ESP32
 */
ADS1115_WE adc = ADS1115_WE(I2C_ADDRESS);

void setup() {
  Wire.begin();
  Serial.begin(9600);
  if(!adc.init()){
    Serial.println("ADS1115 not connected!");
  }

  /* Set the voltage range of the ADC to adjust the gain
   * Please note that you must not apply more than VDD + 0.3V to the input pins!
   * 
   * ADS1115_RANGE_6144  ->  +/- 6144 mV
   * ADS1115_RANGE_4096  ->  +/- 4096 mV
   * ADS1115_RANGE_2048  ->  +/- 2048 mV (default)
   * ADS1115_RANGE_1024  ->  +/- 1024 mV
   * ADS1115_RANGE_0512  ->  +/- 512 mV
   * ADS1115_RANGE_0256  ->  +/- 256 mV
   */
  adc.setVoltageRange_mV(ADS1115_RANGE_6144); //comment line/change parameter to change range

  /* Set the inputs to be compared
   *  
   *  ADS1115_COMP_0_1    ->  compares 0 with 1 (default)
   *  ADS1115_COMP_0_3    ->  compares 0 with 3
   *  ADS1115_COMP_1_3    ->  compares 1 with 3
   *  ADS1115_COMP_2_3    ->  compares 2 with 3
   *  ADS1115_COMP_0_GND  ->  compares 0 with GND
   *  ADS1115_COMP_1_GND  ->  compares 1 with GND
   *  ADS1115_COMP_2_GND  ->  compares 2 with GND
   *  ADS1115_COMP_3_GND  ->  compares 3 with GND
   */
  //adc.setCompareChannels(ADS1115_COMP_0_GND); //uncomment if you want to change the default

  /* Set number of conversions after which the alert pin will assert
   * - or you can disable the alert 
   *  
   *  ADS1115_ASSERT_AFTER_1  -> after 1 conversion
   *  ADS1115_ASSERT_AFTER_2  -> after 2 conversions
   *  ADS1115_ASSERT_AFTER_4  -> after 4 conversions
   *  ADS1115_DISABLE_ALERT   -> disable comparator / alert pin (default) 
   */
  //adc.setAlertPinMode(ADS1115_ASSERT_AFTER_1); //uncomment if you want to change the default

  /* Set the conversion rate in SPS (samples per second)
   * Options should be self-explaining: 
   * 
   *  ADS1115_8_SPS 
   *  ADS1115_16_SPS  
   *  ADS1115_32_SPS 
   *  ADS1115_64_SPS  
   *  ADS1115_128_SPS (default)
   *  ADS1115_250_SPS 
   *  ADS1115_475_SPS 
   *  ADS1115_860_SPS 
   */
  //adc.setConvRate(ADS1115_128_SPS); //uncomment if you want to change the default

  /* Set continuous or single shot mode:
   * 
   *  ADS1115_CONTINUOUS  ->  continuous mode
   *  ADS1115_SINGLE     ->  single shot mode (default)
   */
  //adc.setMeasureMode(ADS1115_CONTINUOUS); //uncomment if you want to change the default

   /* Choose maximum limit or maximum and minimum alert limit (window) in volts - alert pin will 
   *  assert when measured values are beyond the maximum limit or outside the window 
   *  Upper limit first: setAlertLimit_V(MODE, maximum, minimum)
   *  In max limit mode the minimum value is the limit where the alert pin assertion will be 
   *  be cleared (if not latched)  
   * 
   *  ADS1115_MAX_LIMIT
   *  ADS1115_WINDOW
   * 
   */
  //adc.setAlertModeAndLimit_V(ADS1115_MAX_LIMIT, 3.0, 1.5); //uncomment if you want to change the default
  
  /* Enable or disable latch. If latch is enabled the alert pin will assert until the
   * conversion register is read (getResult functions). If disabled the alert pin assertion
   * will be cleared with next value within limits. 
   *  
   *  ADS1115_LATCH_DISABLED (default)
   *  ADS1115_LATCH_ENABLED
   */
  //adc.setAlertLatch(ADS1115_LATCH_ENABLED); //uncomment if you want to change the default

  /* Sets the alert pin polarity if active:
   *  
   * ADS1115_ACT_LOW  ->  active low (default)   
   * ADS1115_ACT_HIGH ->  active high
   */
  //adc.setAlertPol(ADS1115_ACT_LOW); //uncomment if you want to change the default
 
  /* With this function the alert pin will assert, when a conversion is ready.
   * In order to deactivate, use the setAlertLimit_V function  
   */
  //adc.setAlertPinToConversionReady(); //uncomment if you want to change the default

  Serial.println("ADS1115 Example Sketch - Single Shot Mode");
  Serial.println("Channel / Voltage [V]: ");
  Serial.println();
}

void loop() {
  float voltage = 0.0;

  Serial.print("0: ");
  voltage = readChannel(ADS1115_COMP_0_GND);
  Serial.print(voltage);

  Serial.print(",   1: ");
  voltage = readChannel(ADS1115_COMP_1_GND);
  Serial.print(voltage);
  
  Serial.print(",   2: ");
  voltage = readChannel(ADS1115_COMP_2_GND);
  Serial.print(voltage);

  Serial.print(",   3: ");
  voltage = readChannel(ADS1115_COMP_3_GND);
  Serial.println(voltage);

  delay(1000);
}

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;
}

 

Beispielsketch 2: Continuous.ino

In diesem Beispiel misst der ADS1115 kontinuierlich. Auch hier wechseln wir wieder die Kanäle. Der ADS1115 kann immer ausgelesen werden, egal, ob das Wandlungsregister nach dem letzten Lesen aktualisiert worden ist oder nicht. Wenn der Kanal gewechselt wurde, dauert es ungefähr zwei Konversionen bis ein frischer Messwert vorliegt. Um zu verhindern, dass die Werte aus dem vorherigen Kanal gelesen werden, wird bei einem Kanalwechsel ein auf die Datenrate abgestimmtes delay() eingefügt. Das macht die Bibliothek im Hintergrund.

Im Code ist der wesentliche Unterschied, dass die Messung nicht initiiert werden muss.

Hier abgebildet findet ihr nur die relevanten bzw. geänderten Zeilen. Die vollständigen Sketche ladet ihr mit der Bibliothek herunter.

  adc.setVoltageRange_mV(ADS1115_RANGE_6144); // wir nutzen wieder die ganze Range
  ....
  ....
  
  adc.setCompareChannels(ADS1115_COMP_0_GND); // Startwert - wir wechseln später den Kanal
  ....
  ....
  adc.setMeasureMode(ADS1115_CONTINUOUS); // hierwählen wir jetzt den kontinuierlichen Modus

void loop() {
  float voltage = 0.0;

  Serial.print("0: ");
  voltage = readChannel(ADS1115_COMP_0_GND);
  Serial.print(voltage);

  Serial.print(",   1: ");
  voltage = readChannel(ADS1115_COMP_1_GND);
  Serial.print(voltage);
  ....
  ....
  delay(1000);
}

float readChannel(ADS1115_MUX channel) {
  float voltage = 0.0;
  adc.setCompareChannels(channel);
  voltage = adc.getResult_V(); // wir holen einfach einen neuen Wert / kein startSingleMeasurement() notwendig
  return voltage;
}

 

Ansonsten müsste der Sketch sollte ohne weitere Erklärungen verständlich sein.

Beispielsketch 3: Single_Shot_Conv_Ready_Controlled.ino

Ich habe für den Sketch einen etwas sperrigen Namen gewählt. Übersetzt will ich damit sagen: der ADS1115 misst im Single-Shot Modus und steuert die Frequenz der Werteausgabe über die Anzahl der abgeschlossenen Messungen und die Wandlungsrate. Die Wandlungsrate (Conversion Rate) habe ich auf 8 SPS festgelegt. Alle 32 Messungen gibt der Sketch einen Wert aus, sprich alle 4 Sekunden. Ihr müsst natürlich nicht 32 Mal messen, um einen Wert zu lesen. Ich habe das nur gemacht, um die Ausgabe zu entschleunigen. Es soll erkennbar sein, dass die Ausgabe nicht über ein delay, sondern über die Datenrate gesteuert wird.

  adc.setVoltageRange_mV(ADS1115_RANGE_6144); 
  ....
  ....
  adc.setCompareChannels(ADS1115_COMP_0_GND); 
  ....
  ....
  adc.setConvRate(ADS1115_8_SPS); // 8 Wandlungen pro Sekunde
  ....
  ....
  //adc.setMeasureMode(ADS1115_CONTINUOUS); // auskommentiert, also greift der Standard (Single-Shot)
  ....
  ....
void loop() {
  float voltage = 0.0;
  for(int i=0; i<32; i++){ // warten bis 32 Messungen durchgeführt wurden
    adc.startSingleMeasurement();
    while(adc.isBusy()){}
  }
  voltage = adc.getResult_V(); // alternative: getResult_mV for Millivolt
  Serial.print("Channel 0 vs GND [V]: ");
  Serial.println(voltage);
  Serial.println("-------------------------------");
}

 

Beispielsketch 4: Conv_Ready_Alert_Pin_Controlled.ino

Noch sperriger Name. Auch dieser Sketch steuert die Ausgabefrequenz über die Anzahl der abgeschlossenen Wandlungen. Im Gegensatz zum letzten Beispiel fragt der Arduino die Vollendung einer Wandlung aber nicht über isBusy() ab, sondern der ADS1115 teilt dieses Ereignis über den Alarm Pin mit. Geht dieser auf LOW, wird ein Interrupt ausgelöst, der die Variable counter inkrementiert.

Auch hier gilt wieder, dass ich die 32 Messungen nur durchführe, um die Ausgabe zu verlangsamen.

Als neue Funktionen benutzt der Sketch:

  • setAlertPinMode() legt normalerweise fest, nach wie vielen Limitüberschreitungen der Alarm aktiv wird (1, 2 oder 4). Eigentlich ist das hier nicht relevant. Wenn ihr die Funktion aber nicht aufruft, greift die Voreinstellung (ADS1115_DISABLE_ALERT). Bedeutet: die Funktion muss für diesen Sketch mit einem beliebigen Parameter außer ADS1115_DISABLE_ALERT aufgerufen werden.
  • setAlertPol() bestimmt, ob der Alarm Pin im Alarmfall LOW oder HIGH ist. Die Voreinstellung ist LOW (ADS1115_ACT_LOW).
  • setAlertPinToConversionReady() teilt dem ADS1115 mit, dass der Alarm bei einer vollendeten Wandlung ausgelöst werden soll.

Der Conversion Ready Alarm funktioniert auch im kontinuierlichen Modus. Das gilt allerdings nur für den Alarm Pin, also die Hardware Abfrage. Die Software Abfrage über isBusy() funktioniert nur im Single-Shot Modus.

....
....
int interruptPin = 2;
volatile bool convReady = false;
....
....
void setup() {
  ....
  ....
  pinMode(interruptPin, INPUT_PULLUP);
  ....
  ....
  adc.setVoltageRange_mV(ADS1115_RANGE_6144); 
  ....
  ....
  adc.setCompareChannels(ADS1115_COMP_0_GND); 
  ....
  ....
  adc.setAlertPinMode(ADS1115_ASSERT_AFTER_1); // siehe Erklärung
  ....
  ....
  adc.setConvRate(ADS1115_8_SPS); //comment line/change paramater to change SPS
  .... 
  ....
  //adc.setMeasureMode(ADS1115_CONTINUOUS); // auskommentiert, also Single-Shot
  ....
  ....
  //adc.setAlertPol(ADS1115_ACT_LOW); // Der Alarm Pin geht LOW, wenn er aktiv ist
  ....
  ....
  adc.setAlertPinToConversionReady(); // der Alarm Pin wird auf Conversion Ready eingestellt
  ....
  ....
  attachInterrupt(digitalPinToInterrupt(interruptPin), convReadyAlert, FALLING); // Interrupt, wenn Alarm Pin auf Low geht, ISR = convReadyAlert
  adc.startSingleMeasurement(); // Messung wird gestartet 
}

void loop() {
  float voltage = 0.0;
  static int counter = 0;
  if(convReady){
    counter++;
    convReady = false;
    if(counter==32){  // counter ist 32, Wandlungsrate ist 8 SPS --> 4s
      voltage = adc.getResult_V(); 
      Serial.print("Channel 0 vs GND [V]: ");
      Serial.println(voltage);
      Serial.println("-------------------------------");
      counter = 0;
    }
    adc.startSingleMeasurement();   
  }
}

void convReadyAlert(){  // Interrupt Service Routine (ISR)
   convReady = true;
}

 

Beispielsketch 5: Alert_Window_Mode.ino

Jetzt kommen wir zu einer sehr hilfreichen Funktion des ADS1115, und zwar dem Alarm bei einer Limitüberschreitung. Damit könnt ihr sehr bequem Spannungen überwachen. Allerdings gibt es jeweils nur ein Register für das obere und das untere Limit und nicht für jeden Kanal. Wenn ihr die Limits für jeden Kanal individuell setzen wollt, dann müsst ihr das nach jedem Kanalwechsel erneut tun.

Der Alarm Pin wird dabei aktiv, wenn von euch festgelegte Limits über- bzw. unterschritten werden. Unter welchen Bedingungen der ADS1115 den Alarm Pin wieder deaktiviert, hängt von weiteren Einstellungen ab.

Folgende neue Funktion ist relevant:

  • setAlertModeAndLimit() :
    • Legt den Modus fest:
      • Mit ADS1115_WINDOW definiert ihr ein Fenster mit Maximum und Minimum. Bei Werten außerhalb des Limits wird der Alarm Pin aktiv. Ermittelt der ADS1115 danach Werte innerhalb des Limits, wird der Alarm Pin wieder inaktiv – es sei denn die Verriegelung (latch) ist aktiv.
      • Mit ADS1115_MAX_LIMIT wird der Alarm Pin nur dann aktiv, wenn das Maximum überschritten wird. Das Minimum ist der Wert, bei dem der Pin wieder inaktiv wird – es sei denn die Verriegelung ist aktiv.
    • Neben dem Modus übergibt die Funktion auch die Grenzwerte in Volt.

Was es mit der Verriegelung (latch) auf sich hat, erfahrt ihr in Beispielsketch 6. Im aktuellen Beispiel geht der Alarm Pin automatisch auf inaktiv, wenn die o.a. Bedingungen eintreten.

Hinweis: mit aktiviert und deaktiviert beziehe ich mich auf „Alarm an“ / Alarm aus“. Deaktiviert heißt nicht, dass der Alarm Pin funktionslos ist. Im Englischen kann man das klarer mit „asserted / not asserted“ ausdrücken.

....
....
int ledPin = 10; // eine LED, die einen Alarm anzeigt
volatile bool outOfLimit = false;
....
....

void setup() {
  ....  
  pinMode(interruptPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  ....
  ....
  adc.setCompareChannels(ADS1115_COMP_0_GND); 
  ....
  ....
  adc.setAlertPinMode(ADS1115_ASSERT_AFTER_1); // Alarm bei einer Überschreitung
  ....
  ....  
  adc.setMeasureMode(ADS1115_CONTINUOUS); // es macht Sinn eine Überwachung im Continous Modus laufen zu lassen
  ....
  ....
  adc.setAlertModeAndLimit_V(ADS1115_WINDOW, 3.0, 1.5); //you can change modes / limits
  ....
  ....
  //adc.setAlertPinToConversionReady(); //muss in diesem Beispiel auskommentiert werden
  ....
  attachInterrupt(digitalPinToInterrupt(interruptPin), outOfLimitAlert, FALLING);
}

void loop() {
  float voltage = 0.0;
  if(outOfLimit){
    voltage = adc.getResult_V();
    Serial.print("Voltage [V]: ");
    Serial.println(voltage);  
    digitalWrite(ledPin,HIGH);
    delay(1000);
    digitalWrite(ledPin,LOW);
    outOfLimit = false;
    attachInterrupt(digitalPinToInterrupt(interruptPin), outOfLimitAlert, FALLING); 
  } 
}

void outOfLimitAlert(){
  detachInterrupt(2);
  outOfLimit = true;
}

 

Beispielsketch 6: Alert_Window_Mode_with_Latch.ino

Dieser Sketch macht im Prinzip dasselbe wie Alert_Window_Mode.ino, nur dass hier die Verriegelung hinzukommt. Die Verriegelung verhindert, dass der Alarm Pin automatisch wieder deaktiviert wird.

Folgende neue Funktionen sind relevant:

  • setAlertLatch() aktiviert oder deaktiviert die automatische Verriegelung
  • clearAlert() deaktiviert den Alarm Pin. Die Verriegelung rastet beim nächsten Messwert außerhalb der Limits wieder ein. Alternativ zu clearAlert() könnt ihr getResult_V() oder getResult_mV() verwenden.
....
volatile int interruptPin = 2;
int ledPin = 10;
volatile bool outOfLimit = false;
....
....
void setup() {
  ....
  ....
  adc.setCompareChannels(ADS1115_COMP_0_GND);
  ....
  ....
  adc.setAlertPinMode(ADS1115_ASSERT_AFTER_1); // ...AFTER_2 oder ...4 ginge auch
  ....
  ....
  adc.setMeasureMode(ADS1115_CONTINUOUS); 
  ....
  ....
  adc.setAlertModeAndLimit_V(ADS1115_WINDOW, 3.0, 1.5); //ihr könnt auch die Max Limit Methode nehmen
  ....
  ....
  adc.setAlertLatch(ADS1115_LATCH_ENABLED); // Verriegelung wird aktiviert
  ....
  ....
  attachInterrupt(digitalPinToInterrupt(interruptPin), outOfLimitAlert, FALLING);
}

void loop() {
  float voltage = 0.0;
  if(outOfLimit){
    voltage = adc.getResult_V();
    Serial.print("Voltage [V]: ");
    Serial.println(voltage);  
    digitalWrite(ledPin,HIGH);
    delay(1000);
    digitalWrite(ledPin,LOW);
    outOfLimit = false;
    attachInterrupt(digitalPinToInterrupt(interruptPin), outOfLimitAlert, FALLING); 
    adc.clearAlert(); // der Alarm wird gelöscht; alternativ könnt getResult_V() / getResult_mV aufrufen
  } 
}

void outOfLimitAlert(){
  detachInterrupt(2);
  outOfLimit = true;
}

 

Nach der Ausgabe des out-of-limit Wertes und dem Warnleuchten der LED wird zunächst der Interrupt wieder scharfgeschaltet. Dann wird der Alarm mit clearAlert() deaktiviert, d.h. der Pin geht HIGH. Sofern die Out-of-Limit Bedingungen noch bestehen, wird sofort der nächste Interrupt ausgelöst. Als Folge leuchtet die LED fast durchgehend. 

Die Verriegelung gibt mehr Kontrolle, ihr müsst aber beim zeitlichen Ablauf aufpassen. Wenn ihr die Interruptfunktion einschaltet, nachdem ihr den Alarm deaktiviert habt, dann ist der Alarm Pin u. U. schon wieder Low und ein „Falling“ tritt entsprechend nicht mehr ein.

Beispielsketch 7: Auto_Range.ino

In diesem Beispielsketch stelle ich die Funktionen setAutoRange() und setPermanentAutoRangeMode() vor. Mit beiden wird – wie man sich denken kann – die Range automatisch eingestellt. Dazu macht setAutoRange() Folgendes:

  • Falls der ADS1115 im Single-Shot Modus ist, dann wechselt die Funktion in den Continuous Mode.
  • Es wird zunächst die maximale Range (+/- 6.144) eingestellt.
  • Ein Messwert wird aufgenommen.
  • Es wird dann die kleinstmögliche Range gewählt, in der der Messwert noch unter 80 % des Maximalwertes der Range liegt. Ich habe 80 % gewählt, damit gewisse Schwankungen nicht gleich zu einem Overflow führen.
  • Falls der ADS1115 ursprünglich im Single-Shot Mode war, wechselt er wieder dorthin zurück.

Die setAutoRange() Funktion macht nur dann Sinn, wenn die zu erwartenden Messwerte stabil sind oder sich nur langsam ändern. Ein Aufruf der Funktion nimmt eine Zeitspanne mehrerer Wandlungen in Anspruch. Habt ihr 8 SPS als Wandlungsrate eingestellt, dann mach sich das schon deutlich bemerkbar.

setPermanentAutoRangeMode() muss nur ein einziges Mal aufgerufen werden. Mit ihr stellt ihr ein, dass bei jedem Messwert geprüft wird, ob er im Bereich 30 – 80 % des Maximalwertes der aktuellen Range liegt. Nur wenn das nicht der Fall ist, wird im Hintergrund setAutoRange() aufgerufen. Dadurch ist die Funktion schneller. Verwendet die eine ODER die andere Funktion.

Um zu zeigen, dass der Wechsel der Range auch wirklich funktioniert, wird die Range im Beispielsketch auch ausgegeben.

....
  adc.setMeasureMode(ADS1115_CONTINUOUS); //comment line/change parameter to change mode
  .... 
  .... 
  adc.setPermanentAutoRangeMode(true);  //use either this or setAutoRange()
  ....
}

void loop() {
  float voltage = 0.0;
  
  Serial.print("Channel 0 - ");
  readChannel(ADS1115_COMP_0_GND);
  
  Serial.print("Channel 1 - ");
  readChannel(ADS1115_COMP_1_GND);   
  ....
  ....
  Serial.println("-------------------------------");
  delay(1000);
}

void readChannel(ADS1115_MUX channel) {
  float voltage = 0.0;
  adc.setCompareChannels(channel);
  //adc.setAutoRange(); //use either this or setPermanentAutoRangeMode(true)
  voltage = adc.getResult_V(); // alternative: getResult_mV for Millivolt
  printVoltageRange(); // this is just to show that the range is changing with changing voltages 
  Serial.println(voltage);
}
  
void printVoltageRange(){
  unsigned int voltageRange = adc.getVoltageRange_mV();
  Serial.print("Range: ");

  switch(voltageRange){
    case 6144:
      Serial.print("+/- 6144 mV, Voltage [V]: ");
      break;
    case 4096:
      Serial.print("+/- 4096 mV, Voltage [V]: ");
      break;
    case 2048:
      Serial.print("+/- 2048 mV, Voltage [V]: ");
      break;
    case 1024:
      Serial.print("+/- 1024 mV, Voltage [V]: ");
      break;
    case 512:
      Serial.print("+/- 512 mV, Voltage [V]: ");
      break;
    case 256:
      Serial.print("+/- 256 mV, Voltage [V]: ");
      break;
    default:
      Serial.println("Something went wrong");
  }
}

 

Ausgabe von Auto_Range.ino

So könnte dann eine Ausgabe des Sketches aussehen:

Beispielsketche ADS1115 -
Ausgabe von Auto_Range.ino
Ausgabe von Auto_Range.ino

Beispielsketch 8: Result_Format_Options

In diesem Sketch werden die verschiedenen Ausgabeoptionen für die Wandlungsergebnisse vorgestellt:

  • getResult_mV() liefert das Ergebnis in Millivolt
  • getResult_V() gibt das Ergebnis in Volt aus
  • getRawResult() liefert Rohwert, so wie er im Wandlungsregister steht
  • Mit getResultWith Range(x,y) wird der Rohwert auf die Spanne x bis y skaliert. Wenn ihr z.B. (-1023, 1023) wählt, bekommt ihr euer Ergebnis als 10 Bit Wert zurück.
  • Wenn ihr die letzte Funktion mit einem dritten Parameter in der Form getResultWithRange(x,y,z) aufruft, wird das Ergebnis zusätzlich auf einen Spannungsbereich skaliert. Der Parameter wird dabei in Millivolt angegeben. Der Aufruf mit den Parametern (-1023, 1023, 5000) würde euch den Wert liefern, den ihr mit analogRead() mit einem Arduino Uno in den Standardeinstellungen erhalten würdet.
 ....
  adc.setVoltageRange_mV(ADS1115_RANGE_6144); //comment line/change parameter to change range
  adc.setCompareChannels(ADS1115_COMP_0_GND); //comment line/change parameter to change channel
  adc.setMeasureMode(ADS1115_CONTINUOUS); //comment line/change parameter to change mode
  ....
  ....

void loop() {
  
  float voltageInMillivolt = adc.getResult_mV(); 
  Serial.print("Result in Millivolt          [mV]: ");
  Serial.println(voltageInMillivolt);

  float voltageInVolt = adc.getResult_V(); 
  Serial.print("Result in Volt                [V]: ");
  Serial.println(voltageInVolt);

  int rawResult = adc.getRawResult();
  Serial.print("Raw Result                       : ");
  Serial.println(rawResult);

  int scaledResult = adc.getResultWithRange(-1023, 1023);
  Serial.print("Scaled result                    : ");
  Serial.println(scaledResult);

  int scaledResultWithMaxVoltage = adc.getResultWithRange(-1023, 1023, 5000); 
  Serial.print("Scaled result with voltage scale : ");
  Serial.println(scaledResultWithMaxVoltage);

  unsigned int voltRange = adc.getVoltageRange_mV();
  Serial.print("Voltage Range of ADS1115     [mV]: ");
  Serial.println(voltRange);
  
  Serial.println("-------------------------------");
  delay(2000);
}

 

Ausgabe von Result_Format_Options.ino

Und so sieht eine Ausgabe des Result_Format_Option.ino Sketches aus:

Ausgabe Result_Format_Options.ino

Alle Funktionen auf einen Blick

Hier habe ich noch einmal alle Funktionen zusammengefasst. Die Tabelle ist in Englisch, da sie Teil meiner Dokumentation auf Github ist.

Eine Funktion habe ich noch nicht erwähnt, und zwar reset(). Sie benutzt den globalen I2C Reset (0x06). Alle I2C Geräte, die sich auf derselben Kommunikationsleitung befinden und auch auf die globalen Befehle (general calls) hören, führen bei Aufruf der Funktion einen Reset aus. 

Liste der (öffentlichen) Funktionen der ADS1115 Bibliothek
Liste der (öffentlichen) Funktionen der ADS1115 Bibliothek

Tiefere Einblicke in den ADS1115 / ADS1015

Ich beziehe mich in meinen Ausführungen auf den ADS1115. Die gute Nachricht ist: Die Register des ADS1015 sind fast identisch. Bei allen Registern, bei denen seine 12-Bit-Auflösung zum Tragen kommt, sind einfach die untersten 4 Bits auf 0 gesetzt.

Registerüberblick

Register des ADS1115
Register des ADS1115

Der ADS1115 hat eine sehr übersichtliche Registerstruktur. Alle Einstellungen – mit einer Ausnahme – nehmt ihr im Konfigurationsregister (Config Register) vor. Darauf gehe ich gleich noch näher ein.

Die Ergebnisse der Wandlungen ruft ihr im Wandlungsregister (Conversion Register) ab. Es gibt nur eines davon für alle vier Kanäle. Deswegen müsst ihr immer erst den Kanal wählen und dann warten, bis ein Ergebnis für diesen Kanal vorliegt. Das LSB für das Wandlungsregister ist:

LSB = \frac{Range}{2^{15}}

Wenn ihr die Range, so wie ich, in Millivolt definiert habt, rechnet ihr die Spannung in Volt dann folgendermaßen aus:

U \text{[V]} = \frac{LSB \cdot ConvRegister}{1000}

Da das Wandlungsregister vorzeichenbehaftet ist, kann es Werte von +215 bis -215 annehmen.

Das Lesen des Wandlungsregisters löscht einen Limitalarm, sofern der Alarm nicht „verriegelt“ (latch enabled) ist.

In die beiden Limitregister (Lo_thresh und Hi_thesh Register) tragt ihr die Grenzwerte ein, sofern ihr die Alarmfunktion nutzen möchtet. Die Inhalte der Limitregister vergleicht der ADS1115 mit dem Inhalt des Wandlungsregisters. Ändert ihr die Range, müsst ihr auch den Inhalt der Limitregister umrechnen. Meine Bibliothek macht das automatisch. Dadurch ist meine Funktion setVoltageRange() etwas komplex geworden.

Die einzige Einstellung, die ihr außerhalb des Konfigurationsregisters vornehmt (die Limits natürlich ausgenommen), ist die Festlegung des Alarms als „Conversion Ready Alarm“. Um diese Funktion zu aktivieren, müsst ihr in das MSB des Hi_thresh Registers eine „1“ und in das MSB des Lo_Thresh Registers eine „0“ schreiben. Da die Limitregister vorzeichenbehaftet sind, ist das obere Limit damit negativ und das untere positiv. Diese an sich sinnlose Bedingung sagt dem ADS1115, dass er den Alarmpin aktiv schalten soll, wenn eine Wandlung abgeschlossen ist. Die Bibliothek bietet dafür die Funktion setAlertPinToConversionReady().

Das Konfigurationsregister

Konfigurationsregister des ADS1115
Konfigurationsregister

Das Konfigurationsregister enthält die folgenden Bits:

  • COMP_QUE[1:0]: legt fest, nach wie vielen „out-of-range“ Messungen der ADS1115 den Alarmpin aktiviert. Alternativ schalten die Bits die Alarmfunktion aus. Die zugehörige Funktion lautet setAlertPinMode().
  • COMP_LAT aktiviert oder deaktiviert die Verriegelung des Alarms. Bei Verriegelung müsst ihr den Alarm manuell deaktivieren. Das COMP_LAT Bit wird mit setAlertLatch() gesetzt oder gelöscht.
  • COMP_POL legt die Polarität des Alarmpins fest -> setAlertPol().
  • COMP_MODE bestimmt den Alarmmodus. Entweder definiert ihr ein oberes Limit oder ein Fenster. Die zugehörige Funktion setAlertModeAndLimit() bestimmt den Modus und setzt die Limits.
  • DR[2:0] bestimmt die Wandlungsrate -> setConvRate().
  • MODE: legt den kontinuierlichen oder Single-Shot Modus fest -> setMeasureMode().
  • PGA[2:0] legt die Range fest -> setVoltageRange_mV();
  • MUX[2:0] wählt den Kanal bzw. die zu vergleichenden Kanäle -> setCompareChannels().
  • OS hat eine Doppelbedeutung. Beim Schreiben löst eine „1“ im Single-Shot Modus eine Messung aus -> startSingleMeasurement(). Beim Lesen bedeutet eine „0“, dass derzeit eine Wandlung stattfindet und eine „1“ das Gegenteil. Die Funktion dazu laut isBusy().

99 thoughts on “ADS1115 – A/D-Wandler mit Verstärker

  1. Servus Wolfgang!
    Ich würde gerne einen Bruchteil dessen verstehen, was du weißt. Deine Seite ist echt sehr hilfreich und bis jetzt konnte ich mich recht gut „durchwurschteln“ und es gelang mir bis jetzt auch alles. Doch nun stehe ich vor einem Problem, das ich nicht lösen kann. Es handelt sich um 1115_we.
    Ich habe keine Lösung gefunden, von 2 Karten die 8 Kanäle auszulesen. Ich verstehe leider zu wenig von der Materie, aber beim durchsuchen der *.h und *.ccp Datei sah ich, daß es diesbezüglich keine Möglichkeit gibt. Sehe ich das richtig? In einem Beispiel selbst mit 2 Karten sah ich jeweils nur einen Kanal und keine Möglichkeit, andere Kanäle anzusprechen. Wäre total nett, wenn du diesbezüglich darauf antworten könntest. Vielen herzlichen Dank und Gratulation für die super tolle Seite.
    Joe

    1. Hi Joe,
      erst einmal vielen Dank für das Lob! Und die gute Nachricht ist, dass es nicht schwierig ist, zwei ADS1115 Module anzusprechen. Wenn du vier Kommentare herunterscrollst, dann siehst du, dass Axel dieselbe Frage hatte. Ich habe dort mit einem kleinen Sketch gezeigt, wie du zwei ADS1115 initialisierst. Wichtig ist erst einmal, dass beide unterschiedliche I2C-Adressen haben. Wie du das über den Adresspin einstellst, steht im Beitrag. Und dann musst du für jeden der ADS1115 ein Objekt mit eigenem Namen erzeugen:
      ADS1115_WE adc_0 = ADS1115_WE(I2C_ADDRESS_0);
      ADS1115_WE adc_1 = ADS1115_WE(I2C_ADDRESS_1);
      Und dann kannst du beide getrennt voneinander ansprechen. D. h. du musst beide initialisieren und bei beiden, die Einstellungen vornehmen, die du haben willst. Und wenn du dann vom adc_0 z.B. die Spannung auslesen willst, dann tust du das mit adc_0.getVoltageRange_mV(); und für den adc_1 machst du dasselbe mit adc_1.getVoltageRange_mV();.
      Das ist einer der Vorzüge der objektorientierten Programmierung. Du hast eine Klasse (ADS1115_WE) als Schablone und kannst daraus beliebig viele Objekte erzeugen. Wenn du mehr darüber wissen willst, schau hier:
      https://wolles-elektronikkiste.de/bibliotheken-und-klassen-erstellen-teil-i

      1. Guten Morgen Wolfgang!
        Vielen Dank für die Antwort. Leider bin ich noch nicht schlauer geworden. Deine Beispiele habe ich mir angesehen und da hast du auch 2 Karten dabei und das verstehe ich ja noch ansatzweise. Ich weiß aber nicht, wie ich jeden Kanal ansprechen muss. Beim Beispiel „Single_Shot“ sagst du z.B.
        “ voltage = readChannel(ADS1115_COMP_0_GND); ….COMP_1_… usw.“ um die Spg. vom jeweiligen Kanal zu bekommen. Aber ich begreife bzw. sehe nicht, was ich ändern muss, um auf die andere Karte zu greifen und das gleiche zu machen. Beide Karten funktionieren, Adr. 48 & 49 ist gegeben. Auch die Angabe „ADS1115_WE adc_1 = ADS1115_WE(I2C_ADDRESS_1);“ verstehe ich ein klein wenig. Aber wo gebe ich an, dass ich jetzt die Spg. von adc_1 / Kanal3 haben möchte oder adc_0 / Kanal 3. Da stehe ich echt daneben. Da ich das in Deinem Beispiel „Using_two_ADS1115“ nicht finde sondern lediglich „voltage = adc_1.getResult_V();“ ohne Kanalangabe, da du nur einen Kanal definierst.
        Und ich hätte noch eine Bitte. Ich bin zwar logisch denkend, aber ich verstehe die Angaben zu dem ADS1115 nicht und aus dem Datenblatt werde ich einfach nicht schlau und habe diesbezüglich im www auch nichts verständliches gefunden.
        Wenn ich lese „FSR = ±6.144 V“ (FSR=Full scale range) , dann sehe ich eine Range vor mir mit eben + 6,144V bis – 6,144V.
        Das dem nicht so ist, wird einem spätestens im Datenblatt klar, da ich VinX nicht um VDD +0,3 überschreiten darf. Wobei VDD nicht größer als 5,5V sein soll. deshalb begreife ich nicht, wie man auf +-6,144V kommt, wie die anzuwenden wären und wie eigentlich das echte Verhältnis zwischen Analog Ein und der angezeigten Spg. ist. Da ich zuvor die Spg. mittels Arduino Every gemessen habe, habe ich dazu einen Spg. Teiler von 3/1. Das heißt bei max.15V IN bekomme ich 5V raus, die ich dann am Every gemessen habe und das hat auch funktioniert. Wenn ich nun den gleichen Spg. Ausgang an den ADS1115 anschliesse, bekomme ich die Spg. 1:1 angezeigt. Das heißt, schicke ich über den Spg. Teiler 1,3V rein, dann kommt am ADS 0,439V an den Kanal und es wird mir die echte Spg. von 1,3V am Monitor angezeigt. Das verwirrt mich komplett, weil ich darin die Logik nicht erkennen kann, was es dann mit den 6,144V eigentlich zu tun hat. Was ich bis jetzt herausgefunden habe, ist, wenn ich das Messergebnis rechnerisch umsetze: „0,439V / 1,3V * 6,144“ dann kommt 2,07V raus, was heißen würde, dass bei einer angelegten Spg. von genau 2,048V mir der ADS theoretisch 6,144V anzeigt und ich letztendlich nicht max. 5V Vin anlegen kann, sondern nur 2,048V. Kannst du mir bitte dabei helfen, das logisch zu verstehen, denn das ist für mich echt verwirrend. ( Messung immer gegen Ground)
        Vielen Dank!

        Joe

        1. Hi Joe,
          wenn du programmiertechnisch noch nicht so sehr erfahren bist, dann ist der für dich verständlichste Weg wahrscheinlich wie folgt:

          adc_0.setCompareChannels(ADS1115_COMP_0_GND); // adc_0 / Kanal 0 vs GND
          adc.startSingleMeasurement();
          while(adc.isBusy()){}
          voltage = adc.getResult_V();
          Serial.println(voltage);

          adc_0.setCompareChannels(ADS1115_COMP_1_GND); // adc_0 / Kanal 1 vs GND
          adc.startSingleMeasurement();
          while(adc.isBusy()){}
          voltage = adc.getResult_V();
          Serial.println(voltage);

          Und so machst du weiter mit Kanal 2 vs GND (ADS1115_COMP_2)_GND, dann Kanal 3 vs. GND. Du erkennst das Muster?

          Und dann wiederholst du den Code, ersetzt aber adc_0 durch adc_1. Wie gesagt, programmiertechnisch nicht sehr schön, aber pragmatisch. Probiere das erst einmal.

          Es schreit quasi nach einer Funktion, die den Code verkürzt. Aber ich bitte um Nachsicht, dass ich im Rahmen von Kommentaren keine kompletten Programme zur Verfügung stellen kann. Dazu fehlt mir schlicht die Zeit.

          Aufgrund der Randparameter wirst du tatsächlich keine 6,144 Volt messen können. FSR = Full Scale Range heißt, dass die 16 Bit des A/D-Wandlers im größten Messbereich +/- 6,144 Volt repräsentieren. Diese Information braucht man, um die Rohwerte in Spannungen umzurechnen. 2^15 entspricht also +6,144 Volt. -2^15 entspricht -6,144 Volt.

          Zum letzten Punkt:
          „Das heißt, schicke ich über den Spg. Teiler 1,3V rein, dann kommt am ADS 0,439V an den Kanal und es wird mir die echte Spg. von 1,3V am Monitor angezeigt.“
          Dafür habe ich keine andere Erklärung als einen Fehler bei der Schaltung. Wenn am Eingang 0,439 V anliegen, dann werden die auch ausgegeben, unabhängig von der Range (natürlich nur, solange die Range die angelegte Spannung abdeckt).

          Wenn du möchtest, dann kannst du mir ein Foto von deinem Aufbau schicken (wolfgang.ewald@wolles-elektronikkiste.de), dann aber bitte eines (oder auch mehrere) auf dem zu erkennen ist, was wo angeschlossen ist. Und bitte den Sketch, es sei denn, du hast einen Beispielsketch 100% unverändert eingesetzt.

          VG, Wolfgang Ewald

  2. Hallo Wolfgang,

    Super LIB , Vielen Dank für die Arbeit.
    Dennoch komme ich gerade nicht weiter, dazu würde ich zu machen Zeilen einfach mehr Kommentar brauchen:
    Ich habe jetzt 2 Versionen erfolgreich getestet und am laufen nun würde ich das verbinde und krieg es nicht so ganz hin.
    Mein Ziel wäre:
    a) Im Continius-Mode – klappt
    b) 2 Kanäle abwechselnd auslesen – klappt auch
    c) Comp_0_GND als RAW-Wert und Comp_2_3 als Volt-Wert -da kommt bei mir nichts vernünftiges bei raus derzeit ??

    Irgendwie sehe ich den Wald vor lauter Bäumen nicht mehr, kleiner Tip wäre da wohl hilfreich.
    Danke schon mal für die Antwort

    VGA

    1. Hallo Armin,

      im Continuous Mode:

      // Comp_0_GND als RAW-Wert:
      adc.setCompareChannels(ADS1115_COMP_0_GND);
      int raw = adc.getRawResult();
      Serial.println(raw);
      
      //Comp_2_3 als Volt-Wert:
      adc.setCompareChannels(ADS1115_COMP_2_3);
      float voltage = adc.getResult_V();
      Serial.println(voltage)
      

      Wenn das nicht hilft, dann schicke mir mal deinen Sketch zu (Wolfgang.Ewald@wolles-elektronikkiste.de).

      VG, Wolfgang

      1. Hallo Wolfgang,

        perfekt läuft!!
        Ich nutze das zukünftig zur Überwachung eines Akkus.
        ESP8266 + ADS1115+WCS1700
        Es kann jetzt in die Feinjustierung gehen.
        Herausforderung wird noch das Timing, es sollte ja ziemlich genau jede Sekunde ein Wert ermittelt werden. (WattSekunden).

        // Comp_0_1 als RAW-Wert:
        adc.setCompareChannels(ADS1115_COMP_0_GND1);
        int raw = adc.getRawResult();
        Serial.print(„raw: „);
        Serial.println(raw);

        //Comp_2_3 als Volt-Wert:
        adc.setCompareChannels(ADS1115_COMP_2_3);
        float voltage = adc.getResult_V();
        Serial.print(„volt: „);
        Serial.println(voltage);

        float Mamp = 0.0; // Umrechnung raw in Ampere (WCS 1700)
        float rawf = raw;
        Mamp = ((rawf)-20000)/256;
        Serial.print(„A: „);
        Serial.println(Mamp, 3);

        float energy =0.0;
        energy = voltage * Mamp;
        Serial.print(„Ws: „);
        Serial.println(energy, 3);

        load = load + energy;
        Serial.print(„Ladung: „);
        Serial.println(load, 3);
        ….

        Danke für die schnelle RM

        VG Armin

  3. Hallo Wolfgang,
    erstmal: riesengroßes ( !!! ) Lob für Ihre Arbeit, Ihren Einsatz und Ihr Engagement – eine echte Hilfe!

    Klar gibt / gab es auch ein Problem 😉 ( und vielleicht eine Idee dazu von Ihnen? ); aber soeben auch eine inzwischen gefundene Lösung.

    Habe 2 ADS1115 im Einsatz, der erste (0x48) wird erfolgreich initialisiert, der zweite (0x49) hingegen nicht.
    Nehme ich den ersten durch auskommentieren raus (s.u.), wird der zweite aber erfolgreich angesprochen.

    Umgebung: RPI Pico W, I2C via Pin Arduino 1.8.19, ADS1115_WE 1.5.1; „Who-am-I“ identifiziert 2 x ADS1115, I2C Scan findet beide Chips.

    Hatte ein Library-Problem vermutet; aber eben gefunden: ein delay(50); zwischen den beiden „inits“ wirkt Wunder…

    beste Grüße !

    1. Hi Axel,

      einen RPI Pico W habe ich nicht, um das mal 1:1 nachzuvollziehen. Auf einem Arduino Nano geht es ohne delay, also z.B. so:

      #include<ADS1115_WE.h> 
      #include<Wire.h>
      #define I2C_ADDRESS_0 0x48
      #define I2C_ADDRESS_1 0x49
      
      ADS1115_WE adc_0 = ADS1115_WE(I2C_ADDRESS_0);
      ADS1115_WE adc_1 = ADS1115_WE(I2C_ADDRESS_1);
      
      void setup() {
        Wire.begin();
        Serial.begin(9600);
        if(!adc_0.init()){
          Serial.println("ADS1115 0 not connected!");
        }
        else{
          Serial.println("ADS1115 0 successfully connected!");
        }
        if(!adc_1.init()){
          Serial.println("ADS1115 1 not connected!");
        }
        else{
          Serial.println("ADS1115 1 successfully connected!");
        }
      }
      
      void loop() {}
      

      Die Serial-Anweisungen sorgen auch für ein gewisses Delay. Aber auch wenn ich die Erfolgsmeldungen herauskommentiere, erhalte ich kein „not connected“.

      Ist wohl eine Besonderheit dieses (und weiterer?) Boards bzw. der auf ihm implementierten Wire Bibliothek.

      Also, vielen Dank für den Hinweis, der anderen viel Zeit sparen könnte!

      VG, Wolfgang

  4. Hallo Wolfgang, bin eben auf deine Erklärung deiner Lib für den ADS1115 gestoßen. Sehr sehr anschaulich und gut durchdacht. Klasse. Bin kein ausgebildeter Elektroniker und habe deshalb eine Verständnisfrage:
    ich möchte ein Messgerät bauen, dass eine Spannung von 0 bis 3 V messen kann. Das möglichst schnell und möglichst genau. Wie muss ich den Sketch für einen ESP32+ADS115 aufbauen, damit mir das bestmöglichst gelingt?
    Negative Spannungen kommen nicht vor. Ich bräuchte eigentlich kein Range von z.B. +/- sondern von 0V bis 3V.

    Viele Grüße
    Markus

    1. Hallo Markus, ich schaffe es nicht ganze Sketche zu für andere zu schreiben, da fehlt mir die Zeit. Aber vielleicht kann ich dich zumindest in die richtige Richtung lenken. Wenn du nur an brauchst, stellst du den mit adc.setCompareChannels() im Setup ein. Als Range wählst du ADS1115_RANGE_4096. Die schnellste Messung erreichst du mit ADS1115_860_SPS, aber es ist auch die ungenaueste.
      Im Single Shot Mode würdest du dann die Messungen mit den folgenden Zeilen initialisieren und abfragen:

      adc.startSingleMeasurement();
      while(adc.isBusy()){}
      voltage = adc.getResult_V();

      Oder du gehst in den Continuous Mode, dann kannst du die Messwerte zu jedem Zeitpunkt abfragen.

      Die I2C Pins liegen beim ESP32 Dev Board auf SDA = 21 und SCL = 22

      Oder habe ich das Problem nicht verstanden?

      VG, Wolfgang

      1. Hallo Wolfgang,
        Erstmal vielen Dank für die Antwort. Natürlich erwarte ich kein komplettes Sketch von dir zu meinem Problem.

        Genauigkeit hat Vorrang vor Geschwindigkeit. Da ich die Betriebsspannung von 3,3 V für den ADS1115 vom ESP32 abgreifen möchte und ich eine max. Spannung am A0 vom ADS1115 messen möchte, wäre also der Range so, wie du ihn vorgeschlagen hast: ADS1115_RANGE_4096. Wie genau kann ich dann die zu digitalisierende Spannung erfassen?

        Ist die zu messende Spannung im Continuousmode so genau, wie beim Singleshot?
        Der Continuousmode wäre an sich glaube ich besser, da ich eine Änderung der Spannung möglichst genau (und schnell) erfassen, verarbeiten und anzeigen möchte.

        Viele Grüße
        Markus

        1. Hallo Markus,
          theoretisch ist die Auflösung bei einer Range +/-4096 mV Range: 4096 / 215 = 0,125 mV. Praktisch gibt es ein gewisses Rauschen. Das hängt nicht zuletzt von der Qualität deiner Spannungsversorgung und auch dem Rauschen der zu messenden Spannung ab. Auch die Messrate spielt eine Rolle. Grundsätzlich ist eine kleine Messrate besser als eine hohe. Kleinere Abweichungen kann es auch auf einem Steckbrett geben. Die Erfahrung zeigt, dass die Kontakte auf einem Steckbrett nicht immer vernachlässigbar sein. Versuch macht klug! Spiele einfach mal herum.

          Ob kontinuierlich oder single-shot spielt für die Genauigkeit keine Rolle. Hier ein Minimalsketch:

          #include<ADS1115_WE.h> 
          #include<Wire.h>
          #define I2C_ADDRESS 0x48
          
          ADS1115_WE adc = ADS1115_WE(I2C_ADDRESS);
          
          void setup() {
            Wire.begin();
            Serial.begin(9600);
            adc.init();
            adc.setVoltageRange_mV(ADS1115_RANGE_4096); 
            adc.setCompareChannels(ADS1115_COMP_0_GND); 
            adc.setConvRate(ADS1115_128_SPS); 
            adc.setMeasureMode(ADS1115_SINGLE); 
          }
          
          void loop() {
            adc.startSingleMeasurement();
            while(adc.isBusy()){}
            float voltage = adc.getResult_V();
            Serial.print("Volage [V]: ");
            Serial.println(voltage);
            delay(1000);
          }
          
          1. Hallo Wolfgang,

            herzlichen dank für die Infos und Hilfestellung. Das hat mir geholfen 🙂

            Nun kam mir eine neue Idee: könnte ich das zu messende Signal auch mit dem AD-Wandler HX711, wie man ihn bei dem Bau einer Waage nutzen und als fertige Platine bekommen kann? Allerdings weiß ich jetzt nicht, wie man die Eingänge der Platine für die Messung einer Gleichspannung von 0 bis 3 V nutzen kann. Geht wahrscheinlich nicht direkt, oder? Der wurde speziell für Wägezellen mit Wheatstone-Brücke konzipiert., so liest man das immer wieder. Der HX711 hat ja 24bit und könnte somit noch feiner Auflösen.

            Hättest du mir einen Tipp, wie man das schaltungstechnisch dennoch hinbekommen könnte ?

            Viele Grüße
            Markus

            1. Hallo Wolfgang,

              nun habe ich nach dem Abschicken meiner Frage bzgl. des HX711 deinen Beitrag über den 24bit-Wandler ADS1220 gefunden. Der Einsatz dieser Platine liegt natürlich näher. Somit ist meine letzte Frage bzgl. des HX711 hinfällig 🙂

              Das ist dein Beitrag, den ich gefunden habe: https://wolles-elektronikkiste.de/ads1220-4-kanal-24-bit-a-d-wandler

              Ich werde wohl als Referenzspannung die Versorgungsspannung des ESP32 mit 3,3 V nehmen. Dann sollte ich eine Spannung zwischen 0 und 3V messen können, richtig?

              Viele Grüße
              Markus

              1. Hallo Markus,
                ja, der HX711 ist speziell zum Ansteuern von Wheatstone-Brücken entwickelt worden, siehe Datenblatt: https://cdn.sparkfun.com/datasheets/Sensors/ForceFlex/hx711_english.pdf.

                Der ADS1220 hat ein breiteres Anwendungsspektrum. Der ADS1220 misst gegen eine Referenzspannung, entweder intern (2,048 Volt) oder eine externe Referenzspannung. Dieses Verhältnis ermittelt er sehr genau. Schwankungen der Referenzspannung wirken sich nicht auf das Verhältnis aus, wohl aber auf die absoluten Spannungen, die du daraus berechnest. Das heißt, für absolute Spannungsmessungen gibt die Genauigkeit deiner Referenzspannung die Genauigkeit der Messwerte vor. Auch die interne Referenzspannung ist mit 2,045 bis 2,051 Volt spezifiziert. Besser sieht es mit der Referenzspannung des ESP32 auch nicht aus. Da bist du SEHR weit davon entfernt, die 24 Bit auszunutzen. Seine Stärken spielt der ADS1220 in vergleichenden Messungen aus. Dazu gehören Wheatstone Brücken und Widerstandsthermometer (RTCs und NTCs). Darauf bin ich hier eingegangen:
                https://wolles-elektronikkiste.de/ads1220-teil-2-anwendungen

                Für absolute Spannungsmessungen bist du mit einem ADS1115 besser versorgt.

                VG, Wolfgang

  5. Hi Wolle.
    … und wieder einmal kriege ich aus Deiner Kiste hier wertvolles und liebevoll aufbereitetes Wissen. Deine Seite ist ein Segen neben den abertausend teils wirren Daten im Netz. Danke.
    Kleiner Hinweis zur Vollständigkeit. Ich habe im Netz auch ADS1x15 gefunden, die einen speziellen i2c Stecker haben (STEMMA QT / Qwiic). Auch dafür wird Dein Treiber funktionieren. Wenn Du die Zeit aufbringen kannst, könntest Du ein Bild dieser ADS ergänzen und auch Nutzern mit den Dingern Deinen Code nahe legen.
    Danke nochmal. Grüße. Jo.

    1. Hi Jo,

      ja, ich habe neulich mal den neuen UNO R4 WiFi, der ja so eine QWIIC Schnittstelle hat, mit einem entsprechenden ADS1115 probiert und es hat funktioniert. Muss ich mal ergänzen – guter Hinweis. Beim UNO R4 muss man allerdings Wire1 als Schnittstelle wählen. Noch ein Grund das zu ergänzen.

      VG, Wolfgang Ewald

  6. Hallo Wolle Ich mag Deinen Ansatz schöne Homepage.
    Eine Frage habe ich. Wenn ich den ADS1115 an einen UNO anschließe ist die Ausgabe Ok. Wenn ich den ADS3788
    mit einem ESP32 betreibe. Habe ich eine etwas geringeren Output. Ich dachte das Ding hat eine eigene Referenz Spannung. Wo ist mein Denkfehler.

    1. Hallo, die maximale zu messende Spannung kann nicht höher sein als die Versorgungsspannung. Wenn du den ADS1115 allerdings einmal mit 3.3 V und einmal mit 5 V betriebst und sagen wir einmal 1 V Messspannung anlegst, dann solltest du beide Male dasselbe Ergebnis erhalten. Wenn das nicht der Fall ist, dann würde ich am ehesten vermuten, dass irgendwo ein Widerstand in der Schaltung ist, wo keiner sein soll, was auf Breadboards leicht passieren kann (z.B. zwischen dem GND Pin des ADS1115 und dem GND der zu messenden Spannungsquelle). Danach würde ich mal schauen und auch alternativ mal Differenzmessungen (also z.B. A1 gegen A0) probieren. Ich habe ansonsten bisher nicht von versorgungsspannungsabhängigen Abweichungen gehört oder selbst welche festgestellt. Über welche Differenzen reden wir?

  7. Hallo,
    Ist es möglich den Alarm Pin bei überschreitung eines gewissen RAW-Wertes zu aktivieren? oder funktioniert der Befehl setAlertModeAndLimit() : nur mit den Volt?
    Gruss Sven

    1. Hallo, die „Währung“ meiner Bibliothek ist Volt. Insofern ist eine direkte Angabe der Limits als RAW-Wert nicht vorgesehen. Wenn du einen RAW – Wert hast, den du als Limit verwenden möchtest, kannst du ihn einfach in Volt umrechnen und dann übergeben. Dabei ist zu beachten, dass der RAW Wert abhängig von der Range ist.

      Wert_in_Volt = RAW_Wert * Range_in_mV / (1000 * 32768)

      also z.B. RAW = 16000 bei einer Range von +/-6144 mV => Wert_in_Volt = 16000 * 6144 / (32768 * 1000) = 3.0

      Da die Eingabe als RAW ein wahrscheinlich eher seltener Wunsch ist, würde ich dafür keine extra Funktion in der Bibliothek implementieren wollen.

      1. ich danke dir für deine Antwort. Da ich mit zwei verschiedenen Ranges messe, wäre es einfacher gewesen den alert pin nach raw wert anzusteuern. jedenfalls hab ich jetzt gewissheit dass es nur mit den volt möglich ist 😉

  8. Hallo,

    ich habe mal eine Frage zur Parameterübergabe.
    Ich habe mehrere Objekte, denen ich den zu messenden Port als „channel“ übergebe. Der Einfachheit halber wird beim erstellen der Objekte als channel uint16_t -> 0x4000 – 0x7000 definiert. Genau so mache ich es mir den anderen Parametern.
    Beim Testaufbau mit einem MEGA2560 funktioniert das problemlos. Auf dem Inkplate-Board mit ESP32 kommt jedoch eine Fehlermeldung beim kompilieren.

    „invalid conversion from ‚uint16_t‘ {aka ’short unsigned int‘} to ‚ADS1115_MUX‘ [-fpermissive]“

    Was für einen Datentyp erwartet denn die ADS1115_WE.cpp?
    Ich verstehe zwar die Definition der Zuweisung der Werte zum Wortkonstrukt in der Bibo, aber das Handling in diesem Fall ist mir eine Rätsel.

    1. Hallo, der Datentyp für Channel lautet ADS1115_MUX. Das ist in ADS1115_WE.h definiert:

      typedef enum ADS1115_MUX{
          ADS1115_COMP_0_1   = 0x0000,
          ADS1115_COMP_0_3   = 0x1000,
          ADS1115_COMP_1_3   = 0x2000,
          ADS1115_COMP_2_3   = 0x3000,
          ADS1115_COMP_0_GND = 0x4000,
          ADS1115_COMP_1_GND = 0x5000,
          ADS1115_COMP_2_GND = 0x6000,
          ADS1115_COMP_3_GND = 0x7000
      } mux;
      

      Bei einigen Boards scheint der Compiler drüber hinwegzusehen, wenn man stattdessen uint16_t verwendet, bei anderen Boards eben nicht.

      1. 🙂 Hab ich gesehen, aber nicht verstanden.
        Als Datentyp kenne ich int, float, char, …
        Auf das wäre ich nie gekommen, funktioniert jetzt aber.
        Danke Dir ….

        1. Das ist keine Lücke, dass du den Datentyp nicht kennst – er ist ja über die typedef enum Definition individuell von mir definiert. Einfacher wäre es, wenn ich die Channel über #define definiert hätte, also ohne eigenen Datentyp. Dann gibt es aber viele andere, die mich (zu recht) darauf hinweisen würden, dass das nicht „Best Practice“ ist. Eigentlich sollte man sogar enum Klassen verwenden, aber das ist für den normalen Arduino Anwender noch verwirrender. Da ist die Lösung mit typedef enum ein Kompromiss.

  9. Hallo,

    ich noch mal ….
    Habe heute getestet und ob der Werte erst mal dumm geschaut. Irgend etwas beim Single haut nicht hin.

    Ausgabe Single-Shot:
    0: 0.97 1: 1.07 2: 0.98 3: 1.52
    0: 5.99 2: 6.09 3: 6.10 3: 6.02 : 0 -> 3.01, 1-3 offen

    Continuous:
    0: 3.01 1: 2.78 2: 3.01 3: 2.78 -> korrekte Werte
    0: 3.01 1: 0.93 2: 0.94 3 0.93 : 0 -> 3.01, 1-3 offen

    Zieht man die offenen Ports auf Masse wird bei continuous korrekt 0.00 angezeigt, single zeigt -0.05

    1. Sonst bin ich immer sehr vorsichtig, was den Ausschluss möglicher Fehler auf meiner Seite angeht, aber hier kann ich mit Sicherheit sagen, dass auch die Single Shot Funktion richtig funktioniert.

      Hast du den originalen Beispielsketch verwendet? Wenn nein, dann solltest du das als Erstes probieren. Ein Verdrahtungsfehler kann ja ausgeschlossen werden, sonst würde die kontinuierliche Messung auch nicht funktionieren.

      Typische Fehler wären, dass der Teststart adc.startSingleMeasurement(); fehlt oder das Warten auf die Fertigstellung der Umwandlung mit while(adc.isBusy()){}.

      1. Nein, alles da, habe den Sketch direkt aus den Beispielen aufgerufen.
        Irgend etwas mit der Wartezeit funktioniert nicht.
        Komisch, while wartet ja eigentlich nur bis Bit 15 im Register auf 1 geht und der Knilch fertig sein sollte.
        Ein delay(10) vor while liefert korrekte Ergebnisse, hinter while bringt es gar nichts? Auch komisch ….

        Übrigens, schöne Seite, lerne als Anfänger ziemlich viel.

        1. Für mich ist das so erst einmal nicht nachvollziehbar. Bei funktioniert es problemlos. Ich habe es eben noch einmal ausprobiert. Arduino IDE 1.8.19 und 2.0.3 getestet, Arduino Nano, ADS1115_WE Version 1.4.3, Single_Shot.ino / Continuous.ino, Sketche so wie sie sind, ohne jegliche Anpassung -> immer dieselben Ergebnisse.
          Ich kann nur anbieten, dass du mir noch einmal ganz genau deinen Aufbau beschreibst, also was ist mit was verbunden, welchen Mikrocontroller nutzt du, welche Stromquelle, was hängt genau an A0-A3, usw. Auch gerne Fotos. Ich werde dann versuchen, es möglichst 1:1 nachzustellen. Das ist dann aber nichts für die Kommentarfunktion. Bitte an: wolfgang.ewald@wolles-elektronikkiste.de.
          Und was steht auf dem kleinen 10-beinigen IC auf dem Modul?

  10. Hallo,

    mir ist aufgefallen, dass im ersten Sketch alle vier Kanäle gegen GND gemessen werden sollen.
    Folgende Zeile ist aber auskommentiert:
    //adc.setCompareChannels(ADS1115_COMP_0_GND); //uncomment if you want to change the default

    und als default steht weiter oben
    * ADS1115_COMP_0_1 -> compares 0 with 1 (default).
    Die restliche drei Kanäle werden überhaupt nicht gesetzt.
    Ist das nicht notwendig?

    Viele Grüße

  11. Hallo Wolfgang

    Vielen Dank für die ausführliche Beschreibung und Dein Repo dazu… Ich ziehe den Hut vor Dir… Respekt..
    Da ich Neuling bin auf dem Gebiet I2C versuche ich mich gerade in das Thema einzuarbeiten.
    Mein Ziel ist es 12 Sensoren im Wertebereich 0-5 Volt am Arduino Mega einzulesen. Dazu verwende ich die Matlab/Simulink Umgebung.
    Da gibt es verschiedene Blöcke welche mit dem I2C Bus kommunizieren können.

    Der Gedanke hier einfach die Adresse des I2C Device einzutragen funktioniert aber so einfach leider nicht. Es bedarf einiger Optionen und Einstellungen damit man hier die AIN0-3 jeweils lesen kann.

    Dazu habe ich folgende Fargen:

    1. Muss ich als Erstes einen Request an das Device senden und diesem mitteilen, welchen Modus es verwenden soll? Ich habe gelesen es gibt [SINGLE_SHOT] & [CONTINUES] als s.g Sende Mode? Ist das korrekt?

    2.Wenn ich es richtig verstehe, dann muss ich gezielt die Register Adresse ansprechen, welche ich abfragen möchte. Korrekt?

    3. Wo finde ich diese Register Adresse im Datenblatt? Ich habe den Aufbau der Nachricht noch nicht ganz verstanden!

    Vielen Dank vorab und ich hoffe die Fragen sind nicht all zu dumm formuliert.

    Vg Denny

    1. Hallo Denny,
      danke für’s Lob!
      Also, wenn du 12 Sensoren hast, dann brauchst du 3 ADS1115 Module, denn jedes Modul hat 4 Kanäle (A0-A3). Jedes Modul braucht seine eigene I2C Adresse, damit es gezielt ansprechen muss. Du kannst 4 verschiedene Adressen einstellen, indem du den Adresspin mit GNG, VCC, SDA oder SCL verbindest. Die Adressen stehen oben im Beitrag (Kapitel: Ein-/Ausgänge des ADS1115). Ob es geklappt hat, kannst du mit einem I2C-Scanner Sketch prüfen – Link steht auch oben.
      Um das Datenblatt und die Register musst du dich nicht kümmern – obwohl ein Blick ins Datenblatt auch nie schaden kann! Bibliotheken sind dazu da, dass man sich NICHT mit Registern beschäftigen muss. Wenn du z.B. den Single Shot oder Continuous Mode wählen möchtest, dann scheibst du:
      adc.setMeasureMode(ADS1115_CONTINUOUS) bzw. ads.setMeasureMode(ADS1115_SINGLE);
      …sofern das anzusprechende Modul „ads“ und damit kommen wir zum nächsten Punkt, denn für jedes Modul musst du ein eigenes Objekt erzeugen:
      ADS1115_WE name_deines_Moduls = ADS1115_WE(I2C_Adresse_deines_Moduls);
      Die Namen sind frei wählbar, du könntest dein Modul auch „kartoffelsalat“ nennen 😉
      Wenn du weiter herunterscrollst zum Kommentar von Reiner, dann steht da noch ein bisschen mehr.

      Mein Rat: Alles in Schritten einwickeln. Nimm erst einmal einen Sensor und einen ADS1115. Dann versuche 4 Sensoren und einen ADS1115. Für beides kannst du meine Beispielsketche nehmen. Spiele ein bisschen mit den Parametern und lerne so die Bibliothek und die Bauteile kennen. Wenn man gleich alles auf einmal zusammenbaut und programmiert, dann wird man als Anfänger ziemlich sicher scheitern, da man ein komplexes System vor sich hat, dass viele Fehlermöglichkeiten hat.
      Viel Erfolg, Wolfgang

  12. Hallo Wolfgang

    Vergesst mein vorherigen Kommentar. Alles gefunden was ich brauche.!!!
    Genaueres Lesen hilft 😉

    mfG
    Veit

  13. Hallo Wofgang
    Ich suche schon länger um den Eingamgswiderstand für die Eingänge A0-A3 heraus zufinden. Hintergrund ist, daß ich um höhere Spannung (24VDC) zu messen einen Spannungsteiler davor setzen will. Wenn hier ein „geringer“ Innenwiderstand vorliegt muss ich das in die Berechnung des Gesamt-Meßstrom mit einbeziehen.
    Egal wo ich suche ein klare Aussage finde ich nicht. Vielleicht bin ich aber auch zu bl….

    mfG
    Veit

  14. Hallo!
    Ich muss mich als absoluter Neuling outen. 🙂

    Mein erstes „Projekt“:
    Betrieb eines AHT20 (Temperatur/Feuchtigkeit) an einem Pi4:
    Das habe ich hinbekommen, dennoch wohl nicht viel von der Materie verstanden (war wohl zu einfach).
    Ich kann aber sauber über die Konsole die Daten auslesen und in meinen Bashscripten weiterverarbeiten.

    Mein neues Ziel am Pi4:
    Ich würde gerne mit dem ADS1015 bzw. ADS1115 die Betriebsspannung vom Raspberry auslesen.

    Wenn ich das richtig verstanden habe, kann ich wohl die Betriebsspannung direkt an A0 anschließen, wenn der ADS1015/1115 mit der gleichen Spannung betrieben wird. So hätte ich ja direkt den Wert. Korrekt?

    Falls ja, frage ich mich, wie ich eine Unterspannung zuverlässig erkenne. Ich kann ja nicht sekündlich den ADS1015/1115 abfragen (kann ich schon, aber zielführend ist es eher nicht). Kann der A/D-Wandler sich Werte merken bzw. sich melden, wenn ein Wert X unterschritten wird?

    Viele Grüße
    Robert

    1. Hallo Robert,
      zur ersten Frage: Ja, du kannst die Betriebsspannung direkt an A0 anschließen. Und zur zweiten Frage: Auch ja, du kannst einen Alarmwert bzw ein Fenster einstellen. Wenn das verlassen wird, gibt es ein Signal am ALERT Pin. Das könntest du bequem an einem Interruptpin auswerten. Steht im Beitrag unter Beispielsketch 5.

      Meine Bibliothek läuft allerdings nur unter C++/Arduino. Ich gehe davon aus, dass du Python benutzt. Ich habe auch eine Version für MicroPython geschrieben:
      https://github.com/wollewald/ADS1115_mpy
      Aber keine für Python.
      Hoffe, das hilft.
      VG, Wolfgang

      1. Hallo Wolfgang,
        vielen Dank. Ich werde mich dann mal ran wagen. 🙂
        Viele Grüße
        Robert

  15. Hallo Wolfgang,

    vielen Dank für die tolle Library, welche für mich prima funktioniert. Mir ist nun aufgefallen, dass beim Beispiel „Single Shot“ die Pins D20 und D21 bei jeder Messung kurz auf LOW-Pegel geschaltet werden. Dies ist insofern ungünstig, weil ich mit diesen GPIOs andere Signale schalte.

    Kann dies abgeschaltet werden? Mache ich etwas falsch?

    Grüße

    1. Hallo Dominik,
      ein bisschen mehr Information brauche ich schon. Welches Board benutzt du? D20 und und D 21 klingt so nach den I2C Pins des Arduino MEGA 2560. Und in der Tat sind die I2C Pins ja auch die einzigen Pins die meine Bibliothek in irgendeiner Form schaltet. Wolltest du die I2C Pins für etwas anderes benutzen? Wie gesagt, das müsstest du noch genauer erklären.
      VG, Wolfgang

      1. Hi,

        vielen Dank für die schnelle Rückmeldung. Tut mir leid, das Problem lag wohl zwischen den Ohren. Mir war nicht bewusst, dass beim Mega wohl SDA1/SCL1 direkt mit Pin 20 und Pin 21 verbunden sind…. 😉

        Liebe Grüße

    2. Hallo Wolfgang,

      okay, tut mir leid, ich bin noch nicht so lange in der Arduino-Welt unterwegs. Mit scheint, dass Pin 20 zur I2C Routine gehört, welche den Port (Pin D20/D21) abfrägt, obwohl ein anderer I2C-Port genutzt wird. Von daher gehört meine Frage nicht zu diesem Beitrag sondern ist eine eher allgemeine Frage.

      Also ziehe ich meine Frage zurück, bleibe aber bei meiner Aussage, dass die Bibliothek toll funktioniert.

      Vielen Dank.

      Grüße

  16. Hallo Wolfgang,
    kannst Du mir helfen. Deine Seite ist super.
    Problemstellung
    Ich habe einen ADS1115 an einem ESP8266 angeschlossen. Die Messung über die A0 bis A3 das ADS1115 funktioniert mit dem Sketch Single_Shot.ino korrekt. Jetzt möchte ich 4 ADS1115 über den I2C Bus am ESP8266 parallel betreiben, dabei natürlich nacheinander auslesen. Die notwendige Anpassung im Sketch ist mir unklar. Die Adressierung habe ich vorbereitet:
    #define I2C_ADDRESS1 0x48
    #define I2C_ADDRESS2 0x49
    #define I2C_ADDRESS3 0x4A
    #define I2C_ADDRESS4 0x4B
    Leider kann ich im void reconnect() nur eine Adresse zuordnen. Zum Beispiel
    ADS1115_WE adc = ADS1115_WE(I2C_ADDRESS1);
    Wie spreche ich aber den nächsten ADS1115 an? Der Eintrag ADS1115_WE adc = ADS1115_WE(I2C_ADDRESS2);
    erzeugt einen Kompilerfehler.
    Mit freundlichen Grüßen Reiner

    1. Hi Reiner,
      ADS1115_WE ist die Klasse, also die Blaupause aus der du Objekte erzeugen kannst. Die ADS1115 Module mit den unterschiedlichen Adressen sind unterschiedliche Objekte, die du unterschiedlich benennen musst. Also:
      ADS1115_WE adc1 = ADS1115_WE(I2C_ADDRESS1);
      ADS1115_WE adc2 = ADS1115_WE(I2C_ADDRESS2);
      ADS1115_WE adc3 = ADS1115_WE(I2C_ADDRESS3);
      ADS1115_WE adc4 = ADS1115_WE(I2C_ADDRESS4);
      Selbst, wenn es ohne diese Änderung kompilieren würde – woher sollte der ESP8266 wissen, welchen ADS1115 du meinst, wenn du sie alle gleich nennst? Also, nur ein kleiner Eingriff. Und nicht vergessen, auch die Adresse über den Adresspin an den Modulen einzustellen.
      Vielleicht kannst alles auch etwas kompakter programmieren, wenn du die ADS1115_WE Objekte als Array anlegst:
      ADS1115_WE adc[4];
      Und dann im setup:
      adc[0] = ADS1115_WE(AD1115_I2C_ADDRESS1);
      adc[1] = ADS1115_WE(AD1115_I2C_ADDRESS2);
      usw.
      Dann kannst du später Messwerte über for-Schleifen abfragen:
      voltage[i] = adc[i].getResult_V();

      Viel Spaß! VG, Wolfgang

      1. Danke, entsprechend Deiner Information kann ich jetzt alle 4 ADS1115 auslesen.

        MfG Reiner

      2. Danke für deine Arbeit Wolle und das du dir so eine Mühe machst.
        Deine Lib hilft mir gerade unheimlich während der Masterarbeit :-).

  17. Hello Wolfgang,

    After looking for an ADS1115 library ; your library stands out from the others.

    I’m using your library as parent class for child class. The child class is a wrapper class for a dedicated board with 4 X 0-5V ,+5V and ground . The ADS1115 is used in single-ended mode, continous sampling and fixed 1,024 Volts range.

    I save many hours by using your library.

    As ADS1115_WE is a parent class, I modify private: to protected: and using it as:
    class ADC_4CH_IO : protected ADS1115_WE

    During a Arduino test code , the following appreas:

    1) No noise up ADS1115_32_SPS.
    2) One of the 2 cascaded delay has been commented to speed up the one shot of simultaneous 4 inputs reading in a for loop.

    if(!(currentConfReg & 0x0100)){ // => if not single shot mode
    convRate rate = getConvRate();
    delayAccToRate(rate);
    //delayAccToRate(rate);
    }
    By commemting one delay or leaving it,; no deterioration of the accuray, noise or missing convertion has been observed.

    Question: Is it mandotary to leave the two cascaded delay?

    Note the library is used with a ESP32 MCU.

    Grüße

    1. Hi Micheal,

      the reason for the delays is to ensure that you don’t read „fresh“ data after you have initiated the conversion or changed channel. The time needed to get the new data depends on the rate. Under certain conditions it takes the time of two conversions. But I would say just try it.

      Best wishes, Wolfgang

  18. Hallo lieber Wolfgang,
    ich versuche schon den ganzen Tag hinter die Funktionen zu blicken.
    Leider verstehe ich es nicht genau, auch im Forum konnte mir bisher keiner helfen.
    Nun wende ich mich an dich.
    Ich würde gerne deine Lib zum ADS1115 nutzen, aber 4 einzelne Varibalen für den ADC 0 bis 3 haben.
    Ich bekomme das einfach nicht hin. Auch wie deine Funktion hinter dem Void Loop genau funktioniert , verstehe ich nicht.
    Ich wollte dann die ADS1115 Adafriut Lib nutzen, die habe ich verstanden und konnte auch die einzelnen ADCs als einzele Variable deklarieren und nutzen, jedoch ist die ADAfruit Lib zum ADS1115 nicht kompatibel mit der RTC und dem OLED auf meinem Board. Jedesmal wenn die ADAfriut lib den ADS1115 aufruft, rebootet mein Arduino, bzw. ESP8266 –> Unter der Adafruit Lib :
    Nur der ADS1115 = ok
    Nur RTC und OLED = ok
    Alles zusammen = Reboot, sobald der ADS aufgerufen wird.
    Also ist die ADAfruit Lib nicht das was ich brauchen kann.

    Lieber Wolfgang, vielleicht kannst du mir ein einfaches Beispiel mit deiner ADS1115 lib zukommen lassen,
    in welchem ich die ADC 0-3 Kanäle einzelnen abfragen und als Variablen
    ADS0, ADS1, ADS2, ADS3 nutzen kann. Das wäre voll nett.

    Liebe Grüsse
    Rainere

    1. Hallo Rainer,

      Ich habe gerade kein vollständiges Beispiel, aber die einzelnen Kanäle abzufragen, ist kein Problem. Wichtig ist vielleicht zu wissen, dass der ADS1115 nur einen A/D-Wandler hat. Und für diesen stellt man ein welche Eingänge man vergleichen will (Multiplexing). Also z.B. 0 gegen Gnd, 1 gegen Gnd, 0 gegen 1, usw.
      Ich gehe davon aus, dass du die Eingänge 0, 1, 2 und 3 jeweils gegen Gnd messen willst. Das würde dann im Continuous Modus in etwa so aussehen:
      adc.setCompareChannels(ADS1115_COMP_0_GND);
      voltage_0 = adc.getResult_V();
      adc.setCompareChannels(ADS1115_COMP_1_GND) ;
      voltage_1 = adc.getResult_V();
      adc.setCompareChannels(ADS1115_COMP_2_GND);
      voltage_2 = adc.getResult_V();
      adc.setCompareChannels(ADS1115_COMP_3_GND) ;
      voltage_3 = adc.getResult_V();

      Wenn Du für jeden Kanal andere Einstellungen brauchst, z.B. unterschiedliche Messbereiche, dann musst du nach jedem Kanalwechsel die entsprechende Einstellung erneut vornehmen. Denn wie schon geschrieben, es gibt nur einen Wandler, alle Einstellungen gelten für diesen einen Wandler und du stellst ein welche Eingänge verglichen werden sollen.

      Hoffe das hilft. Viel Erfolg, Wolfgang

      1. Hallo Wolfgang,
        erstmal sorry für die sehr späte Antwort.
        Und ein liebes Danke für deine Beispiele.
        Ja , sie funktioniren gut, nur leioder „kolidiert“
        deine Lib mit der meiner RTC.
        Will sagen. Biede als einzelnes geht.
        Beide zusammen und es geht nichts mehr.
        Ich hatte das Thema schon und hatte eine RTC lib rausgefiltert, die mit meinen I2C Sensoren und
        dem OLED TFT gehen, es gab schon einige RTC Libs , die dann mit meiner Umgebung nicht klar kamen.
        Oh man, das ist jetzt echt schade….
        ABer deine ADS1115 Lib behalte ich im hinterkopf, für künftige Projekte.
        Oder es gibt mal einen schlauen kopf, der Weis warum die 2 Libs RTC und Deine sich nicht „mögen“
        Liebe Grüsse
        Rainer

        1. Wie äußert sich denn die Kollision? Gibt es eine Fehlermeldung? Von da aus könnte ich dann vielleicht etwas machen.

          1. Hallo Wolfgang,
            Danke für Dein Angebot zum nachschauen.
            Nein eine direkte Fehlermeldung gibt es nicht.
            Die RTC zeigt „Müll“ und die ADS Werte sind = null.
            Ich werde die Tage aus meinem Programm „nur“ mal 4 I2C- Devices ( Sensor, OLED TFT, RTC, ADS1115) in einen einzelnen Sketch setzen und sukzesive reduzieren.
            Dann kann ich dir ganz genau berichten, was kollidiert… Vielleicht bleiben ja nur 2 Libs übrig und dort kann man im *.h nachschauen woran es liegt….
            Eines davon ist ja die lib von dir, da ohne diese alles geht. Die andere Suche ich raus.
            Ich vermute eben die RTCm lib, da ich hier schon viele Probleme mit dem I2C TFT hatte.
            Ich habe lange gesucht und probiert bis das OLED TFT mit der RTC auf dem I2C Bus funktionierten , bzw. muss schon fast sagen hamonierten.

  19. Hallo ich habe eine Frage zum ADS1115. Ich muss einen Datenlogger basteln und habe dafür einen Adafruit Feather MO, ein ADS 1115 , einen Sauerstoffsensor und jetzt soll noch bei den geloggten Daten der Zeitstempel angezeigt werden. Dafür würde ich den Featherwing Addalogger nehmen.
    Mein Problem ist aber, dass sowohl der ADS 1115 und der Addalogger an den SCL und SDA Port des Feathers angeschlossen werden müssen.
    Wie kann ich über einen Analog/ Digitalwandler Messwerte mit Zeitstempel auf eine SD- Karte loggen?
    Kann mir jemand helfen?

    1. Hallo Julia, du kannst mehrere I2C Geräte an die SDA/SCL Leitung hängen, sofern die Geräte unterschiedliche I2C Adressen haben. Über die Adresse weiß das jeweilige Gerät, „dass es gemeint ist“. Und da sich am ADS1115 vier verschiedene Adressen einstellen lassen, solltest du kein Problem haben. VG, Wolfgang

  20. Hallo Wolfgang,
    ist es möglich das die Eingänge bei Differenzmessung gegen A3 vertauscht sind?

  21. Hello Wolfgang! I am trying to take advantage of the nice work you have done, but I’ve run into a slight issue, probably due to my lack of understanding of a function. I have 2 ADS1115 devices in my system, adc0 = 0x48, and adc1 = 0x49. It seems only the adc1 device is providing real measurements, while the adc0 device provides values as if all inputs are open. (I test one input at a time.)

    I do this in the setup:

    adc0.setVoltageRange_mV(ADS1115_RANGE_6144); //options 4096, 2048, 1024, 0512, 0256
    adc0.setConvRate(ADS1115_8_SPS);
    adc0.setMeasureMode(ADS1115_SINGLE);
    adc0.setCompareChannels(ADS1115_COMP_0_GND);
    adc0.setCompareChannels(ADS1115_COMP_1_GND);
    adc0.setCompareChannels(ADS1115_COMP_2_GND);
    adc0.setCompareChannels(ADS1115_COMP_3_GND);

    and in the loop, I do this:

    float a00, a01, a02, a03, a10, a11, a12, a13, vref;

    a00 = readChannel(0, ADS1115_COMP_0_GND);
    a01 = readChannel(0, ADS1115_COMP_1_GND);
    a02 = readChannel(0, ADS1115_COMP_2_GND);
    a03 = readChannel(0, ADS1115_COMP_3_GND);

    I modified your „readChannel“ function to have an index, either 0 or 1, depending on adc0 or adc1.

    float readChannel(int i, ADS1115_MUX channel)
    {
    float voltage = 0.0;

    if (i=0) {
    adc0.setCompareChannels(channel);
    adc0.startSingleMeasurement();
    delay(200);
    while (adc0.isBusy()){}
    voltage = adc0.getResult_V();
    return voltage;
    }

    if (i=1) {
    adc1.setCompareChannels(channel);
    adc1.startSingleMeasurement();
    delay(200);
    while (adc1.isBusy()){}
    voltage = adc1.getResult_V();
    return voltage;
    }
    }

    Can you see what I am doing wrong?

    Thank you kindly for any advice.

    1. Hello Robert,

      I don’t see any obvious bugs. Have you setup the modules correctly regarding the address pins?

      You don’t need to call all the setCompareChannels(); in the setup. The ADS1115 is a multiplexing system, and therefore only the last call defines which channel is active. But this has nothing to do with the issue.

      Your modified readChannel() appears correct to may. The delay(200) should not be needed, but also this has nothing to do with the issue.

      Can you send the complete sketch to me (Wolfgang.Ewald@wolles-elektronikkiste.de)? Maybe the issue is somewhere in the rest of the code. Best wishes, Wolfgang

  22. Hallo Wolfgang,
    Hast Du ein Gespür wie schnell man den Gain (on the fly) anpassen kann?
    Ich muss 2 Kanäle mit festem, aber unterschiedlichen, Gain pro Kanal auslesen.
    Kann ich in loop()

    adc.setVoltageRange_mV(ADS1115_RANGE_6144);
    adc.setCompareChannels(ADS1115_COMP_0_GND);
    Chan0V = adc.getResult_V() ;

    adc.setVoltageRange_mV(ADS1115_RANGE_256);
    adc.setCompareChannels(ADS1115_COMP_1_GND);
    Chan1mA = 10 * adc.getResult_mV();

    direkt hintereinander ausführen?
    oder braucht es delays() dazwischen?
    Danke.

    Laszlo

    1. Hallo Laszlo,

      du hattest die Frage auch auf GitHub gestellt und ich habe sie da beantwortet. Aber vielleicht interessiert es andere auch. Also ich würde es folgendermaßen machen:

      1. ausgehend von den Beispielsketchen die Funktion zum Auslesen erweitern:

      float readChannel(ADS1115_MUX channel, ADS1115_RANGE range) {
      float voltage = 0.0;
      adc.setCompareChannels(channel);
      setVoltageRange_mV(range);
      voltage = adc.getResult_V(); // alternative: getResult_mV for Millivolt
      return voltage;
      }

      2. die Funktion dann folgendermaßen aufrufen:
      adc.readChannel(ADS1115_COMP_0_GND, ADS1115_RANGE_6144);
      bzw.
      adc.readChannel(ADS1115_COMP_1_GND, ADS1115_RANGE_256);

      Du kannst es aber auch direkt so machen, wie du es vorgeschlagen hast. In einem meiner letzten Updates der Bibliothek habe ich für Kanalwechsel im Continuous Mode ein automatisches delay eingefügt. Vorher war es in der Tat notwendig ein delay einzufügen. Jetzt musst du dich nicht mehr darum kümmern.

      VG, Wolfgang

  23. Hallo Wolfgang,

    vielen Dank für die tolle Library! Diese funktioniert(e) bis heute Problemlos. Meinem Windmesser konnte ich zuverlässig die Windrichtung entlocken.

    Nach ca. einem Monate 24/7 haben sich allerdings die Werte seit mehr als 2 Stunden nicht mehr geändert. Strom aus/an hat geholfen und es ging weiter. Ob jetzt ein HW oder SW Probleme Problem vorlag – egal.

    Gibt es eine einfache Möglichkeit die „ordnungsgemäße Funktion“ zu überwachen? Ich nutze den ADS1115_CONTINUOUS Modus.
    GGf. mit einem Aufruf (is_busy) – wenn kein „false“ innerhalb 200 Millisekunden erscheint, gibt es ein Problem und das Programm kann versuchen einen adc_init() auszulösen.

    Hast Du eine besser Idee?
    VG Werner

    1. Hallo, ich denke die Überprüfung über die isBusy() Funktion ist eine gute Idee. Eine andere Möglichkeit wäre ein Watchdog Timer. Da habe ich mal etwas zu gemacht: Watchdog Timer. Der Vorteil ist, dass automatisch ein Reset durchgeführt wird, wenn das Programm hängt. VG, Wolfgang

  24. Hallo Wolfgang!
    Ein sehr interessanter Bericht zum ADS1115. Vielen Dank dafür schon mal.
    Ich hänge aktuell allerdings an dem Problem den ADS1115 über einen weiteren I2C-Anschluss anzusteuern.

    Ich nutze die Adafruit_ADS1015.h
    SDA und SCL Pins habe ich mit 21 bzw. 13 beim ESP32 definiert.

    Ansprechen möchte ich den ADS über die gleiche Funktion wie ich es auch mit einem BME280 und BH1750 schon gemacht habe:

    void startADS1115() {
    ads.setGain(GAIN_TWOTHIRDS);
    bool status3 = ads.begin(0x48, &I2Cone);
    if (!status3) {
    Serial.println(„Could not find a valid ADS1115 sensor, check wiring!“);
    display.setCursor(0, 10);
    display.clearDisplay();
    display.print(„Could not find valid ADS1115 sensor, check wiring!“);
    display.display();
    while (1);

    Als Fehlermeldung erhalte ich dann allerdings folgendes:

    no matching function for call to ‚Adafruit_ADS1115::begin(int, TwoWire*)‘

    Hast du eine Idee woran das liegen könnte?
    Habe mehrere Foren durchforstet – leider ohne Erfolg.
    Dann bin ich auf deine Seite gestossen.
    Vielleicht ist das Problem ja dir bekannt?!

    Beste Grüße,
    Nico

    1. Hallo Nico, das funktioniert nicht, weil die Adafruit Bibliothek keine begin Funktion hat, der man Parameter übergeben könnte. In Adafruit_ADS015.h ist die begin Funktion definiert als

      void begin(void);

      Richtig wäre also
      ads.bgin();
      ads.setGain(GAIN_TWOTHIRDS);

      Die I2C Adresse ist in der Bibliothek festgelegt, du musst sie nicht übergeben. Wenn du sie ändern möchtest musst du das direkt in Adafruit_ADS015.h, Zeile 31 tun:

      #define ADS1015_ADDRESS (0x48)

      Am besten fährt man meistens, wenn man auf den mitgelieferten Beispielsketchen aufbaut.

      Viel Erfolg!

  25. Hi,
    erstmal klasse Arbeit!
    So wie ich das bisher verstanden habe, ist das auslesen differential zB zwischen A0 und A1 nicht möglich, oder?

    1. Hi Dominic,

      danke für das freundliche Feedback. Und doch, es geht! Ich habe hier nur die gekürzten Sketche abgedruckt, damit es nicht unübersichtlich wird. Wenn du die Bibliothek herunterlädst und in die Beispiele gehst, dann findest du dort die erlaubten Parameter für die Funktionen. Die Kanäle, deren Differenz gemessen werden soll, stellst du mit adc.setCompareChannels() ein. Das sind die Parameter:

      /* Set the inputs to be compared
      *
      * ADS1115_COMP_0_1 -> compares 0 with 1 (default)
      * ADS1115_COMP_0_3 -> compares 0 with 3
      * ADS1115_COMP_1_3 -> compares 1 with 3
      * ADS1115_COMP_2_3 -> compares 2 with 3
      * ADS1115_COMP_0_GND -> compares 0 with GND
      * ADS1115_COMP_1_GND -> compares 1 with GND
      * ADS1115_COMP_2_GND -> compares 2 with GND
      * ADS1115_COMP_3_GND -> compares 3 with GND
      */
      adc.setCompareChannels(ADS1115_COMP_0_GND); //comment line/change parameter to change channel

      Also 0/1 geht zum Beispiel, aber nicht jede Kombination wie z.B. 0/2.

  26. Hallo Herr Ewald,

    ich habe zwei Fragen.
    a) Wie kann ich in der kontinuierlichen Methode alle Kanäle abfragen?
    und
    b) Wie kann ich ihre wunderbare Bibliothek so einsetzen, dass ich bei einem ESP32 zwei I2C Schnittstellen nutzen kann, also insgesamt acht ADS1115 einsetzen kann, also somit 32 Messkanäle nutzen kann,

    Beste Grüsse Jost

    1. Hallo,
      zu a) der ADS1115 hat nur ein Datenregister in dem die Wandlungsergebnisse abgelegt werden. Das heißt es kann nur ein Kanal zu einer bestimmten Zeit ausgewertet werden. Neue Ergebnisse überschreiben die alten Ergebnisse. Der Wechsel des Channels erfolgt mit adc.setCompareChannels(). Das Problem dabei ist, dass das Datenregister nach einem Kanalwechsel noch so lange den Messwert des vorherigen Kanals enthält, bis ein frischer Messwert des aktuellen Kanals vorliegt. Wie lange das dauert, hängt von der eingestellten Konversionsrate ab. Das heißt im Zweifelsfall muss man ein ausreichendes delay einbauen. Ich empfehle deswegen eher den Trigger Mode denn bei diesem lässt sich mit while(meinObjekt.isBusy()) abwarten bis die aktuelle Konversion fertig ist. Oder man prüft alternativ mit dem alertPin (data ready).
      zu b) ich befürchte, dass ich dafür meine Bibliothek modifizieren muss. Das würde jetzt etwas weit führen, im Detail auszuführen, warum das so ist. Kurz ausgedrückt gibt ist nur ein Wire Objekt vorgesehen. Da kann ich leider kurzfristig nicht helfen.
      VG, Wolfgang

  27. Hi,

    Good reading and useful library, I tend to agree, www is full of Arduino stuff, but most of it is over simplified and misses details or assumes loads of prior knowledge, not too much systematic material.

    Sorry my Deutsch it too rusty for any practical use 😀

    Best Regards

    Urmas Joost

    1. Many thanks, always great to get feedback and even better when it’s positive! Stay healthy, Wolfgang

  28. Eigentlich genau was ich gesucht habe um auf dem ADS1115 Full Speed zu kriegen um Strommesszangen SCT013 zu messen. Leider schmiert die Lib aufm ESP 8266 ab 🙁 Irgendeine Idee dazu?

    WEMOS Board an D1/D2 connectiert. Die Adafruit Lib funktioniert.

    1. Spontan kann ich dir das nicht beantworten. Ich muss das in den nächsten Tagen selbst ausprobieren. Eine Fehlermeldung gibt es nicht?

      1. Das Ding ist im Bootloop. Einfach mit deinem Beispiel:

        ADS1115 Example Sketch – Single Shot, Conversion Ready (isBusy) controlled

        ————— CUT HERE FOR EXCEPTION DECODER —————

        Soft WDT reset

        >>>stack>>>

        ctx: cont
        sp: 3ffffd50 end: 3fffffc0 offset: 01a0
        3ffffef0: 3ffee560 00000001 3ffee560 40202a08
        3fffff00: 60000314 00000100 00000000 40202acc
        3fffff10: 00000001 00000002 3ffee560 40202c70
        3fffff20: 00000048 00000048 000a0d00 40203815
        3fffff30: 3ffe86ab 00000000 000a0d00 3ffee520
        3fffff40: 00000001 3ffee378 00000002 40202e74
        3fffff50: 40201848 3ffee4b8 3ffe86a9 4020170c
        3fffff60: 40201801 3ffee4b8 3ffee384 40201737
        3fffff70: 40201848 3ffee4b8 3ffee384 4020119b
        3fffff80: 3ffee378 3ffee378 00000008 40201474
        3fffff90: 3fffdad0 3ffee378 00000008 402010c3
        3fffffa0: 3fffdad0 00000000 3ffee4e0 40202150
        3fffffb0: feefeffe feefeffe 3ffe84e8 40100e11
        <<<stack<<<

        ————— CUT HERE FOR EXCEPTION DECODER —————

        ets Jan 8 2013,rst cause:2, boot mode:(3,6)

        load 0x4010f000, len 3664, room 16
        tail 0
        chksum 0xee
        csum 0xee
        v39c79d9b
        ~ld

        1. OK, also mit „freundlichen Grüßen vom Watchdog Timer“ (Soft WDT Reset). Es wundert mich ein wenig, weil der Watchdog eigentlich erst nach 3s „Nichtstun“ zuschlägt. Ich habe dein Problem aber nachvollziehen können. Die Lösung lautet:

          In der while-Schleife in Zeile 17: while(adc.isBusy()){} fütterst du den Watchdog mit ESP.wdtFeed(); , also:
          while(adc.isBusy()){ ESP.wdtFeed(); }

          Ich habe diese Lösung selbst ausprobiert.

          1. Danke. Das mit dem Watchdog beim ESP hatte ich mir noch gar nicht so genau angesehn und das jetzt mal zum Anlass genommen :). Gut zu wissen.

    1. Hi Gustavo, the ADS1115 is a multiplexing device. It has only one ADC, one interrupt pin, one register for the interrupt limits, etc. That means you have to switch between the channels and when there’s an interrupt you know it’s from the active channel. I would not recommend using two channels in continuous mode because you read what’s currently in the conversion register which might „old“ data. If you need different thresholds for the two channels it becomes even more complex. Then you have to change the threshold every time you change the channel. In that case, I would prefer to not use the interrupt and read and compare „manually“, i.e. by your sketch. It would be great if the ADS1115 could switch the channels automatically but this function does not exist. Hope this helps.

  29. Guten Morgen Wolle,
    Danke für deine Lib. Ich habe deine Beispielsketches mit einem ESP32 getestet. Der ADS1115 wird mit einer Betriebsspannung von 5V betrieben, somit war noch ein Pegelwandler für SDA und SCL erforderlich. Funktioniert alles prima. Eine „kleine“ Abweichung (bis zu ca. 500µV) gibt es, wenn ich den Regler z.B. auf 0V eingestellt habe, werden -0,43 mV ausgegeben. Das liegt mit Sicherheit an meiner Verkabelung.

    Für das Beipiel 1 – Single_Shot habe ich eine Funktion ergänzt, dass die Ranges für alle 4 Channels automatisch angepasst werden. Es wäre natürlich cool, wenn du diese Funktion in deine Lib übernimmst. Falls interesse besteht, kann ich dir das Beispiel per eMail zu schicken

    VG

    1. Hallo, danke für das Feedback – und schicke mi gerne das Beispiel zu. Kann ein paar Tage dauern, bis ich mir das anschaue, aber ich werde ds auf jeden Fall tun.

  30. Hola, quisiera saber para el cambio de direccionamiento que trae por defecto (yo tengo la placa aads1115 de color azul), que hace referencia la tabla 5 de la hoja de datos, si consiste en una simple conexión externa de los pines de la plaqueta o además hay que cortar alguna pista interna de la plaqueta. gracias.

    1. Hola,
      hay diferentes módulos azules. Su módulo tiene un pin „ADDR“? Puede conectar este pin a GND, VCC, SDA o SCL. También hay módulos sin el pin „ADDR“. Entonces es difícil, pero no imposible. Si puede soldar bien, puede crear un puente de cable desde el Pin1 a GND, VCC, SDA o SCL. Espero que esta traducción de Google tenga sentido. Yo no hablo español.

  31. Danke Erich & Wolle, ich war faul und hab alles wieder nur überflogen…Lesekultur am Computer 2020…
    Falls weitere Faule mitlesen: tds_voltage = (adc1 * 0.1875)/1000;

  32. Zitat:
    – Sollte die Formel Voltage = sensorValue*5/1024.0; angepasst werden? (Das *5 check ich nicht)

    Da wurde wohl ein 10 Bit ADC mit 5 Volt Messbereich verwendet.
    Klar, die Formel musst Du anpassen. Nimm einfach die Formel für U[V] aus dem Beitrag.
    Gruß
    Erich

    1. Genau, vielen Dank Erich. Der Arduino A/D Wandler z.B. hat 10 Bit und 5 V Referenz (wenn man nichts anderes einstellt). D. h. der A/D Wandlerwert „sensorValue“ muss in Volt umgerechnet werden. Die ADS1115 Bibliothek liefert dir direkt die Spannung in Volt.

      Ich bin von verschiedenen Seiten gefragt worden, ob ich nicht auch den Rohwert ausgeben kann. Das wäre von -32767 bis +32767. Ist nur für viele vielleicht missverständlich, weil die Zahl nur in Zusammenhang mit der Voltage Range eine Bedeutung bekommt. Aber es wird ein update in den nächsten Tagen kommen. Auch wird es dann eine Möglichkeit geben, die Ausgabe zu skalieren mit einer Funktion namens getResultWithRange(unteres Limit, oberes Limit, maximale Spannung). Mit getResultWith Range(0,1023,5000) würde man dann eine Arduino Ausgabe simulieren.

      Was ich bis heute nicht ganz verstanden habe ist, warum (fast) alle Welt immer 5/1024 als Faktor nimmt und nicht 5/1023. Eigentlich muss der Maximalwert 1023 eine Spannung von 5 Volt bedeuten. Das passt aber nicht zum Faktor 5/1024.

  33. Guten Morgen Wolle

    Noch eine Frage, Du hast Dich da ja schon eingearbeitet, ev hast Du einen Denkanstoss…
    Ich habe einen Wassersensor der normalerweise an einem Analog In (10Bit) angeschlossen wird.
    Dann wird gelesen und mit einer ziemlich seltsamen Formel (Copyright Seed) die TDS (Anzahl gelöster Teilchen im Wasser) ausgerechnet:

    sensorValue = analogRead(sensorPin);
    //Convert analog reading to Voltage
    Voltage = sensorValue*5/1024.0;
    //Convert voltage value to TDS value
    tdsValue=(133.42*Voltage*Voltage*Voltage – 255.86*Voltage*Voltage + 857.39*Voltage)*0.5;

    Jetzt will ich diesen Sensor mit dem ADS1115 auslesen der ja 16 Bit macht….klappt ja auch aber die Werte sind natürlich irgendwo anders.
    – Kann man einen Kanal des ADS1115 einfach mit 10 bit Auflösung betreiben?
    – Sollte die Formel Voltage = sensorValue*5/1024.0; angepasst werden? (Das *5 check ich nicht)

    Dankbar für einen Hinweis oder eine Idee

    Gruss

    Roman

    PS: Zum obigen Post : Das Modem auf dem MKR 1400 spricht nur I2S
    (Habs nach 8 Stunden geschafft, I2S ist eine Nummer…)

  34. Wolle, Du bist ein Ass! Aus irgendeiner kosmologischen Fügung behandelst Du immer Themen die ich auch gerade aufs Auge gedrückt bekomme- Danke dafür!

    Meine Frage zum ADS1115: Ich möchte mit einem Mikro OHNE AutoGain die digitalisierten Musikdaten über I2C direkt dem Modem auf dem Arduino MKR 1400 übergeben. (Der Code soll ab einem gewissen Lärmpegel ein SMS senden und einen Rückruf auf den uController erlauben.

    Andere Leser interessiert ja ev. die Sounddigitalisierung und weiterverarbeitung über I2C auch noch.

    Hättest Du was für mich (uns)

    Andächtig lauschend und dankbar für Deinen Blog

    Roman

    1. Hallo Roman, vielen Dank. Mit Musikdigitalisierung habe ich mich noch gar nicht beschäftigt. Aber ich denke für wirklich guten Sound dürfte die Rechenleistung eines Arduino nicht ausreichen. Einfach nur einen Lärmpegel zu messen wäre wiederum sehr einfach. Da gibt es ja Module für. Zum Autogain: das habe ich nicht ganz verstanden. Eigentlich macht der ads1115 bzw. meine Bibliothek kein Autogain, sondern du bestimmst das Gain. VG, Wolfgang

      1. Hallo Wolfgang
        Es geht um einen Telefonanruf, die Rechenleistung des Arduinos wird da nicht gross beansprucht, ich will ja den bereits digitalsierten Datenstrom über I2C direkt ans Modem weiterleiten.
        Ich glaub das ist keine grosse Sache. Mit diesem Hardwaresetup will ich auch gleich den Pegel messen und daher kann ich kein Autogain beim A/D Wandler brauchen weil sonst immer der gleiche Pegel beim Arduino ankommt. Das mit der integrierten Pegelmessung in diesem Setup wird vermutlich nicht einfach, resp. Dein Hinweis auf ein eigenes Modul ist sicher bequemer aber ich hab keine Pins mehr übrig und und und….jetzt erstmal ein Bier….
        Ich probier das mal mit einem I2S MEMS Microphone und dann noch mit dem ads1115 mit einem „normalen“ Mikro und Deiner Library.
        Beste Grüsse & nochmals Danke für Deinen Blog
        Roman

        1. Hallo, das mit dem Bier ist eine gute Idee – ich schließe mich an. Wie gesagt, ich habe mich bisher nicht mit dem Thema beschäftigt und bevor ich noch Blödsinn schreibe…ach ja, ich wollte ja ein Bier trinken…

        2. Ich würde auf jenden Fall ein Mikrofon- Modul, das ein Envelope-Ausgang bereitstellt einsetzen, und nicht das Signal vom Mikro digitalisieren. Dafür ist der ADS1115 zu langsam.
          Es gibt viel unsägliches Schrott auf dem Markt der Mikrofonmodule.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert