Русский
Русский
English
Статистика
Реклама

Proteus

Снова о автономной Arduino-метеостанции на батарейках

03.03.2021 00:13:52 | Автор: admin

Еще донедавна мне не удавалось найти в Интернете любительскую метеостанцию с питанием от батареек. Я имею ввиду бытовую автономную метеостанцию с измерениями параметров в помещении, на улице и отображением информации на дисплее метеостанции. Любители не заморачиваются на этой проблеме, а питают свои автономные девайсы от солнечных батарей, аккумуляторов и т.п. Уточню проблема касается только одного из узлов метеостанции базы, а первые проекты малогабаритных беспроводных автономных выносных датчиков на Ардуино появились 10 лет назад. Вместе с тем, промышленные устройства такого плана бытовые метеостанции, комнатные термостаты годами работают от пары батареек АА и этот факт является той целью, которой хотелось бы достичь.


Это было донедавна. Несколько дней назад меня поразил очередной проект @Berkseo, как поражают все его проекты: "Беспроводная мини погодная станция с e-paper экраном на батарейках". Тут все на уровне промышленного продукта. Удивляет единственное в устройстве нет внешнего датчика.


Год назад разместил статью Автономная метеостанция на контроллере ATMEGA328P и питанием от батареек с беспроводным выносным датчиком. Хотя прототип и выполнял свои функции, но имел серьезные недостатки слишком малая частота обновления данных и большое энергопотребление. Ниже другой вариант метеостанции с новым алгоритмом, элементной базой и кодом с низкоуровневыми вставками. Все это позволило выйти на время работы метеостанции от одного комплекта батареек даже несколько больше, чем гарантируют производители некоторых товаров такого плана.



Что сделано:


Датчики DHT22 и DS18B20, которые использовались в предыдущем проекте, заменены энергосберегающим модулем это датчик температуры и влажности HTU21D. Период измерений, отправки/приема данных уменьшен с 15-ти мин до 53,5 сек. Сделан переход на устойчивую частоту работы контроллера (8 МГц) при напряжении питания ниже 3В. Для уменьшения объемов занимаемой памяти в скетчах использованы некоторые функции С/С++. И главное, принципиально изменен алгоритм передачи пакетов с выносного датчика и алгоритм приема этих пакетов базой метеостанции. Теперь для обеспечения надежного приема пакетов с выносного датчика в нем формируется и отправляется с интервалом около 0,3 сек не один, а три пакета с данными о параметрах воздуха на улице и состоянии батареек. Только после отправки третьего пакета контроллер в. датчика вместе с периферией уходит в сон. База метеостанции уходит спать после приема одного из 6-ти пакетов с выносного датчика и просыпается за полсекунды до поступления очередной серии пакетов с выносного датчика.


Метеостанция состоит из двух автономных узлов с питанием от двух батареек AA: базы и выносного датчика. Назовем их для простоты анализатором (по-другому база) и беспроводным в.датчиком (выносным датчиком).


Анализатор, построен на контроллере ATMEGA328P, измеряет температуру и влажность (датчик температуры и влажности HTU21D) в помещении, а также измеряет и анализирует величину напряжения питания узла, которое обеспечивают две батарейки АА 1,5 В. На контроллер также поступает сигнал с приемника LoRa, который по эфиру принимает информацию с выносного датчика. Вся инфа с контроллера выводится на ЖК-дисплей NOKIA 5110.


В в.датчике, тоже собранном на контроллере ATMEGA328P, измеряется температура и влажность воздуха на улице (модуль HTU21D), а также напряжение питания выносного узла, организованного на двух батарейках АА 1,5 В. Передатчик LoRa этого узла передает инфу о температуре, влажности и состоянии батарейки на анализатор. С в.датчика выполняется отправка 3-х пакетов с интервалом около 0,3 сек, затем контроллер ATMEGA328P, передатчик LoRa и модуль HTU21D для экономного расходования заряда батареек переводятся в режим сна. Измерения и отправка данных с в.датчика выполняется с циклом несколько меньше 1-ой минуты.


Работа анализатора построена по следующему алгоритму:


Вначале, при включении обеих узлов метеостанции, контроллер анализатора подает команды на измерение температуры и влажности внутри помещения и выводит эти параметры на дисплей, затем устанавливает приемник LoRa в режим прослушивания эфира. После приема сигнала с в.датчика и успешной дешифрации принятых данных контролер подает команду на повторное измерение температуры, влажности и выводит инфу в полном объеме на экран. Затем анализатор уходит в сон, просыпаясь примерно за полсекунды до планируемого поступления сигнала с в.датчика. Приняв и дешифровав один из трех пакетов с в.датчика, повторно выполняет свои измерения, выводит информацию на экран и снова уходит спать. Если по каким-то причинам сигнал с в.датчика отсутствует около одной минуты (например, сели батарейки), что по времени соответствует отправке 6-ти пакетов с в.датчика, анализатор проводит измерения только в помещении, изредка сканируя эфир: а вдруг в.датчик появился в эфире?! Это сделано для того, чтобы постоянно работающий на прием модуль LoRa не посадил за короткое время батарейки анализатора.


