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 meine zugehörige Bibliothek vorstellen. Ich gehe zunächst auf die technischen Eigenschaften ein, dann möchte ich anhand von Beispielsketchen zeigen, wie ihr den ADS1115 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 Module. Wenn ihr im Löten von SMDs fit seid, könnt ihr natürlich auch den „nackten“ ADS1115 verwenden. Meine Bibliothek sollte damit genauso funktionieren.

Wozu brauche ich den ADS1115?

„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.

Technische Eigenschaften des ADS1115

Verschiedene ADS1115 Module
Verschiedene ADS1115 Module

Ein- und Ausgänge des ADS1115

Der ADS1115 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 ist, 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

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).
  • 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.
  • Kontinuierlicher Modus oder Einfachmessungen („Single-Shot“).
  • Interne Spannungsreferenz.
  • Geringer Stromverbrauch: 150 µA im kontinuierlichen Modus, 2.5 µA im Power-Down Modus. Der ADS1115 geht nach einer Single-Shot Messung automatisch in den Power-Down Modus.

Weitere Informationen findet ihr im Datenblatt des ADS1115.

ADS1115 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. Am einfachsten testet ihr das, indem ihr euch Rohdaten im Binärformat ausgeben lasst. Wenn die letzten drei Bits immer 0 sind, dann habt ihr ein solches erwischt. Schaut auch mal hier für mehr Details.

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 besten erklären. Ich habe die Bibliothek mit sechs Beispielsketchen ausgestattet. Auf Single_Shot.ino gehe ich am intensivsten ein. Das meiste wiederholt sich dann in den anderen Sketchen.

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:

  • 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

ADS1115_WE adc(I2C_ADDRESS);
// ADS1115_WE adc = ADS1115_WE(); // Alternative: uses default address 0x48

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 Funktion setAutoRange() vor. Mit ihr 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.

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
  ....
  ....
void loop() {
  float voltage = 0.0;
  
  Serial.print("Channel 0 - ");
  voltage = readChannel(ADS1115_COMP_0_GND);
  Serial.println(voltage);

  Serial.print("Channel 1 - ");
  voltage = readChannel(ADS1115_COMP_1_GND);
  Serial.println(voltage);
  ....
  ....
  Serial.println("-------------------------------");
  delay(1000);
}

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

  switch(voltageRange){
    case 6144:
      Serial.print("+/- 6144 mV, Voltage [mV]: ");
      break;
    case 4096:
      Serial.print("+/- 4096 mV, Voltage [mV]: ");
      break;
    case 2048:
      Serial.print("+/- 2048 mV, Voltage [mV]: ");
      break;
    case 1024:
      Serial.print("+/- 1024 mV, Voltage [mV]: ");
      break;
    case 512:
      Serial.print("+/- 512 mV, Voltage [mV]: ");
      break;
    case 256:
      Serial.print("+/- 256 mV, Voltage [mV]: ");
      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 Funktionen der ADS1115 Bibliothek
Liste der (öffentlichen) Funktionen der ADS1115 Bibliothek

Tiefere Einblicke in den ADS1115

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().

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

  1. 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

  2. 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

  3. 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!

  4. 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.

  5. 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

  6. 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

  7. 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.

  8. 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.

  9. 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.

  10. 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;

  11. 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.

  12. 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…)

  13. 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.