339 lines
13 KiB
Go
339 lines
13 KiB
Go
// 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)
|
||
}
|