Аудиоинформер скорости ветра [Амперка / Вики]

Аудиоинформер скорости ветра [Амперка / Вики] Анемометр

Анемометр v.1.0

Сделал измеритель скорости ветра для будущей метеостанции. Не уверен, правда, что будет нормально работать, т.к. опыта в области анемометростроения у меня нет. Зато вдоволь наигрался с оптопарой от шариковой мыши и проверил её возможности в плане измерения скорости (частоты) вращения

DSC01200

Из закормов Родины взял советский шаговый двигатель

DSC01163

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

DSC01168

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

DSC01170

DSC01173

Далее подобрал шпильку диаметром 5 мм и металлическую трубку

DSC01176

Шпильку подстыковал к другому концу выходного вала и снаружи зафиксировал трубкой.

DSC01177

Трубка одевается на вал втугую, но для надежности дополнительно залил внутрь эпоксидку

DSC01181

Перехожу к ходовым испытаниям. Спаял схему

tachometer

Написал небольшую программу — тахометр, которая по формуле рассчитывает количество оборотов в соответствии с количеством импульсов, поступающих на вход микроконтроллера за единицу времени. Каждый замер длится 1 секунду. Результаты замеров записываются в массив данных. Затем вычисляются средняя (RPM) и максимальная частота вращения (RPMMAX). Скачать скетч для ардуинки можно тут

К валу подсоединил двигатель постоянного тока, и покрутил на разных оборотах.

DSC01159

Получилось измерять скорость вращения примерно до 1800 об/мин, что соответствует 30 об/сек. При дальнейшем увеличении частоты вращения, показания резко снижаются. Не понятно что на это влияет — то ли сам алгоритм не успевает считать, то ли не хватает быстродействия фототранзистора. А может и то и другое. В любом случае, в качестве анемометра схема вполне работоспособна.

monitor

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

DSC01197

Вытряхнул из него внутренности

DSC01198

С мыслью «из чего бы сделать крыльчатку?» прогулялся в магазин детских товаров. Немного побродил и таки нашел нужную погремушку! Купил, принёс домой

DSC01065

Достал 2 больших шарика. Диаметр у них 50 мм

DSC01066

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

DSC01194

Стойки, на которых держатся крыльчатки, изготовил из спиц от зонтика. Они лёгкие и прочные. Закрепил стойки к чашечкам с помощью винтов М3, второй конец одел на шпильку вала. Длину стоек выбрал произвольно, около 70 мм. Не знаю много это или мало. Так же непонятно — сколько чашечек нужно? в Интернете находил конструкции с 3 шт, поэтому сделал пока тоже с 3-мя. Изделие в сборе

DSC01202

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

Вариант 1. переделываем pulseinlong()

На функции pulseIn() я сначала и зациклился — а нельзя ли ее приспособить к этому делу? В недрах папок Arduino (в файле wiring_pulse.c) обнаружился ее более продвинутый вариант под названием pulseInLong(). Введен он был, как я выяснил, где-то около версии 1.6.

5, но чем этот вариант лучше оригинальной функции, так и не понял. Судя по тому, что функция не введена в официальный перечень — или ничем, или имеет какие-то невыясненные ограничения. Но структура ее мне показалась более прозрачной и проще поддающейся переделке в нужном направлении. Выглядит вызов функции так же, как и pulseIn():

unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout)

В функции последовательно работают три условно-бесконечных цикла (с принудительным выходом по заданному timeout). В первом цикле ожидается перепад в состояние, заданное параметром state (HIGH или LOW) — чтобы пропустить текущий импульс, в середину которого мы, возможно, попали.

Во втором ожидается начало следующего периода (обратный перепад), после чего определяется количество микросекунд на старте. Наконец, третий цикл — измерительный, ожидается опять перепад в состояние state, фиксируется разность количества микросекунд и возвращается в значении функции.

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

#define Tone_PIN 12 // выход частоты – см. в тексте 
#define IN_PIN 8 //вход обнаружения частоты

volatile unsigned long ttime = 0;        //Период срабатывания датчика

