DS18B20 -non blocking- wie Werte verwenden?

Hallo,
ich habe wieder mal ein kleines Problem und komme nicht auf die Lösung.
Ich möchte mit der non blocking Dallas Bibliothek <NonBlockingDallas.h> 4 Werte einlesen. Es ist soweit alles verdrahtet, Widerstand eingelötet, mit dem Beispiel funktioniert es auch.
Aber wie kann ich die eingelesenen Temperaturen verwenden oder verwendbaren Variablen zuweisen? Irgendwie stehe ich auf dem Schlauch.

Das Beispiel läuft:

#include <OneWire.h>
#include <DallasTemperature.h>
#include <NonBlockingDallas.h>                  //Include the NonBlockingDallas library

#define ONE_WIRE_BUS 6                          //PIN of the Maxim DS18B20 temperature sensor
#define TIME_INTERVAL 1000                      //Time interval among sensor readings [milliseconds]

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature dallasTemp(&oneWire);
NonBlockingDallas sensorDs18b20(&dallasTemp);    //Create a new instance of the NonBlockingDallas class

void setup() {
  Serial.begin(9600);
  while (!Serial)
    ;

  //Initialize the sensor passing the resolution, unit of measure and reading interval [milliseconds]
  sensorDs18b20.begin(NonBlockingDallas::resolution_12, NonBlockingDallas::unit_C, TIME_INTERVAL);

  //Callbacks
  sensorDs18b20.onIntervalElapsed(handleIntervalElapsed);
  sensorDs18b20.onTemperatureChange(handleTemperatureChange);

  //Call the following function if you want to request the temperature without waiting for TIME_INTERVAL to elapse
  //The temperature value will then be provided with the onTemperatureChange callback when ready
  sensorDs18b20.requestTemperature();
}

void loop() {
  
 sensorDs18b20.update();
  
  /*
   *  EVEN THOUGH THE SENSOR CONVERSION TAKES UP TO 750ms
   *  THE NonBlockingDallas LIBRARY WAITS FOR THE CONVERSION
   *  WITHOUT BLOCKING THE loop AND CALLS THE CALLBACKS
   *  WHEN THE TEMPERATURE VALUE IS READY
   */ 
}

//Invoked at every sensor reading (TIME_INTERVAL milliseconds)
void handleIntervalElapsed(float temperature, bool valid, int deviceIndex){

  Serial.print("Sensor ");
  Serial.print(deviceIndex);
  Serial.print(" temperature: ");
  Serial.print(temperature);
  Serial.println(" °C");

  /*
   *  DO SOME AMAZING STUFF WITH THE TEMPERATURE
   */
}

die Werte werden alle 4 angezeigt. Aber wie weise ich sie zu?

Danke schon mal vorab

Gruß

Indem Du die Variable

temperature

benutzt, die Du auch anzeigst.

Gruß Tommy

Am einfachsten in ein globales Array abspeichern:

float temps[numSensors];
...
 if (valid)
   temps[deviceIndex] = temperature;

Ggf. zu jedem Sensor noch ein Flag für "neuer Wert" speichern.

eine Variable, 4 Werte. Da hängts ja bei mir.
Der Beispielsketch läuft und zeigt der Reihe nach 4 Werte mit dem richtigen Index an.
Diese Werte sollen nun auf

Temp_TB1
Temp_TB2
Temp_BG1
Temp_BG2

gelegt werden.

Du mußt schon selbst wissen, welcher Index zu welcher Meßstelle gehört.

if (deviceIndex == Index_TB1) Temp_TB1 = temperature;
else if ...

Kurze Frage, wozu braucht man das?
Die dallasTemperature bringt eine .setWaitForConversion(false); // makes it async` mit.
Wozu braucht es da noch eine zusätzliche Lib?

Weil man nicht verstanden hat, wie das funktioniert.
Ich hatte die Frage auch schon auf der Zunge :wink:

Gruß Tommy

mh, jetzt ist es raus

Wie wende ich die normale an, um das warten auf die Werte zu vermeiden?
einfach mit

deaktivieren, dass gewartet wird?

Und über millis() selbst für die nötige Zeit sorgen, nach der abgefragt wird.

