Умный улей на Arduino

Соты3D

Всем привет!

Если Вы еще не сделали «красные» весы, то теперь точно придется, поскольку описанное далее является расширением той конструкции.

В итоге мы получим дополнительную информацию о температуре за бортом, а также температуру/влажность внутри улья.

Еще я добавил оповещение о вылете роя и снятой крышке улья, что в общем-то было и в третьей версии весов

На самом деле, были собраны наработки из так и не увидевших свет(или существующих в единственном экземпляре) ответвлений и версий СМС-весов.

Почему-же тогда они трансформировались в «умный улей»?

Все очень просто — существующие на рынке системы с таким набором измеряемых параметров носят это название.

Поэтому я не стал спорить, а сделал конструкцию, доступную для самостоятельной сборки.

Измерения будут производиться каждые три часа от момента включения, статистика пока как и прежде СМС-ками в таком вот виде:

SMS

Здесь, первый столбец — процент заряда батареи, далеее идут температура на улице, температура в улье, влажность там-же.

Последние две колонки — изменение веса от заката до текущего момента и со времени старта.

«Шапка» сообщения «Bat% GSM/31 T_out Thive Humid% Delta3D Delta0» отправляется только на закате, с ней суточная дельта веса и от запуска системы.

Таким образом, Вам будет приходить 9 СМС в сутки.

Да, их удобно читать только на смартфоне, повернув экран горизонтально, красивые графики будут в следующих обновлениях программы.

Если все еще интересует сборка такой системы, тогда — поехали!

Прежде всего, нам потребуются датчики: температуры и влажности AM2320, температурный ds18b20.

Еще выводной резистор 10к и кабель 4P 70см :

кабель

По поводу выбора датчика — от DHT22 пришлось отказаться, поскольку для его корректной работы на Arduino 8MHz пришлось править драйвер(библиотеку).

AM3220 напротив, использует нормальный протокол I2C, правда два провода все-же придется припаять.

Но я-бы рекомендовал взять метра 2 четырехжильного телефонного кабеля и распаять на датчик/Ардуино.

Да, еще потребуются 3 провода мама-папа, 2 папа-папа и 2 с папами на одном конце, второй все-равно паять 😉

Они как раз должны были остаться от сборки красных весов.

время втыкать

Вставляем «маленький» датчик температуры с резистором в провода мамам-папа, AM2320 в четырехжильный.

Папы оставшихся четырех кабелей в соответствии с цветовой дифференциацией — во второй разьем 70-сантиметрового хвоста (у меня вместо красного-сиреневый).

Теперь жестоко отрываем два коннектора на коротких зеленом и черном проводе — их мы будем паять на Arduino.

А все оттого, что в Ардуино порт I2C только один — на пинах А4 и А5:

I2C

, которые в pro mini ну совсем в небогоугодном месте, и если другие порты можно переназначить (даже пользовать аналоговые в качестве цифровых), то этот — нет.

А дальше все совсем уж просто — подключаем/паяем провода к макетной плате/ардуино:

Соединения

Для простоты восприятия, я «снес» все проводники «красных» весов, оставив подключенными только описываемые здесь компоненты.

Но все соединения из предыдущего проекта должны присутствовать, просто это уже нечитабельная паутина выходит на фото.

Касательно программ, у Вас должна быть свежая версия Arduino IDE (у меня на 1.6.4 прога не компилировалась).

Ну и библиотеки OneWire, Adafruit_AM2320,
Adafruit_Sensor, HX711 нужно скачать/распаковать в arduino-«номерВашейверсии»\libraries.

Теперь прошиваем в Arduino новый скетч (не забыв вместо 123456789012 подставить свой номер телефона с кодом страны):

char phone_no[]="+123456789012"; // Номер телефона +код страны, на который будут приходить SMS

byte pin2sleep=9; //  Пин включения/выключения GSM-модуля