Для сборки устройства понадобятся радиодетали:


  1. Контроллер ATMEGA328P-PU 2 шт.
  2. Датчик влажности и температуры HTU21D/SHT21/Si7021 2 шт.
  3. ЖК-дисплей NOKIA 5110 1 шт.
  4. Приемник-передатчик LoRa Rа-01 2 шт.
  5. Макетная плата (стеклотекстолит), монтажные провода, батарейки АА, кварцевые резонаторы 8 МГц, резисторы, конденсаторы, другие мелочи.

Ориентировочная стоимость компонентов по ценам AliExpress примерно $25.


Для работы с контроллерами ATMEGA328P в качестве программатора я использую плату Arduino UNO. На Youtube есть хорошее видео по установке загрузчика и загрузки скетчей в контроллер ATMEGA328P с помощью платы Arduino UNO.


На этот раз мы не будем устанавливать новые фьюзы программой SinaProg, а воспользуемся, на мой взгляд, более универсальным способом созданием новых конфигураций плат в платформе Arduino IDE.


В новые контроллеры надо установить загрузчик Arduino as ISP и надо учитывать то, что контроллеры ATMEGA328P поступают в продажу с заводской настройкой фьюз для мониторинга (контроля) напряжения питания не ниже 2,7 В. Мы же будем работать от батареек, напряжение на которых при разряде может быть ниже установленного заводского порога 2,7 В, и с кварцем 8 МГц. Установим загрузчик и изменим фьюзы под наши условия, используя в качестве программатора плату Arduino UNO, в такой последовательности:


  1. Найти по адресу c:\Program Files\Arduino\hardware\arduino\avr\ файл boards.txt и открыть его текстовом редакторе с форматированием, например, AkelPad.
  2. Дополнить файл блоком, который приведен под спойлером, и сохранить файл.

    блок установок 1
    ##############################################################

    amega.name=Mega Low (8 MHz, >1.8V)

    amega.upload.tool=avrdude
    amega.upload.protocol=arduino
    amega.upload.maximum_size=32256
    amega.upload.maximum_data_size=2048
    amega.upload.speed=57600

    amega.bootloader.tool=avrdude
    amega.bootloader.low_fuses=0xFF
    amega.bootloader.high_fuses=0xDA
    amega.bootloader.extended_fuses=0xFE
    amega.bootloader.unlock_bits=0x3F
    amega.bootloader.lock_bits=0x0F
    amega.bootloader.file=optiboot/optiboot_atmega328.hex

    amega.build.mcu=atmega328p
    amega.build.f_cpu=8000000L
    amega.build.board=AVR_UNO
    amega.build.core=arduino
    amega.build.variant=standard

  3. В плату Arduino UNO загрузить скетч ArduinoISP.ino из примеров платформы Arduino IDE (Файл > Примеры > ArduinoISP).
  4. Собрать схему (плата Arduino UNO, контроллер ATMEGA328P, кварц 16 МГц) для установки в контроллер загрузчика ArduinoISP (инструкции тут), подключить ее компьютеру и записать в контроллер бутлоадер Arduino as ISP.
  5. Заменить кварц в схеме 16 МГц на 8 Мгц. В меню ИНСТРУМЕНТ выбрать из списка плату Mega Low (8 MHz, >1.8V), которая появилась в меню после дополнения файла boards.txt новым блоком, выбрать тут же Программатор: Arduino as ISP и, нажав Записать загрузчик изменить фьюзы и другие установки в контроллере.
  6. Далее загружаем в контроллер необходимый скетч, используя ту же схему, что и для установки загрузчика (п.4), через Скетч > Загрузить через программатор.

Выносной датчик


В.датчик построен на контроллере ATMEGA328P. В нем осуществляется прием данных с HTU21D по протоколу I2C, измерение и анализ величины напряжения питания узла и управление передатчиком LoRa.