unsigned long periodInLong(uint8_t pin, uint8_t state, unsigned long timeout)
{
  // cache the port and bit of the pin in order to speed up the
  // pulse width measuring loop and achieve finer resolution.  calling
  // digitalRead() instead yields much coarser resolution.
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  uint8_t stateMask = (state ? bit : 0);

  unsigned long startMicros = micros();

  // wait for any previous pulse to end
  while ((*portInputRegister(port) & bit) != stateMask) {
    if (micros() - startMicros > timeout)
      return 0;
  }

  // wait for the pulse to start
  while ((*portInputRegister(port) & bit) == stateMask) {
    if (micros() - startMicros > timeout)
      return 0;
  }

  unsigned long start = micros();
  // wait for the pulse to stop
  while ((*portInputRegister(port) & bit) != stateMask) {
    if (micros() - startMicros > timeout)
      return 0;
  }
  // wait for the pulse to start
  while ((*portInputRegister(port) & bit) == stateMask) {
    if (micros() - startMicros > timeout)
      return 0;
  }
  return micros() - start;
}

void setup() {
  pinMode(IR_PIN, OUTPUT); //на выход
  pinMode(IN_PIN, INPUT); //вывод обнаружения частоты на вход
  Serial.begin(9600);
//  tone(Tone_PIN, 1000); 
}

void loop() {
      ttime=periodInLong(IN_PIN, LOW, 1000000); //ожидание 1 сек
        Serial.println(ttime);
       if (ttime!=0) {//на случай, если частота пропала
       float f = 1000000/float(ttime); // Вычисляем частоту сигнала в Гц
        Serial.println(f,1);}
      delay(500);
}

Обратите внимание на вывод Tone_PIN и закомментированный вызов функции tone() в разделе setup(). Это сделано для проверки еще одного обстоятельства, о чем в конце статьи.

Для проверки работы на вывод 8 (произвольно выбранный в качестве IN_PIN) подавался сигнал от самодельного генератора на основе часового кварца и счетчика-делителя 561ИЕ16. На выходе его мы получаем частоты, кратные степеням двойки, от 2 до 2048 Гц, а также 16384 Гц (и при желании, еще 32768 Гц прямо с генератора).

Результаты выборки последовательных измерений для частот 2, 8, 64, а также 2048 и 16384 герца объединены в одну таблицу на рисунке (верхняя строчка — длительность в микросекундах, следующая — рассчитанная частота):

Аудиоинформер скорости ветра [Амперка / Вики]
Как мы видим из этих данных, способ вполне удовлетворительно работает для низких частот (ниже примерно 100 герц), но «дребезжит» на высоких частотах. Это нормально, если вспомнить, что функция micros() завязана на переполнения и счетчики Timer0, а вызов ее и манипуляции с длинным целым занимают значительное время. Простые условно-бесконечные циклы в контроллерах в принципе не очень надежная штука.

Огромное преимущество этого способа в том, что он позволяет задействовать в качестве измерительного любой цифровой вывод контроллера. Недостаток в том, что основной цикл зависает на время измерения (для единиц герц оно занимает сумасшедшие, по меркам контролера, доли секунды) и одновременно не очень желательно использовать какие-то другие прерывания. Этого недостатка практически лишены два других способа, зато они привязаны к определенным выводам контролера.

Вторая версия

Есть еще одна причина все переделать. Как отмечалось в первой теоретической части, скорость звука изменится на 1 м/с при изменении температуры примерно на 1.5 °С. Погрешности измерений по обоим осям складываются. Нужно понимать, что порывы теплого или холодного воздуха могут существенно исказить показания такого анемометра. Нет смысла в показаниях 4 м/с при легком дуновении теплого ветерка. диаграмма температуры и скорости Из диаграммы натурного эксперимента видно, что даже медленное изменение температуры вызывает дрейф измеренной скорости, а быстрое изменение температуры на 1 градус скачком поменяло измеренную скорость ветра на 1.5 м/с, в то время как датчик температуры медленно отрабатывает это изменение. Важно заметить, что эксперимент этот проходил прямо у меня на столе и изменение температуры было естественным — я ничего не трогал и искусственно ничего не нагревал.

Про анемометры:  анемометра чашечный на АлиЭкспресс — купить онлайн по выгодной цене

И тут на помощь приходит тот же принцип, что и при измерении расстояния. Если помним, датчики у оригинального HC-SR04 расположены вместе, поэтому результаты не зависят от наличия ветра. Если измерить скорость звука на известном расстоянии сначала в одном направлении, а затем в другом, то разница этих двух показаний, деленная пополам и будет искомой скоростью ветра в проекции на эту ось.

При этом, изменение температуры в диапазоне ±25°С дает погрешность ±4%, что абсолютно не критично и мы можем обойтись вообще без термометра. Да и зачем нам термометр? Если мы знаем время прохождения сигнала в обоих направлениях, то по формулам из прошлой статьи мы легко вычислим температуру, а значит сможем уточнить скорость ветра.

