GyverLibs / microDS18B20

Легкая и удобная в обращении библиотека для работы с 1-Wire термометрами DS18B20
MIT License
45 stars 11 forks source link

Датчик зависает, притянув пин данных к нулю. #13

Closed vladikas closed 2 years ago

vladikas commented 2 years ago

Специфическая, видимо, ситуация. Всё равно решил написать. ESP8266 собирает данные с bme280 и ds18b20 и отсылает данные на narodmon. Если народмон недоступен, то спотыкается на секунду. При этом ds зависает, притянув ногу к нулю и вообще ни на что не реагирует. Помогает только отключение питания или перевод ds на паразитное питание, тогда ds сам перезагружается, потеряв паразитное питание, замкнув пин данных, и проблемы как бы и нет. Ниже код, для получения этой ситуации. ESP тут в режиме точки доступа, но это не важно, главное, чтоб у неё интернета не было.

#include <ESP8266WiFi.h>
#include <SimpleTimer.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <microDS18B20.h>

SimpleTimer myTimer;

//инициализация датчика температуры
static const uint8_t    dsPin = D5;// GPIO14 пин датчика температуры 
MicroDS18B20<dsPin> tempSensor;

Adafruit_BME280 bme; // use I2C interface D1-GPIO5, D2-GPIO4
Adafruit_Sensor *bme_temp = bme.getTemperatureSensor();
Adafruit_Sensor *bme_pressure = bme.getPressureSensor();
Adafruit_Sensor *bme_humidity = bme.getHumiditySensor();

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

WiFi.mode(WIFI_AP); 
WiFi.softAP("ESP_TEST");

tempSensor.requestTemp();//запросить измерение температуры

Serial.print(F("BME connecting... \r\n"));
for (int i = 10; i > 1; i--) 
    {
    Serial.println(i);
    if(bme.begin(0x76))
        {
        break;
        }
    delay(10);
    }

myTimer.setInterval(10000, sendDataToNarodmon);//раз в пять минут посылать данные
myTimer.setInterval(2000, bmeRead); //таймер для измерения BME280
myTimer.setInterval(2000, readTempDS);  //таймер для измерения температуры
}

void bmeRead(){
sensors_event_t temp_event, pressure_event, humidity_event;
bme_temp->getEvent(&temp_event);
bme_pressure->getEvent(&pressure_event);
bme_humidity->getEvent(&humidity_event);

Serial.print(F("Temperature = "));
Serial.print(temp_event.temperature);
Serial.println(F(" *C"));

Serial.print(F("Humidity    = "));
Serial.print(humidity_event.relative_humidity);
Serial.println(F(" %"));

Serial.print(F("Pressure    = "));
Serial.print(pressure_event.pressure);
Serial.println(F(" hPa"));
}

//температура
void readTempDS(void){
Serial.print(F("readTempDS  = "));
// читаем прошлое значение
Serial.print(tempSensor.getTemp());
Serial.println(F(" *C"));

Serial.print(F("tempSensor.online      = "));Serial.println(tempSensor.online());
Serial.print(F("tempSensor.readTemp    = "));Serial.println(tempSensor.readTemp());
uint8_t address[8];
  if (tempSensor.readAddress(address)) {  // если успешно, выводим
    Serial.print('{');
    for (uint8_t i = 0; i < 8; i++) {
      Serial.print("0x");
      Serial.print(address[i], HEX);  // Выводим адрес
      if (i < 7) Serial.print(", ");
    }
    Serial.println('}');

  } else Serial.println("Not connected");
// запрашиваем новое измерение
tempSensor.requestTemp();   
}

void sendDataToNarodmon(void){
Serial.println(F("sendDataToNarodmon"));

WiFiClient client;
client.connect(F("narodmon.ru"), 8283); //Подключаемся
 String line = client.readStringUntil('\n');

}

void loop(void){
myTimer.run();
}
GyverLibs commented 2 years ago

а зачем каждый раз запрашивать адрес датчика?

vladikas commented 2 years ago

C одной стороны, как бы и не зачем. Это артефакт остался от поиска проблемы. А с другой стороны каким образом это может влиять на датчик? Я долго искал проблему, изначально в коде этого запроса не было. Проблема в таком варианте возникает стабильно, если что-то убрать с этого кода, то лотерея.

GyverLibs commented 2 years ago

Вроде бы датчик притягивает пин, когда идёт преобразование. Библиотека таймера точно работает корректно? Может можно сделать более минимальный пример, где проявляется бага?

vladikas commented 2 years ago

Я попробую, но мне кажется, что вряд-ли получится. И так два дня воевал, а результата ноль. Может это партия датчиков глючная.

GyverLibs commented 2 years ago

Примеры из библиотеки работают?

vladikas commented 2 years ago

Да, всё работает. Подозреваю, что это нюансы работы прерываний на ESP8266.

GyverLibs commented 2 years ago

Прерывания запрещаются на период чтения и отправки битов данных, не думаю что в этом дело

vladikas commented 2 years ago

