Codeoptimierung 1-wire Temperatursensoren

Begonnen von PeMue, 28 März 2019, 19:48:40

Vorheriges Thema - Nächstes Thema

PeMue

Hallo zusammen,

ich brauche mal Hilfe von Softwareentwicklern.

Problemstellung:
Ich möchte auf einem ESP8266 1-wire Sensoren auslesen, die einzelnen Taskschritte sollten aber möglichst wenig Zeit in Anspruch nehmen, da parallel noch ein Serial Server läuft, der auf Anfragen der Heizing (Viessmann GWG Protokoll) reagieren soll.
Dazu habe ich die 1-wire Abfrage in verschiedene Tasks zerlegt und die einzelnen Zeiten gemessen.

Definitionen:
// for 1-wire
#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_PIN 0
#define T_PRECISION 12
#define REQUIRESALARMS false                     // no 1-wire alarms for this firmware necessary, redefinition from library

// setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_PIN);
// pass our oneWire reference to Dallas Temperature
DallasTemperature sensors(&oneWire);
// array to hold device addresses
DeviceAddress devices[10];
int devicesFound = 0;
// array for raw temperature
float tempC[10];


1-wire Loop:
//- -----------------------------------------------------------------------------------------------------------------------
// OneWireLoop
//- -----------------------------------------------------------------------------------------------------------------------
void OneWireLoop(void)
{
  // variable for time needed for 1-wire conversion
  static long time_between_measurements;

  // save the time (in ms) since start of processor
  //static long currentMillis = millis();
  //static long currentMillis = 0;
  static long currentStateMillis = 0;
  static int wait_conv_ms = 750 / (1 << (12 - T_PRECISION));

  static enum {START, WAIT_CONV, READ, COLLECT, SEND, WAIT_NEXT, } state=START;
  //static unsigned long int warteSeit;
 
  // loop variable
  static int j=0;

  // state machine, depending on stati
  switch (state)
  {
    // START: actual time stored, count incremented, start measurement
    case START:
      // overwrite time stamp with actual time
      //currentMillis = millis(); // redundant, only for testing
      startTime = millis();
     
      // increment cycle_count
      cycle_count++;
      Serial.print("Before starting (START) "); Serial.print(millis()-startTime, DEC); Serial.println(" ms.");
      sensors.setWaitForConversion(false);  // makes it async -> can be skipped, defined above
      sensors.requestTemperatures();
      Serial.print("After request (START) "); Serial.print(millis()-startTime, DEC); Serial.println(" ms.");
      state=WAIT_CONV;
    break;

    case WAIT_CONV:
      if ((millis()-startTime)>wait_conv_ms) // if wait_conv_ms has been reached, switch to COLLECT (collect data)
      {
        currentStateMillis=millis();
        state=READ;
        Serial.print("After conversion (WAIT_CONV) "); Serial.print(millis()-startTime, DEC); Serial.print(" ms, ");
        Serial.print(millis()-currentStateMillis, DEC); Serial.println(" ms.");
      }
    break;

    case READ:
      if (j < devicesFound) // if still devices available to read temperature
      {
        currentStateMillis=millis();
        tempC[j]=sensors.getTempC(devices[j]);
        Serial.print("After read temperature (READ) loop "); Serial.print(j, DEC);
        Serial.print(" "); Serial.print(millis()-startTime, DEC); Serial.print(" ms, ");
        Serial.print(millis()-currentStateMillis, DEC); Serial.println(" ms.");
        j++;
      }
      else state=COLLECT;
    break;

    case COLLECT:
      j = 0; // set loop variable to 0 again
      currentStateMillis=millis();
      sendstr = "OK VALUES THz 1 cnt=";
      //sendstr += String(cycle_count*UPDATE_TIME/1000, DEC);
      sendstr += String(cycle_count, DEC);
      sendstr += ",";

      // print the device information, must be changed because it is too slow
      for (int i = 0; i < devicesFound; i++)
      {
        sendstr += printAddressStr(devices[i]);
        sendstr += "=";
        // previous function replaced by READ loop
        //sendstr += printTemperatureStr(devices[i]);
        sendstr += String(tempC[j]);
        if (i != devicesFound - 1)
          sendstr += ",";
      }
      Serial.print("After string collection (COLLECT) "); Serial.print(millis()-startTime, DEC); Serial.print(" ms, ");
      Serial.print(millis()-currentStateMillis, DEC); Serial.println(" ms.");
      // get the time for conversion
      //time_between_measurements = millis() - startTime;
      time_between_measurements = millis() - startTime;
      sendstr += ",time_tot=";
      sendstr += String(time_between_measurements, DEC);
      //sendstr += " ms";
      state=SEND;
    break;

    case SEND:
      if (connected)
      {
        currentStateMillis=millis();
        // send string to UPD port, use sendstr instead of replyPacket
    if (Udp.beginPacketMulticast(ipMulti, portMulti, WiFi.localIP()) == 1)
        {
          //Serial.println("after Udp.beginPacketMulticast ...");
      Udp.write(sendstr.c_str());
          //Serial.println("after Udp.write ...");
          Udp.endPacket();
          yield();
          //Serial.println("after Udp.endPacket ...");
    }
      }
      // get the time for sending
      time_between_measurements = millis() - startTime;
      sendstr += ",t_tot=";
      sendstr += String(time_between_measurements, DEC);
      Serial.print("After sending "); Serial.print(millis()-startTime, DEC); Serial.print(" ms, ");
      Serial.print(millis()-currentStateMillis, DEC); Serial.println(" ms.");
      Serial.println(sendstr);
      state=WAIT_NEXT;
    break;
   
    case WAIT_NEXT:
      if ((millis()-startTime)>=UPDATE_TIME) // if UPDATE_TIME has been reached, switch to START again
      {
        currentStateMillis=millis();
        state=START;
        Serial.print("After restart again "); Serial.print(millis()-startTime, DEC); Serial.print(" ms, ");
        Serial.print(millis()-currentStateMillis, DEC); Serial.println(" ms.");
      }
    break;
  } 
}
//- -----------------------------------------------------------------------------------------------------------------------


zugehörige Zeitmessung:
Vitotronic 1-wire demo
Connecting to PMWLAN02 .... connected ...
OTA initialized ...
UDP port at IP 192.168.188.28, UDP port 12345 opened
Locating devices ...
Found 3 device(s).
Parasite power is: OFF
Device 0 Address: 109E85B700080021
Device 1 Address: 10C175B5020800C5
Device 2 Address: 288B22A8000000C2
Before starting (START) 0 ms.
After request (START) 2 ms.
After conversion (WAIT_CONV) 751 ms, 0 ms.
After read temperature (READ) loop 0 763 ms, 12 ms.
After read temperature (READ) loop 1 778 ms, 13 ms.
After read temperature (READ) loop 2 791 ms, 13 ms.
After string collection (COLLECT) 792 ms, 1 ms.
After sending 793 ms, 1 ms.
OK VALUES THz 1 cnt=1,109E85B700080021=25.75,10C175B5020800C5=25.75,288B22A8000000C2=25.75,time_tot=792,t_tot=793
After restart again 5000 ms, 0 ms.


Kurzfassung des Problems:
Wie bekomme ich das Auslesen der Temperaturen
tempC[j]=sensors.getTempC(devices[j]);
schneller als die gemessenen 13 ms? Oder anders gefragt: liegt das an dem (langsamen) 1-wire Bus?

Für Tipps wäre ich echt dankbar, mir gehen langsam die Ideen aus.

Danke + Gruß

Peter
RPi3Bv1.2 rpiaddon 1.66 6.0 1xHM-CC-RT-DN 1.4 1xHM-TC-IT-WM 1.1 2xHB-UW-Sen-THPL-O 0.15 1x-I 0.14OTAU  1xCUNO2 1.67 2xEM1000WZ 2xUniroll 1xASH2200 3xHMS100T(F) 1xRFXtrx 90 1xWT440H 3xTFA30.3150 5xFA21
RPi1Bv2 LCDCSM 1.63 5.8 2xMAX HKT 1xMAX RT V200KW1 Heizung Wasser

Tom Major

ZitatOder anders gefragt: liegt das an dem (langsamen) 1-wire Bus?

Denke schon das es daran liegt.
Du schickst zum Lesen ca. 20 Byte über den Bus und die Bit time slots sind im Bereich von 60us. Das allein macht schon ca. 10ms ohne Berücksichtigung weiterer overheads.
Früher: FHEM 5.x
Jetzt: RaspberryMatic / ioBroker

pizmus

Hallo PeMue,
falls Du keine Software-Lösung findest, könntest Du zusätzliche Hardware einsetzen um die CPU zu entlasten. Das könnte ein Raspberry Pi für die dedizierte Aufgabe sein, oder ein 1-wire Controller wie der von Esera. Es hängt natürlich von vielen Faktoren ab, was da für Dich die beste Lösung ist.
Viele Grüße,
pizmus

Morgennebel

Zitat von: PeMue am 28 März 2019, 19:48:40
Ich möchte auf einem ESP8266 1-wire Sensoren auslesen, ... parallel noch ein Serial Server läuft

Ein ESP8266 kostet 3-9 EUR pro Stück. Deine Arbeitszeit mehr. Kauf einfach noch einen :)

Ciao, -MN
Einziger Spender an FHEM e.V. mit Dauerauftrag seit >= 24 Monaten

FHEM: MacMini/ESXi, 2-3 FHEM Instanzen produktiv
In-Use: STELLMOTOR, VALVES, PWM-PWMR, Xiaomi, Allergy, Proplanta, UWZ, MQTT,  Homematic, Luftsensor.info, ESP8266, ESERA

HCS

Oder evtl. auf den ESP32 ausweichen und es in einem eigenen Thread machen.

mumpitzstuff

Je nach Menge der Daten kommen da meines Wissens sehr schnell ein paar ms zusammen. One Wire ist kein high performance bus. Es gibt aber einen Overdrive Modus, mit dem man das noch beschleunigen könnte, ich glaube aber dein Master und auch deine Sensoren müssen das dann irgendwie unterstützen.

PeMue

Hallo zusammen,

danke für Eure Hinweise.
Zitat von: pizmus am 15 April 2019, 12:18:58
falls Du keine Software-Lösung findest, könntest Du zusätzliche Hardware einsetzen um die CPU zu entlasten. Das könnte ein Raspberry Pi für die dedizierte Aufgabe sein, oder ein 1-wire Controller wie der von Esera.
Beim Optolink Adapter läuft nur der serialServer (mit 4800 Baud) und die 1-wire Messung. Scheinbar funktioniert es, selbst mit den 13 ms, ich werde berichten.

Zitat von: Morgennebel am 15 April 2019, 12:36:47
Ein ESP8266 kostet 3-9 EUR pro Stück. Deine Arbeitszeit mehr. Kauf einfach noch einen :)
Ja, wäre eine Möglichkeit. Aber wieder ein zusätzlicher Stromfresser und ein Gerät mehr. Also im Moment keine Alternative. Und: ich versuche auch meine (rudimentär vorhandene) Softwarekenntnisse erweitern.

Zitat von: HCS am 15 April 2019, 12:37:27
Oder evtl. auf den ESP32 ausweichen und es in einem eigenen Thread machen.
In diesem Fall müsste ich auf Deine Expertise zurückgreifen  :).

Zitat von: mumpitzstuff am 15 April 2019, 14:53:22
Je nach Menge der Daten kommen da meines Wissens sehr schnell ein paar ms zusammen. One Wire ist kein high performance bus. Es gibt aber einen Overdrive Modus, mit dem man das noch beschleunigen könnte, ich glaube aber dein Master und auch deine Sensoren müssen das dann irgendwie unterstützen.
Das muss mit dem gehen, was die DS18x20 Sensoren hergeben, scheinbar funktioniert es ...

Gruß Peter
RPi3Bv1.2 rpiaddon 1.66 6.0 1xHM-CC-RT-DN 1.4 1xHM-TC-IT-WM 1.1 2xHB-UW-Sen-THPL-O 0.15 1x-I 0.14OTAU  1xCUNO2 1.67 2xEM1000WZ 2xUniroll 1xASH2200 3xHMS100T(F) 1xRFXtrx 90 1xWT440H 3xTFA30.3150 5xFA21
RPi1Bv2 LCDCSM 1.63 5.8 2xMAX HKT 1xMAX RT V200KW1 Heizung Wasser

Prof. Dr. Peter Henning

Das wird nicht schneller gehen, das Bustiming ist eben so langsam. Eventuell kann man noch mit dem Overdrive Mode arbeiten - aber eben gerade nicht bei den Temperatursensoren.

LG

pah

P.S.: "1-wire Controller wie der von Esera" - machen wir hier nicht etwas zuviel Werbung für ein bestimmtes Produkt, dessen zugehöriges Modul NICHT zum normalen FHEM-Umfang gehört?

Morgennebel

Zitat von: Prof. Dr. Peter Henning am 17 April 2019, 16:26:05
P.S.: "1-wire Controller wie der von Esera" - machen wir hier nicht etwas zuviel Werbung für ein bestimmtes Produkt, dessen zugehöriges Modul NICHT zum normalen FHEM-Umfang gehört?

Ich glaube jeder Modulautor ist stolz auf seine Arbeit und freut sich, wenn seine Module genutzt werden. Ich sehe ja auch viele Hinweise auf YAAH :-)

Ciao, -MN
Einziger Spender an FHEM e.V. mit Dauerauftrag seit >= 24 Monaten

FHEM: MacMini/ESXi, 2-3 FHEM Instanzen produktiv
In-Use: STELLMOTOR, VALVES, PWM-PWMR, Xiaomi, Allergy, Proplanta, UWZ, MQTT,  Homematic, Luftsensor.info, ESP8266, ESERA

Prof. Dr. Peter Henning