Start mit requestTemperatures() - das gilt für alle und nach x ms (je nach Auflösung alle abfragen. Danach evtl. gleich wieder requestTemperatures().

Wenn Du früher abfragst, erhältst Du den alten Wert.

Gruß Tommy

Im folgenden Code wird alle 750 ms die Temperatur vom ersten Sensor ausgegeben.
Nebenbei blinkt die OnBoard-LED an Pin 13 im Takt von 100ms.

Würdest Du mit einem delay() - egal ob aus der lib oder im eigenen Code - auf die Convertierung warten, würde die LED nicht so schnell blinken....

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

constexpr uint32_t resolutionTime {750};  // Zeit in ms - 12 bit brauchen 750 ms WaitTime
uint32_t lastReadTime = 0;

void setup(void)
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  sensors.begin();
  sensors.setWaitForConversion(false);
  sensors.requestTemperatures();
  lastReadTime = millis();
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop(void)
{
  readSensors();
  heartBeat(100);
}

void readSensors()
{
  if (millis() - lastReadTime > resolutionTime)
  {
    lastReadTime = millis();
    Serial.print("Temperature: ");
    Serial.println(sensors.getTempCByIndex(0));
    Serial.println();
    sensors.requestTemperatures();
  }
}

void heartBeat(const uint32_t blinkTime)
{
  static uint32_t lastblink = 0;
  if (millis() - lastblink > blinkTime)
  {
    lastblink = millis();
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }
}

das ging schon mal hervorragend, Danke

Die Sensorabfrage mit eigener Zeitvorgabe folgt morgen, macht ja so auch Sinn

Die Sensorabfrage mit Zeitvorgabe wird noch mal verschoben.
Ich habe noch ein anderes Problem. Ist eigentlich kein wirkliches Problem.

Ich habe eine Heizung, die wird über den PIN 3 und ein SSR geregelt. Das macht ein PID Regler über die Bibliothek Relay.h. Die Einschaltdauer liegt bei einer Sekunde.

Nun habe ich noch eine 2te Heizung, die ich auch regeln will. Dazu habe ich mir den Regler selbst geschrieben. Diese hängt am Pin11.
Ich habe die PWM Frequenz auf 30 Hz für den Timer 2 gesetzt (Arduino Nano) mit
TCCR2B = TCCR2B & B11111000 | B00000111;
Das funktioniert auch, aber das SSR schaltet natürlich jetzt recht häufig, muss es aber ja gar nicht.
1 Hz wie aus der Bibliothek würde mir reichen.
Wie kann ich die PWM Frequenz weiter runter setzen? Der max. Divisor ist 1024, oder geht da noch mehr?
Oder gibt es einen besseren Weg?

Danke schonmal

Warum macht man sowas?
Warum wird eine so niedrige Frequenz mit PWM gefahren?

Wenn der Code vernünftig nonblocking ist, kannst Du komplett auf PWM verzichten.
Wobei mir noch nicht klar ist, warum man eine Heizung im Sekundenrhytmus taktet.

Es handelt sich um einen Durchlauferhitzer zur geregelten Erwärmung einer Flüssigkeit.
Ich formuliere es mal um:

Ziel:
möglichst exakte Regelung der Temperatur des durchfließenden Mediums.

Bisherige Lösung, die auch funktioniert:
SSR über relay.h mit 1Hz Einschaltdauer

Da nun ein 2ter Strang geregelt werden soll möchte ich das obige Ziel nun wieder erreichen.
Mein angedachter Weg war, ähnlich wie Relay.h, das Relais in 1s takten zu takten über einen PID Regler.
Das funktioniert auch, aber eben mit 30Hz. und das ist Quatsch.

Wenn es einen eleganteren Weg gibt einen Ausgang für ein Relais über einen Regler zu takten bin ich dafür offen. Ich kenne ihn nur nicht. Mit meinem Weg hab ich in 10h über 1Mio. Schaltungen, da wird das SSR irgendwann auch die Fahne schwenken.

Wenn es sich um AC handelt, dann sollte das im SSR mit einem Triac erledigt werden, und der meckert auch nicht über 100 Hz Dauerbetrieb.

Da musste ich schon schmunzeln.
Ok!
Gehen wir mal davon aus, das Du die Temperatur-Präzision auf das unterste Limit (9 bit) begrenzt, liegt die Zeit die Du brauchst um einen neuen Wert aus dem Sensor zu bekommen bei bis zu 94(!) ms allein für die Conversion. (Datenblatt des DS18B20 Seite 3)

In der Zeit bist Du blind und kannst nicht reagieren.

Über 33Hz kann man jetzt nachdenken oder auch nicht.
Aber Du sollst zwei Kreise bekommen, mit denen Du unabhängig arbeiten kannst.
Jeder Kreis eigene obere Grenze, eigenes Timing.

Ganz ohne PWM. Den PID darfst dann selbst drin verinnerlichen, da ich Deinen Code nicht kenne.

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

constexpr uint8_t regelKreise {2};
constexpr uint8_t ssrPin[regelKreise] {3, 4};
constexpr uint32_t ssrSwitchTime[regelKreise] {1000, 33}; // 1000ms ^=1Sek ^= 1Hz /
uint32_t ssrLastSwitchTime[regelKreise] = {0, 0};         // Merker letzter Schaltpunkt
constexpr int8_t maxValue[regelKreise] {40, 45};          // obere TempGrenze
int8_t sensorsValue[regelKreise] {0, 0};                  // Merker für gemessene Temp

constexpr uint32_t resolutionTime {1000};  // Zeit in ms - 12 bit brauchen 750 ms WaitTime
uint32_t lastReadTime = 0;



void setup(void)
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  sensors.begin();
  sensors.setWaitForConversion(false);
  sensors.requestTemperatures();
  lastReadTime = millis();
  pinMode(LED_BUILTIN, OUTPUT);
  for (byte b = 0; b < regelKreise; b++)
  {
    pinMode(ssrPin[b], OUTPUT);
    digitalWrite(ssrPin[b], LOW);
  }
}

