В статье рассказываю о датчике влажности почвы и использовании его с платами Ардуино.
Предыдущая статья Навигация по рубрике Следующая статья
Общее описание.
Это емкостной датчик влажности почвы. Измеряет емкость между двумя электродами сложной формы, выполненными на печатной плате.
Электроды закрыты маской печатной платы. Поэтому датчик не подвержен коррозии.
Датчик повторяет форм фактор китайского датчика с Али Экспресс.
Есть несколько конструктивных вариантов.
- Электронные компоненты открыты.
- Электронные компоненты залиты компаундом.
- Электронные компоненты залиты компаундом, есть фиксатор кабеля.
- Электронные компоненты залиты компаундом и закрыты защитной крышкой.
Датчик является цифровым измерительным устройством с сетевым интерфейсом TinyOneWireNet. Результат измерений выводится в виде цифрового кода с учетом калибровочных коэффициентов. Коэффициенты содержатся в EEPROM датчика.
Информация с датчиков считывается по 2х проводной линии связи, к которой может быть подключено несколько устройств. Поддерживаются все сетевые функции интерфейса TinyOneWireNet.
Электрическая схема и принцип действия.
Электронная часть датчика представляет собой точный измеритель емкости, преобразователь емкость-код. В принципе, может быть использован для других типов датчиков, преобразующих не электрические величины в значения электрической емкости. Существует довольно много таких емкостных датчиков. Например, датчики давления, уровня жидкости, механической силы, влажности и много других.
Принципиальная схема устройства предельно проста.
Принцип измерения основан на заряде емкости через резистор с фиксированным сопротивлением (R1), срабатывании компаратора по достижению заданного значения и измерении длительности импульса на выходе компаратора.
В качестве источника заряда измеряемой емкости используется напряжение питания (вывод 1 PIC-контроллера), а порог срабатывания компаратора формируется из того же напряжения резисторным делителем. Таким образом, изменение напряжения питания не влияет на точность измерения емкости.
При своей простоте измеритель емкости имеет высокие параметры:
- разрешающая способность измерения длительности импульса заряда 0,0625 мкс;
- широкий диапазон преобразования ( 0,6 … 38000 пкФ ), может быть изменен выбором резистора R1;
- высокую точность и разрешающую способность измерения ( 0,6 пкФ );
- используется цифровая фильтрация для измеряемого кода, а также вычисление из него относительного значения параметра;
- ток потребления в режиме ожидания не более 0,5 мА, в режиме измерения увеличивается до 0,8 мА.
Диапазон измерения емкости (0,6 – 38000 пкФ) определяется сопротивлением заряжающего резистора (100 кОм) и может быть изменен выбором резистора с другим сопротивлением. В последних вариантах датчика я уменьшил его до 47 кОм, чтобы снизить влияние токов утечек.
Даже при этом, диапазон выходного кода от “сухого” до “мокрого” датчика составляет 9 … 500 единиц. Или приблизительно 500 единиц на 100%, разрешающая способность 0,2 %.
При измерении емкости, превышающий верхний порог, или замыкании измерительных входов, устройство определяет ошибку преобразования.
Устройство выводит результат измерений в виде:
- кода;
- длительности импульса;
- емкости;
- относительной влажности с откалиброванными нижним (0%) и верхним (100%) значениями.
Это позволяет использовать другие методы калибровки измерителя, хотя он содержит и собственный алгоритм.
Работа с датчиком влажности почвы в системе Ардуино.
Я подключил два устройства к плате ArduinoNano.
Адреса задал им с помощью монитора на базе платы Ардуино.
Устройства откалиброваны с помощью того же монитора. Ниже расскажу, как это сделать программно.
Для измерения влажности необходимо собственно запустить измерение с помощью функции
uint8_t startMeasurement_SoilMoistureSensor(uint8_t adr, uint8_t averaging); // запуск измерения влажности
и через определенное время (время выполнения измерения) считать результат в коде
uint8_treadCode_SoilMoistureSensor(uint8_tadr, uint16_t * code); // чтение результата измерений влажности (код)
или в процентах
uint8_treadHumidity_SoilMoistureSensor(uint8_tadr, float * hum); // чтение результата измерений влажности (%)
Аргумент averaging в первой функции задает число выборок влажности для усреднения значений измерения.
Число выборок вычисляется, как 2 в степени averaging. Averaging может принимать значения от 0 до 8.
Значение averaging | Число выборок | Время измерения при максимальной емкости, мс |
0 | 1 | 5,1 |
1 | 2 | 10,2 |
2 | 4 | 20,4 |
3 | 8 | 40,8 |
4 | 16 | 81,6 |
5 | 32 | 163,2 |
6 | 64 | 326,4 |
7 | 128 | 652,8 |
8 | 256 | 1305,6 |
Время одной выборки вычисляется, как 1 мс (разряд емкости) + время заряда, которое зависит от значения емкости.
Исходная формула для расчета это зависимость напряжения на конденсаторе (Uc) от напряжения заряда (Up), времени заряда (t), сопротивления цепи заряда (R) и емкости (C):
Uc = Up ( 1 – e-t/RC )
Если напряжение заряда 5 В, порог 3,3 В, получаем:
3,3 = 5 ( 1 – e-t/RC )
И после преобразований:
C= - t / R * ln 0.34 = t * 0.927 / R
Обратите внимание, что значение измеренной емкости не зависит от напряжения питания.
Максимальное значение кода (65535) умножаем на разрешающую способность измерителя времени (0,0625 мкс). Получаем максимальное время измерения 4,096 мс.
- При сопротивлении цепи заряда 100 кОм получаем максимальное значение измеряемой емкости 38 нФ.
- При сопротивлении 47 кОм максимальное значение равно 80,7 нФ.
Важно, что время измерения зависит от измеряемой емкости. В таблице выше приведены значения для максимальной емкости. Если вам известно предельное значение измеряемой емкости, то время измерения можно уменьшить пропорционально. Но не забывайте к каждой выборке прибавлять 1 мс для разряда измеряемого конденсатора.
Теперь можно разработать программу, которая выводит измеренные значения 2х датчиков в виде кодов и относительной влажности.
// измерение влажности
#include <TinyOneWireNet.h>
#define NET_PIN 2 // вывод данных сети
#define BETWEEN_COM_TIME 2 // пауза между командами (мс)
TinyOneWireNet sens(NET_PIN);
uint16_t code;
float hum;
void setup() {
Serial.begin(9600);
sens.onLine(10);
}
void loop() {
Serial.println("");
if( sens.startMeasurement_SoilMoistureSensor(14, 7) == 0 ) {
// запуск измерения без ошибки
delay(500);
Serial.print("Sens 1 Code= ");
if( sens.readCode_SoilMoistureSensor(14, & code) == 0 ) {
// чтение кода без ошибок
Serial.print(code);
}
else {
// ошибка чтения кода
Serial.print("- - -");
}
Serial.print(" Hum= ");
delay(BETWEEN_COM_TIME);
if( sens.readHumidity_SoilMoistureSensor(14, & hum) == 0 ) {
// чтение влажности без ошибок
Serial.print(hum);
Serial.println(" %");
}
else {
// ошибка чтения влажности
Serial.println("- - -");
}
}
else {
// ошибка запуска измерения
delay(500);
Serial.println("Error sens 1");
}
delay(BETWEEN_COM_TIME);
if( sens.startMeasurement_SoilMoistureSensor(17, 7) == 0 ) {
// запуск измерения без ошибки
delay(500);
Serial.print("Sens 2 Code= ");
if( sens.readCode_SoilMoistureSensor(17, & code) == 0 ) {
// чтение кода без ошибок
Serial.print(code);
}
else {
// ошибка чтения кода
Serial.print("- - -");
}
Serial.print(" Hum= ");
delay(BETWEEN_COM_TIME);
if( sens.readHumidity_SoilMoistureSensor(17, & hum) == 0 ) {
// чтение влажности без ошибок
Serial.print(hum);
Serial.println(" %");
}
else {
// ошибка чтения влажности
Serial.println("- - -");
}
}
else {
// ошибка запуска измерения
delay(500);
Serial.println("Error sens 2");
}
delay(BETWEEN_COM_TIME);
}
Чтение калибровочных коэффициентов производится функциями:
uint8_t readCoeff0_SoilMoistureSensor(uint8_t adr, uint16_t * cf); // чтение коэффициента влажности 0%
uint8_t readCoeff100_SoilMoistureSensor(uint8_t adr, uint16_t * cf); // чтение коэффициента влажности 100%
Давайте узнаем, какие коэффициенты установлены для датчика.
// чтение калибровочных коэффициентов
#include <TinyOneWireNet.h>
#define NET_PIN 2 // вывод данных сети
#define BETWEEN_COM_TIME 2 // пауза между командами (мс)
TinyOneWireNet sens(NET_PIN);
uint16_t coeff;
void setup() {
Serial.begin(9600);
sens.onLine(10);
}
void loop() {
Serial.print("Koeff 0% = ");
if( sens.readCoeff0_SoilMoistureSensor(17, & coeff) == 0 ) Serial.print(coeff);
else Serial.print("- - -");
delay(BETWEEN_COM_TIME);
Serial.print(" Koeff 100% = ");
if( sens.readCoeff100_SoilMoistureSensor(17, & coeff) == 0 ) Serial.println(coeff);
else Serial.println("- - -");
delay(BETWEEN_COM_TIME);
}
Загрузка калибровочных коэффициентов происходит под управлением функциями:
void loadCoeff0_SoilMoistureSensor(uint8_t adr, uint16_t cf); // загрузка коэффициента влажности 0%
void loadCoeff100_SoilMoistureSensor(uint8_t adr, uint16_t cf); // загрузка коэффициента влажности 100%
Надо считать текущее значение кода, и загрузить его через одну из этих функций.
Вот скетч, который загружает калибровочный коэффициент 0%. Для упрощения я не обрабатываю ошибки обмена.
// калибровка 0%
#include <TinyOneWireNet.h>
#define NET_PIN 2 // вывод данных сети
#define BETWEEN_COM_TIME 2 // пауза между командами (мс)
TinyOneWireNet sens(NET_PIN);
uint16_t cd;
void setup() {
Serial.begin(9600);
sens.onLine(10);
sens.startMeasurement_SoilMoistureSensor(17, 7);
delay(500);
sens.readCode_SoilMoistureSensor(17, & cd);
Serial.print("Read code = ");
Serial.print(cd);
delay(BETWEEN_COM_TIME);
sens.loadCoeff0_SoilMoistureSensor(17, cd);
sens.readCoeff0_SoilMoistureSensor(17, & cd);
Serial.print(" Read coeff = ");
Serial.println(cd);
}
void loop() {
}
Функции загрузки в EEPROM датчиков отрабатывают необходимую задержку сами. Поэтому после них не требуется пауза перед вызовом других функций.
В следующей статье расскажу о сетевом датчике температуры на базе DS18B20. С ним работать еще проще.