go-dht/dht.go

339 lines
13 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Read temperature and humidity data from a DHT11, DHT22, AM2302 sensors on a Raspberry Pi.
// ---
// Чтение температуры и влажности с датчиков DHT11, DHT22, AM2302 на Raspberry Pi.
package dht
import (
"errors"
"gitfox.ru/victor/go-wiringpi"
)
const (
ERR_SETUP_FAILED = "WiringPi setup failed!"
ERR_BAD_VALUE = "Bad value!"
MAX_TIMINGS = 85
BAD_VALUE = 999
DATA_SIZE = 5
DATA_BITS = DATA_SIZE * 8
)
var (
IterationCount = 10
QueryTimeout = uint(500)
)
// Signal Sensor we're ready to read by pulling pin UP for 10 milliseconds, pulling pin down for 18 milliseconds and then back up for 40 microseconds.
// ---
// Установка высокого уровня на 10 миллисекунд, затем низкого на 18 миллисекунд, а затем снова высокого на 40 микросекунд.
func initDHT(pin int) {
// Initialize sensor.
// ---
// Инициализация датчка.
wiringpi.PinMode(pin, wiringpi.OUTPUT)
wiringpi.DigitalWrite(pin, wiringpi.HIGH)
wiringpi.Delay(10)
wiringpi.DigitalWrite(pin, wiringpi.LOW)
wiringpi.Delay(18)
wiringpi.DigitalWrite(pin, wiringpi.HIGH)
wiringpi.DelayMicroseconds(40)
// Select pit mode INPUT for read.
// ---
// Переключение пина в режим считывания данных.
wiringpi.PinMode(pin, wiringpi.INPUT)
}
// Decode data.
// ---
// Декодирование данных.
func decodeData(data [DATA_SIZE]int) (temperature float64, humidity float64, err error) {
if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) {
humidity = float64(((data[0] << 8) + data[1])) / 10
if (humidity > 100) {
humidity = float64(data[0]) // для DHT11
}
temperature = float64((((data[2] & 0x7F) << 8) + data[3])) / 10
if (temperature > 125) {
temperature = float64(data[2]) // для DHT11
}
if (data[2] & 0x80 == 0x80) {
temperature *= -1
}
return
}
return BAD_VALUE, BAD_VALUE, errors.New(ERR_BAD_VALUE)
}
// Convert bollean value to integer (0 or 1).
// ---
// Преобразование логического значения в целое число (0 или 1).
func btoi(b bool) int {
if b {
return 1
}
return 0
}
// Returns absolute value (without a sign).
// ---
// Возвращает абсолютное значение целого числа (без знака).
func abs(n int) int {
if n < 0 {
n *= -1
}
return n
}
// Sensor read algorithm #1.
// ---
// Алгоритм №1 получения данных с датчика.
func read_dht_1(pin int) (data [DATA_SIZE]int, err error) {
laststate := wiringpi.HIGH
counter := 0
bits := 0
initDHT(pin)
// Detect change and read data.
// ---
// Детектирование изменений и чтение данных.
for i := 0; i < MAX_TIMINGS; i++ {
counter = 0
for (wiringpi.DigitalRead(pin) == laststate) {
counter++
wiringpi.DelayMicroseconds(2)
if (counter == 255) {
break
}
}
laststate = wiringpi.DigitalRead(pin)
if (counter == 255) {
break
}
// First 2 state changes are sensor signaling ready to send, ignore them.
// Each bit is preceeded by a state change to mark its beginning, ignore it too.
// ---
// Первые 2 изменения состояния - это сигналы датчика о готовности к отправке, они пропускаются.
// Каждому биту предшествует изменение состояния, обозначающее его начало, которое также пропускается.
if ((i >= 4) && (i % 2 == 0)) {
data[bits / 8] <<= 1
if (counter > 16) { // Each array element has 8 bits. Shift left 1 bit.
data[bits / 8] |= 1 // Каждый элемент массива имеет 8 бит. Сдвиг влево на 1 бит.
}
bits++
}
if bits >= DATA_BITS { // Prevent index out of range
break // Защита от выхода за пределы массива
}
}
if ((bits == DATA_BITS) && (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF))) {
return
}
return data, errors.New(ERR_BAD_VALUE)
}
// Decode signals.
// ---
// Декодирование данных, полученных с датчика.
func decode_signals(duration [DATA_BITS]int) (result [DATA_BITS]bool) {
// The initial values for the centroids are the minimum and maximum.
// ---
// Начальными значениями для центроидов являются минимальное и максимальное.
min := 0
max := 0
for i := range duration {
if (duration[i] < min) {
min = duration[i]
}
if (duration[i] > max) {
max = duration[i]
}
}
// Assign each observation to the nearest centroid.
// ---
// Назначение каждому значению ближайшую центроидную точку.
for true {
nMap := [DATA_BITS]bool{}
for j := range nMap {
if ((abs(min - duration[j]) > abs(max - duration[j]))) {
nMap[j] = true
} else {
nMap[j] = false
}
}
// Update step: update the centroid location with the mean of the assigned observations.
// ---
// Обновление положения центроида со средним значением.
nMin := 0
nMax := 0
observations := 0
for j := range nMap {
observations += btoi(nMap[j])
if (nMap[j]) {
nMax += duration[j]
} else {
nMin += duration[j]
}
}
// Prevent devision by zero.
// ---
// Предотвращение деления на 0.
if observations == 0 {
nMax = 0
} else {
nMax /= observations
}
if (DATA_BITS - observations) == 0 {
nMin = 0
} else {
nMin /= DATA_BITS - observations
}
// Check convergence: k-means has converged if no assignments have changed.
// ---
// Проверка на сходимость: среднее значение k сходится, если значения не изменились.
if nMap == result {
break
}
// Save min/max values before the next itaration.
// ---
// Сохранение мин/макс значений перед следующей итерацией.
min = nMin
max = nMax
result = nMap
}
return
}
// Sensor read algorithm #2.
// ---
// Алгоритм №2 получения данных с датчика.
func read_dht_2(pin int) (data [DATA_SIZE]int, err error) {
lastState := wiringpi.HIGH
stateDuration := 0
stateChanges := 0
bits := 0
// Saved state duration array.
// ---
// Массив для сохранения состояния сигналов.
signalState := [DATA_BITS]int{}
initDHT(pin)
for stateChanges = 0; (stateChanges < MAX_TIMINGS) && (stateDuration < 255) && (bits < DATA_BITS); stateChanges++ {
stateDuration = 0
for true {
if ((wiringpi.DigitalRead(pin) == lastState) && (stateDuration < 255)) {
stateDuration++
wiringpi.DelayMicroseconds(1)
} else {
break
}
}
lastState = wiringpi.DigitalRead(pin)
// First 2 state changes are sensor signaling ready to send, ignore them.
// Each bit is preceeded by a state change to mark its beginning, ignore it too.
// ---
// Первые 2 изменения состояния - это сигналы датчика о готовности к отправке, они пропускаются.
// Каждому биту предшествует изменение состояния, обозначающее его начало, которое также пропускается.
if ((stateChanges > 2) && (stateChanges % 2 == 0)) {
signalState[bits] = stateDuration
bits++
}
}
// Make sure if all 40 bits are read, otherwise return with a bad read.
// ---
// Проверка, что все 40 бит считаны. В противном случае возвращается ошибка.
if (bits == DATA_BITS) {
// Use the k-means decoder instead of the split one, as it's slightly more robust with neglible runtime increase.
// ---
// Использование метода модуляции, основанного на алгоритме K-means, который используется для улучшения возможностей декодирования,
// так как он более надежен при незначительном увеличении времени выполнения.
signalData := decode_signals(signalState)
for j := range signalData { // Each array element has 8 bits. Shift left 1 bit.
data[j / 8] <<= 1 // Каждый элемент массива имеет 8 бит. Сдвиг влево на 1 бит.
if (signalData[j]) {
data[j / 8] |= 1 // A state with a duration assigned to an upper cluster is a '1'
} // Состояние с длительностью, присвоенное верхнему кластеру, равно "1"
}
// Checksum calculation.
// ---
// Подсчет контрольной суммы.
if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) {
return
}
}
return data, errors.New(ERR_BAD_VALUE)
}
// Converts temperature from Celsius to Fahrenheit
// ---
// Преобразует температуру из градуса Цельсия в градус Фаренгейта
func CelsiumToFahrenheit(celsium float64) float64 {
return celsium * 1.8 + 32
}
// Reading temperature and humidity data by sequentially calling two different algorithms with a delay between calls to reduce the likelihood of errors.
// ---
// Функция получения показаний температуры и влажности с применением подхода снижения вероятности возвращения ошибки путем посдеовательного вызова
// в цикле двух различных алгоритмов с задержкой меджу вызовами
func ReadDHT(pin int) (temperature float64, humidity float64, err error) {
// Initialize WiringPi
// ---
// Инициализация WiringPi
if err = wiringpi.Setup(); err != nil {
return 0, 0, errors.New(ERR_SETUP_FAILED)
}
// Sensor data array.
// ---
// Массив для приема данных с датчика.
data := [DATA_SIZE]int{}
// Calling sequentially two algorithms with a repeat call if an error occurs.
// The maximum number of call iterations is 10 (IterationCount) with a delay of 100 ms (QueryTimeout).
// ---
// Последовательный вызов двух алгоритмов с повторным вызовом в случае возникновения ошибки.
// Максимальное количество итераций вызова - 10 (IterationCount) с задержкой в 500 мс (QueryTimeout).
for n := 0; n < IterationCount; n++ {
// 1st algorithm call.
// ---
// Вызов 1-го алгоримта.
if data, err = read_dht_1(pin); err == nil {
if temperature, humidity, err = decodeData(data); err == nil {
return
}
}
// 2nd algorithm call.
// ---
// Вызов 2-го алгоритма.
wiringpi.Delay(QueryTimeout)
if data, err = read_dht_2(pin); err == nil {
if temperature, humidity, err = decodeData(data); err == nil {
return
}
}
wiringpi.Delay(QueryTimeout)
}
return decodeData(data)
}