Есть лишь одна маленькая загвоздка — придется использовать два HC-SR04 на одной оси. В промышленных образцах датчики попеременно выполняют роль приемника и передатчика. В нашем случае для этого придется подключить пищалки напрямую к arduino и программно генерировать 8 импульсов 40 кГц на одной, после чего вычленять их из другой.

Зная про определенные сложности на этом пути, мне представляется проще купить еще 2 датчика по 55 рублей и попытаться обойтись малой кровью. Этим я займусь в следующий раз. А пока на двух датчиках сделаю измерение скорости ветра по одной оси и измерение температуры в такой конфигурации. Главная проблема здесь убрать помехи, которые дают такой большой разброс показаний в спокойном воздухе.

Выносной модуль и измерительная схема датчиков ветра

В качестве фотоизлучателей были выбраны светодиоды ИК-диапазона АЛ-107Б. Эти старинные светодиоды, конечно, не самые лучшие в своем классе, зато имеют миниатюрный корпус диаметром 2,4 мм и способны пропускать ток до 600 мА в импульсе. Между прочим, при испытаниях выяснилось, что образец этого светодиода около 1980 года выпуска (в корпусе красного цвета) имеет примерно вдвое большую эффективность (выразившуюся в дальности уверенной работы фотоприемника), чем современные экземпляры, купленные в «Чипе-Дипе» (они имеют прозрачный желтовато-зеленый корпус).

Через светодиод в датчике скорости пропускался постоянный ток около 20 мА (резистор 150 Ом при питании 5 вольт), а в датчике направления — импульсный (меандр со скважностью 2) ток около 65 мА (те же 150 Ом при питании 12 вольт). Средний ток через один светодиод датчика направления при этом около 33 мА, всего через четыре канала — около 130 мА.

В качестве фотоприемников были выбраны фототранзисторы L-32P3C в корпусе диаметром 3 мм. Сигнал снимался с коллектора, нагруженного на резистор 1,5 или 2 кОм от питания 5 В. Эти параметры подобраны так, чтобы на расстоянии ~20 мм между фотоизлучателем и приемником на вход контроллера поступал сразу полноразмерный логический сигнал в 5-вольтовых уровнях без дополнительного усиления.

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

Основой для крепления приемников и излучателей послужили отрезки кабельного канала (видны на фото датчиков выше), вырезанные так, чтобы у основания образовать «ушки» для крепления на скобе. Для каждого из этих обрезков к запирающей крышке изнутри приклеивалась пластиковая пластинка, по ширине равная ширине канала.

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

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

Принципиальная схема блока обработки датчиков ветра выглядит так:

О том, откуда берется питание 12-14 вольт, см. далее. Кроме компонентов, указанных на схеме, выносной блок содержит датчик температуры-влажности, который на схеме не показан. Делитель напряжения, подключенный к выводу A0 контроллера, предназначен для контроля напряжения источника питания с целью своевременной замены.

В схеме используется «голый» контроллер Atmega328 в DIP-корпусе, запрограммированный через Uno и установленный на панельку. Такие контроллеры с уже записанным Arduino-загрузчиком, продаются, например, в «Чипе-Дипе» (или загрузчик можно записать самостоятельно).

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

На схеме серыми прямоугольниками обведены компоненты, относящиеся отдельно к каналам скорости и направления. Рассмотрим функционирование схемы в целом.

Работа контроллера в целом управляется сторожевым таймером WDT, включенным в режиме вызова прерывания. WDT выводит контроллер из режима сна через заданные промежутки времени. В случае, если в вызванном прерывании таймер взводится заново, перезагрузки с нуля не происходит, все глобальные переменные остаются при своих значениях. Это позволяет накапливать данные от пробуждения к пробуждению и в какой-то момент обрабатывать их — например, усреднять.

В начале программы сделаны следующие объявления библиотек и глобальных переменных (чтобы не загромождать текст и без того обширных примеров, здесь выпущено все, что относится к датчику температуры-влажности):

#include <VirtualWire.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
. . . . .
#define ledPin 13 //вывод светодиода (PB5 вывод 19 ATmega)
#define IR_Pin 10 //управление транзистором IRLU (PB2 вывод 16 Atmega)
#define in_3p 9 //вход приемника разряд 3
#define in_2p 8 //вход приемника разряд 2
#define in_1p 7 //вход приемника разряд 1
#define in_0p 6 //вход приемника разряд 0
#define IR_PINF 5 //(PD5,11) вывод для ИК-светодиода частоты
#define IN_PINF 4 //(PD4,6) вход обнаружения частоты 