#include <avr/sleep.h>  // Библиотека перевода ARDUINO в режим сна
#include <SoftwareSerial.h> // Библиотека программного последовательного порта
#include "HX711.h" // Библиотека HX711 https://github.com/bogde/HX711 или beefree.xyz/wp-content/uploads/2019/07/HX711-master.zip
HX711 scale;
#include <EEPROM.h> // Библиотека работы с EEPROM (постоянной перезаписываемой памятью)

#include <OneWire.h> //библиотека для работы с устройствами компании Dallas (в нашем случае датчик температуры ds18b20) https://github.com/PaulStoffregen/OneWire
OneWire  ds(6); //назначение порта датчика

#include "Adafruit_Sensor.h" //библиотека работы с сенсорами компании Adafruit в целом https://github.com/adafruit/Adafruit_Sensor
#include "Adafruit_AM2320.h" // и датчика температуры/влажности am2320 в часности https://github.com/adafruit/Adafruit_AM2320
Adafruit_AM2320 am2320 = Adafruit_AM2320();

SoftwareSerial mySerial(10, 11); // Назначаем порты вввода/вывода для GSM-модуля

float delta00; // Изменение веса с момента старта

float delta01; // Изменение веса с предыдущего срабатывания

float raw00; //"сырые" данные с АЦП на момент старта

float raw01; //"сырые" данные с АЦП от предыдущего срабатывания

float raw02; //текущие данные с АЦП

float rawz02; //текущие данные с АЦП

word calibrate0=20880; //калибровочный коэфициент для датчиков (возможно необходима корректировка под Вашу модель)

word daynum=0; //количество дней(срабатываний) от старта

int notsunset=0; //признак заката
int Hwarning=0;
int Halarm=0;

word i;

boolean setZero=false;

float swarm0w; //переменные для роевого предупреждения
float w0; 
float DeltaW=0;
unsigned long swarm0t; 
unsigned long TimeNow;

unsigned long time3h; // время для отсылки статистики

// Далее - текстовые переменные для обработчика информации от модуля связи

char ch = 0;
char ch1 = 0;
char ch2 = 0;
char ch3 = 0;
char ch4 = 0;

char csq = 0;
char csq1 = 0;
char csq2 = 0;
char csq3 = 0;
char csq4 = 0;

byte present = 0; //переменные для функции чтения температуры с датчика Dallas Semiconductor
byte type_s;
byte data[12];
byte addr[8];
float celsius, fahrenheit; //переменные температуры DS

void readVcc() // Функция чтения заряда батареи из GSM-модуля
{
  ch = mySerial.read();
   while (mySerial.available() > 0) {  ch = mySerial.read(); } // очищаем входной буфер порта

 mySerial.println("AT+CBC"); //запрашивем статус батареи у GSM-модуля
// delay(200);
for ( i = 0; i <= 20 ; i++) { delayMicroseconds(10000);}
 while (mySerial.available() > 0) { // считываем входную строку после запятой
 ch = mySerial.read();
     if (ch ==','){ 
       ch1 = mySerial.read();
       ch2 = mySerial.read();
       ch3 = mySerial.read();
       ch4 = mySerial.read();       
     }
   }
}

void SignalQ() // Считывание мощности сигнала GSM
{
  csq = mySerial.read();
   while (mySerial.available() > 0) {  csq = mySerial.read(); } // очищаем входной буфер порта

 mySerial.println("AT+CSQ"); //запрашиваем у GSM-модуля мощность сигнала
// delay(200);
for ( i = 0; i <= 20 ; i++) { delayMicroseconds(10000);}
 while (mySerial.available() > 0) { //считываем входную строку после двоеточия
 csq = mySerial.read();
     if (csq ==':'){ 
       csq1 = mySerial.read();
       csq2 = mySerial.read();
       csq3 = mySerial.read();
       csq4 = mySerial.read();       
     }
   }
}


