Onlybee.aiНачать бесплатно
Обучение · Техника · ~45 мин

Подключение к onlybee.ai

Прошивка, токен из личного кабинета, калибровка гирей — и график веса в приложении

Железо собрано — осталось научить его отправлять вес в ваш личный кабинет. Понадобится компьютер с бесплатной программой Arduino IDE, WiFi с интернетом и гиря известного веса для калибровки. Программировать не нужно: готовая прошивка вставляется целиком, вы меняете в ней три строки.

Шаг 1 · Компьютер

Установите Arduino IDE и поддержку ESP32

Arduino IDE — программа, которой прошивают плату. Скачайте с arduino.cc (раздел Software), установите.

  • Settings → в поле «Additional boards manager URLs» вставьте адрес пакета ESP32: https://espressif.github.io/arduino-esp32/package_esp32_index.json
  • Tools → Board → Boards Manager → найдите «esp32 by Espressif» → Install.
  • Tools → Library Manager → найдите «HX711 by Bogdan Necula» → Install.
  • Подключите плату кабелем. Tools → Board → «DOIT ESP32 DEVKIT V1». Tools → Port → выберите появившийся порт.
Кабель нужен «с данными». Кабель «только для зарядки» внешне такой же, но порт в списке не появится — это самая частая причина «компьютер не видит плату».
Шаг 2 · Токен

Получите токен весов в личном кабинете

Токен — «пароль устройства»: по нему сервис понимает, чьи это весы и к какому улью писать вес.

Откройте карточку своей семьи в onlybee.ai → кнопка «Подключить весы» → мастер выдаст токен вида onbee_… и покажет готовые строки для прошивки.

Токен показывается ОДИН раз — сразу скопируйте его. Потеряли — не страшно: в «Управлять весами» перевыпустите новый, старый погаснет.
Токен привязан к улью, поэтому вписывать номер улья в прошивку не нужно. Переставили весы под другой улей — смените привязку в «Управлять весами», без перепрошивки.
Шаг 3 · Прошивка

Вставьте прошивку и заполните три строки

Готовый текст прошивки — в мастере «Подключить весы» (и в этом разделе ниже). Вставьте его в Arduino IDE целиком и заполните три строки в самом верху:

  • WIFI_SSID — имя вашей WiFi-сети. Важно: сеть 2.4 ГГц, сети 5 ГГц плата не видит вообще. У телефона-точки доступа включите «максимальную совместимость».
  • WIFI_PASS — пароль сети.
  • PAT — токен onbee_… из шага 2.
scale-wifi.ino — прошивка целиком
/*
 * onlybee.ai — весы улья: ESP32 DevKit v1 + HX711.
 * Плата просыпается раз в 10 минут, меряет вес, шлёт в сервис и засыпает.
 * После deep sleep ESP32 стартует заново, поэтому вся работа в setup(), loop() пуст.
 * Ноль зашит константой (tare() НЕ вызываем — иначе вес улья обнулится при пробуждении).
 * Токен привязан к улью в личном кабинете, номер улья в прошивке не нужен.
 *
 * Железо: DOIT ESP32 DEVKIT V1, HX711 (DT→GPIO19, SCK→GPIO18, VCC→3V3, GND→GND).
 */
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <esp_system.h>
#include <esp_sleep.h>
#include <limits.h>
#include "HX711.h"

// ─────────── ЗАПОЛНИ ЭТИ 3 СТРОКИ ───────────
const char* WIFI_SSID = "МОЯ_СЕТЬ";          // имя WiFi-сети — только 2.4 ГГц!
const char* WIFI_PASS = "ПАРОЛЬ_СЕТИ";
const char* PAT       = "onbee_ВАШ_ТОКЕН";   // токен из мастера «Подключить весы»
// ────────────────────────────────────────────

const char* API_URL = "https://onlybee.ai/api/sensor/readings";

// Калибровка — снимается на шаге 5 гайда, в финальной сборке:
// OFFSET = значение raw при пустой платформе; CAL_FACTOR = units ÷ вес гири (кг).
// До калибровки оставьте как есть — вес будет в «попугаях», это нормально.
const long  OFFSET     = 0;
const float CAL_FACTOR = 1.0;