volatile unsigned long ttime = 0;        //Период срабатывания датчика
float ff[4]; //значения частоты датчика скорости для осреднения
char msg[25]; //посылаемый месседж
byte count=0;//счетчик
int batt[4]; //для осреднения батарейки
byte wDir[4]; //массив направлений ветра
byte wind_Gray=0; //байт кода направления ветра

Для инициации режима сна и WDT (пробуждение каждые 4 с) служат следующие процедуры:

// перевод системы в режим сна
void system_sleep() {
  ADCSRA &= ~(1 << ADEN); //экв. cbi(ADCSRA,ADEN); выключим АЦП
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // режим сна
  sleep_mode();                        // система засыпает
    sleep_disable(); // система продолжает работу после переполнения watchdog
    ADCSRA |= (1 << ADEN); /экв. sbi(ADCSRA,ADEN); включаем АЦП
}

//****************************************************************
// ii: 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
void setup_watchdog(int ii) {
  byte bb;
  if (ii > 9 ) ii=9;
  bb=ii & 7;
  if (ii > 7) bb|= (1<<5); //в bb - код периода
  bb|= (1<<WDCE);
  MCUSR &= ~(1<<WDRF);
  // запуск таймера
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  // установка периода срабатывания сторожевого таймера
  WDTCSR = bb;
  WDTCSR |= (1<<WDIE); //прерывание WDT  
}
//****************************************************************  
// Обработка прерывания сторожевого таймера 
ISR(WDT_vect) {
        wdt_reset();
}


Датчик скорости выдает частоту прерывания оптического канала, порядок величин — единицы-десятки герц. Мерить такую величину экономичнее и быстрее через период (этому была посвящена публикация автора «

»). Здесь выбран метод через модифицированную функцию pulseInLong(), который не привязывает измерение к определенным выводам контроллера (текст функции periodInLong() можно найти в указанной публикации).

В функции setup() объявляются направления выводов, инициализируются библиотека передатчика 433 МГц и сторожевой таймер (строка для IN_PINF в принципе лишняя, и вставлена для памяти):

void setup() {
  pinMode(IR_PINF, OUTPUT); //на выход
  pinMode(IN_PINF, INPUT); //вывод обнаружения частоты на вход
  pinMode(13, OUTPUT); //светодиод
  vw_setup(1200); // скорость соединения VirtualWire
  vw_set_tx_pin(2);   //D2, PD2(4) вывод передачи VirtualWire
//  Serial.begin(9600); // Serial-порт для контроля при отладке
  setup_watchdog(8); //WDT период 4 c
  wdt_reset();
}

Наконец, в основном цикле программы мы сначала каждый раз при пробуждении (каждые 4 секунды) считываем напряжение и рассчитываем частоту датчика скорости ветра:

