Подключение к onlybee.ai
Прошивка, токен из личного кабинета, калибровка гирей — и график веса в приложении
Железо собрано — осталось научить его отправлять вес в ваш личный кабинет. Понадобится компьютер с бесплатной программой Arduino IDE, WiFi с интернетом и гиря известного веса для калибровки. Программировать не нужно: готовая прошивка вставляется целиком, вы меняете в ней три строки.
Установите 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 → выберите появившийся порт.
Получите токен весов в личном кабинете
Токен — «пароль устройства»: по нему сервис понимает, чьи это весы и к какому улью писать вес.
Откройте карточку своей семьи в onlybee.ai → кнопка «Подключить весы» → мастер выдаст токен вида onbee_… и покажет готовые строки для прошивки.
Вставьте прошивку и заполните три строки
Готовый текст прошивки — в мастере «Подключить весы» (и в этом разделе ниже). Вставьте его в Arduino IDE целиком и заполните три строки в самом верху:
- WIFI_SSID — имя вашей WiFi-сети. Важно: сеть 2.4 ГГц, сети 5 ГГц плата не видит вообще. У телефона-точки доступа включите «максимальную совместимость».
- WIFI_PASS — пароль сети.
- PAT — токен onbee_… из шага 2.
/*
* 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()
Залейте и посмотрите в монитор порта
Нажмите Upload (кнопка со стрелкой). После заливки откройте Serial Monitor (лупа справа сверху), скорость 115200.
Хорошая картина: «WiFi OK», затем каждые несколько секунд «вес=… POST -> 201». Код 201 значит «сервис принял данные».
| Код ответа | Что значит | Что делать |
|---|---|---|
| 201 | данные приняты ✓ | ничего — работает |
| 401 | токен неверный или отозван | скопируйте токен заново, проверьте кавычки |
| 409 | токен привязан к другому улью | уберите HIVE_ID из прошивки или перевыпустите токен |
| -1 / WiFi FAIL | сеть недоступна | имя/пароль, сеть 2.4 ГГц, сигнал |
Ноль и гиря: превращаем «попугаи» в килограммы
Два числа в прошивке делают из сырых значений килограммы. Снимаются один раз, в финальной сборке весов:
OFFSET (ноль): при ПУСТОЙ платформе посмотрите в мониторе значение raw — это и есть OFFSET. Впишите в прошивку.
CAL_FACTOR (масштаб): поставьте на платформу гирю известного веса (мы калибровали гирей 16 кг). Посмотрите units в мониторе и поделите на вес: units ÷ кг = CAL_FACTOR. Пример: 322608 ÷ 16 = 20163.
- Проверка: пустая платформа ≈ 0.00, гиря ≈ её вес. Нажмите кнопку RST на плате с гирей — должен снова показать вес гири, а не ноль.
- Перепаяли что-то или передвинули датчики — ноль «уезжает». Это нормально: просто переснимите OFFSET. У нас после перепайки питания ноль сдвинулся на −0.65 кг.
- Важно: прошивка НЕ обнуляется при каждом включении (иначе вес улья стал бы «нулём» после любой перезагрузки). Ноль зашит числом — потому его и снимают один раз.
Установка и проверка в кабинете
Поставьте весы под улей на ровное основание. Включите питание от батареи (USB отключён!).
Откройте onlybee.ai → карточка семьи → вкладка «Мониторинг»: вес, привес и график; полоса «Весы · телеметрия» покажет уровень сигнала, здоровье датчика и статус «на связи». Общая картина по всем ульям — раздел «Мониторинг весов».