void loop(void)
{
  readSensors();
  setSSR();
  heartBeat(100);
}

void readSensors()
{
  if (millis() - lastReadTime > resolutionTime)
  {
    lastReadTime = millis();
    Serial.print("Temperature: ");
    for (byte b = 0; b < regelKreise; b++)
    {
      sensorsValue[b] = sensors.getTempCByIndex(b);
      Serial.print(sensorsValue[b]);
      Serial.print('\t');
    }
    sensors.requestTemperatures();
    Serial.println();
  }
}

void setSSR()
{
  for (byte b = 0; b < regelKreise; b++)
  {
    if (sensorsValue[b] < maxValue[b])                            // Solange TempGrenze nicht erreicht
    {
      if (millis() - ssrLastSwitchTime[b] >= ssrSwitchTime[b])    // aber Zeit um
      {
        digitalWrite(ssrPin[b], !digitalRead(ssrPin[b]));         // toggle SSR-Pin
        ssrLastSwitchTime[b] = millis();                          // merke die Schaltzeit
        Serial.print(F("Heizungskreis "));
        Serial.print(b);
        Serial.print(F(" neuer Zustand: "));
        Serial.println(digitalRead(ssrPin[b]) ? F("EIN") : F("AUS"));
      }
    }
    else                                                          // Wenn Temperatur erreicht
    { digitalWrite(ssrPin[b], LOW); }
  }
}

void heartBeat(const uint32_t blinkTime)
{
  static uint32_t lastblink = 0;
  if (millis() - lastblink > blinkTime)
  {
    lastblink = millis();
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }
}