скетч в.датчика
/*   Снова о автономной Arduino-метеостанции на батарейках, выносной датчик   http://personeltest.ru/aways/habr.com/ru/post/544936/*/#include <avr/io.h>#include <util/delay.h>#include <SPI.h>#include <LoRa.h>#include <LowPower.h>#include <Wire.h>#include <avr/power.h>#include "HTU21D.h"#define VccHTU 8  //питание и подтяжка HTU21D (pin 14 AtMega328P, D8)HTU21D myHTU21D;float Tout; // температураint Hout;  // влажностьunsigned int sleepCounter, sleepCounter0; // счетчик, задающий время снаint pct;  //счетчик числа пакетов перед уходом в сонString messageOut; // LoRa-сообщениеfloat BatOut; // напряжение батареекconst int batteryPin = A0; // pin 23 (Atmega328P), к которому подключена батарея для измерения напряженияconst float typVbg = 1.132; //калибровачная константа, 1.0 - 1.2int counter = 0;// измерение опорного напряженияfloat readVcc() {  byte i;  float result = 0.0;  float tmp = 0.0;  for (i = 0; i < 1; i++) {    // Read 1.1V reference against AVcc    // set the reference to Vcc and the measurement to the internal 1.1V reference#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)    ADMUX = _BV(MUX5) | _BV(MUX0);#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)    ADMUX = _BV(MUX3) | _BV(MUX2);#else    // works on an Arduino 168 or 328    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);#endif    _delay_ms(3); // Wait for Vref to settle    ADCSRA |= _BV(ADSC); // Start conversion    while (bit_is_set(ADCSRA, ADSC)); // measuring    uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH    uint8_t high = ADCH; // unlocks both    tmp = (high << 8) | low;    tmp = (typVbg * 1023.0) / tmp;    result = result + tmp;    _delay_ms(5);  }  return result;}void Measurement () {  // измерение температуры и влажности  Hout = myHTU21D.readHumidity();  Hout = 62;  //delete!  float Tout_p = myHTU21D.readTemperature();  Tout = 0.1 * int(Tout_p * 10 + 0.5);  //округление до десятых  // измерение напряжения батареек  BatOut = 0.1 * int(readVcc() * 10 + 0.5);  if (BatOut < 2.2) {    BatOut = 0.0;  } else {    BatOut = 2.2;  }}void SendMessage () {  // отправка данных (температура, влажность, состояние батареек)  if (BatOut > 2.1) {    messageOut = String(Tout) + "#" + String(Hout) + "$" + String("BGood");  }  else {    messageOut = String(Tout) + "#" + String(Hout) + "$" + String("BLow");  }  LoRa.beginPacket();  LoRa.print(messageOut);  LoRa.endPacket();}void setup() {  Serial.begin(9600);  Serial.println("Power ON");  analogReference(DEFAULT);  pinMode(VccHTU, OUTPUT);  digitalWrite(VccHTU, 1);  _delay_ms(200);  myHTU21D.begin();  int counter = 0;  while (!LoRa.begin(433E6) && counter < 10) {    Serial.println("Не удалось найти LoRa-передатчик!");    counter++;    _delay_ms(500);  }  LoRa.setTxPower(4); //мощность передатчика, 2...20 дБ  LoRa.setSyncWord(0xF3);}void loop() {  digitalWrite(VccHTU, 1);  if (pct < 3)  { // измерения, отправка пакетов    Serial.println(messageOut);    Measurement ();    SendMessage ();  } else {// измерения, отправка пакета и длительный сон    Serial.println(messageOut);    Serial.println("sleep ...");    Measurement ();    SendMessage ();    for (sleepCounter = 6; sleepCounter > 0; sleepCounter--)    {      digitalWrite(VccHTU, 0);      digitalWrite(VccHTU, 1);      LoRa.sleep ();      LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);    }    pct = 0;  }  pct++;  if (pct >= 3) pct = 3; //защита от переполнения счетчика}int main() {  init();  setup();  for (;;) {    loop();  }}

Электрическая схема в.датчика:



Питание и подтяжка выводов модуля HTU21D осуществляется с пина 14 контроллера ATMEGA328P. Это сделано для того, чтобы программно обнулить питание HTU21D и перевести этот датчик в режим низкого энергопотребления во время сна.


Изначально в схеме в.датчика планировалось использовать барометр-термометр BMP280, но мне не удалось программно перевести BMP280 в режим низкого потребления во сне. Хотя по даташиту BMP280 для перехода в режим низкого потребление требуется, как и для HTU21D, кратковременное обнуление питания. Разрыв питания BMP280 во время сна снижает потребляемый ток в схеме ATMEGA328P + BMP280 с 130 мкА до 5 мкА, но, повторюсь, смоделировать этот разрыв питания программно у меня пока не получилось.


В в.датчике формируется и отправляется с интервалом около 0,3 сек три пакета с данными о температуре и влажности на улице и состоянии батареек. Если напряжение на батарейках выше установленного порога (2,2 В), то в коде пакета присутствует BGood, а ниже BLow. После отправки третьего пакета контроллер в.датчика вместе с периферией уходят в сон. Цикл отправки серий пакетов 53,5 сек.


Анализатор


Мозг анализатора контроллер ATMEGA328P. Он принимает сигналы с датчика HTU21D по протоколу I2С и по SPI взаимодействует с приемником LoRa и дисплеем NOKIA 5110.


