Хочеш знати, скільки й коли їсть твій кіт – без здогадок? У цій статті ми зберемо прості та точні ваги для котячої миски на базі ESP32-C3 dev-board із вбудованим OLED-дисплеєм та датчика ваги з модулем HX711. Додамо кнопку Tare для обнулення та режим калібрування. Це буде серія публікацій із трьох частин. Сьогодні працюємо над Частиною 1. Наприкінці отримаємо стабільні покази у грамах прямо на екрані – готову основу для Частини 2 (надсилання у Telegram і веб-дашборд) та Частини 3 (автоматичний підрахунок з’їденого корму з нотифікаціями).
Зміст
Що збудуємо
- Ваги з індикацією MASS(g) на вбудованому OLED плати ESP32-C3.
- Tare: обнулення одним натисканням кнопки.
- Калібрування: режим для розрахунку власного коефіцієнта точності.
- Стабілізація показів (усереднення), відсікання від’ємних значень.
- Механічна платформа під миску: 2 листи фанери 17.5×17.5 см, товщина 6 мм кожен.
Далі – розширимо до IoT-системи з Telegram та веб-дашбордом (Частина 2) і навчимо алгоритм рахувати, скільки кіт реально з’їв, із розумними нотифікаціями (Частина 3).
Компоненти (BOM)
Електроніка
- ESP32-C3 dev-board із вбудованим OLED
- HX711 (підсилювач тензодатчика), живлення 3.3 В.
- Тензодатчик 1–5 кг, підбирай під вагу миски + запас (у мене датчик на 2 кг)
- Модуль тактової кнопки для Tare
- Макетна плата/проводи/кріплення, живлення 5 В USB (або акумулятор – опційно)
Платформа під миску (механіка)
- 2 листи фанери 17.5×17.5 см, товщина 6 мм:
- Верхній лист – опорна платформа для миск
- Нижній лист – база, у якій фіксується тензодатчик.
- Кріплення: гвинти М3(у мене M3 для датчика 47x10x6 мм)/М4, шайби, гайки, антивібраційні ніжки (гумові).
Рекомендації по платформі
- Жорстка збірка: без люфтів і прогинів.
- Тензодатчик ставимо так, щоб вага передавалася через одну точку (з боку чутливого елемента).
- Критично: верхній лист має спиратися через тензодатчик, а не обходити його іншими точками.
Схема підключення (wiring)
Оскільки дисплей вбудований, окреме підключення OLED не потрібне – використовуємо його стандартні I²C-лінії на платі.
Піни (ESP32-C3):
- HX711:
DT → GPIO2,SCK → GPIO1,VCC 3.3V,GND - Вбудований OLED (SSD1306 72×40, I²C): використовується внутрішнє підключення SDA=GPIO5, SCL=GPIO6.
- Модуль кнопки Tare: один контакт SIG/OUT →
GPIO3,GND→GND,VCC→3.3V.
Підключення тензодатчика до HX711:
- червоний: Е +
- чорний: E-
- білий: А-
- синій: А+
Поради по електриці
- Дроти від тензодатчика до HX711 — якомога коротші.
- Загальна земля (GND) для всіх модулів.
- Винось HX711 ближче до датчика, а плату з OLED — туди, де зручно дивитися.
Підготовка середовища
Налаштування Arduino IDE для роботи з ESP-32 можна знайти ТУТ.
Огляд прошивки
- У
setup()ініціалізуємо HX711 і вбудований OLED (черезU8g2). - Перемикач
CALIBRATION_MODE:true— показуємо RAW VAL (сирі значення) для розрахунку коефіцієнтаfalse— робочий режим: застосовуємоCALIBRATION_FACTOR,tare(), показуємо грами великим шрифтом.
- Кнопка Tare: фіксуємо натиснення (фронт HIGH→LOW), виводимо підказки “TARING…” / “TARED!” на дисплей.
- Оновлення екрана кожні ~200 мс; від’ємні значення форсуємо до 0 г.
Повний скетч
#include "HX711.h"
#include <U8g2lib.h>
#include <Wire.h> // Обов'язково для I2C
// ===============================================
// КОНФІГУРАЦІЯ ESP32-C3
// ===============================================
// HX711 використовує GPIO 1 та 2, щоб не конфліктувати з OLED на GPIO 5 та 6.
const int LOADCELL_DOUT_PIN = 2; // DT -> GPIO 2
const int LOADCELL_SCK_PIN = 1; // SCK -> GPIO 1
// Кнопка Tare (Тарування) - використовуємо GPIO 3
const int TARE_BUTTON_PIN = 3;
HX711 scale;
// Вбудований OLED 72x40 (SSD1306) підключений по I2C всередині плати.
// Конструктор U8g2 з явним зазначенням I2C піни: SCL=6, SDA=5.
U8G2_SSD1306_72X40_ER_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, /* clock=*/ 6, /* data=*/ 5);
// ===============================================
// КАЛІБРУВАННЯ
// ===============================================
// Встановіть 'true' для визначення коефіцієнта. Після розрахунку встановіть 'false'.
const bool CALIBRATION_MODE = false;
// ВСТАНОВИТИ ВАШ КОЕФІЦІЄНТ ТУТ (використовується, коли CALIBRATION_MODE = false)
// Визначте своє значення після калібрування.
const float CALIBRATION_FACTOR = 995700.0;
// Глобальна змінна для відстеження стану кнопки
bool lastButtonState = HIGH;
void handleButton() {
bool currentButtonState = digitalRead(TARE_BUTTON_PIN);
// 1. Виявлення НАТИСКАННЯ (перехід з HIGH на LOW)
if (lastButtonState == HIGH && currentButtonState == LOW) {
// Кнопка натиснута
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_7x13_tf);
u8g2.drawStr(0, 20, "TARING...");
u8g2.sendBuffer();
// Виконання обнулення
scale.tare(20);
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_7x13_tf);
u8g2.drawStr(0, 20, "TARED!");
u8g2.sendBuffer();
}
lastButtonState = currentButtonState;
}
void setup() {
Serial.begin(115200);
// Використовуємо INPUT_PULLUP, оскільки кнопка зазвичай підключається до GND.
pinMode(TARE_BUTTON_PIN, INPUT_PULLUP);
// 1. Ініціалізація HX711
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
if (!scale.is_ready()) {
Serial.println("\n\n!!! ПОМИЛКА: HX711 не знайдено. Перевірте підключення до GPIO 1 та 2 !!!\n");
}
// 2. Ініціалізація OLED
u8g2.begin();
u8g2.setFont(u8g2_font_6x10_tf);
u8g2.clearBuffer();
u8g2.drawStr(0, 10, "Starting...");
u8g2.drawStr(0, 25, "HX711 Init...");
u8g2.sendBuffer();
// 3. Налаштування ваг
if (CALIBRATION_MODE) {
scale.set_scale(1.0);
scale.tare(20);
Serial.println("\n--- РЕЖИМ КАЛІБРУВАННЯ ---");
Serial.println("1. Обнулення виконано.");
Serial.println("2. Покладіть відомий вантаж.");
u8g2.clearBuffer();
u8g2.drawStr(0, 10, "CALIB MODE");
u8g2.drawStr(0, 25, "SEE SERIAL");
u8g2.sendBuffer();
} else {
// Режим роботи
scale.set_scale(CALIBRATION_FACTOR);
scale.tare(20);
Serial.println("\n--- РЕЖИМ ВАГ ---");
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_7x13_tf);
u8g2.drawStr(0, 10, "WEIGHT:");
u8g2.sendBuffer();
}
}
void loop() {
// 1. Обробка кнопки Tare
handleButton();
if (!scale.is_ready()) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_6x10_tf);
u8g2.drawStr(0, 10, "HX711 ERR");
u8g2.sendBuffer();
delay(1000);
return;
}
if (CALIBRATION_MODE) {
// ------------------------------------
// Логіка для РЕЖИМУ КАЛІБРУВАННЯ
// ------------------------------------
long raw_value = scale.get_value(5);
Serial.print("Сире значення: ");
Serial.println(raw_value);
// Відображаємо сире значення на OLED
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_7x13_tf);
u8g2.drawStr(0, 10, "RAW VAL:");
char raw_str[15];
sprintf(raw_str, "%ld", raw_value);
u8g2.setFont(u8g2_font_6x10_tf);
u8g2.drawStr(0, 25, raw_str);
u8g2.sendBuffer();
delay(500);
} else {
// ------------------------------------
// Логіка для РЕЖИМУ ВАГ (вивід на OLED)
// ------------------------------------
float weight_kg = scale.get_units(5);
int grams = (int)(weight_kg * 1000);
if (grams < 0) {
grams = 0; // Встановлюємо 0, якщо значення негативне
}
Serial.print("Weight: ");
Serial.print(grams);
Serial.println("g");
// Виведення на OLED-дисплей
u8g2.clearBuffer();
// Верхній рядок: Заголовок
u8g2.setFont(u8g2_font_7x13_tf);
u8g2.drawStr(5, 10, "MASS(g):");
// Основне значення (ГРАМИ)
char grams_str[10];
sprintf(grams_str, "%d", grams);
u8g2.setFont(u8g2_font_fub11_tf);
u8g2.drawStr(20, 25, grams_str);
u8g2.sendBuffer();
delay(200);
}
}Як відкалібрувати
- Режим Калібрування: Встановлення
CALIBRATION_MODE = trueтаscale.set_scale(1.0). - Тарування (Обнулення):
scale.tare(20)— пояснення, як обнулити ваги. - Визначення Сирого Значення: Отримання сирого значення без вантажу.
- Зважування Еталона: Додавання точного вантажу (наприклад, 100 г) та запис сирого значення під навантаженням.
- Розрахунок Коефіцієнта (CALIBRATION_FACTOR):
- Формула: Коефіцієнт=0.1 Сире значення для 0.1 кг.
- Обговорення знаку: Іноді потрібен негативний коефіцієнт.
Поради точності
- Бери середнє з 5–10 зчитувань.
- Дай HX711 1–3 хв на стабілізацію після увімкнення.
- Не торкайся платформи під час Tare і зважування.
- Слідкуй за механікою: жорстка база, без люфтів.
Результат – ваги для котячої миски
На OLED бачиш заголовок MASS(g): і поточну вагу великими цифрами. У Serial теж грамами. Від’ємні значення форсуємо до 0 г (зручно, коли миска трохи «гуляє»).

