20+ 1-wire devices

Hello,

I (would like to) use 10+ 1-wire sensors (with long kabels) on a RP.
Any suggestions how to best do this ?
At this moment I do it direct with the 1-wire driver from HASS, but keep lossing connection.

I found this combination Arduino+ESP8266+DS18B20 and was wondering or that would be not to hard to translate to HASS

/*
 * ESP8266-01 with multiple DS10B20 temperature sensors
 * reads sensors asyncronsly for less blocking time
 * 
 * Use GPIO2 for the sensors, a single 4.7K pullup resisor is needed.
 * 
 */
#include <OneWire.h>

const int nsensors = 2;
byte sensors[][8] = {
   { 0x28, 0xC1, 0x02, 0x64, 0x04, 0x00, 0x00, 0x35 },
   { 0x28, 0xE7, 0x0B, 0x63, 0x04, 0x00, 0x00, 0x44 }
};
int16_t tempraw[nsensors];
unsigned long nextprint = 0;

OneWire  ds(2);  // on pin 2 (a 4.7K pullup is necessary)

void setup() {
  Serial.begin(115200);
  delay(10);

  Serial.println();
  Serial.println();
}

void loop() 
{
  ds18process(); //call this every loop itteration, the more calls the better.

  if(millis() > nextprint) {
    Serial.print("Temp F: ");
    for(byte j=0; j<nsensors; j++){
      Serial.print(j); Serial.print("=");
      Serial.print(ds18temp(1, tempraw[j])); Serial.print("  ");
    }
    Serial.println();
    nextprint = millis() + 1000; //print once a second
  }
}

/* Process the sensor data in stages.
 * each stage will run quickly. the conversion 
 * delay is done via a millis() based delay.
 * a 5 second wait between reads reduces self
 * heating of the sensors.
 */
void ds18process() {
  static byte stage = 0;
  static unsigned long timeNextStage = 0;
  static byte sensorindex = 100;
  byte i, j;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];

  if(stage == 0 && millis() > timeNextStage) {
    if (!ds.search(addr)) {
      //no more, reset search and pause
      ds.reset_search();
      timeNextStage = millis() + 5000; //5 seconds until next read
      return;
    } else {
      if (OneWire::crc8(addr, 7) != addr[7]) {
        Serial.println("CRC is not valid!");
        return;
      }
      //got one, start stage 1
      stage = 1;
    }
  }
  if(stage==1) {
    Serial.print("ROM =");
    for ( i = 0; i < 8; i++) {
      Serial.write(' ');
      Serial.print(addr[i], HEX);
    }
    //find sensor
    for(j=0; j<nsensors; j++){
      sensorindex = j;
      for(i=0; i<8; i++){
        if(sensors[j][i] != addr[i]) {
          sensorindex = 100;
          break; // stop the i loop
        }
      }
      if (sensorindex < 100) { 
        break; //found it, stop the j loop
      }
    }
    if(sensorindex == 100) {
      Serial.println("  Sensor not found in array");
      stage = 0;
      return;
    }
    Serial.print("  index="); Serial.println(sensorindex);
  
    ds.reset();
    ds.select(sensors[sensorindex]);
    ds.write(0x44, 0);        // start conversion, with parasite power off at the end
    stage = 2; //now wait for stage 2
    timeNextStage = millis() + 1000; //wait 1 seconds for the read
  }
  
  if (stage == 2 && millis() > timeNextStage) {
    // the first ROM byte indicates which chip
    switch (sensors[sensorindex][0]) {
      case 0x10:
        Serial.print("  Chip = DS18S20");  // or old DS1820
        Serial.print("  index="); Serial.println(sensorindex);
        type_s = 1;
        break;
      case 0x28:
        Serial.print("  Chip = DS18B20");
        Serial.print("  index="); Serial.println(sensorindex);
        type_s = 0;
        break;
      case 0x22:
        Serial.print("  Chip = DS1822");
        Serial.print("  index="); Serial.println(sensorindex);
        type_s = 0;
        break;
      default:
        Serial.println("Device is not a DS18x20 family device.");
        stage=0;
        return;
    }
  
    present = ds.reset();
    ds.select(sensors[sensorindex]);
    ds.write(0xBE);         // Read Scratchpad
  
    Serial.print("  Data = ");
    Serial.print(present, HEX);
    Serial.print(" ");
    for ( i = 0; i < 9; i++) {           // we need 9 bytes
      data[i] = ds.read();
      Serial.print(data[i], HEX);
      Serial.print(" ");
    }
    Serial.print(" CRC=");
    Serial.print(OneWire::crc8(data, 8), HEX);
    Serial.print(" index="); Serial.print(sensorindex);
    Serial.println();
  
    int16_t raw = (data[1] << 8) | data[0];
    if (type_s) {
      raw = raw << 3; // 9 bit resolution default
      if (data[7] == 0x10) {
        // "count remain" gives full 12 bit resolution
        raw = (raw & 0xFFF0) + 12 - data[6];
      }
    } else {
      byte cfg = (data[4] & 0x60);
      // at lower res, the low bits are undefined, so let's zero them
      if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
      else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
      else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
      //// default is 12 bit resolution, 750 ms conversion time
    }
    tempraw[sensorindex] = raw;
    stage=0;
  }
}