скетч в.датчика
/*   Снова о автономной Arduino-метеостанции на батарейках, анализатор   http://personeltest.ru/aways/habr.com/ru/post/544936/*/#include <avr/io.h>#include <util/delay.h>#include <SPI.h>#include <LoRa.h>#include <LowPower.h>#include "HTU21D.h"#include <LCD5110_Graph.h>#define VccHTU 8  //питание и подтяжка HTU21D(pin 14 AtMega328P, D8)HTU21D myHTU21D;float Tin; // температура в помещенииint Hin;  // влажность в помещенииLCD5110 myNokia(3, 4, 5, 6, 7);extern uint8_t SmallFont[];extern uint8_t MediumNumbers[];float BatIn = 0; // напряжение батареиconst int batteryPin = A0; // pin 23(Atmega328P), к которому подключена батарея для измерения напряженияconst float typVbg = 1.132; //калибровачная константа, 1.0 - 1.2unsigned int sleepCounter;  //счетчик, задающий время снаint r; //счетчик циклов прослушивания эфираint mlc;  //счетчик циклов работы без в.датчикаString LoRaData, Tout_str, Hout_str, BatIn_str, BatOut_str;// измерение напряжения батареекfloat readVcc() {  byte i;  float result = 0.0;  float tmp = 0.0;  for (i = 0; i < 1; i++) {    // Read 1.1V reference against AVcc    // set the reference to Vcc and the measurement to the internal 1.1V reference#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)    ADMUX = _BV(MUX5) | _BV(MUX0);#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)    ADMUX = _BV(MUX3) | _BV(MUX2);#else    // works on an Arduino 168 or 328    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);#endif    _delay_ms(3); // Wait for Vref to settle    ADCSRA |= _BV(ADSC); // Start conversion    while (bit_is_set(ADCSRA, ADSC)); // measuring    uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH    uint8_t high = ADCH; // unlocks both    tmp = (high << 8) | low;    tmp = (typVbg * 1023.0) / tmp;    result = result + tmp;    _delay_ms(5);  }  return result;}void Measurement() {  float Tin0;  // измерение напряжения батареи:  BatIn = readVcc();  // измерение температуры  и влажности в помещении  Hin = myHTU21D.readHumidity();  // Hin = 58; // delete!  float Tin_p = myHTU21D.readTemperature();  Tin = 0.1 * int(Tin_p * 10 + 0.5);  //округление до десятых  //  Tin = 21.4; // delete!}void draw() {  myNokia.enableSleep();  myNokia.clrScr();  //Tin  char chr_Tin [5];  String Tin_str = String(Tin);  myNokia.setFont(SmallFont);  myNokia.print("            C", LEFT, 0);  myNokia.print("In", LEFT, 8);  myNokia.setFont(MediumNumbers);  Tin_str.toCharArray(chr_Tin, 5); //количество знаков+1  myNokia.print(String(chr_Tin), CENTER, 0);  //Tout  char chr_Tout [5];  myNokia.setFont(SmallFont);  myNokia.print("            C", LEFT, 16);  myNokia.print("Out", LEFT, 24);  myNokia.setFont(MediumNumbers);  Tout_str.toCharArray(chr_Tout, 5);  myNokia.print(String(chr_Tout), CENTER, 16);  // Hin, Hout  char chr_Hout [5];  Hout_str.toCharArray(chr_Hout, 4);  myNokia.setFont(MediumNumbers);  myNokia.print(String(Hout_str), RIGHT, 32);  myNokia.setFont(SmallFont);  myNokia.print("    In Out", LEFT, 40);  myNokia.print("      %", LEFT, 32);  myNokia.setFont(MediumNumbers);  myNokia.print(String(Hin), LEFT, 32);  myNokia.setFont(SmallFont);  // Battery Level  if (BatIn < 2.2) {    myNokia.setFont(SmallFont);    myNokia.print("Bat", LEFT, 0);  }  if (BatOut_str == "BLow") {    myNokia.setFont(SmallFont);    myNokia.print("Bat", LEFT, 16);  }  myNokia.disableSleep();  _delay_ms(5);}void drawStart() {  myNokia.enableSleep();  myNokia.clrScr();  //Tin  char chr_Tin [5];  String Tin_str = String(Tin);  myNokia.setFont(SmallFont);  myNokia.print("            C", LEFT, 0);  myNokia.print("In", LEFT, 8);  myNokia.setFont(MediumNumbers);  Tin_str.toCharArray(chr_Tin, 5); //количество знаков+1  myNokia.print(String(chr_Tin), CENTER, 0);  // Battery Level  if (BatIn < 2.2)  {    myNokia.setFont(SmallFont);    myNokia.print("Bat!", RIGHT, 28);  }  //Hin  myNokia.setFont(SmallFont);  myNokia.print("         %", LEFT, 18);  myNokia.print("In", LEFT, 28);  myNokia.setFont(MediumNumbers);  myNokia.print(String(Hin), CENTER, 18);  //No signal!  myNokia.setFont(SmallFont);  myNokia.print("Out - - -", CENTER, 40);  myNokia.update();  myNokia.disableSleep();  _delay_ms(5);}void setup() {  Serial.begin(9600);  pinMode(VccHTU, OUTPUT);  digitalWrite(VccHTU, 1);  Serial.println("Power ON!");  analogReference(DEFAULT);  // инициализация дисплея  myNokia.InitLCD();  myNokia.setFont(SmallFont);  myNokia.clrScr();  myNokia.print(">>>>>", CENTER, 20);  myNokia.update();  _delay_ms(1000);  myNokia.setFont(SmallFont);  myNokia.clrScr();  myNokia.print("))-->", CENTER, 20);  myNokia.update();  if (!LoRa.begin(433E6)) {    Serial.println("Ошибка загрузки LoRa-приемника!");    while (1);    myNokia.setFont(SmallFont);    myNokia.clrScr();    myNokia.print(" ->  ->", CENTER, 20);    myNokia.update();  }  // Диапазон для синхрослова  между "0-0xFF".  LoRa.setSyncWord(0xF3);  Serial.println("Прослушивание эфира. Ожидание пакета с в.датчика ...");  myHTU21D.begin();  Measurement();  drawStart();  digitalWrite(VccHTU, 0);  _delay_ms(1000);  myNokia.clrScr();  myNokia.print("Waiting", CENTER, 10);  myNokia.print("Message from", CENTER, 22);  myNokia.print("OUTSIDE", CENTER, 34);  myNokia.update();}void loop() {  r++;  digitalWrite(VccHTU, 1);  if (r < 600)  // 8 MHz;  {    mlc = 0;    // Прослушивание эфира, прием, дешифрация, если сигнал с в.датчика принят,    // то измерения в помещении, вывод инфы на экран и - в спячку.    {      int packetSize = LoRa.parsePacket();      if (packetSize) {        while (LoRa.available()) {          LoRaData = LoRa.readString();        }        int pos1 = LoRaData.indexOf('#');        int pos2 = LoRaData.indexOf('$');        Tout_str = LoRaData.substring(0, pos1);        Hout_str = LoRaData.substring(pos1 + 1, pos2);        BatOut_str = LoRaData.substring(pos2 + 1, LoRaData.length());        if ((LoRaData).substring(pos1, pos1 + 1) == "#") {          Serial.println("Принято, декодировано! r = " +  String(r));          r = 0;          Measurement();          draw();          digitalWrite(VccHTU, 0);          // sleepCounter = 49; 16 MHz          // sleepCounter = 48; 8 MHz          for (sleepCounter = 48; sleepCounter > 0; sleepCounter--)          {            digitalWrite(VccHTU, 1);            LoRa.sleep ();            LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);          }        }      }    }  } else {    r = 600;    if (mlc < 250) //4 часа, время работы без датчика    {      Serial.println("Работа без в.датчика.");      LoRa.sleep ();      Measurement();      drawStart();      digitalWrite(VccHTU, 0);      for (sleepCounter = 6; sleepCounter > 0; sleepCounter--)      {        digitalWrite(VccHTU, 1);        LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);      }      mlc++;    } else {      r = 0;      mlc = 0;    }  }  _delay_ms(110); }int main() {  init();  setup();  for (;;) {    loop();  }}