Я видел. В меру своих познаний ковырялся в коде библиотеки. Просто у ESP есть свои хитрости с прерываниями. Например, для обслуживания WiFi время выделяется. Или особым образом регламентируются обработчики прерываний.

GyverLibs commented 2 years ago

У 8266 основное обслуживание идёт в yield или в конце луп, на чтение дс18 не должно влиять

vladikas commented 2 years ago

Если есть какие-то идеи, что мне попробовать, то пиши. После праздников ещё пару дней поковыряю. Если не найду проблему, то оставлю паразитное питание и всё будет работать.

GyverLibs commented 2 years ago

Хорошо бы локализовать проблему, получить минимальный пример, на котором проявляется бяка

vladikas commented 2 years ago

Повоевал сегодня с кодом немного. Так уж получается, что выше как раз минимальный код, когда появляется проблема. Подвигал DS по всем доступным пинам, проблема не исчезает. Попробовал другую библиотеку, она работает отлично. В общем, я либо буду использовать другую библиотеку, либо буду использовать паразитное питание. В любом случае спасибо тебе за то, что откликнулся. Пример с другой библиотекой, где всё ок:

#include <ESP8266WiFi.h>
#include <SimpleTimer.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Adafruit_BME280.h>

SimpleTimer myTimer;

//инициализация датчика температуры
#define ONE_WIRE_BUS D5// GPIO14 пин датчика температуры 
// 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);

// arrays to hold device address
DeviceAddress insideThermometer;

Adafruit_BME280 bme; // use I2C interface D1-GPIO5, D2-GPIO4
Adafruit_Sensor *bme_temp = bme.getTemperatureSensor();
Adafruit_Sensor *bme_pressure = bme.getPressureSensor();
Adafruit_Sensor *bme_humidity = bme.getHumiditySensor();

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

WiFi.mode(WIFI_AP); 
WiFi.softAP("ESP_TEST");

Serial.print(F("BME connecting... \r\n"));
for (int i = 10; i > 1; i--) 
    {
    Serial.println(i);
    if(bme.begin(0x76))
        {
        break;
        }
    delay(10);
    }

  // report parasite power requirements
  Serial.print("Parasite power is: "); 
  if (sensors.isParasitePowerMode()) Serial.println("ON");
  else Serial.println("OFF");
  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0"); 
    // show the addresses we found on the bus
  Serial.print("Device 0 Address: ");
  printAddress(insideThermometer);
  Serial.println();

  // set the resolution to 9 bit (Each Dallas/Maxim device is capable of several different resolutions)
  sensors.setResolution(insideThermometer, 9);

  Serial.print("Device 0 Resolution: ");
  Serial.print(sensors.getResolution(insideThermometer), DEC); 
  Serial.println();
  sensors.requestTemperatures(); // Send the command to get temperatures

myTimer.setInterval(10000, sendDataToNarodmon);//раз в пять минут посылать данные
myTimer.setInterval(2000, bmeRead); //таймер для измерения BME280
myTimer.setInterval(2000, readTempDS);  //таймер для измерения температуры
}

// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}

void bmeRead(){
sensors_event_t temp_event, pressure_event, humidity_event;
bme_temp->getEvent(&temp_event);
bme_pressure->getEvent(&pressure_event);
bme_humidity->getEvent(&humidity_event);

Serial.print(F("Temperature = "));
Serial.print(temp_event.temperature);
Serial.println(F(" *C"));

Serial.print(F("Humidity    = "));
Serial.print(humidity_event.relative_humidity);
Serial.println(F(" %"));

Serial.print(F("Pressure    = "));
Serial.print(pressure_event.pressure);
Serial.println(F(" hPa"));
}

//температура
void readTempDS(void){
  Serial.println("readTempDS");
  float tempC = sensors.getTempC(insideThermometer);
  if(tempC == DEVICE_DISCONNECTED_C) 
  {
    Serial.println("Error: Could not read temperature data");
    return;
  }
  Serial.print("Temp C: ");
  Serial.print(tempC);

  Serial.print("\r\nDevice 0 Address: ");
  printAddress(insideThermometer);
  Serial.println();

sensors.requestTemperatures(); // Send the command to get temperatures
}

void sendDataToNarodmon(void){
Serial.println(F("sendDataToNarodmon"));

WiFiClient client;
client.connect(F("narodmon.ru"), 8283); //Подключаемся
 String line = client.readStringUntil('\n');

}

void loop(void){
myTimer.run();
}
GyverLibs commented 2 years ago

Это всё таки не похоже на минимальный код) там ещё один датчик и куча всего лишнего. Нужно локализовать проблему, и я её решу

vladikas commented 2 years ago

Я практически построчно удалял из кода "лишнее". Например, не опрашивал BME по влажности. Проблема исчезает.

GyverLibs commented 2 years ago

Видимо звёзды сошлись, или адафрут опять насрали в библиотеку. Вообще как то странно написан код, может компилятор свинью подсунул? mocrods18b20 работает предельно просто, там digitalRead/write и delay, ломаться абсолютно нечему

vladikas commented 2 years ago

Попробую другой вариант, заменю библиотеку для BME280.

GyverLibs commented 2 years ago

А ds18 подключен туда же куда bme280? Так нельзя делать, это не будет работать)

vladikas commented 2 years ago

Нет конечно. BME занимает аппаратные выводы i2c D1 и D2. DS изначально был на D5. Это у меня модуль NodeMcu V3.

GyverLibs commented 2 years ago

Так я смотрю у ДС вообще пин не указан

GyverLibs commented 2 years ago

Наводите порядок в скетче, и всё будет работать

vladikas commented 2 years ago

static const uint8_t dsPin = D5;// GPIO14 пин датчика температуры и

define ONE_WIRE_BUS D5// GPIO14 пин датчика температуры

GyverLibs commented 2 years ago

static const uint8_t dsPin = D5

В microds18 пин не задаётся таким образом

А что за конструкция с запросом адреса в условии, и при else идёт request? Откуда это взято?) Да и с бме280 очень странный код, там в пару строк всё делается без объектов-прослоек

GyverLibs commented 2 years ago
#include <SimpleTimer.h>
SimpleTimer myTimer;

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
Adafruit_BME280 bme;

#include <microDS18B20.h>
MicroDS18B20<D5> sensor;

void setup() {
  Serial.begin(9600);
  bme.begin();
  myTimer.setInterval(2000, bmeRead);
  myTimer.setInterval(2000, readTempDS);
}

void loop() {
  myTimer.run();
}

void bmeRead() {
  Serial.print(bme.readTemperature());
  Serial.print(',');
  Serial.print(bme.readHumidity());
  Serial.print(',');
  Serial.println(bme.readPressure());
}

void readTempDS() {
  if (sensor.readTemp()) Serial.println(sensor.getTemp());
  else Serial.println("DS18 error");
  sensor.requestTemp();
}
vladikas commented 2 years ago

static const uint8_t dsPin = D5

В microds18 пин не задаётся таким образом

А что за конструкция с запросом адреса в условии, и при else идёт request? Откуда это взято?)

И то и другое взято из твоих уроков https://kit.alexgyver.ru/tutorials/ds18b20/ Ну разве что там было "#define DS_PIN 2", но компилятору без разницы статическая константа или дефайн. Ведь компилируется и работает же.

Работа с BME из примера с гитхаба https://github.com/adafruit/Adafruit_BME280_Library/blob/master/examples/bme280_unified/bme280_unified.ino

GyverLibs commented 2 years ago

тогда я не понимаю как вот это может компилироваться

static const uint8_t dsPin = D5;// GPIO14 пин датчика температуры
MicroDS18B20 tempSensor;
vladikas commented 2 years ago

А... Не обратил внимание. Это почему-то гитхаб выкинул что в угловых скобках. Вроде нажал кнопочку "code". Первый раз что-то в гитхаб пишу.

static const uint8_t    dsPin = D5;
MicroDS18B20<dsPin> tempSensor;
GyverLibs commented 2 years ago

По моему примеру выше код работает? Если добавить необходимый запрос на народмон

vladikas commented 2 years ago

Минимальный с пустым запросом на народмон работает. Если делаю полноценный проект (формирование страницы для локального отображения и прочее), то опять DS валится в это странное состояние.

GyverLibs commented 2 years ago

Не могу найти у ДС ситуацию, в которой он зажимает шину и не отпускает. Может это пин есп шалит?

vladikas commented 2 years ago

Поигрался с разными датчиками в итоговом проекте. Упростил запросы к BME. Достаточно не спрашивать адрес датчика DS и вроде всё работает нормально (длительно не проверял). Но самое интересное, что датчик, купленный в другом месте и в другое время работает, там где предыдущие падают в ошибку. Который нормально работает: {0x28, 0xAA, 0xA3, 0x65, 0x40, 0x14, 0x1, 0x59} Падают в ошибку: {0x28, 0xFF, 0x64, 0x01, 0xB2, 0x19, 0x53, 0x5D} {0x28, 0xFF, 0x64, 0x01, 0xB2, 0x18, 0x11, 0x63} {0x28, 0xFF, 0x64, 0x01, 0xBD, 0x68, 0x4A, 0x71} И ещё раз перепроверил, что именно притягивает к нулю - именно пин DS. Так что может быть это партия такая глючная (прилагаю фотку). А тот, который работает нормально, тот в герметичном корпусе. Screenshot

GyverLibs commented 2 years ago

Ну вот я выше кидал минимальный код для опроса и работы системы, ты пишешь

опять DS валится в это странное состояние

В итоге как дела то обстоят?)

vladikas commented 2 years ago

Про тот "минимальный минимальный" я же написал, что он работает нормально. Код из самого первого поста в ветке стабильно роняет DS после первого же запроса к народмону. Проще забить болт, чем отлавливать баг, который работает только на определённом датчике. Может закрыть?

GyverLibs commented 2 years ago

Можно