void loop() {
  wdt_reset(); //обнуляем таймер
  digitalWrite(ledPin, HIGH); //включаем светодиод для контроля
  batt[count]=analogRead(0); //читаем и сохраняем текущий код батарейки
/*=== частота ==== */ 
  digitalWrite(IR_PINF, HIGH); //включаем ИК-светодиод датчика скорости
  float f=0; //переменная для частоты
      ttime=periodInLong(IN_PINF, LOW, 250000); //ожидание 0,25 сек
//        Serial.println(ttime); //для контроля при отладке
       if (ttime!=0) {//на случай отсутствия частоты
       f = 1000000/float(ttime);} // вычисляем частоту сигнала в Гц
       digitalWrite(IR_PINF, LOW); //выключаем ИК-светодиод
 ff[count]=f; //сохраняем вычисленное значение в массиве    
. . . . .

Время горения ИК-светодиода (потребляющего, напомню, 20 мА) здесь, как видите, будет максимальным при отсутствии вращения диска датчика и составляет при этом условии около 0,25 секунды. Минимальная измеряемая частота, таким образом, составит 4 Гц (четверть оборота диска в секунду при 16 отверстиях).

Про анемометры:  Датчик ветра на АлиЭкспресс — купить онлайн по выгодной цене

Как выяснилось при калибровке датчика (см. далее), это соответствует примерно 0,2 м/с скорости ветра Подчеркнем, что это минимальная измеряемая величина скорости ветра, но не разрешающая способность и не порог трогания (который окажется гораздо выше).

Далее следуют процедуры, которые выполняются каждое четвертое пробуждение (то есть каждые 16 секунд). Значение частоты датчика скорости из накопленных четырех значений мы передаем не среднее, а максимальное — как показал опыт, это более информативная величина.

//каждые 16 сек усредняем батарейку и определяем максимальное значение 
//частоты из 4-х значений:
if (count==3){ 
    f=0; //значение частоты
    for (byte i=0; i<4; i  ) if (f<ff[i]) f=ff[i]; //максимальное значение из четырех
    int fi=(int(f*10) 1000); //доводим до 4 дес. разрядов для отправки
    int volt=0; //код батарейки
    for (byte i=0; i<4; i  ) volt=volt batt[i];
    volt=volt/4 100; //средний код на 100 больше = 3 дес.разряда 
    volt=volt*10; //до 4 дес. разрядов
. . . . .

Далее — определение кода Грея направления. Здесь для снижения потребления вместо постоянно включенных ИК-светодиодов на все четыре канала одновременно через ключевой полевой транзистор с помощью функции tone() подается частота 5 кГц. Обнаружение наличия частоты на каждом из разрядов (выводы in_0p – in_3p) производится методом, аналогичным антидребезгу при считывании показаний нажатой кнопки.

Сначала в цикле дожидаемся, имеется ли на выводе высокий уровень, и затем проверяем его через 100 мкс. 100 мкс есть полпериода частоты 5 кГц, то есть при наличии частоты минимум со второго раза мы опять попадем на высокий уровень (на всякий случай повторяем четыре раза) и это означает, что он точно там есть. Эту процедуру повторяем для каждого из четырех бит кода:

/* ===== Wind Gray ==== */
//направление:
  tone(IR_Pin,5000);//частоту 5 кГц на транзистор
  boolean yes = false;
  byte i=0;
  while(!yes){ //разряд 3
    i  ;
    boolean state1 = (digitalRead(in_3p)&HIGH);
    delayMicroseconds(100); // задержка в 100 микросекунд 
    yes=(state1 & !digitalRead(in_3p));
    if (i>4) break; //пробуем четыре раза
  } 
  if (yes) wDir[3]=1; else wDir[3]=0;
    yes = false;
    i=0;
  while(!yes){ //разряд 2
    i  ;
    boolean state1 = (digitalRead(in_2p)&HIGH);
    delayMicroseconds(100); // задержка в 100 микросекунд 
    yes=(state1 & !digitalRead(in_2p));
    if (i>4) break; //пробуем четыре раза
  } 
  if (yes) wDir[2]=1; else wDir[2]=0;
    yes = false;
    i=0;
  while(!yes){ //разряд 1
    i  ;
    boolean state1 = (digitalRead(in_1p)&HIGH);
    delayMicroseconds(100); // задержка в 100 микросекунд 
    yes=(state1 & !digitalRead(in_1p));
    if (i>4) break; //пробуем четыре раза
  } 
  if (yes) wDir[1]=1; else wDir[1]=0;
    yes = false;
    i=0;
  while(!yes){ //разряд 0
    i  ;
    boolean state1 = (digitalRead(in_0p)&HIGH);
    delayMicroseconds(100); // задержка в 100 микросекунд 
    yes=(state1 & !digitalRead(in_0p));
    if (i>4) break; //пробуем четыре раза
  } 
  if (yes) wDir[0]=1; else wDir[0]=0;
  noTone(IR_Pin); //выключаем частоту
  //собираем в байт в коде Грея:
  wind_Gray=wDir[0] wDir[1]*2 wDir[2]*4 wDir[3]*8; //прямой перевод в дв. код
  int wind_G=wind_Gray*10 1000; //дополняем до 4-х дес. разрядов
. . . . .

Максимальная длительность одной процедуры будет при отсутствии частоты на приемнике и равна 4×100 = 400 микросекунд. Максимальное время горения 4-х светодиодов направления будет тогда, когда не засвечен ни один приемник, то есть 4×400 = 1,6 миллисекунды.

Алгоритм, кстати, точно так же будет работать, если вместо частоты, период которой кратен 100 мкс, просто подать постоянный высокий уровень на светодиоды. При наличии меандра вместо постоянного уровня мы просто экономим питание вдвое. Мы можем еще сэкономить, если завести каждый ИК-светодиод через отдельную линию (соответственно, через отдельный вывод контроллера со своим ключевым транзистором), но зато при этом усложняется схема, разводка и управление, а ток в 130 мА в течение 2 мс каждые 16 секунд — это, согласитесь, немного.

Наконец, беспроводная передача данных. Для передачи данных от места установки датчиков до табло метеостанции был выбран самый простой, дешевый и надежный способ: пара передатчик/приемник на частоте 433 МГц. Согласен, способ не самый удобный (из-за того, что девайсы рассчитаны на передачу битовых последовательностей, а не целых байтов, приходится изощряться в конвертации данных между нужными форматами), и уверен, что многие со мной захотят поспорить в плане его надежности. Ответ на последнее возражение простой: «ты просто не умеешь их готовить!».

Секрет в том, что обычно остается за кадром различных описаний обмена данными по каналу 433 МГц: поскольку приборы эти чисто аналоговые, то питание приемника должно быть очень хорошо очищено от любых посторонних пульсаций. Ни в коем случае не следует питать приемник от внутреннего 5-вольтового стабилизатора Arduino!

В данном случае передатчик работал непосредственно от напряжения аккумулятора 12 В, приемник и передатчик были снабжены стандартными самодельными антеннами в виде отрезка провода длиной 17 см. (Напомню, что провод для антенн пригоден только одножильный, а размещать антенны в пространстве необходимо параллельно друг другу.)

Пакет информации длиной в 24 байта (с учетом влажности и температуры) без каких-то проблем уверенно передавался со скоростью 1200 бит/с по диагонали через садовый участок 15 соток (около 40-50 метров), и затем через три бревенчатых стенки внутрь помещения (в котором, например, сотовый сигнал принимается с большим трудом и не везде).

Условия, практически недостижимые для любого стандартного способа на 2,4 ГГц (типа Bluetooth, Zig-Bee и даже любительский Wi-Fi), притом, что потребление передатчика здесь составляет жалкие 8 мА и только в момент собственно передачи, остальное время передатчик потребляет сущие копейки. Передатчик конструктивно размещен внутри выносного блока, антенна торчит сбоку горизонтально.

Объединяем все данные в один пакет (в реальной станции к нему добавятся еще температура и влажность), состоящий из единообразных 4-байтных частей и предваряемый сигнатурой «DAT», отправляем его на передатчик и завершаем все циклы:

/*=====Transmitter=====*/
  String strMsg="DAT"; //сигнатура - данные
  strMsg =volt; //присоединяем батарейку 4 разряда
  strMsg =wind_G; //присоединяем wind 4 разряда
  strMsg =fi; //присоединяем частоту 4 разряда
  strMsg.toCharArray(msg,16); //переводим строку в массив
//  Serial.println(msg); //для контроля
  vw_send((uint8_t *)msg, strlen(msg)); // передача сообщения
  vw_wait_tx(); // ждем завершения передачи - обязательно!
  delay(50); //  еще на всякий случай задержка
   count=0; //обнуляем счетчик
}//end count==3 
else count  ;
  digitalWrite(ledPin, LOW); //гасим сигнальный светодиод
  system_sleep(); //систему — в сон
} //end loop