Работа анализатора начинается в setup'e с инициализации модулей, измерения параметров воздуха, анализа напряжения на батарейках и вывода этой инфы на дисплей. Далее уже в loop'e прослушивается эфир приемником LoRa. После приема и дешифрации сигнала с в.датчика повторно проводятся измерения, анализа напряжения на батарейках и вывод измеренной и принятой инфы на дисплей. Выполнив эту работу все элементы схемы уходят поспать примерно на полсекунды меньше, чем период отправки пакетов с в.датчика. В следующем цикле контроллер просыпается и включает приемник приблизительно за 0,5 сек до ожидаемого прихода сигнала с в.датчика. Таким образом, контроллер и периферия анализатора работают около полсекунды с периодом (циклом) меньше минуты (53,5 сек). Если радиосигнал с в.датчика не поступает на приемник анализатора на протяжении приблизительно одной минуты (время, достаточное для приема одного из 6-ти пакетов), то анализатор переходит в режим работы без в.датчика на 4 часа, измеряя параметры воздуха и оценивая состояние батареек только в помещении с индикацией на дисплее этих данных. Период обновления данных в режиме работы без в. датчика 56,7 сек. В конце четырехчасового цикла работы анализатора без в.датчика он прослушивает эфир: а вдруг в.датчик снова в эфире?




Для перевода модуля HTU21D в режим низкого энергопотребления во время сна его питание также, как и в в.датчике, организовано с контроллера ATMEGA328P (пин 14).


В целом, на дисплее анализатора видна такая картинка:



Дисплей из-за низкого разрешения и малого размера экрана плотно забит символами. Эта картинка смотрелась бы намного лучше на современном дисплее с электронными чернилами. В будущем в своих проектах буду использовать e-paper дисплей.


Ресурс батареек и другое


Для расчета срока работы батареек понадобится время и потребляемый ток во время выполнения работы (операционное время) и сна. Операционное время и рабочий ток измерялись с использованием тестовых скетчей, идея которых взята отсюда.


Рабочий ток измерялся с использованием тех же тестовых скетчей. Для исключения разрывов цепи питания или значительного увеличения величины выходного сопротивления батареек можно использовать шунт 3,9...5,6 Ом и параллельно подключенный к нему цифровой мультиметр с механическим переключением в режиме вольтметра на диапазоне 2000 мкВ. Это критично при измерении потребления тока сна анализатора, поскольку разрыв питания или значительное ограничение тока приводят к цикличесому ресету анализатора. Да и выносной датчик может переходить в постоянный рестарт. По мере возможности необходимо проверять ток потребления разными способами на разных диапазонах шкал прибора и с батарейками, которые планируется использовать, притом, обязательно без вывода результатов на монитор порта Ардуино. Невыполнение этих правил сказались на результатах измерений тока в предыдущем моем посте на тему метеостанции в одних случаях они занижены, в других завышены.


Результаты измерений сведены в таблицу:


в.датчик анализатор
Операционное время функции измерений параметров воздуха, состояния батареек 0,25 сек 0,39 сек
Операционный ток функции измерений параметров воздуха, состояния батареек 3,4 мА 3,5 мА
Операционное время функции передачи/приема сигнала 42 мсек 83 мсек
Операционный ток функции передачи/приема сигнала 30,0 мА

(4 дБ)


11,5 мА
Ток сна 10 мкА 190 мкА

Что бросается в глаза, глядя на эту таблицу. Операционный ток передачи сигнала 30,0 мА при мощности передатчика LoRa 4 дБ. Для сравнения, ток передачи для модуля nRF24L01 13,5 мА. Вывод очевиден: надо переходить на nRF24L01, но не все так просто.


В режиме приемника в nRF24L01 используется так называемыйLNA (малошумящий усилитель). Разработчик библиотеки предполагает, что нет никакого программного обеспечения, которое могло бы повлиять на режим LNA.В режиме приема модуль постоянно демодулирует сигнал для поиска входящего пакета. Именно по этой причине Berkseo не поставил внешний датчик. У меня задача, вроде, попроще организовать режим сна с библиотекой LowPower.h. Сомневаюсь, что задача имеет решение. Буду благодарен за ваши мнения на этот счет.


Средний ток потребления по данным таблицы в. датчика 0,13 мА. Емкости батареек типа АА GP Litium для выносного датчика должно хватить на 2,5 года.


