Введение
Весы на чипе: Тензо + HX711
Введение
Весы - это устройство, которое позволяет измерять массу тела или предмета. Существует множество разных типов весов, но один из самых распространенных и простых в использовании - это электронные весы, которые основаны на принципе работы тензодатчика. Тензодатчик - это датчик, который меняет свое электрическое сопротивление в зависимости от приложенной к нему силы. Таким образом, можно измерять силу, действующую на тензодатчик, а затем переводить ее в массу, зная ускорение свободного падения.
Однако тензодатчик выдает очень слабый сигнал, который необходимо усилить и преобразовать в цифровой формат для дальнейшей обработки. Для этого используется специальный микросхема - аналого-цифровой преобразователь (АЦП) HX711. Эта микросхема специально разработана для работы с тензодатчиками и имеет высокую точность, разрешение и скорость измерения. АЦП HX711 подключается к тензодатчику и к микроконтроллеру, который может считывать данные с АЦП и выполнять различные операции с ними, такие как калибровка, тарирование, вывод на дисплей и т.д.
В этой статье мы рассмотрим, как сделать простые весы, используя тензодатчик и АЦП HX711. Мы познакомимся с основными характеристиками и принципами работы этих компонентов, а также с тем, как подключить их к микроконтроллеру ESP8266 и написать программу для работы с ними. После этого мы сможем измерять массу любого предмета с помощью наших весов.
Что такое тензодатчик
Тензодатчик - это датчик, который преобразует величину деформации в электрический сигнал, который можно измерить и обработать. Тензодатчики используются для измерения силы, давления, ускорения, перемещения, крутящего момента и других физических величин. Существуют разные типы тензодатчиков, которые основаны на разных физических принципах, таких как тензорезистивный, пьезоэлектрический, оптико-поляризационный, пьезорезистивный и волоконно-оптический.
Наиболее распространенным типом тензодатчика является тензорезистивный, который меняет свое сопротивление в зависимости от приложенной к нему силы. Такой датчик состоит из упругого элемента, на котором закреплен тензорезистор. Тензорезистор - это проводник, который имеет мелкоячеистую сетку или спираль из тонкой металлической или полупроводниковой нити. Когда на упругий элемент действует сила, он деформируется вместе с тензорезистором, что приводит к изменению длины и площади поперечного сечения нити. По закону Ома, сопротивление проводника пропорционально его длине и обратно пропорционально его площади поперечного сечения, поэтому сопротивление тензорезистора меняется в зависимости от деформации.
Для повышения точности и устойчивости измерений, часто используются несколько тензорезисторов, соединенных в мостовую схему.
Общий принцип работы представлен на схеме ниже:
Для измерения сопротивления тензорезистора мост питается постоянным напряжением, а выходное напряжение моста подается на измерительный прибор, например, вольтметр. Когда мост сбалансирован, то есть сопротивления во всех плечах равны, выходное напряжение равно нулю. Когда на тензорезистор действует сила, его сопротивление меняется, и мост становится несбалансированным, появляется выходное напряжение, которое пропорционально изменению сопротивления тензорезистора, а, следовательно, и силе, приложенной к датчику.
Наглядно это представлено на схеме ниже:
Данный тип тензодатчиков относится к, так называемому, мостовом типу. Чаще всего, он выполнен в виде алюминиевого бруска специальной формы с 4 крепежными отверстиями.
Вариант исполнения несколько отличается от максимальной разрешенной прилагаемой нагрузки, но принципы работы остаются теми же. В продаже часто встречаются тензодатчики на 1, 2, 5, 10 и 20 кг.
Также на торговых площадках можно увидеть и тензодатчики и, так называемого, полумостового типа с максимальной нагрузкой до 50 кг.
Выглядят они так:
Тензорезисторы в них объединены в полумост. Датчики соединяются между собой в полный мост и, затем, подключаются к АЦП, который фиксирует значения напряжения. Имеют на выходе 3 провода. Чаще всего используется следующая схема подключения из 4 датчиков:
Для одного датчика схема будет выглядеть так:
Обработка полученных с тензодатчика значений. Модуль HX711.
Как мы выяснили ранее, при изменении нагрузки на тензодатчик меняется значение сопротивления тензорезистора и, соответственно, падает напряжение на выходе. То есть, зная точное значение напряжения на входе и измерив напряжение на выходе, мы можем выявить закономерность падения напряжения, в зависимости от приложенной силы. Затем преобразовать аналоговое значение напряжения в цифровой вид и, с помощью микроконтроллера, вычислить прилагаемое усилие. Для этого преобразования нам потребуется АЦП.
Но проблема состоит в том, что падение напряжения, при приложении силы к тензодатчику, весьма незначительно. Всего лишь несколько милливольт. Встроенный в микроконтроллер АЦП попросту не в состоянии отследить эти изменения. Требуется усиление сигнала. Для этих целей мы будем использовать внешний модуль АЦП, на базе микросхемы HX711. Ниже его технические характеристики:
- Напряжение питания – 6-5.5 В
- Рабочее напряжение – 5 В
- Количество каналов подключения тензодатчиков – 2
- Коэффициент усиления – 32, 64, 128
- Дифференциальный вход с напряжением – ± 40 мВ
- Разрядность АЦП – 24 бит
- Частота измерений – 80 Гц
- Рабочий ток <10 мА
- Рабочая температура – -40 – 85 °С
- Размеры: 38x21x10 мм
Внешний вид его представлен на фото ниже:
Также есть несколько других вариантов данного модуля.
Отличается от предыдущего наличием внешнего защитного экрана от наводок для микросхемы HX711.
Также есть и такой вариант:
На нем используется немного более усовершенствованная обвязка микросхемы, что в теории позволяет получать более стабильные и точные показания. Но на практике разницы с первым "зеленым" вариантом микросхемы практически нет.
Модуль имеет две группы контактов JP1 и JP2.
Назначение контактов группы JP1:
- E+, E- питание тензодатчиков;
- A-, A+ дифференциальный вход канала А;
- B-, B+ дифференциальный вход канала B.
Назначение контактов группы JP2:
- VCC - напряжение питания;
- GND – общий контакт;
- DT – линия данных;
- SCK – линия синхронизации.
У дифференциального входа канала А возможно программно изменять коэффициент усиления сигнала с 64 до 128.
Дифференциальный вход канала B имеет фиксированный коэффициент 32.
Чем выше коэффициент усиления, тем более точные показания можно снимать с тензодатчиков.
Контакты группы JP2 служат для подключения к микроконтроллеру.
- VCC - 3.3В или 5В
- GND - GND
- DT - любой GPIO пин
- SCK - любой GPIO пин
Работа с модулем HX711
Для начала необходимо соединить тензодатчик и АЦП HX711 с микроконтроллером. На схеме ниже представлен один из вариантов подключения к NodeMCU ESP8266:
После подключения модуля HX711 к тензодатчику и микроконтроллеру нам необходимо каким-то образом получить с него значения для дальнейшей обработки. Для этого подробно разберем протокол обмена данными по которому работает данный модуль
Описание работы протокола передачи данных
Протокол связи на самом деле достаточно прост для понимания и чем-то похож на SPI. Для его работы требуется всего два пина:
- dataPin: вывод, на который побитово будут поступать данные
- clockPin: пин синхронизации, задает начало и конец передачи каждого бита данных на dataPin
Пока цифровые данные не готовы для извлечения,цифровой выход DOUT модуля HX711 имеет высокий уровень сигнала. При этом на входе синхронизации PD_SCK должен быть низкий уровень. Появление низкого уровня на выходе DOUT указывает на то, что данные готовы для извлечения.
С помощью подачи 25 – 27 импульсов на вывод PD_SCK данные выдвигаются на выход DOUT. По каждому импульсу на выводе PD_SCK 1 бит данных выдвигается на цифровой выход DOUT, причем старшими разрядами вперед. Так происходит до тех пор, пока все 24 бита не будут использованы для выдвижения данных. 25-ый импульс на входе PD_SCK снова вернет на выходе DOUT высокий уровень сигнала.
Завершается передача данных подачей определенного числа импульсов на вход PD_SCK для выбора входа и коэффициента усиления при следующем сборе данных на АЦП. Для того, чтобы не возникали ошибки при последовательной передаче данных, количество импульсов, подаваемых на вход PD_SCK, должно быть в диапазоне 25 – 27 в пределах интервала (или цикла) обработки.
Структура передаваемых данных показана на схеме ниже:
Программная реализация протокола
Исходя из этой информации можно написать простой скетч в Arduino IDЕ. Объявляем пины, устанавливаем режим работы и необходимые уровни сигнала:
#include <Arduino.h>
#define CLOCK_PIN 13 //GPIO12
#define OUT_PIN 12 //GPIO13
#define GAIN 1 //Коэффициент усиления chA 128=1; chA 64=2; chB 32=2
void setup() {
Serial.begin( 115200 ); //Открываем серийный порт
pinMode(CLOCK_PIN, OUTPUT);//пин тактирования на вывод
pinMode(OUT_PIN, INPUT); //пин тактирования на вход
digitalWrite(CLOCK_PIN, LOW); //устанавливаем на пине тактирования низкий уровень как готовность к приему данных
}
Далее в цикле loop() будем постоянно проверять уровень сигнала на OUT_PIN. Как только он примет значение 0, то есть данные готовы для извлечения, выполняем чтение. Как было сказано выше, модуль выдает 24 битный сигнал. Или же, говоря другими словами, 3 байта данных. Для удобства, объявим одномерный массив из 3 элементов, в каждом из которых будет записан ровно 1 байт полученных данных.
Каждый раз, для получения нового бита данных, мы будем отправлять короткий импульс на CLOCK_PIN, сигнализирующий о готовности к получению.
После получения всех 24 бит данных нам необходимо подать завершающую серию импульсов модулю HX711 на CLOCK_PIN для выбора канала и коэффициента усиления на следующей итерации.
- Канал А. Коэффициент усиления 128 - 10
- Канал А. Коэффициент усиления 64 - 101010
- Канал В. Коэффициент усиления 32 - 1010
В программе это выглядит так:
void loop() {
while (digitalRead(OUT_PIN) == HIGH) //Ожидаем сигнала о готовности данных для извлечения
byte data[3]; //Объявляем байтовый массив для хранения полученных данных
for (byte j = 3; j--;) {
data[j] = shiftIn(OUT_PIN,CLOCK_PIN, MSBFIRST); //заполняем массив блоками данных
}
// отправляем завершающий блок данных
for (int i = 0; i < GAIN; i++) {
digitalWrite(CLOCK_PIN, HIGH);
digitalWrite(CLOCK_PIN, LOW);
}
//преобразуем байтовый массив в десятичное число
data[2] ^= 0x80;
long hxdata=((uint32_t) data[2] << 16) | ((uint32_t) data[1] << 8) | (uint32_t) data[0];
Serial.println(hxdata);//выводим в консоль
delay(1000); //задержка перед следующим чтением данных
}
Функция shiftIn считывает данные по одному биту за раз, начиная с самого старшего или младшего значащего бита. Для каждого бита она поднимает тактовый вывод в высокий уровень, читает следующий бит с вывода данных и опускает тактовый вывод в низкий уровень.
Считав 8 бит, мы переходим к следующему элементу массива. Таким образом, после завершения считывания, мы имеем массив из 3 байт данных.
Далее нам нам необходимо полученное число перевести из двоичной системы счисления в десятичную. Для этого мы к третьему элементу массива data (старшему байту числа) применяем оператор ^= с операндом 0x80. Оператор ^= выполняет побитовое исключающее или между двумя операндами и присваивает результат левому операнду. Побитовое исключающее или возвращает 1, если биты разные, и 0, если одинаковые. Например, если data2 = 0x12, то после выполнения этого кода data2 станет равным 0x92, потому что 0x12 ^ 0x80 = 0x92.
Далее, мы три байта из массива data объединяем в одно 32-битное целое число без знака. Оператор << сдвигает биты влево на указанное количество позиций, а оператор | выполняет побитовое или между двумя операндами.
Далее, мы выводим полученное десятичное число в консоль, ожидаем 1 секунду и переходим к следующей итерации цикла.
Прошиваем скетч и смотрим в консоль:
Данные принимаются, цифры отображаются. В целом, задачу по преобразованию аналогового значения напряжения на тензодатчике в цифры для микроконтроллера, мы выполнили. Осталось только научиться переводить полученные показания в привычные нам граммы и килограммы.
Калибровка датчика. Получаем реальный вес.
Осталось решить простую задачу. Как перевести показания датчика в реальный вес в граммах? На самом деле, не так уж и сложно. Для этого нам необходимо знать какое значение выдает датчик без нагрузки. То есть, узнать условное начало отсчета. Или другими словами выполнить тарирование. Далее, при размещении веса на датчике, будем вычитать из полученных показаний это значение. Разница и будет нашим весом. Но это, все еще не граммы.
Далее, нам будет необходимо установить соответствие полученного значения реальному весу в граммах. Проще всего это сделать, взяв груз с заранее известным весом. Потом, просто посчитать коэффициент по следующей формуле:
К=(Вг -Вп)/Гр
Где:
- Вг - показания датчика с грузом
- Вп - показания датчика без груза
- Гр - вес груза в граммах
- К - коэффициент
Далее, зная коэффициент, мы можем легко переводить значения, полученные с датчика, в граммы по формуле:
Гр=(Вг -Вп)/К
Где:
- Вг - показания датчика с грузом
- Вп - показания датчика без груза
- Гр - вес груза в граммах
- К - коэффициент
Переходим к нашему скетчу. Вынесем определение веса в отдельную функцию, введем дополнительные переменные и добавим в Setup() расчет коэффициента. Полный код ниже:
#include <arduino.h>
#define CLOCK_PIN 13 //GPIO12
#define OUT_PIN 12 //GPIO13
#define GAIN 1 //Коэффициент усиления chA 128=1; chA 64=2; chB 32=2
long tareWeight; //вес пустых весов
long hxdata; //вес с грузом
float weight; //вес в граммах
float k=0; //коэффициент
float knownWeight=150.2; //известный вес груза в граммах
void setup() {
Serial.begin(9600 ); //Открываем серийный порт
pinMode(CLOCK_PIN, OUTPUT);//пин тактирования на вывод
pinMode(OUT_PIN, INPUT); //пин тактирования на вход
digitalWrite(CLOCK_PIN, LOW); //устанавливаем на пине тактирования низкий уровень как готовность к приему данных
tareWeight=getWeight();//получаем показания датчика без груза
Serial.println("Положите известный вес на платформу в течении 10 секунд");
delay(10000);//ожидаем 10 секунд
hxdata=getWeight();
k=(hxdata-tareWeight)/knownWeight;
Serial.printf("К=%.2f. Уберите вес с платформы в течении 10 секунд\n",k);
delay(10000); //ждем 10 секунд
}
long getWeight()
{
while (digitalRead(OUT_PIN) == HIGH); //Ожидаем сигнала о готовности данных для извлечения
byte data[3]; //Объявляем байтовый массив для хранения полученных данных
for (byte j = 3; j--;) {
data[j] = shiftIn(OUT_PIN,CLOCK_PIN, MSBFIRST); //заполняем массив блоками данных
}
// отправляем завершающий блок данных
for (int i = 0; i < GAIN; i++) {
digitalWrite(CLOCK_PIN, HIGH);
digitalWrite(CLOCK_PIN, LOW);
}
//преобразуем байтовый массив в десятичное число
data[2] ^= 0x80;
return ((uint32_t) data[2] << 16) | ((uint32_t) data[1] << 8) | (uint32_t) data[0];
}
void loop() {
hxdata=getWeight();
weight=(hxdata-tareWeight)/k; //вычисляем вес в граммах
Serial.printf("Вес: %.2f грамм\n",weight);//выводим вес в консоль
delay(10000); //задержка 10 секунд перед следующим чтением данных
}
После загрузки скетча, открываем консоль и видим текст, с предложением положить груз с известным весом на платформу весов. Вес задается в переменой knownWeight. Спустя 10 секунд, в консоли выдается рассчитанный коэффициент. После этого, у вас есть 10 секунд, чтобы снять груз с платформы. Далее программа переходит в рабочий режим и можно пробовать взвешивать различные грузы. В консоли будет выводится вес в граммах, с учетом вычисленного коэффициента.
На скриншоте ниже лог консоли:
Возможные проблемы
Тензодатчик, сам по себе, довольно чувствительный элемент. На его показания могут влиять целый ряд факторов, таких как:
- Температура
- Напряжение в цепи
- Электромагнитные наводки от сигнальных проводов
- Ошибки в проектировании модуля и много другое
Показания с одним и тем же грузом, могут сильно отличаться в разные периоды времени и в различных атмосферных условиях. Чтобы свести погрешность измерений к минимуму, можно предпринять несколько действий:
- Не используйте длинные провода. В особенности те, которые идут от тензодатчика к HX711. В идеале его вообще лучше заменить на хороший кабель с экранированием. Или, как минимум, скрутить в косичку, чтобы минимизировать наводки.
- Используйте хороший источник питания. Желательно со стабильным напряжением 5 вольт. Хотя модуль HX711 может работать и от 3.3 вольт, но дрейф показаний при питании от 5 вольт значительно ниже.
- Не все модули HX711 одинаково хороши. Самые нестабильные показания были замечены на модуле с зеленой платой. На красном все несколько лучше. Фиолетовый где-то посередине. Идеальный вариант - отказаться от китайской HX711 и использовать, например, Analog Device AD7799. Показания не плавают и точность заметно выше.
- Влияние температуры можно минимизировать, проведя измерения в различных условиях и составив таблицу влияния температуры на показания. Затем, исходя из этих данных, вычислить температурный коэффициент.
Обзор библиотек для работы с HX711
В данной статье мы рассмотрели как работать модулем HX711 в Arduino IDE. Протокол обмена данными не такой уж и сложный, но если у вас это вызывает затруднения, то существует несколько сторонних библиотек, упрощающих эту задачу. Перечислю некоторые из них:
- HX711 от bodge - пожалуй, самая популярная и продвинутая библиотека. Имеет улучшенную функцию тарирования, расчет веса в граммах и унциях, ввод поправочного коэффициента, установка множителя усиления "на лету", фильтрацию показаний и многое другое.
- GyverHX711 - библиотека от небезызвестного Алекса Гайвера. Обладает чуть более скромным функционалом. В частности, не умеет выводить вес в граммах и учитывать калибровочный коэффициент, но имеет несколько уникальных фишек и весьма мало весит.
- HX711_ADC от olkal - развитие идей библиотеки от bodge. Умеет все то же самое, но делает чуть лучше. Имеет продвинутую систему фильтрации полученных значений.
Заключение
В этой статье мы изучили, как создать весы, используя тензодатчик и модуль HX711. Было рассмотрено, что такое тензодатчик, как он работает и как его подключить. Также выяснили, какие функции выполняет модуль HX711, как он общается с микроконтроллером и как его настроить. Разобрали протокол обмена данными с модулем и написали скетч для работы с ним. Также было проведен краткий обзор различных библиотек для взаимодействия с модулем HX711. И, напоследок, было дано несколько советов по улучшению точности и стабильности измерений. Надеюсь, что эта статья была полезна и интересна для вас и вы сможете применить полученные знания в своих проектах. Спасибо за внимание!