Размер пакета можно сократить, если отказаться от требования представления каждой из величин разнообразных типов в виде единообразного 4-байтового кода (например, для кода Грея, конечно, хватит и одного байта). Но универсализации ради я оставил все как есть.

Питание и особенности конструкции выносного блока. Потребление выносного блока подсчитываем таким образом:

— 20 мА (излучатель) ~20 мА (контроллер со вспомогательными цепями) в течение примерно 0,25 с каждые четыре секунды — в среднем 40/16 = 2,5 мА; — 130 мА (излучатели) ~20 мА (контроллер со вспомогательными цепями) в течение примерно 2 мс каждые 16 секунд — в среднем 150/16/50 ≈ 0,2 мА;

Накинув на этот расчет потребление контроллера при съеме данных с датчика температуры-влажности и при работе передатчика, смело доводим среднее потребление до 4 мА (при пиковом около 150 мА, заметьте!). Батарейки (которых, кстати, потребуется аж 8 штук для обеспечения питания передатчика максимальным напряжением!) придется менять слишком часто, потому возникла идея питать выносной блок от 12-вольтовых аккумуляторов для шуруповерта — их у меня образовалось как раз две штуки лишних.

Емкость их даже меньше, чем соответствующего количества АА-батареек — всего 1,3 А•часа, но зато никто не мешает их менять в любое время, держа наготове второй заряженный. При указанном потреблении 4 мА емкости 1300 мА•часов хватит примерно на две недели, что получается не слишком хлопотно.

Отметим, что напряжение свежезаряженного аккумулятора может составить до 14 вольт. На этот случай поставлен входной стабилизатор 12 вольт — чтобы не допустить перенапряжений питания передатчика и не перегружать основной пятивольтовый стабилизатор.