/* Converts raw temp to Celsius or Fahrenheit
 * scale: 0=celsius, 1=fahrenheit
 * raw: raw temp from sensor
 * 
 * Call at any time to get the last save temperature
 */
float ds18temp(byte scale, int16_t raw) 
{
  switch(scale) {
    case 0: //Celsius
      return (float)raw / 16.0;
      break;
    case 1: //Fahrenheit
      return (float)raw / 16.0 * 1.8 + 32.0;
      break;
    default: //er, wut
      return -255;
  }
}
1 Like

You could simply pack all the desired sensor data in a JSON format and send it to HASS via MQTT. There are several Arduino libraries for MQTT clients available.

Thanx for your help, but this is a bit of abra cadabra for me.
I’m only working with HASS and Python sinds a couple of months now
:thinking:

1 Like

Hi, if you want to dive into this I could help you with the code.
I just added some lines for mqtt, without any verification, to give you some hints.

/*
 * ESP8266-01 with multiple DS10B20 temperature sensors
 * reads sensors asyncronsly for less blocking time
 * 
 * Use GPIO2 for the sensors, a single 4.7K pullup resisor is needed.
 * 
 */
#include <OneWire.h>
#include <ESP8266WiFi.h>
#include <MQTT.h>

const char ssid[] = "ssid";
const char pass[] = "pass";
const char mqttBroker = "HASS broker IP";
const char mqttPort = 1883;
const char mqttDeviceName = "someName";
const char mqttUser = "HASS user or MQTT user in broker"
const char mqttPwd = "HASS password or MQTT password in broker"
const char mqttPubTopic = "/homeassistant/DS18S20_STAT"  // topic to send the data to
const char mqttSubTopic = "/homeassistant/DS18S20_CMD"   // topic to receive data from HASS if necessary

WiFiClient net;
MQTTClient client;

const int nsensors = 2;
byte sensors[][8] = {
   { 0x28, 0xC1, 0x02, 0x64, 0x04, 0x00, 0x00, 0x35 },
   { 0x28, 0xE7, 0x0B, 0x63, 0x04, 0x00, 0x00, 0x44 }
};
int16_t tempraw[nsensors];
unsigned long nextprint = 0;

OneWire  ds(2);  // on pin 2 (a 4.7K pullup is necessary)

void setup() {
  Serial.begin(115200);
  delay(10);
  
  WiFi.begin(ssid, pass);
  client.begin(mqttBroker, mqttPort, net);
  client.onMessage(messageReceived);

  connect();
}

void loop() 
{
  if (!client.connected()) {
    connect();
  }

  ds18process(); //call this every loop itteration, the more calls the better.

  if(millis() > nextprint) {
    Serial.print("Temp F: ");
    for(byte j=0; j<nsensors; j++){
      Serial.print(j); Serial.print("=");
      Serial.print(ds18temp(1, tempraw[j])); Serial.print("  ");
	  client.publish(mqttPubTopic, "{ \"sensor_id\":\"" + String(j) +"\", \"temperature\":\"" + String(ds18temp(1, tempraw[j]))+ "\" }" );
    }
    Serial.println();
    nextprint = millis() + 1000; //print once a second
  }
  client.loop();
}

void connect() {
  Serial.print("checking wifi...");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(1000);
  }

  Serial.print("\nconnecting...");
  while (!client.connect(name, mqttUser, mqttPwd)) {
    Serial.print(".");
    delay(1000);
  }

  Serial.println("\nconnected!");

  client.subscribe(mqttSubTopic);
}

