Розумні ваги для котячої миски на ESP32-C3 та HX711

Spread the love

Хочеш знати, скільки й коли їсть твій кіт – без здогадок? У цій статті ми зберемо прості та точні ваги для котячої миски на базі 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, GNDGND, VCC3.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);
  }
}

Як відкалібрувати

  1. Режим Калібрування: Встановлення CALIBRATION_MODE = true та scale.set_scale(1.0).
  2. Тарування (Обнулення): scale.tare(20) — пояснення, як обнулити ваги.
  3. Визначення Сирого Значення: Отримання сирого значення без вантажу.
  4. Зважування Еталона: Додавання точного вантажу (наприклад, 100 г) та запис сирого значення під навантаженням.
  5. Розрахунок Коефіцієнта (CALIBRATION_FACTOR):
  6. Формула: Коефіцієнт=0.1 Сире значення для 0.1 кг​.
  7. Обговорення знаку: Іноді потрібен негативний коефіцієнт.

Поради точності

  • Бери середнє з 5–10 зчитувань.
  • Дай HX711 1–3 хв на стабілізацію після увімкнення.
  • Не торкайся платформи під час Tare і зважування.
  • Слідкуй за механікою: жорстка база, без люфтів.

Результат – ваги для котячої миски

На OLED бачиш заголовок MASS(g): і поточну вагу великими цифрами. У Serial теж грамами. Від’ємні значення форсуємо до 0 г (зручно, коли миска трохи «гуляє»).

Розумні ваги для котячої миски на ESP32-C3
Розумні ваги для котячої миски на ESP32-C3 та HX711

Залишити коментар