Выносной блок в подходящем пластиковом корпусе размещается под крышей, к нему на разъемах подведен кабель питания от аккумулятора и соединения с датчиками ветра. Основная сложность в том, что схема оказалась крайне чувствительной к влажности воздуха: в дождливую погоду уже через пару часов начинает сбоить передатчик, измерения частоты показывают полную кашу, а измерения напряжения аккумулятора показывают «погоду на Марсе».

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

Про анемометры:  Датчики направления и скорости ветра купить в Москве | NEOPOD

Стыки корпуса промазываются пластилином (с учетом того, что их придется разнимать), и дополнительно проклеиваются сверху полосками сантехнического скотча. Неплохо дополнительно аккуратно укрепить эпоксидкой используемые разъемы внутри: так, указанный на схеме выносного модуля DB-15 сам по себе не герметичен, и между металлическим обрамлением и пластиковой основой будет медленно просачиваться влажный воздух.

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

Чтобы этого избежать, необходимо внутри корпуса оставить патрончик или мешочек с влагопоглотителем — силикагелем (мешочки с ним иногда вкладывают в коробки с обувью или в некоторые упаковки с электронными устройствами). Если силикагель неизвестного происхождения и долго хранился, его перед использованием необходимо прокалить в электродуховке при 140-150 градусах несколько часов. Если корпус герметизирован как следует, то менять влагопоглотитель придется не чаще, чем в начале каждого дачного сезона.

Конструкция

Вооружившись паяльником конструкция была беспощадно распаяна на составляющие. Новую версию решил не делать так основательно, а зря. Никогда не угадаешь, где найдешь, где потеряешь. Получилось как-то так.
прототип 2 датчика вместе
Во-первых, приемник расположил как можно ближе к плате, а передатчик удалил всего лишь на 20 см. Второй комплект перевернул на 180 градусов и пищалки скрепил попарно изолентой. Чем точнее соблюсти соосность обоих пар датчиков, тем лучше. В идеале мы должны получить абсолютно идентичные показания скорости прохождения сигнала в обоих направлениях в спокойном воздухе. Натурные испытания подтвердили нашу теорию. В такой конфигурации получается мало помех и весьма точные показания независимо от температуры, что подтверждается графиком ниже.
диаграмма температур и скорости v2
Во-первых, приемник расположил как можно ближе к плате, а передатчик удалил всего лишь на 20 см. Второй комплект перевернул на 180 градусов и пищалки скрепил попарно изолентой. Чем точнее соблюсти соосность обоих пар датчиков, тем лучше. В идеале мы должны получить абсолютно идентичные показания скорости прохождения сигнала в обоих направлениях в спокойном воздухе. Натурные испытания подтвердили нашу теорию. В такой конфигурации получается мало помех и весьма точные показания независимо от температуры, что подтверждается графиком ниже.
Аудиоинформер скорости ветра [Амперка / Вики]

Вначале я пробовал просто дуть по направлению от синей пары к черной. Моих легких явно недостаточно. Но любопытный факт — воздух в легких успел нагреться на 1°, что раньше вызвало бы скачок скорости на 1.5 м/с, т.к. DS18B20 просто ничего не заметил. Отметим, что мои легкие способны дать всего лишь 0.5 м/с. Дальше я включил большой напольный вентилятор и направил все также от синего к черному. Видно как пошел более прохладный воздух из глубины комнаты и даже DS18B20 начал отрабатывать это снижение, но теперь его значения не используются для расчета скорости. Сделал открытие, что мой вентилятор дует со скоростью около 2 м/с. Дальше в течение паузы видим постепенное увеличение температуры и отличную корреляцию между рассчитанной и измеренной температурой. В конце поставил вентилятор с другой стороны и получил 2 м/с в обратном направлении с падением температуры. Ура, товарищи, это работает!

Механический датчик направления ветра — электронный флюгер

Основой флюгера (как и датчика скорости далее) служит П-образная скоба из дюраля Д-16, изображенная на чертеже вверху слева. В нижнее углубление запрессовывается кусочек фторопласта, в котором делается ступенчатое углубление последовательно сверлами 2 и 3 мм.

В это углубление острым концом вставляется ось (для флюгера — из латуни). Сверху она свободно проходит через отверстие 8 мм. Над этим отверстием винтами М2 к скобе прикрепляется прямоугольный кусочек того же фторопласта толщиной 4 мм так, чтобы он перекрывал отверстие.

image

Ось в месте трения о фоторопласт можно отполировать, а площадь трения уменьшить, отзенковав отверстие во фторопласте. (

См. на эту тему ниже UPD от 13.09.18 и 05.06.19

). Для флюгера это не играет особой роли — некоторая «заторможенность» ему даже полезна, а для анемометра придется постараться минимизировать трение и инерцию.