Man könnte das sogar noch weiter treiben und die ssrSwitchTime variabel machen. Je weiter unter dem zu erreichendem Temperaturniveau die Zeit fürs einschalten hochsetzen.

  void setSSR()
  {
  for (byte b = 0; b < regelKreise; b++)
  {
    if (sensorsValue[b] < maxValue[b] - 6)                      // bis 6Grad unter Abschalttemperatur
    { digitalWrite(ssrPin[b], HIGH); }                       // dauerhaft heizen
    else if (sensorsValue[b] <= maxValue[b] - 5 &&              // zwischen 5 bis 2
             sensorsValue[b] < maxValue[b] - 2)                 // unter oberem Totpunkt
    {
      if (!digitalRead(ssrPin[b]))                           // Wenn aus
      {
        if (millis() - ssrLastSwitchTime[b] >= 10)           // und kurze Pause um
        {
          digitalWrite(ssrPin[b], HIGH);                     // beschreibe SSR-Pin
          ssrLastSwitchTime[b] = millis();                   // merke die Schaltzeit
        }
      }
      else if (millis() - ssrLastSwitchTime[b] >= 100)       // wenn an & lange Einschaltzeit um
      {
        digitalWrite(ssrPin[b], LOW);                        // beschreibe SSR-Pin
        ssrLastSwitchTime[b] = millis();                     // merke die Schaltzeit
      }
    }
    else if (sensorsValue[b] <= maxValue[b] - 1 &&              // zwischen -2 bis oberer Totpunkt
             sensorsValue[b] < maxValue[b])
    {
      if (millis() - ssrLastSwitchTime[b] >= 10)             // Kurze Ein- und kurze Ausschaltzeit
      {
        digitalWrite(ssrPin[b], !digitalRead(ssrPin[b]));    // toggle SSR-Pin
        ssrLastSwitchTime[b] = millis();                     // merke die Schaltzeit
      }
      else                                                   // Wenn Temperatur erreicht
      { digitalWrite(ssrPin[b], LOW); }
    }
  }

Das mit PWM zu lösen und dafür den Timer zu verbiegen, halte ich für nicht sinnvoll.

ich ja auch. Drum schreibe ich ja hier. Ich kann es nur mit meinem Programierhorizont umsetzen und da ist Ende.
Der Sensor läuft mit 9bit. Icch möchte ja von den 30Hz weg, weil es keinen Sinn macht. 1Hz reicht für meine Bedürfnisse.

jetzt wirds spannend, das muss ich mir genau anschauen und nachvollziehen, was da was macht und wo mein Regler eingebaut werden muss.

Eigentlich ist er 2 stufig, es wird erst bis auf 10° vor Ziel voll geheizt,
dann gebe ich eine Vorsteuerung vor mit der ich auf Zielgröße kommen müsste und da addiert sich ein P-Anteil drauf.
Das war mit der Ausgabe am PWM einfach, Vorsteuerung 120 =ca halbe Leistung, reicht um Temperatur zu halten.

Jetzt muss ich aber erst mal deinen Sketchvorlag nachvollziehen, da steckt viel Neues für mich drin.

Hallo,
ich hab mal in meiner Bastelkiste gesucht und was gefunden. Es handelt sich um eine Function für z.B ein Relais. Es wird mit einem Wert für 0-100% mit einer feste Zykluszeit angesteuert. Verändert wird das ein/aus Verhältniss für den Ausgang.

Heinz

bool analogrelay( int wert) {
  static uint32_t altzeit;
  const uint32_t gesamt = 10000; // zykluszeit gesamt
  if (wert <=0) wert=0;
  uint32_t ein = wert * gesamt / 100; // skaliert auf 0-100%
  bool on;

  if (millis() - altzeit >= gesamt) {
    altzeit = millis();
    on = true;
    //Serial.print(gesamt); Serial.print("  ");
    //Serial.print(ein); Serial.println(" ein");
  }

  if (millis() - altzeit >= ein) {
    on = false;
  }
  return (on);
}

puh, das ist harte Kost für mich, die Art der Deklarierung kannte ich bis jetzt noch nicht. Habe versucht das nach zu vollziehen und gelesen, dass dies unter professionellen Code fällt.

constexpr uint8_t regelKreise {2};

wird über die 2 ein PIN zugewiesen? eher nicht und ich verstehe es falsch. Ein array mit 2 Plätzen?

constexpr uint8_t ssrPin[regelKreise] {3, 4};

und das wird dann mit den SSR Pins 3 und 4 befüllt?
und was macht dann in der ssrSwitchtime in der Klammer die 33noch?

Fragen über Fragen

Die Konstante regelKreise wird mit dem Wert 2 initialisiert

Die Konstanten ssrPin0 und ssrPin1 erhalten die Werte 3 und 4