void SendStat() //функция чтения и отправки веса в виде СМС (за сутки по прерыванию от фотодатчика на закате)
{

  detachInterrupt(digitalPinToInterrupt(0)); // запрет прерываний
  delayMicroseconds(10000); // в режиме прерывания функция delay не работает  - используем delayMicroseconds

      notsunset=0;
 for (int i=0; i <= 250; i++){
      if ( !digitalRead(2) ){ notsunset++; } //Это точно закат?
      delayMicroseconds(1000);
   }

  if ( notsunset==0 )
  { 

  scale.power_up(); //Включение модуля HX711

    digitalWrite(13, HIGH);  

  raw01=raw02;

  raw02=scale.get_units(240); // "прогрев" модуля HX711 и тензодатчиков
  raw02=scale.get_units(20); // берем усредненные данные по 20 считываниям

scale.power_down(); //Выключение весов

for ( i = 0; i <= 300 ; i++) { delayMicroseconds(10000);}  

digitalWrite(pin2sleep, LOW); //И вот только теперь, после замеров, включаем GSM-модуль

for ( i = 0; i <= 2100 ; i++) { delayMicroseconds(10000);}

  daynum++; 
  delta00=(raw02-raw00)/calibrate0; // вычисление изменения веса
  delta01=(raw02-raw01)/calibrate0;


  readVcc(); 
  SignalQ();
  TcelS();

  for ( i = 0; i <= 20 ; i++) { delayMicroseconds(10000);}
  mySerial.println("AT+CMGF=1");    //  Начало блока отправки СМС
    for ( i = 0; i <= 200 ; i++) { delayMicroseconds(10000);}
  mySerial.print("AT+CMGS=\"");
  mySerial.print(phone_no); 
  mySerial.write(0x22);
  mySerial.write(0x0D);  // шеснадцатеричный эквивалент возврата каретки
  mySerial.write(0x0A);  // шеснадцатеричный эквивалент новой строки

    for ( i = 0; i <= 200 ; i++) { delayMicroseconds(10000);}

  mySerial.println("Bat%  GSM/31  T_out  Thive  Humid% Delta3D  Delta0");
  // процент заряда батареи
  mySerial.print(ch1);
  mySerial.print(ch2);
  if (ch3 != ',') {mySerial.print(ch3);} else  mySerial.print("_");
  if (ch4 != ',' && ch3 != ',') {mySerial.print(ch4);} else  mySerial.print("_");
  // устойчивость приема сигнала GSM
  mySerial.print("__");
  mySerial.print(csq1);
  mySerial.print(csq2);
  mySerial.print(csq3);
  if (csq4 != ',') {mySerial.print(csq4);} else  mySerial.print("_");
  mySerial.print("_____");
  //температура за бортом
  mySerial.print(celsius);
  mySerial.print("__");
  //температура внутри улья
  mySerial.print(am2320.readTemperature());
  mySerial.print("__");
  //влажность внутри улья
  mySerial.print(am2320.readHumidity());
  mySerial.print("__");
  // изменение веса за сутки
  mySerial.print(delta01);
  mySerial.print("__");
  // изменение веса с момента включения
  mySerial.print(delta00);

    mySerial.println (char(26));// код ASCII комбинации ctrl+z 
  for ( i = 0; i <= 700 ; i++) { delayMicroseconds(10000);}

digitalWrite(pin2sleep, HIGH); // Выключение GSM-модуля

for ( i = 0; i <= 700 ; i++) { delayMicroseconds(10000);}

  }

attachInterrupt(0, SendStat , RISING); // Включаем прерывание (запуск функции DayOut) по затемнению фоторезистора (закат)

digitalWrite(13, LOW);

}