const uint64_t SLEEP_MINUTES = 10;   // период отправки, минут
const int HX_DT = 19, HX_SCK = 18, LED_PIN = 2;
const int  SAMPLES     = 10;
const long SPREAD_WARN = 5000;
const uint32_t WIFI_TIMEOUT_MS = 20000;   // не ждём сеть вечно — это батарея

// Переменные в RTC-памяти: переживают deep sleep (обычные — нет)
RTC_DATA_ATTR uint32_t bootCount      = 0;
RTC_DATA_ATTR uint32_t sendFails      = 0;
RTC_DATA_ATTR uint32_t totalUptimeSec = 0;   // время с первого включения (сон + работа)

HX711 scale;

const char* resetReasonStr() {
  switch (esp_reset_reason()) {
    case ESP_RST_POWERON:   return "POWERON";
    case ESP_RST_SW:        return "SW";
    case ESP_RST_PANIC:     return "PANIC";
    case ESP_RST_INT_WDT:   return "INT_WDT";
    case ESP_RST_TASK_WDT:  return "TASK_WDT";
    case ESP_RST_DEEPSLEEP: return "DEEPSLEEP";   // норма при пробуждении
    case ESP_RST_BROWNOUT:  return "BROWNOUT";    // просадка питания — тревога
    case ESP_RST_EXT:       return "EXT";
    default:                return "OTHER";
  }
}

// Заснуть. Дальше код НЕ идёт: после таймера плата стартует с setup().
void goSleep() {
  scale.power_down();                    // HX711 в сон: иначе жрёт ~1.5 мА круглосуточно
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
  totalUptimeSec += millis() / 1000 + SLEEP_MINUTES * 60;  // копим «возраст» устройства
  Serial.printf("Сплю %llu мин\n", SLEEP_MINUTES);
  Serial.flush();
  esp_sleep_enable_timer_wakeup(SLEEP_MINUTES * 60ULL * 1000000ULL);
  esp_deep_sleep_start();
}

bool sendReading(float kg, long rawAvg, long spread) {
  WiFiClientSecure client;
  client.setInsecure();
  client.setHandshakeTimeout(15);

  HTTPClient http;
  http.setConnectTimeout(10000);
  http.setTimeout(10000);
  if (!http.begin(client, API_URL)) return false;

  http.addHeader("Content-Type", "application/json");
  http.addHeader("Authorization", String("Bearer ") + PAT);

  char body[256];
  snprintf(body, sizeof(body),
    "{\"weightKg\":%.2f,\"rawAdc\":%ld,\"spreadRaw\":%ld,\"rssiDbm\":%d,"
    "\"freeHeap\":%u,\"uptimeSec\":%lu,\"bootCount\":%lu,\"sendFails\":%lu,"
    "\"resetReason\":\"%s\"}",
    kg, rawAvg, spread, WiFi.RSSI(),
    ESP.getFreeHeap(), totalUptimeSec + millis() / 1000, bootCount, sendFails,
    resetReasonStr());

  int code = http.POST((uint8_t*)body, strlen(body));
  if (code > 0) Serial.printf("POST -> %d  %s\n", code, http.getString().c_str());
  else          Serial.printf("POST ERR %d: %s\n", code, http.errorToString(code).c_str());
  http.end();
  return code == 201;
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  delay(100);

  bootCount++;
  Serial.printf("\n=== onlybee scale === пробуждение #%lu | reset=%s\n", bootCount, resetReasonStr());

  // WiFi запускаем СРАЗУ и не ждём: пока он поднимается, читаем вес.
  // Так эфирное время (самое дорогое по батарее) короче на пару секунд.
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);

  scale.begin(HX_DT, HX_SCK);
  scale.set_scale(CAL_FACTOR);
  scale.set_offset(OFFSET);            // ноль зашит — tare() НЕ вызываем!

  if (!scale.wait_ready_timeout(3000)) {
    Serial.println("HX711 не отвечает — проверь DT/SCK/питание");
    sendFails++;
    goSleep();
  }

  long mn = LONG_MAX, mx = LONG_MIN;
  double sum = 0;
  for (int i = 0; i < SAMPLES; i++) {
    long r = scale.read();
    if (r < mn) mn = r;
    if (r > mx) mx = r;
    sum += r;
  }
  long  rawAvg = (long)(sum / SAMPLES);
  long  spread = mx - mn;
  float kg     = (rawAvg - OFFSET) / CAL_FACTOR;

  Serial.printf("вес=%.2f кг | raw=%ld | spread=%ld\n", kg, rawAvg, spread);
  if (rawAvg == 0)                                  Serial.println("  ! raw=0 — DT/SCK/питание HX711");
  else if (rawAvg > 8000000 || rawAvg < -8000000)   Serial.println("  ! рейл — мост открыт, ищи обрыв");
  else if (spread > SPREAD_WARN)                    Serial.println("  ! большой разброс — контакт/качает улей");

  // теперь дожидаемся сеть (она поднималась параллельно)
  uint32_t t0 = millis();
  while (WiFi.status() != WL_CONNECTED && millis() - t0 < WIFI_TIMEOUT_MS) delay(200);

  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi FAIL — пропускаю отправку, сплю дальше");
    sendFails++;
    goSleep();
  }
  Serial.printf("WiFi OK  IP=%s  RSSI=%d dBm\n", WiFi.localIP().toString().c_str(), WiFi.RSSI());

  if (sendReading(kg, rawAvg, spread)) {
    sendFails = 0;
    digitalWrite(LED_PIN, HIGH); delay(60); digitalWrite(LED_PIN, LOW);
  } else {
    sendFails++;
  }

  goSleep();
}

void loop() { }   // пусто: между циклами плата спит, работа — в setup()
Кавычки в строках — только прямые "…". macOS любит заменять их на «умные» “…” — прошивка перестаёт собираться с ошибкой «missing terminating character». Отключите: Настройки → Клавиатура → «Умные кавычки и тире». Если ошибка уже есть — сотрите кавычки и наберите заново с клавиатуры. Кнопка «Скопировать» переносит текст без этой проблемы.
Шаг 4 · Заливка

Залейте и посмотрите в монитор порта

Нажмите Upload (кнопка со стрелкой). После заливки откройте Serial Monitor (лупа справа сверху), скорость 115200.

Хорошая картина: «WiFi OK», затем каждые несколько секунд «вес=… POST -> 201». Код 201 значит «сервис принял данные».

Код ответаЧто значитЧто делать
201данные приняты ✓ничего — работает
401токен неверный или отозванскопируйте токен заново, проверьте кавычки
409токен привязан к другому ульюуберите HIVE_ID из прошивки или перевыпустите токен
-1 / WiFi FAILсеть недоступнаимя/пароль, сеть 2.4 ГГц, сигнал
Шаг 5 · Калибровка

Ноль и гиря: превращаем «попугаи» в килограммы

Два числа в прошивке делают из сырых значений килограммы. Снимаются один раз, в финальной сборке весов:

OFFSET (ноль): при ПУСТОЙ платформе посмотрите в мониторе значение raw — это и есть OFFSET. Впишите в прошивку.

CAL_FACTOR (масштаб): поставьте на платформу гирю известного веса (мы калибровали гирей 16 кг). Посмотрите units в мониторе и поделите на вес: units ÷ кг = CAL_FACTOR. Пример: 322608 ÷ 16 = 20163.

  • Проверка: пустая платформа ≈ 0.00, гиря ≈ её вес. Нажмите кнопку RST на плате с гирей — должен снова показать вес гири, а не ноль.
  • Перепаяли что-то или передвинули датчики — ноль «уезжает». Это нормально: просто переснимите OFFSET. У нас после перепайки питания ноль сдвинулся на −0.65 кг.
  • Важно: прошивка НЕ обнуляется при каждом включении (иначе вес улья стал бы «нулём» после любой перезагрузки). Ноль зашит числом — потому его и снимают один раз.
Шаг 6 · Под улей

Установка и проверка в кабинете

Поставьте весы под улей на ровное основание. Включите питание от батареи (USB отключён!).

Откройте onlybee.ai → карточка семьи → вкладка «Мониторинг»: вес, привес и график; полоса «Весы · телеметрия» покажет уровень сигнала, здоровье датчика и статус «на связи». Общая картина по всем ульям — раздел «Мониторинг весов».

В телеметрии есть подсказки про железо: «датчик: шумит» — проверить пайку; «просадка питания!» — батарея/стабилизатор не тянут; слабый сигнал — поднести роутер/антенну. Это те же проверки, что в гайде по диагностике.
Письмо с пасеки Михаила — раз в неделю

12 семей в Калужской области. Что было за неделю: привес контрольного улья с весов, работы сезона и новые статьи. Первый выпуск — со стартом «Живой пасеки» этой осенью, до тех пор писем не будет.