Средний ток потребления анализатора 0,27 мА. Ресурс батареек АА GP Litium в анализаторе 1,2 года. Для беспроводного комнатного термостата Computherm Q7RF, например, срок действия батареек: около 1 года.


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


Составил код на С в Atmel Studio и эмулировал его в Proteus'е для для барометра-термометра.



На картинке ниже показаны результаты сравнения кода для одного и того же устройства на языке С и в среде разработки Arduino IDE.



Объем флеш-памяти, занимаемой в коде в Ардуино 12968 байт, на С 5954 байта и оценочно на Ассемблере не больше 200 байт.


Из этих чисел сделал несколько выводов, в которых убедился на собственном опыте:
Код на Ассемблере уменьшает размер памяти на порядки. Соответственно пиковое потребление падает в сотни раз. С десятков миллиампер при прошивках контроллеров устройств на Ардуино или С, С++ до десятых миллиампера на Ассембере.
Поиск компромисса. Так благодаря использованию компилируемых в Arduino IDE библиотек и функций на С/С++ в некоторых скетчах этого поста удалось уйти от предупреждения: Недостаточно памяти, программа может работать нестабильно. Притом, чем проще код, тем выше соотношение: размер памяти в Arduino IDE к памяти на С/С++. Для простейшего кода мигания светодиодом в несколько строк это соотношение составит 6 раз, а проигрыш в производительности 28 раз.


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


И, наконец, искренне благодарю AlexanderS, который донес до меня идею виртуальной шкалы времени или синхронизации, а также других участников обсуждения статьи Автономная метеостанция на контроллере ATMEGA328P и питанием от батареек с беспроводным выносным датчиком (ittakir, Javian, smart_alex, Polaris99, gerasimenkoao, igrushkin, enjoyneering) за предложения, конструктивную критику и замечания.


Спасибо, кто дочитал. Всем отличного иммунитета во времена коронавируса и не только.


Ссылки по теме


Узел беспроводного датчика с низким энергопотреблением


Беспроводная мини погодная станция с e-paper экраном на батарейках


Превращаем Arduino в полноценный AVRISP программатор


LoRa и сон


Узнайте о битах конфигурации ATmega328P и о том, как использовать их с внешним кварцевым резонатором


Калькулятор фьюзов AVR


Почему многие не любят Arduino

Подробнее..

На распутье Ардуино, Cи или Ассемблер?

22.03.2021 10:20:41 | Автор: admin

Сначала короткая предыстория появления этого поста. Относительно давно, помигав светодиодом, захотелось сделать что-то полезное. Так появился Беспроводной программируемый по Wi-Fi комнатный термостат с монитором качества воздуха и другими полезными функциями. Как назло, в это время перестал работать мой промышленный термостат. Меня выручила еще сырая поделка, наспех спрятанная в картонную коробочку. За время отопительного сезона напрягал лишь один недостаток прототипа это необходимость таскать по квартире удлинитель 220В и кабель, который всегда путался под шваброй ногами. Поэтому решил сделать нечто похожее, но автономное, притом, с питанием от батареек, как в серийном образце. Тут я завис надолго.


Приступая к задаче, для меня было очевидно одно вряд ли программы промышленных автономных устройств составлены на платформе Arduino IDE. Где все спрятано в громоздкие тяжеловесные библиотеки, а простые коды (скетчи) занимают в редакторе несколько десятков строк, делая работу в этой среде комфортной и не требующей особых усилий. Уточню сразу дальше речь выборе языка программирования между Ардуино, Си или Ассемблером. Хотя, понятно, на потреблении устройства в целом сказывается слишком много факторов таких, как энергопотребление подключенных модулей, потребление самого контроллера, который управляет периферией, не последнюю роль тут играет оптимальное построение самого кода и алгоритм работы устройства.


направу ехати женату быти; налеву ехати богату быти


В начале пути, перед выбором направления меня оптимистично настроила статья Почему многие не любят Arduino. Ниже, для наглядности, картинка оттуда с кодом мигалки.




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


На изображении ниже компиляция кода мигалки на Ассемблере. Как видно, былая компактность испарилась количество строк в 3 раза больше, чем в Ардуино.





И так, с 2 картинок выше видно размер памяти, занимаемой в контроллере кодом мигалки одним светодиодом, написанным на Ардуино, составляет 1030 байт, на Си 176 байт, на Ассемблере 42 байта.


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


барометр-термометр на Си
/*    На распутье - Ардуино, Cи или Ассемблер?    http://personeltest.ru/aways/habr.com/ru/post/547752/ */#include <avr/io.h>#include <avr/interrupt.h>#include <util/delay.h>#include "bmp180/bmp180.c"#include "uart.c"#include <stdio.h>#include <stdlib.h>#include <string.h>#include "nokia/nokia5110.h"int main(void) {    serial_init();    DDRD |= (1 << 7);  //pin 13, atmega328p    PORTD &= ~(1 << 7);    nokia_lcd_init();    while (1) {        init_sensor(bmp180_mode_0);        calculate();        PORTD |= (1 << 7);        _delay_ms(100);        printf("Temperature: %.2f C, Pressure: %.2f Pa, \n", (float) bmp_180.temperature / 10, (float)bmp_180.pressure);        nokia_lcd_clear();        nokia_lcd_write_string("789",1);        nokia_lcd_set_cursor(0, 10);        nokia_lcd_write_string("22.2", 3);        nokia_lcd_render();        _delay_ms(2000);        PORTD &= ~(1 << 7);        _delay_ms(100);        int a=54325;        char buffer[20];        itoa(a,buffer,2);   // here 2 means binary        printf("Binary value = %s\n", buffer);        itoa(a,buffer,10);   // here 10 means decimal        printf("Decimal value = %s\n", buffer);        itoa(a,buffer,16);   // here 16 means Hexadecimal        printf("Hexadecimal value = %s\n", buffer);    }    return 0;}