// *************************************************************************************************
void Send3D() // отправка статистики каждые 3 часа
{

  detachInterrupt(digitalPinToInterrupt(0));



  scale.power_up(); //включаем весы


    digitalWrite(13, HIGH);  


for ( i = 0; i <= 2100 ; i++) { delayMicroseconds(10000);}


  rawz02=scale.get_units(240); //Прогреваем HX711 и измерительный мост
  rawz02=scale.get_units(20); //читаем данные с весов

for ( i = 0; i <= 300 ; i++) { delayMicroseconds(10000);}

  digitalWrite(pin2sleep, LOW); // Включаем модем

  daynum++; 
  delta00=(rawz02-raw00)/calibrate0; // calculate weight changes 
  delta01=(rawz02-raw01)/calibrate0;

  readVcc(); 
  SignalQ();
  TcelS();

  for ( i = 0; i <= 20 ; i++) { delayMicroseconds(10000);}
  mySerial.println("AT+CMGF=1");    //  Part of SMS sending 

    for ( i = 0; i <= 200 ; i++) { delayMicroseconds(10000);}
  mySerial.print("AT+CMGS=\"");
  mySerial.print(phone_no); 
  mySerial.write(0x22);
  mySerial.write(0x0D);  // hex equivalent of Carraige return    
  mySerial.write(0x0A);  // hex equivalent of newline
//  delay(2000);
    for ( i = 0; i <= 200 ; i++) { delayMicroseconds(10000);}

    // процент заряда батареи
  mySerial.print(ch1);
  mySerial.print(ch2);
  if (ch3 != ',') {mySerial.print(ch3);} else  mySerial.print("_");
  if (ch4 != ',' && ch3 != ',') {mySerial.print(ch4);} else  mySerial.print("_");
  // устойчивость приема сигнала GSM
  mySerial.print("__");
  mySerial.print(csq1);
  mySerial.print(csq2);
  mySerial.print(csq3);
  if (csq4 != ',') {mySerial.print(csq4);} else  mySerial.print("_");
  mySerial.print("_____");
  //температура за бортом
  mySerial.print(celsius);
  mySerial.print("__");
  //температура внутри улья
  mySerial.print(am2320.readTemperature());
  mySerial.print("__");
  //влажность внутри улья
  mySerial.print(am2320.readHumidity());
  mySerial.print("__");
  // изменение веса от заката
  mySerial.print(delta01);
  mySerial.print("__");
  // изменение веса с момента включения
  mySerial.print(delta00);

   mySerial.println (char(26));//код ASCII символа ctrl+z
  for ( i = 0; i <= 700 ; i++) { delayMicroseconds(10000);}

digitalWrite(pin2sleep, HIGH); // Выключаем модуль связи

    for ( i = 0; i <= 700 ; i++) { delayMicroseconds(10000);}

  scale.power_down(); // выключаем весы

  attachInterrupt(0, SendStat , RISING); // Interrupt by HIGH level
   digitalWrite(13, LOW);  

}

// *************************************************************************************************

void switchto9600() //переключение скорости порта GSM-модуля на 9600, активация режима "сна"
{
mySerial.begin(115200); // Открытие програмного последовательного порта
delay(16000); // wait for boot
mySerial.println("AT");
delay(200);
mySerial.println("AT");
delay(200);
mySerial.println("AT+IPR=9600");    //  Команда смены скорости
delay(200);
mySerial.begin(9600);
mySerial.println("AT&W0"); // запись изменений
delay(200);
mySerial.println("AT&W");
delay(200);
mySerial.println("AT+CSCLK=1"); // активация режима "сна" - засыпает по высокому уровню на DTR (pin2sleep), включение - по низкому
}

void TcelS() { //функция чтения температуры с датчика ds18b20


  if ( !ds.search(addr)) {
    ds.reset_search();
    delay(250);
    return;
  }

  if (OneWire::crc8(addr, 7) != addr[7]) {
      return;
  }

  switch (addr[0]) {
    case 0x10:
      type_s = 1;
      break;
    case 0x28:
      type_s = 0;
      break;
    case 0x22:
      type_s = 0;
      break;
    default:
      return;
  } 

  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);  

  delay(1000); 

  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);    


  for ( i = 0; i < 9; i++) {      
    data[i] = ds.read();

  }


  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; 
    if (data[7] == 0x10) {

      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);

    if (cfg == 0x00) raw = raw & ~7; 
    else if (cfg == 0x20) raw = raw & ~3; 
    else if (cfg == 0x40) raw = raw & ~1; 

  }
  celsius = (float)raw / 16.0;
  fahrenheit = celsius * 1.8 + 32.0;

}

void Walert() // отправка СМС о роении/злоумышленниках
{
digitalWrite(pin2sleep, LOW); // Включение модема
  delay(21000); // Ожидание загрузки

  mySerial.println("AT+CMGF=1");    //  Раздел отправки СМС
  delay(2000);
  mySerial.print("AT+CMGS=\"");
  mySerial.print(phone_no); 
  mySerial.write(0x22);
  mySerial.write(0x0D);  //  шестнадцатеричный эквивалент возврата каретки
  mySerial.write(0x0A);  // шестнадцатеричный эквивалент новой строки
  delay(2000);

   if (Hwarning != 0) // warning about hive invasion
   {
    mySerial.println("WARNING!!!"); 
    mySerial.print("Hive  "); 
    mySerial.print(Hwarning);
    mySerial.println("  is opened! "); 
  }

  if (Halarm != 0)
   {
    mySerial.println("ALARM!!!");  // alarm about swarm
    mySerial.print("Bees in hive  "); 
    mySerial.print(Halarm);
    mySerial.println("  is SWARMING!!! "); 
  }

   mySerial.println (char(26));//код ASCII последдовательности ctrl+z
  delay(7000);

  digitalWrite(pin2sleep, HIGH); // Выключение GSM-модуля
  delay(7000);

Hwarning=0;
Halarm=0;
DeltaW=0;
for (int i = 0; i <= 24; i++) { //часто моргаем светодиодом
    digitalWrite(13, HIGH);
    delay(250);
    digitalWrite(13, LOW);
    delay(250);
  }
  }



void setup() { // Процедура инициализации - выполняется однократно при старте

  // Инициализация модуля АЦП
  // HX711.DOUT - pin 7
  // HX711.PD_SCK   - pin 8
  scale.begin(7, 8);
  scale.set_scale();

  am2320.begin(); // инициализация датчика влажности/температуры


  pinMode(13, OUTPUT);  // Режим работы пина светодиода (13)
  pinMode(pin2sleep, OUTPUT);// Init ON/OFF pin for GSM Инициализация пина включения/выключения модема
  pinMode(2, INPUT_PULLUP); // Включение внутреннего напряжения подтяжки на пин 2 (для фотодатчика)
  Serial.begin(9600);

digitalWrite(pin2sleep, LOW); // Включение GSM-модуля
  delay(21000); // Ожидание запуска


// -------------------------------------------------------------------------------

switchto9600(); //Переключение скорости порта GSM-модуля

// -------------------------------------------------------------------------------

mySerial.begin(9600);

delay(200);

setZero=digitalRead(2); //проверка необходимости сброса параметров (при затененном фоторезисторе)

//if (EEPROM.read(500)==EEPROM.read(501) || setZero) // сброс параметров при первом включении
if (setZero)
{
  raw00=scale.get_units(240); // "прогрев" модуля HX711 и тензорезисторов
  raw00=scale.get_units(20); // берем усредненные данные по 20 считываниям

EEPROM.put(500, raw00); //запись данных в ППЗУ

for (int i = 0; i <= 24; i++) { //моргаем светодиодом
    digitalWrite(13, HIGH);
    delay(500);
    digitalWrite(13, LOW);
    delay(500);
  }
}
else {
EEPROM.get(500, raw00); // весы просто выключались/меняли батарею - считывание исходных параметров АЦП

digitalWrite(13, HIGH); // Включаем светодиод на 12 секунд
    delay(12000);
digitalWrite(13, LOW);
}

delay(200); // Тестовая СМС при первом включении

readVcc();
SignalQ();
TcelS();

delay(200);
  mySerial.println("AT+CMGF=1");    
  delay(2000);
  mySerial.print("AT+CMGS=\"");
  mySerial.print(phone_no); 
  mySerial.write(0x22);
  mySerial.write(0x0D);  // шеснадцатеричный эквивалент возврата каретки
  mySerial.write(0x0A);  // шеснадцатеричный эквивалент новой строки
  delay(2000);
  mySerial.println("INITIAL BOOT OK");
  mySerial.println("Bat%  GSM/31  T_out  Thive  Humid% Delta3D  Delta0");
  // процент заряда батареи
  mySerial.print(ch1);
  mySerial.print(ch2);
  if (ch3 != ',') {mySerial.print(ch3);} else  mySerial.print("_");
  if (ch4 != ',' && ch3 != ',') {mySerial.print(ch4);} else  mySerial.print("_");
  // устойчивость приема сигнала GSM
  mySerial.print("__");
  mySerial.print(csq1);
  mySerial.print(csq2);
  mySerial.print(csq3);
  if (csq4 != ',') {mySerial.print(csq4);} else  mySerial.print("_");
  mySerial.print("_____");
  //температура за бортом
  mySerial.print(celsius);
  mySerial.print("__");
  //температура внутри улья
  mySerial.print(am2320.readTemperature());
  mySerial.print("__");
  //влажность внутри улья
  mySerial.print(am2320.readHumidity());
  mySerial.println (char(26));// код ASCII комбинации ctrl+z 
  delay(7000);

raw02=raw00;

digitalWrite(pin2sleep, HIGH); // Выключение GSM-модуля

delay(7000);

//////////////////////////////

swarm0w=scale.get_units(10);
swarm0w=scale.get_units(10);
swarm0w=scale.get_units(10); //set initial data for swarm warning
swarm0t=millis();             // set initial time for swarm warning

///////////////////////////////

  scale.power_down(); //Выключение модуля HX711


attachInterrupt(0, SendStat , RISING); // Включаем прерывание (запуск функции DayOut) по затемнению фоторезистора (закат)

}

void loop() {

// set_sleep_mode(SLEEP_MODE_PWR_DOWN);  sleep_mode(); delay(3000);   //Выбираем режим сна Ардуино  и спим

delay(30000);

  scale.power_up(); //включаем весы (hx711)

w0=scale.get_units(10);
w0=scale.get_units(10);
w0=scale.get_units(50); //читаем вес


  scale.power_down(); //выключаем весы

TimeNow=millis();

// отсылка статистики каждые 3 часа
if (TimeNow-time3h > 10800000 ) 
{
  time3h=TimeNow;
  Send3D();
}


// проверка веса улья на роение ил снятие крышки

DeltaW=((swarm0w-w0)/calibrate0);
if (TimeNow-swarm0t > 600000 ) 
{
  swarm0t=TimeNow;
  swarm0w=w0;
}
  else
    {
    if (DeltaW > 5 ) 
      {
        Hwarning=1; 
        Walert();
        swarm0t=TimeNow;
        swarm0w=w0;
       }
        else {if (DeltaW > 1 ) 
        {
         Halarm=1; 
         Walert();
         swarm0t=TimeNow;
         swarm0w=w0;
        }
      }
  }

}


Фиксики

И «Тыдыщ!», как говорят любимые мультперсонажи моего сына (да что греха таить — мои тоже 😉

Система при выключенном (в режиме ожидания) модуле GSM потребляет около 7мА, что вполне позволяет использовать маленький модуль солнечной батареи 5/6V 1W.

В паре с LiIon батареей емкостью от 2000мАЧ и модулем заряда TP4056 это обеспечит нормальное функционирование системы.

Но ей-богу, для установки на улей все надо паять хоть на макетную плату:

Макетка

И прятать во влагозащищенную коробку(i16):

Распределительная коробка

Что я собственно говоря и делал в СМС-весах:

Поскольку в «паутине» с коннекторами, даже на столе иной раз что-то выскакивает/перестает контачить.

Ну а пока у меня все, искренне Ваш,

электропчеловод Андрей.

Умный улей на Arduino: 4 комментария

  1. Андрей, опишите, пожалуйста, каким образом система определяет снятие крышки улья и как она на это реагирует. Спасибо.

    1. Добрый день!
      Проверка веса улья происходит каждые 30 секунд.

      Если происходит уменьшение более, чем на 5 кг — приходит СМС — WARNING!!! Hive is opened!

      Если от 1 кг за 10 минут — предупреждение Bees in hive is SWARMING!!!

  2. Начало отправки статистики каждые 3 часа берется от заката или от времени первого старта весов.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *