СМС-весы пчеловода: “правильные” датчики и “зимняя прошивка”

NewSensor

С начала работ над весами я постоянно слышу голоса пчеловодов, желающих видеть улей, стоящий на 4 тензодатчиках!

Мол моя конструкция – “неправославна”, и лучше меньше, да лучше.

Кроме того, высказывются мнения о возможной неустойчивости конструкции, где улей задней стенкой опирается на одну точку (датчик).

Думаете капитуляция???

Нееет! я просто переделал датчики, и поменял код программы, теперь систему можно использовать и по-моему, и по-вашему 😉

Суть проста – в режиме “еретика” три планки тензодатчиков (как на заглавном фото) ставятся под задние стенки трех ульев.

Передние части по-прежнему покоятся на простых брусочках.

В “правильной” конфигурации, датчики 2 и 3 устанавливаются под один улей.

Таким образом мы можем получать точный вес одного улья , а вес второго – по моему (и BroodMinder-а) методу.

Никаких дополнительных перенастроек программа не требует, поскольку добавилась четвертая строка, как сумма веса ульев 2 и 3 :

NewSMS

В результате, появляется шанс сравнить два принципа измерения.

Для основания датчиков я взял отрезки шалевки 330х100х25мм (по ширине ульев Варре, Паливоды, и даже Украинских лежаков на узко-высоких рамках).

По краям вырезал пазы 35х25мм, по центру – 90х35мм (так и эстетичней, и есть куда плату АЦП спрятать).

Что касается программы, то все в нашем мире циклично – “Зима близко”, а это значит, что солнечных дней будет меньше.

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

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

Код программы (скетч) привожу ниже:

char phone_no[]="+123456789012"; // Your phone number that receive SMS with counry code 

byte pin2sleep=5; //  Set powerON/OFF pin

#include <avr/sleep.h>  // ARDUINO sleep mode library

#include <SoftwareSerial.h> // Sofrware serial library
#include "HX711.h" // HX711 lib. https://github.com/bogde/HX711 or beefree.xyz/wp-content/uploads/2019/07/HX711-master.zip
#include <EEPROM.h> // EEPROM lib.

HX711 scale0(7, 14);
HX711 scale1(8, 14);
HX711 scale2(9, 14);
#define SENSORCNT 3
HX711 *scale[SENSORCNT];

SoftwareSerial mySerial(5, 4); // Set I/O-port TXD, RXD of GSM-shield
//SoftwareSerial mySerial(4, 3); // Set I/O-port TXD, RXD of GSM-shield

float delta00; // delta weight from start
float delta10;
float delta20;
float delta01; // delta weight from yesterday
float delta11;
float delta21;

float raw00; //raw data from sensors on first start
float raw10;
float raw20;
float raw01; //raw data from sensors on yesterday
float raw11;
float raw21;
float raw02; //actual raw data from sensors
float raw12;
float raw22;

word calibrate0=20880; //calibration factor for each sensor
word calibrate1=20880;
word calibrate2=20880;


word daynum=0; //numbers of day after start

word i;

int notsunset=0;
int Hwarning=0;
int Halarm=0;


boolean setZero=false;

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;

void readVcc() // read battery capacity 
{
  ch = mySerial.read();
   while (mySerial.available() > 0) {  ch = mySerial.read(); } // empty input buffer from modem

 mySerial.println("AT+CBC?"); //ask gprs for battery status
// delay(200);
for ( i = 0; i <= 20 ; i++) { delayMicroseconds(10000);}
 while (mySerial.available() > 0) { //read input string between coma and CR
 ch = mySerial.read();
     if (ch ==','){ 
       ch1 = mySerial.read();
       ch2 = mySerial.read();
       ch3 = mySerial.read();
       ch4 = mySerial.read();       
     }
   }
}

void SignalQ() // read signal strenght 
{
  csq = mySerial.read();
   while (mySerial.available() > 0) {  csq = mySerial.read(); } // empty input buffer from modem

 mySerial.println("AT+CSQ"); //ask gprs for battery status
// delay(200);
for ( i = 0; i <= 20 ; i++) { delayMicroseconds(10000);}
 while (mySerial.available() > 0) { //read input string between coma and CR
 csq = mySerial.read();
     if (csq ==':'){ 
       csq1 = mySerial.read();
       csq2 = mySerial.read();
       csq3 = mySerial.read();
       csq4 = mySerial.read();       
     }
   }
}


void nolighting() // disable false statistic when night lighting flash
{
  detachInterrupt(digitalPinToInterrupt(0));

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

//  attachInterrupt(0, nolighting , FALLING); // Interrupt by LOW level
  attachInterrupt(0, SendStat , RISING); // Interrupt by HIGH level
}

// **********************************************************************
void SendStat() //send weight
{

  detachInterrupt(digitalPinToInterrupt(0));
  delayMicroseconds(10000);

 notsunset=0;
// for ( i=0; i <= 250; i++){
//      if ( !digitalRead(2) ){ notsunset++; } //is a really sunset now? you shure?
//      delayMicroseconds(100);
//   }

  if ( notsunset==0 )
  { 

  scale0.power_up(); //power up all scales 
  scale1.power_up();
  scale2.power_up();

    digitalWrite(13, HIGH);  

digitalWrite(pin2sleep, LOW); // Turn ON modem
for ( i = 0; i <= 2100 ; i++) { delayMicroseconds(10000);}

  raw01=raw02;
  raw11=raw12;
  raw21=raw22;
  raw02=scale0.get_units(240); //warm-up Wheatstone bridges and hx711-s
  raw02=scale0.get_units(20); //read data from scales
  raw12=scale1.get_units(20);
  raw22=scale2.get_units(20);

  daynum++; 
  delta00=(raw02-raw00)/calibrate0; // calculate weight changes 
  delta01=(raw02-raw01)/calibrate0;
  delta10=(raw12-raw10)/calibrate1;
  delta11=(raw12-raw11)/calibrate1; 
  delta20=(raw22-raw20)/calibrate2;
  delta21=(raw22-raw21)/calibrate2;

  readVcc(); 
  SignalQ();

//  delay(200);
  for ( i = 0; i <= 20 ; i++) { delayMicroseconds(10000);}
  mySerial.println("AT+CMGF=1");    //  Part of SMS sending 
  //delay(2000);
    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("Turn ");
  mySerial.println(daynum);
  mySerial.print("Hive1  ");
  mySerial.print(delta01);
  mySerial.print("   ");
  mySerial.println(delta00);
  mySerial.print("Hive2  ");
  mySerial.print(delta11);
  mySerial.print("   ");
  mySerial.println(delta10);
  mySerial.print("Hive3  ");
  mySerial.print(delta21);
  mySerial.print("   ");
  mySerial.println(delta20);
    mySerial.print("Sum23 ");
  mySerial.print(delta11+delta21);
  mySerial.print("   ");
  mySerial.println(delta10+delta20);


  mySerial.print("Battery capacity is ");
  mySerial.print(ch1);
  mySerial.print(ch2);
  mySerial.print(ch3);
  mySerial.print(ch4);
  mySerial.println(" %");
  mySerial.print("GSM signal strenght:  ");
  mySerial.print(csq1);
  mySerial.print(csq2);
  mySerial.print(csq3);
  if (csq4 != ',') {mySerial.print(csq4);}
  mySerial.println(" of 31");
  mySerial.println (char(26));//the ASCII code of the ctrl+z is 26
  for ( i = 0; i <= 700 ; i++) { delayMicroseconds(10000);}

  }


digitalWrite(pin2sleep, HIGH); // Turn OFF GSM-shield
    for ( i = 0; i <= 220 ; i++) { delayMicroseconds(10000);}
  digitalWrite(pin2sleep, LOW); // Turn OFF GSM-shield
    for ( i = 0; i <= 220 ; i++) { delayMicroseconds(10000);}
  digitalWrite(pin2sleep, HIGH);
  scale0.power_down(); //power down all scales 
  scale1.power_down();
  scale2.power_down();
  attachInterrupt(0, SendStat , RISING); // Interrupt by HIGH level
//  attachInterrupt(0, nolighting , FALLING); // Interrupt by LOW level
  digitalWrite(13, LOW);

}
// *************************************************************************************************

void switchto9600()
{
mySerial.begin(115200); // Open software serial port
delay(16000); // wait for boot
mySerial.println("AT");
delay(200);
mySerial.println("AT");
delay(200);
mySerial.println("AT+IPR=9600");    //  Change Serial Speed 
delay(200);
mySerial.begin(9600);
mySerial.println("AT&W0");
delay(200);
mySerial.println("AT&W");
}

void setup() { // Setup part run once, at start

scale[0] = &scale0; //init scale
scale[1] = &scale1;
scale[2] = &scale2;

scale0.set_scale();
scale1.set_scale();
scale2.set_scale();

  pinMode(13, OUTPUT);  // Led pin init
  pinMode(pin2sleep, OUTPUT);// Init ON/OFF pin for GSM
  pinMode(2, INPUT_PULLUP); // Set pullup voltage
  Serial.begin(9600);

digitalWrite(pin2sleep, LOW); // Turn ON modem
  delay(21000); // Wait for modem booting


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

switchto9600(); //for AiThinker GSM A6

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

mySerial.begin(9600);

delay(200);

setZero=digitalRead(2);

//if (EEPROM.read(500)==EEPROM.read(501) || setZero) // first boot/reset with hiding photoresistor
if (setZero)
{
raw02=scale0.get_units(240); //warm-up Wheatstone bridges and hx711-s
raw00=scale0.get_units(20); //read data from scales
raw10=scale1.get_units(20);
raw20=scale2.get_units(20);

EEPROM.put(500, raw00); //write data to eeprom
EEPROM.put(504, raw10);
EEPROM.put(508, raw20);
for (int i = 0; i <= 24; i++) { //blinking LED13 on reset/first boot
    digitalWrite(13, HIGH);
    delay(500);
    digitalWrite(13, LOW);
    delay(500);
  }
}
else {
EEPROM.get(500, raw00); // read data from eeprom after battery change
EEPROM.get(504, raw10);
EEPROM.get(508, raw20);
digitalWrite(13, HIGH); // turn on LED 13 on 12sec. 
    delay(12000);
digitalWrite(13, LOW);
}

delay(200); // Test SMS at initial boot

readVcc();
SignalQ();
delay(200);
  mySerial.println("AT+CMGF=1");    
  delay(2000);
  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);
  mySerial.println("INITIAL BOOT OK");

  mySerial.print("Battery capacity is ");
  mySerial.print(ch1);
  mySerial.print(ch2);
  mySerial.print(ch3);
  mySerial.print(ch4);
  mySerial.println(" %");
  mySerial.print("GSM signal strenght:  ");
  mySerial.print(csq1);
  mySerial.print(csq2);
  mySerial.print(csq3);
  if (csq4 != ',') {mySerial.print(csq4);}
  mySerial.println(" of 31");
  mySerial.println (char(26));//the ASCII code of the ctrl+z is 26
  delay(7000);

raw02=raw00;
raw12=raw10;
raw22=raw20;



digitalWrite(pin2sleep, HIGH); // Turn OFF GSM-shield
  delay(2200);
  digitalWrite(pin2sleep, LOW); // Turn OFF GSM-shield
  delay(2200);
  digitalWrite(pin2sleep, HIGH);

  scale0.power_down(); //power down all scales 
  scale1.power_down();
  scale2.power_down();

attachInterrupt(0, SendStat , RISING); // Interrupt by HIGH level
// attachInterrupt(0, nolighting , FALLING); // Interrupt by LOW level
}

void loop() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); //Set ARDUINO sleep mode
  sleep_mode(); // Go to sleep
  delay(3000);

}

В остальном-же сборка и настройка системы производится как и в предыдущей статье.

Да, еще, при установке улья на 4 датчика, все они должны находиться в одной плоскости.

Этого проще всего добиться, пользуясь строительным уровнем.

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

Сами тензодатчики подвержены влиянию температуры (~0.5кг/10градусов),плюс влияние влажности/осадков на вес корпуса улья.

Основной вопрос, на который я так ни от кого и не получил ответ – вот допустим на приросте 30 кг мы качаем мед, что если прирост 28 а медоносы отцвели, или наоборот – как раз пик цветения?

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

В общем, полученную конструкцию датчиков нарекаю “табуреткой” (ибо вид тоже соответствующий 😉

Сама -же программа успешно укладыват в сон всю систему, что позволяет даже на одном аккумуляторе функционировать до месяца, главное, чтобы фотоэлемент “видел” уголочек неба.

Теперь по поводу зимовки, на мой взгляд, с частыми оттепелями, нахождение пчел в ульях на улице несет большие потери.

Гораздо эффективней в таком случае будет их хранение в погребах/сараях без посторонних звуков и света.

Всем пчеловодам желаю успешной зимовки их подопечных!

В следующей публикации будет затронута тема создания CMC-весов без паяльника (в стиле Ардуино-проектов)

С уважением, электропчеловод Андрей.

11 thoughts on “СМС-весы пчеловода: “правильные” датчики и “зимняя прошивка””

  1. Чтобы улей был устойчив при использовании двух датчиков, достаточно их разнести по углам задней стенки. Передняя опирается на брусок.

    Что касается зимовки, то как говорит заведующий лаборатории пчеловодства Дмитрий Рахматулин: “зимой холодно пчеловоду, а не пчелам”. Я как и многие в Белоруссии использую холодную зимовку на открытом воздухе. Если пчел совсем не утеплять в зиму, то оттепели им не страшны и количество вышедших семей из зимовки сильно возрастает.

    1. Так вот как раз и попытался разнести в данной конструкции датчиков.

      У меня вообще есть мысль попробовать ульи с открытым дном – проще контролировать температуру клуба пчел зимой.

  2. Как добавить в программу функцию отправки смс не только по заходу солнца но и по восходу

    1. Добрый день!

      В коде программы все строки attachInterrupt(0, SendStat , RISING); меняете на attachInterrupt(0, SendStat , CHANGE);

      Спасибо за интерес к публикации!

  3. Хорошо бы добавить “резервный” способ отправки смс, в обход фоторезистора. Например по звонку с номера на который настроена отправка СМС.

    1. Я экспериментировал с данным режимом – именно отправка СМС по звонку. Все работает. Но для этого нужно, чтобы модуль связи (AiThinker A6) не пребывал в режиме сна. А это энергопотребление на порядок выше.
      В принципе, SIM800L “спит” чутко, реагируя на входящий звонок, но и кушает во всех режимах вдвое больше.

      1. Подскажите, пожалуйста, какую строчку закомментировать, чтобы не было спящего режима?

        1. Чтобы не засыпал сам контроллер, убираются все строки из блока void loop() {
          }

          Чтобы не засыпал модем, убрать все строки
          digitalWrite(pin2sleep, HIGH);
          из кода.

          Но СМС по обратному звонку сюда я не включал.

Leave a Reply

Your email address will not be published. Required fields are marked *