В проект входят следующие компоненты: контроллер ATMEGA328P, модуль давления-температуры BMP180 и дисплей Nokia 3110. ATMEGA328P принимает инфу с датчика BMP180 и после преобразований отображает ее на дисплее Nokia 3110, затем спит. Сон задается сторожевым таймером Watchdog. Проект собирается в Atmel Studio 7 и эмулируется в Proteus 8 Pro. Этот проект Atmel Studio был создан для отладки кода в Proteus'e. В библиотеке Proteus 8 Pro модуля BMP280 нет, поэтому пришлось составить код с включением BMP180. Светодиод в коде для наглядности, чтобы придать динамику статичной картинке.




Ниже электрическая схема устройства. При монтаже схемы обращайте внимание на функциональное назначение выводов контроллера и модулей. Подключение кварца XTAL1, XTAL2 (ATMEGA328P). Уточню, схему барометра-термометра на BMP180 я "в железе" не собирал, поэтому тут могут проявиться проблемы, которые не видны при эмуляции в Proteus'e.





Для скачивания zip-файла проекта в Atmel Studio 7 перейдите по ссылке тут все виртуальные проекты и коды программ из этой публикации.


Файл прошивки bmp180-nokia3110-watchdog-1.hex для Proteus'а находится в папке Debug. В файле main.c есть закомментированный код барометра-термометра на BMP280. Его электрическая схема такая же, как и у барометра-термометра на BMP180. Он успешно собирается в Atmel Studio 7 и работает "в железе". Для работы "в железе" пришлось внести изменения в строке #define BMP280_ADDR 0x77 файла библиотеки bmp280.c, а именно: заменить начальный адрес 0x77 на 0x76. Не забудьте сделать эту корректировку, если будете использовать в своих проектах код барометра-термометра на BMP280 из main.c, с уже подключенной библиотекой bmp280.c.


Ниже код этого же барометра-термометра в платформе Arduino IDE. Естественно, с другими библиотеками.


барометр-термометр на Ардуино
/*    На распутье - Ардуино, Cи или Ассемблер?    http://personeltest.ru/aways/habr.com/ru/post/547752/ */#include <SPI.h>#include <LowPower.h>#include <SimpleTimer.h>#include <Adafruit_BMP280.h>#include <Adafruit_GFX.h> //https:esp8266.ru/forum/threads/esp8266-5110-nokia-lcd.1143/#post-16942#include <Adafruit_PCD8544.h> //https:esp8266.ru/forum/threads/esp8266-5110-nokia-lcd.1143/#post-16942Adafruit_BMP280 bmp280;float Press, Tin; //давление, температураAdafruit_PCD8544 display = Adafruit_PCD8544(5, 7, 6);void setup() {  Serial.begin(9600);  display.begin();  // display.clearDisplay();  display.setContrast(60); // установка контраста  while (!bmp280.begin(BMP280_ADDRESS - 1)) {    Serial.println(F("Could not find a valid BMP280 sensor, check wiring!"));    delay(100);  }}void loop() {  // измерение температуры, давления  Tin = bmp280.readTemperature();  Press = bmp280.readPressure() / 133.3;  Serial.println("Temperature: " + String(Tin) + "*C");  Serial.println("Pressure: " + String(Press) + "mm Hq");  display.clearDisplay();  //давление, мм рт.ст.  {    display.setTextSize(1);    display.setCursor(20, 5);    display.println (Press, 0); // нет знаков после запятой    display.setCursor(41, 5);    display.println("mmHq");  }  //температура, *C  {    display.setTextSize(2);    display.setCursor(15, 20);    display.println (Tin, 1); // один знак после запятой    display.setCursor(66, 20);    display.println("C");  }  display.display();  LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);}

Ресурсы, потребляемые программой барометра-термометра на Си и в Arduino IDE наглядно показаны на картинке:




Как видно, эти примеры потребляют 5954 байт (С) и 12956 байт (Arduino IDE) в Flash. Соотношение изменилось с 6-ти раз для мигалки до 2-х с небольшим. К сожалению, линейной зависимости нет чем объемней код, тем меньше соотношение размеров памяти Ардуино к Си. В идеале на этой картинке должен присутствовать 3 столбец с кодом на Ассемблере, но такого кода в Интернете я не нашел, а составить код самому мне не под силу.


Попутно замечу, что использование компилируемых в Arduino IDE библиотек и функций на С/С++ особо имеет смысл в тех случаях, когда размер занимаемой памяти превышает или близок к размеру памяти контроллера. Мне, например, часто удается уходить от предупреждения: Недостаточно памяти, программа может работать нестабильно.





Теперь посмотрим еще один вариант это цифровой термометр-гигрометр на AM2302 (DHT22), ATtiny13 и MAX7219, код которого составлен на Ассемблере.


Автор статьи задался целью разработать простой термометр-гигрометр выполненном на одном из самых маленьких микроконтроллеров ATtiny13 с весьма скромными характеристиками 1Кб программной памяти, 64 байтами ОЗУ и 5 интерфейсными выводами. Он решил эту непростую задачку, выбрав Ассемблер, заодно вспомнив те далекие времена, когда код можно было составлять на низкоуровневых языках, используя машины типа ZX-Spectrum.


Ниже скриншот со сборкой данного кода в Atmel Studio 7.





Код устройства на Ассемблере занимает 738 байт памяти в контроллере. Безусловно, программа барометра-термометра, о котором шла речь выше, будь ее код составлен на Ассемблере, заняла бы больше места. По нескольким причинам в схеме реализовано управление дисплеем Nokia3110 по интерфейсу SPI (это 5 линий связи, тут 3), связь с датчиком BMP280 осуществляется по протоколу I2C (2 линии, тут 1) и дополнительные символы, которые позволяют не гадать температура это или другой параметр.


Из того, что я нашел в Интернете, можно утверждать, Ассемблер даст выигрыш в размере кода для относительно больших проектов процентов 10-20 по сравнению с Си. Но надо учитывать, что в больших проектах Си может уменьшить размер кода за счёт лучшей оптимизации.


Код Ассемблера выполняется практически на машинном уровне: один цикл одна команда. В качестве аргумента приведу пример из справочника по командам ассемблера AVR. Установка бита в регистре ввода/вывода SBI A, b. Эта команда устанавливает заданный бит в регистре ввода-вывода. На выполнение этой операции контроллерами megaAVR потребуется 2 цикла и на tinyAVR, XMEGA 1 цикл. Для схемы с контроллером ATtiny13 и резонатором 9,6 МГц выполнение команды займет один цикл, то есть 1/9600000 Гц = 0,104 мксек.


Выполнение похожей операции на языке Си, например, задать состояние порта PORTB = 32; займет в этой же схеме не меньше времени. А о Ардуино и говорить нечего там придется выполнить объемную функцию void digitalWrite(uint8_t pin, uint8_t val);. Подробно о размерах кода в Си и Ардуино читайте тут.


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


Теперь о энергосбережении немножко издали. Вспомним, что код Ассемблера выполняется на машинном уровне: один цикл одна или несколько команд. в зависимости от типа контроллера. Это десятые доли микросекунды. То есть, на выполнение программы с размером несколько десятков байт уйдут единицы-десятки микросекунд. Дальше контроллер бесконечно будет крутить этот набор 0 и 1, затрачивая энергию на перезаряд емкости затворов сотен полевых транзисторов, на которых построен кристалл контроллера, а также чтение и записи данных в его память. Длительность периода повтора будет зависеть только от размера кода в памяти контроллера, неважно на каком языке он составлен. Просто на Ассемблере он будет наименьшим, а на Ардуино наибольшим. Соответственно, период цикла для кода на Ассемблере наименьший, на Ардуино наибольший.


Уменьшить эти затраты можно остановив процессор или программно уменьшив частоту его работы. В Ассемблере переход в "спящий" режим сна выполняет функция управления контроллером SLEEP. В других можно использовать функцию WDT (WatchDog Timer), а в Ардуино еще и функцию LowPower.powerDown (SLEEP_1S, ADC_OFF, BOD_OFF), заодно отключив все лишнее, что не используется в конкретной задаче. В эффективности этой функции сможет убедиться каждый, заменив в скетче мигалки (скетч на картинке вначале статьи) функцию отсчета времени delay(1000); этой функцией и включив в разрыв питания контроллера амперметр. Да, не забудьте подключить библиотеку LowPower.h. На Си это сделал автор этой статьи. Ток в цепи питания attiny13a с паузой 1,5мА, со сном 240мкА. Потребление в 6(!) раз меньше.


Допустим, вы намерены собрать барометр-термометр и задумываетесь о энергосбережении. Понятно, что давление/температура в заданной разрядности не изменятся за несколько минут, которые для контроллера целая вечность. Ему можно выделить это время для сна. После сна он снова выполнит свою работу: примет информацию с датчика, преобразует в понятные для человека циферки и выведет все это на дисплей. И в таком режиме работа-сон он будет крутиться, пока не сядут батарейки. Объем работы контроллера, вернее время, которое контроллер будет занят выполнением работы, зависит от того, на каком языке составлена программа барометра-термометра. Если есть возможность загрузить в контроллер код на выбор ArduinoIDE, C, Assembler, с одинаковым временем сна, то в каком из трех предложенных вариантов батарейки сядут раньше (позже)? Мой ответ ArduinoIDE (Assembler).


Кстати, настоятельно рекомендую хотя бы вскользь посмотреть Реальная правда о Программистах ненавидящих Arduino. Поучительный материал. Автор подробно описывает свою историю от первых радостей мигания светодиодом и чуда по тем временам вывода температуры на дисплей от мобилки до Ардуино с дельными советами.


Так куда же идти? На мой взгляд, для любителей, как я, это платформа Arduino IDE с низкоуровневыми вставками. Тем же, кому тесно в Ардуино, в С. Assembler не освоить за несколько месяцев. Да и осваивать не имеет особого смыла коды на С можно оптимизировать иногда до размеров не намного больше, чем в Ассемблере.


Спасибо за внимание. Всего наилучшего!


Ссылки по теме


Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru