From 0d89442d062c2f82453f0e2cbe154d9c90fa83f8 Mon Sep 17 00:00:00 2001 From: Luiza Date: Sun, 12 Jun 2022 11:44:26 +0200 Subject: [PATCH] =?UTF-8?q?Prze=C5=9Blij=20pliki=20do=20''?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DHT.cpp | 289 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ DHT.h | 105 ++++++++++++++++++++ 2 files changed, 394 insertions(+) create mode 100644 DHT.cpp create mode 100644 DHT.h diff --git a/DHT.cpp b/DHT.cpp new file mode 100644 index 0000000..3e78bb7 --- /dev/null +++ b/DHT.cpp @@ -0,0 +1,289 @@ +/*! + * file DHT.cpp + * mainpage DHT series of low cost temperature/humidity sensors. + * section intro_sec Introduction + * section author Author + * section license License + */ + +#include "DHT.h" +#define MIN_INTERVAL 2000 /**< min wartość interwału */ +#define TIMEOUT \ + UINT32_MAX /**< Używana programowo do przekroczenia limitu czasu. Nie limit czasu. Wpisz: uint32_t. */ +/*! @brief Tworzy nową klasę DHT + * @param pin numer pinu do którego podłączony jest czujnik + * param type typ czujnika: DHT21, 22 i tp. + * @param count liczba czujników */ + +DHT::DHT(uint8_t pin, uint8_t count) { + (void)count; // Obejście, aby uniknąć ostrzeżenia kompilatora. + _pin = pin; +#ifdef __AVR + _bit = digitalPinToBitMask(pin); + _port = digitalPinToPort(pin); +#endif + _maxcycles = + microsecondsToClockCycles(1000); //Limit czasu 1 milisekundy odczytu impulsów z czujnika DHT. Zauważ, że liczba jest teraz ignorowana, + //ponieważ algorytm odczytu DHT dostosowuje się w oparciu o szybkość procesora. +} + +/*! + * @brief Skonfiguruj piny czujnika i ustaw czasy ściągania. + * @param usec Opcjonalnie podaj czas podciągania (w mikrosekundach) przed rozpoczęciem odczytu DHT. Domyślnie 55 (patrz deklaracja funkcji w DHT.h). + */ +void DHT::begin(uint8_t usec) { + // Ustaw piny + pinMode(_pin, INPUT_PULLUP); + // Użycie tej wartości gwarantuje, że millis() - lastreadtime będzie od razu >= MIN_INTERVAL. + // Zwróć uwagę, że to przypisanie jest zawijane, ale odejmowanie również. + _lastreadtime = millis() - MIN_INTERVAL; + DEBUG_PRINT("Maksymalne cykle zegara DHT: "); + DEBUG_PRINTLN(_maxcycles, DEC); + pullTime = usec; +} + +/*! + * @brief Odczyt temperatury + * @param S Skala. Wartość logiczna: + * - prawda = stopnie Fahrenheita + * - fałsz = Celsjusz + * @param force true w trybie wymuszania + * @return Wartość temperatury w wybranej skali + */ +float DHT::readTemperature(bool S, bool force) { + float f = NAN; + if (read(force)) { + f = ((word)(data[2] & 0x7F)) << 8 | data[3]; + f *= 0.1; + if (data[2] & 0x80) + f *= -1; + if (S) + f = convertCtoF(f); + } + return f; +} + +/*! + * @brief konwertuje stopnie Celsjusza na Fahrenheita +*/ +float DHT::convertCtoF(float c) { return c * 1.8 + 32; } + +/*! + * @brief konwertuje stopnie Fahrenheita na Celsjusza +*/ +float DHT::convertFtoC(float f) { return (f - 32) * 0.55555; } + +/*! + * @brief odczyt wilgotności + * @param force tryb odczytu + * @return float value - wilgotność w % + */ +float DHT::readHumidity(bool force) { + float f = NAN; + if (read(force)) { + f = ((word)data[0]) << 8 | data[1]; + f *= 0.1; + } + return f; +} + + +/*! + * @brief Obliczany wskaźnik ciepła + * Uproszczona wersja, która odczytuje temperaturę i wilgotność z czujnika + * @param isFahrenheit * prawda, jeśli stopnie Fahrenheita, fałsz, jeśli stopnie Celsjusza + * (domyślnie prawda) + * @return float wskaźnik ciepła + */ +float DHT::computeHeatIndex(bool isFahrenheit) { + float hi = computeHeatIndex(readTemperature(isFahrenheit), readHumidity(), + isFahrenheit); + return hi; +} + +/*! + * @brief obliczany wskaźnik ciepła Używając równań Rothfusza i Steadmana. (http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml) + * @param temperature temperatura w wybranej skali + * @param percentHumidity wilgotność w procentach + * @param isFahrenheit prawda, jeśli stopnie Fahrenheita, fałsz, jeśli stopnie Celsjusza + * @return float wskaźnik ciepła + */ +float DHT::computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit) { + float hi; + if (!isFahrenheit) + temperature = convertCtoF(temperature); + hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) + + (percentHumidity * 0.094)); + + if (hi > 79) { + hi = -42.379 + 2.04901523 * temperature + 10.14333127 * percentHumidity + + -0.22475541 * temperature * percentHumidity + + -0.00683783 * pow(temperature, 2) + + -0.05481717 * pow(percentHumidity, 2) + + 0.00122874 * pow(temperature, 2) * percentHumidity + + 0.00085282 * temperature * pow(percentHumidity, 2) + + -0.00000199 * pow(temperature, 2) * pow(percentHumidity, 2); + if ((percentHumidity < 13) && (temperature >= 80.0) && + (temperature <= 112.0)) + hi -= ((13.0 - percentHumidity) * 0.25) * + sqrt((17.0 - abs(temperature - 95.0)) * 0.05882); + else if ((percentHumidity > 85.0) && (temperature >= 80.0) && + (temperature <= 87.0)) + hi += ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2); + } + + return isFahrenheit ? hi : convertFtoC(hi); +} + +/*! + * @brief Odczytaj wartość z czujnika lub zwróć ostatnią z mniej niż dwóch sekund + * @param force prawda, jeśli używasz trybu force + * @return zwróć wartość zmiennoprzecinkową + */ +bool DHT::read(bool force) { + // Sprawdź, czy czujnik został odczytany mniej niż dwie sekundy temu i wróć wcześniej, aby użyć ostatniego odczytu. + uint32_t currenttime = millis(); + if (!force && ((currenttime - _lastreadtime) < MIN_INTERVAL)) { + return _lastresult; // zwróć ostatni poprawny pomiar + } + _lastreadtime = currenttime; + + // Zresetuj 40 bitów odebranych danych do zera. +data[0] = data[1] = data[2] = data[3] = data[4] = 0; + +#if defined(ESP8266) + yield(); // Obsługuj WiFi / zresetuj program watchdog +#endif + + // Wyślij sygnał startu. Zobacz arkusz danych DHT dla pełnego schematu sygnału: + // http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20AM2302.pdf + // Przejdź w stan wysokiej impedancji, aby umożliwić podciąganiu podniesienie poziomu linii danych i rozpoczęcie procesu odczytu. + pinMode(_pin, INPUT_PULLUP); + delay(1); + + // Najpierw ustaw niską linię danych na okres zgodnie z typem czujnika + pinMode(_pin, OUTPUT); + digitalWrite(_pin, LOW); + delayMicroseconds(1100); // karta danych mówi „co najmniej 1ms” + delay(20); // data sheet says at least 18ms, 20ms just to be safe + + uint32_t cycles[80]; + { + // End the start signal by setting data line high for 40 microseconds. + pinMode(_pin, INPUT_PULLUP); + + // Delay a moment to let sensor pull data line low. + delayMicroseconds(pullTime); + + // Now start reading the data line to get the value from the DHT sensor. + + // Turn off interrupts temporarily because the next sections + // are timing critical and we don't want any interruptions. + InterruptLock lock; + + // First expect a low signal for ~80 microseconds followed by a high signal + // for ~80 microseconds again. + if (expectPulse(LOW) == TIMEOUT) { + //DEBUG_PRINTLN(F("DHT timeout waiting for start signal low pulse.")); + _lastresult = false; + return _lastresult; + } + if (expectPulse(HIGH) == TIMEOUT) { + //DEBUG_PRINTLN(F("DHT timeout waiting for start signal high pulse.")); + _lastresult = false; + return _lastresult; + } + + // Now read the 40 bits sent by the sensor. Each bit is sent as a 50 + // microsecond low pulse followed by a variable length high pulse. If the + // high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds + // then it's a 1. We measure the cycle count of the initial 50us low pulse + // and use that to compare to the cycle count of the high pulse to determine + // if the bit is a 0 (high state cycle count < low state cycle count), or a + // 1 (high state cycle count > low state cycle count). Note that for speed + // all the pulses are read into a array and then examined in a later step. + for (int i = 0; i < 80; i += 2) { + cycles[i] = expectPulse(LOW); + cycles[i + 1] = expectPulse(HIGH); + } + } // Timing critical code is now complete. + + // Inspect pulses and determine which ones are 0 (high state cycle count < low + // state cycle count), or 1 (high state cycle count > low state cycle count). + for (int i = 0; i < 40; ++i) { + uint32_t lowCycles = cycles[2 * i]; + uint32_t highCycles = cycles[2 * i + 1]; + if ((lowCycles == TIMEOUT) || (highCycles == TIMEOUT)) { + //DEBUG_PRINTLN(F("DHT timeout waiting for pulse.")); + _lastresult = false; + return _lastresult; + } + data[i / 8] <<= 1; + // Now compare the low and high cycle times to see if the bit is a 0 or 1. + if (highCycles > lowCycles) { + // High cycles are greater than 50us low cycle count, must be a 1. + data[i / 8] |= 1; + } + // Else high cycles are less than (or equal to, a weird case) the 50us low + // cycle count so this must be a zero. Nothing needs to be changed in the + // stored data. + } +/* + DEBUG_PRINTLN(F("Received from DHT:")); + DEBUG_PRINT(data[0], HEX); + DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[1], HEX); + DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[2], HEX); + DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[3], HEX); + DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[4], HEX); + DEBUG_PRINT(F(" =? ")); + DEBUG_PRINTLN((data[0] + data[1] + data[2] + data[3]) & 0xFF, HEX); +*/ + // Check we read 40 bits and that the checksum matches. + if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) { + _lastresult = true; + return _lastresult; + } else { + //DEBUG_PRINTLN(F("DHT checksum failure!")); + _lastresult = false; + return _lastresult; + } +} + +// Expect the signal line to be at the specified level for a period of time and +// return a count of loop cycles spent at that level (this cycle count can be +// used to compare the relative time of two pulses). If more than a millisecond +// ellapses without the level changing then the call fails with a 0 response. +// This is adapted from Arduino's pulseInLong function (which is only available +// in the very latest IDE versions): +// https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/wiring_pulse.c +uint32_t DHT::expectPulse(bool level) { +#if (F_CPU > 16000000L) + uint32_t count = 0; +#else + uint16_t count = 0; // To work fast enough on slower AVR boards +#endif +// On AVR platforms use direct GPIO port access as it's much faster and better +// for catching pulses that are 10's of microseconds in length: +#ifdef __AVR + uint8_t portState = level ? _bit : 0; + while ((*portInputRegister(_port) & _bit) == portState) { + if (count++ >= _maxcycles) { + return TIMEOUT; // Exceeded timeout, fail. + } + } +// Otherwise fall back to using digitalRead (this seems to be necessary on +// ESP8266 right now, perhaps bugs in direct port access functions?). +#else + while (digitalRead(_pin) == level) { + if (count++ >= _maxcycles) { + return TIMEOUT; // Exceeded timeout, fail. + } + } +#endif + + return count; +} diff --git a/DHT.h b/DHT.h new file mode 100644 index 0000000..c3a6f28 --- /dev/null +++ b/DHT.h @@ -0,0 +1,105 @@ +/*! + * @file DHT.h + * + * This is a library for DHT series of low cost temperature/humidity sensors. + * + * You must have Adafruit Unified Sensor Library library installed to use this + * class. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit andopen-source hardware by purchasing products + * from Adafruit! + * + * Written by Adafruit Industries. + * + * MIT license, all text above must be included in any redistribution + */ + +#ifndef DHT_H +#define DHT_H + +#include "Arduino.h" + +/* Uncomment to enable printing out nice debug messages. */ +//#define DHT_DEBUG + +#define DEBUG_PRINTER \ + Serial /**< Define where debug output will be printed. \ + */ + +/* Setup debug printing macros. */ +#ifdef DHT_DEBUG +#define DEBUG_PRINT(...) \ + { DEBUG_PRINTER.print(__VA_ARGS__); } +#define DEBUG_PRINTLN(...) \ + { DEBUG_PRINTER.println(__VA_ARGS__); } +#else +#define DEBUG_PRINT(...) \ + {} /**< Debug Print Placeholder if Debug is disabled */ +#define DEBUG_PRINTLN(...) \ + {} /**< Debug Print Line Placeholder if Debug is disabled */ +#endif + +/* Define types of sensors. */ +static const uint8_t DHT22{22}; /**< DHT TYPE 22 */ + +#if defined(TARGET_NAME) && (TARGET_NAME == ARDUINO_NANO33BLE) +#ifndef microsecondsToClockCycles +/*! + * As of 7 Sep 2020 the Arduino Nano 33 BLE boards do not have + * microsecondsToClockCycles defined. + */ +#define microsecondsToClockCycles(a) ((a) * (SystemCoreClock / 1000000L)) +#endif +#endif + +/*! + * @brief Class that stores state and functions for DHT + */ +class DHT { +public: + DHT(uint8_t pin, uint8_t type, uint8_t count = 6); + void begin(uint8_t usec = 55); + int readTemperature(bool S = false, bool force = false); + int convertCtoF(float); + int convertFtoC(float); + int computeHeatIndex(bool isFahrenheit = true); + int computeHeatIndex(float temperature, float percentHumidity, + bool isFahrenheit = true); + int readHumidity(bool force = false); + bool read(bool force = false); + +private: + uint8_t data[5]; + uint8_t _pin, _type; +#ifdef __AVR + // Use direct GPIO access on an 8-bit AVR so keep track of the port and + // bitmask for the digital pin connected to the DHT. Other platforms will use + // digitalRead. + uint8_t _bit, _port; +#endif + uint32_t _lastreadtime, _maxcycles; + bool _lastresult; + uint8_t pullTime; // Time (in usec) to pull up data line before reading + + uint32_t expectPulse(bool level); +}; + +/*! + * @brief Class that defines Interrupt Lock Avaiability + */ +class InterruptLock { +public: + InterruptLock() { +#if !defined(ARDUINO_ARCH_NRF52) + noInterrupts(); +#endif + } + ~InterruptLock() { +#if !defined(ARDUINO_ARCH_NRF52) + interrupts(); +#endif + } +}; + +#endif