Теперь о съеме величины угла поворота. Классический энкодер Грея на 16 положений применительно к нашему случаю выглядит так, как показано на рисунке:
image
Размер диска был выбран, исходя из условия надежной оптической изоляции пар излучатель-приемник друг от друга. При такой конфигурации щели шириной 5 мм располагаются с промежутком также 5 мм, а оптические пары расположены на расстоянии ровно 10 мм. Размеры скобы, к которой крепится флюгер, были рассчитаны именно исходя из диаметра диска 120 мм. Все это, конечно, можно уменьшить (особенно, если подобрать светодиоды и фотоприемники как можно меньшего диаметра), но было принята во внимание сложность изготовления энкодера: выяснилось, что фрезеровщики за такую тонкую работу не берутся, потому его пришлось выпиливать вручную надфилем. А тут чем больше размеры, тем надежнее результат и меньше хлопот.

На сборочном чертеже выше показано крепление диска к оси. Тщательно отцентрованный диск крепится винтиками М2 к капролоновой втулке. Втулка размещается на оси так, чтобы зазор вверху был минимальным (1-2 мм) — так, чтобы ось в нормальном положении вращалась свободно, а при перевороте острие не выпадало из гнезда внизу. Блоки фотоприемников и излучателей прикрепляются к скобе сверху и снизу диска, более конкретно об их конструкции далее.

Вся конструкция помещается в пластиковый (АБС или поликарбонат) корпус 150×150×90 мм. В собранном виде (без крышки и флюгера) датчик направления выглядит следующим образом:

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

На верхушку оси крепится собственно флюгер. Он изготовлен на основе такой же латунной оси, в разрез на тупой стороне которой впаивается хвостовик из листовой латуни. На остром конце на некоторую длину нарезается резьба М6, и на ней с помощью гаек закрепляется круглый груз-противовес, отлитый из свинца:

Груз рассчитан так, чтобы центр тяжести приходился точно на место крепления (передвигая его вдоль резьбы, можно добиться идеальной балансировки). Крепление флюгера к оси осуществляется с помощью нержавеющего винта М3, который проходит через отверстие в оси флюгера и ввинчивается в резьбу, нарезанную в оси вращения (крепящий винт виден на фото выше). Для точной ориентации верхушка оси вращения имеет полукруглое углубление, в которое ложится ось флюгера.

Способ 3. самый простой: по внешнему событию

Это самый простой и довольно очевидный способ. Мы запускаем внешнее прерывание (по перепаду на выводе) и одновременно фиксируем системное время все той же функцией micros(). Как только произойдет второе такое прерывание, мы вычисляем разницу и таким образом получаем период.

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

Скетч, реализующий эту идею, выглядит таким образом:

#define Tone_PIN 12 // выход частоты– см. в тексте 
#define IN_PIN 2 //вход обнаружения частоты

volatile unsigned long ttime = 0;        //Время срабатывания датчика
volatile unsigned long time_old = 0;        //Предыдущее время
volatile uint8_t flag=0;

void setup() {
  pinMode(IR_PIN, OUTPUT); //на выход
  pinMode(IN_PIN, INPUT); //вывод обнаружения частоты на вход
  attachInterrupt(0, impuls, RISING);   //Прерывание по нарастающему фронту на D2
  Serial.begin(9600);
 // tone(Tone_PIN, 8); 
 delay(1000);
}

void impuls(){
    if(flag!=1) ttime =micros()-time_old;
    time_old = micros();
}

void loop() {
      flag=1; //чтобы ttime не изменилось в процессе вывода
        Serial.println(ttime);
       if (ttime!=0) {//на случай отсутствия частоты
       float f = 1000000/float(ttime); // Вычисляем частоту сигнала в Гц
        Serial.println(f,1);}
      flag=0;
      delay(500);
}

Вход прерывания здесь будет другой — вывод номера 2 (вывод PD2). Как и ранее, флаг защищает от изменения переменой ttime в процессе вывода. Касательно отсутствия частоты на входе здесь действительны те же соображения, что и в предыдущем случае. Результаты измерения тех же частот представлены в таблице:

image
Как мы видим, здесь ошибка в измерениях находится в полном соответствии с разрешением функции micros(), равным 4 мкс — то есть фактически результат колеблется в пределах плюс-минус одного тика системного таймера, все законно. Это не лучшим образом сказывается на измерениях высоких частот, но для нашего диапазона вполне подходит. Потому этот способ можно рекомендовать для обычных применений.

Оцените статью
Анемометры
Добавить комментарий