void messageReceived(String &topic, String &payload) {
  if (topic == mqttSubTopic {
	// do something with payload
  }
}

/* Process the sensor data in stages.
 * each stage will run quickly. the conversion 
 * delay is done via a millis() based delay.
 * a 5 second wait between reads reduces self
 * heating of the sensors.
 */
void ds18process() {
  static byte stage = 0;
  static unsigned long timeNextStage = 0;
  static byte sensorindex = 100;
  byte i, j;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];

  if(stage == 0 && millis() > timeNextStage) {
    if (!ds.search(addr)) {
      //no more, reset search and pause
      ds.reset_search();
      timeNextStage = millis() + 5000; //5 seconds until next read
      return;
    } else {
      if (OneWire::crc8(addr, 7) != addr[7]) {
        Serial.println("CRC is not valid!");
        return;
      }
      //got one, start stage 1
      stage = 1;
    }
  }
  if(stage==1) {
    Serial.print("ROM =");
    for ( i = 0; i < 8; i++) {
      Serial.write(' ');
      Serial.print(addr[i], HEX);
    }
    //find sensor
    for(j=0; j<nsensors; j++){
      sensorindex = j;
      for(i=0; i<8; i++){
        if(sensors[j][i] != addr[i]) {
          sensorindex = 100;
          break; // stop the i loop
        }
      }
      if (sensorindex < 100) { 
        break; //found it, stop the j loop
      }
    }
    if(sensorindex == 100) {
      Serial.println("  Sensor not found in array");
      stage = 0;
      return;
    }
    Serial.print("  index="); Serial.println(sensorindex);
  
    ds.reset();
    ds.select(sensors[sensorindex]);
    ds.write(0x44, 0);        // start conversion, with parasite power off at the end
    stage = 2; //now wait for stage 2
    timeNextStage = millis() + 1000; //wait 1 seconds for the read
  }
  
  if (stage == 2 && millis() > timeNextStage) {
    // the first ROM byte indicates which chip
    switch (sensors[sensorindex][0]) {
      case 0x10:
        Serial.print("  Chip = DS18S20");  // or old DS1820
        Serial.print("  index="); Serial.println(sensorindex);
        type_s = 1;
        break;
      case 0x28:
        Serial.print("  Chip = DS18B20");
        Serial.print("  index="); Serial.println(sensorindex);
        type_s = 0;
        break;
      case 0x22:
        Serial.print("  Chip = DS1822");
        Serial.print("  index="); Serial.println(sensorindex);
        type_s = 0;
        break;
      default:
        Serial.println("Device is not a DS18x20 family device.");
        stage=0;
        return;
    }
  
    present = ds.reset();
    ds.select(sensors[sensorindex]);
    ds.write(0xBE);         // Read Scratchpad
  
    Serial.print("  Data = ");
    Serial.print(present, HEX);
    Serial.print(" ");
    for ( i = 0; i < 9; i++) {           // we need 9 bytes
      data[i] = ds.read();
      Serial.print(data[i], HEX);
      Serial.print(" ");
    }
    Serial.print(" CRC=");
    Serial.print(OneWire::crc8(data, 8), HEX);
    Serial.print(" index="); Serial.print(sensorindex);
    Serial.println();
  
    int16_t raw = (data[1] << 8) | data[0];
    if (type_s) {
      raw = raw << 3; // 9 bit resolution default
      if (data[7] == 0x10) {
        // "count remain" gives full 12 bit resolution
        raw = (raw & 0xFFF0) + 12 - data[6];
      }
    } else {
      byte cfg = (data[4] & 0x60);
      // at lower res, the low bits are undefined, so let's zero them
      if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
      else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
      else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
      //// default is 12 bit resolution, 750 ms conversion time
    }
    tempraw[sensorindex] = raw;
    stage=0;
  }
}

/* Converts raw temp to Celsius or Fahrenheit
 * scale: 0=celsius, 1=fahrenheit
 * raw: raw temp from sensor
 * 
 * Call at any time to get the last save temperature
 */
float ds18temp(byte scale, int16_t raw) 
{
  switch(scale) {
    case 0: //Celsius
      return (float)raw / 16.0;
      break;
    case 1: //Fahrenheit
      return (float)raw / 16.0 * 1.8 + 32.0;
      break;
    default: //er, wut
      return -255;
  }
}

EDIT:
To sum this up in natural speech, those are the elementary steps:

  • connect ESP8266 to WiFi
  • connect to MQTT broker of HASS
  • send data to HASS in a defined topic via publish
  • in HASS you need to create MQTT sensors that subscribe to the topic and interpret the payload
1 Like

I’m logging my central heating floor distribution unit. There are 12 groups, i’m Measuring the in and return temperature. So in total 24 sensors reading temps every 30 seconds.

I use ds18b20 sensors connected to a rpi with node red. Node red also calculates the delta temps and sends the readings over MQTT. Besides that node red converts the serial number of the sensor to a meaningful name.

Running for 7 months now without any problems.

It’s not hard to setup.

The node-red flow is faily simple:

2 Likes

Instead of using an Arduino, you could use an ESP8266 board, particularly the NodeMCU variant with ESPHome, which is an easy-to-use firmware generator made for HASS. It supports many kinds of sensors out of the box, including the DS18B20. There is a CLI getting started guide here, and if you’re using Hass.io then you’ll be able to install this addon.

Another vote for ESPHome. If you can setup home assistant, you can setup ESPHome on an ESP8266 or ESP32 board. I have no experience with the DS18B20, but I’ve had no issue setting up other sensors and switches using ESPHome.

Hello rtenkooster,

This looks almols exectly like what I would like to do.

Is HASS running on the same RP as nod red ?

I really have to read in on nod red, but seems to be exactrly what I’m looking for.

Do you maybe have the code for me ? and how did you fisical connect to the RP ?

Kind regards,

Erik Siemens

I think I will go for the ESP8266 after all. Mainly becourse ESPHome is an internal part of HASS sinds ver. 0.82.

What is not completely clear to me, can I just connect it directly to my RP for firmware update, or do I need a special cable ? or WiFi conection or something else extra ?

Maybe, an advice; use a ESP8266 or ESP32 price wise almoste the same when buying in the Netherlands

I do use Home Assistant and not HASS.IO

Kind reagards,

Erik

Hi, I don’t have experience with ESPHome.

I do upload the first firmware via an FTDI with my PC. This firmware contains certain libraries for OTA and HTTP update (ESP8266 must be connected to your WiFi then for sure).

Flashing directly from RPi also seems to be possible: click

So the answer is yes, yes and yes. :wink:
You can flash:

  • via RPi
  • via cable (FTDI)
  • via WiFi after flashing a firmware that supports OTA

@Siepie Here are the two main ways of flashing an ESPHome-generated firmware to an ESP8266 dev board. Keep in mind that you only have to physically connect to the device once to flash it initally, then you can use OTA for subsequent updates.


Direct

Option 1 (Directly):
You can connect the ESP dev board to the Rpi directly, where it will shop up on the esphome dashboard after a restart of the addon, making this process a bit more time consuming. This option will not require any software to be installed on your computer.

Option 2 (Download and flash):
You will have to generate a .bin firmware file directly by using COMPILE instead of UPLOAD, and then clicking on the download button. The firmware will be downloaded to your computer, which you will be able to use to flash the dev board with using the esphomeflasher program. Note that you may have to install the drivers for the board’s UART chip. The links to the drivers are on the esphomeflasher page.


Remote

Option 3 (OTA - Remote update)
You can only use this option if the board is already running ESPHome! It is only useful for updates, you will need to follow either of the options above to flash the board initially.

To the top-right of the ESPHome dashboard page, there is a drop-down specifying the currently selected device for flashing. Change this to OTA. You will now be able to click UPLOAD on the configuration you have previously used for that same device you want to flash. This will automatically find and update the device, as long as the hostname and update passwords match up, and the device is connected to the local network via WiFi.

1 Like

I have a lot of experience with esp modules, however connecting 24 Dallas sensors to a single 8266 was to much in my case.

Tried different firmwares, including espeasy, for 1-6 sensors No problem, more however meant a unstable setup for me.

How did you fisically connect everything right now ?


I have connected them all together, powered them externally with a 3.3v regulator. Then wired the gnd and data to the rpi gpio

1 Like

OK, looks alright.
Only I have to use long wires (maybe even 10+ m), I think directly connectring will not work anymore.

Thats why I posted the link at the beginnning. The text “The solution is to emulate an asynchronous system by using the Arduino millis() function and a variable” tiggered me.
What it does (when I understand right) is triggering every single sensor seperatelly for a realy short while. So you have loads less data and les capassative distrortion.

I only would like to do it without the Arduino.

Do you think that is possisble ?

Kind regards

(sorry for my bad Englisch :blush:)

With ESPHome you won’t need to write any code. You just need to specify all the sensors you want to use, and bam, everything’s showing up in Home Assistant.

1 Like

do I not get problems with long wires and the amount of (1-wire) sensors ?
1 messurement for each sensor a minute or so is more than enough.

Don’t I need to put something in the esp8266 board ?

You might get some problems with the long wires, but from what I’ve read, people have vastly differing setups, and for some it works just fine, while for others it doesn’t. I’ve also seen people who fixed their issues by using additional pull-up resistors/resistors of different values, but with the extremely low cost of ESP8266 boards, you might as well use several is different areas of your home to avoid the extra headaches.

1 Like

At this moment I already got all the wires with the sensors connected on the ends comming together at my RP.
At the beginning of this post I put becourse of this a link of someone who made this (many sensors with long cables) with some code to read out asynchronous. Based on the uniq number of each sensor.

I’ve had a look at the Instructable, and I think that the only drawbacks from not having an asynchronous system are the small delays introduced from parsing the data received from the sensors. In your case, where you said that you only want to get readings every minute or so, this wouldn’t matter as much. Do you already have an ESP board? If not, I recommend you the NodeMCU ones. They have UART converters built in, allowing for easy programming.