Радиочастотный пакетный импульс 433 МГц с микросхемой ATTiny85

Недавно я создал эскиз ATTiny85.

Мой проект состоит из:

  • Только 1 микросхема ATTiny85
  • 1 радиочастотный модуль 433 МГц => используя вывод данных 2
  • 2x входных цифровых контакта => используя контакт 3 и контакт 4

Работа на частоте 16 МГц с использованием ядра платы ATTiny85 с сайта https://github.com/damellis/attiny

Все работает на 5 В, 1 А 7805, используя только 24,4 мА в режиме ожидания 29,5 мА при передаче.

Я немного оптимизировал свой код, и эскиз работает так, как задумано. Однако иногда модуль RF 433 просто продолжает отправлять данные в течение нескольких секунд вместо отправки пакета, а затем ждет моего 7-секундного тайм-аута.

Я использую внутренние подтягивающие резисторы на контактах 3 и 4.

Это происходит периодически.. Я хотел бы знать, сталкивался ли кто-нибудь с подобной проблемой и знает, как предотвратить эту "неконтролируемую передачу".. Или предложения по улучшению кода и т. д.

>

Как ни странно, с помощью этого дешевого модуля я могу получить точные сигналы на расстоянии 450+ метров прямой видимости (фермерская земля) на моем чувствительном приемнике.

Модуль RF 433

У меня есть катушка SMD, я также припаял провод LAN длиной 17,3 см в качестве антенны.

Водопад СДР

Я ожидаю постоянного всплеска каждые 7 секунд.

Код

Код начинается с циклического выполнения оператора переключателя по состояниям, когда входной сигнал H/L или когда таймер истекает и состояние изменяется.

Когда инициируется состояние триггера, цикл for копирует и объединяет сжатую шестнадцатеричную строку, а затем следующий цикл for отправляет эти сигналы через контакт 2.

После отправки сигнала состояние меняется на тайм-аут ожидания перед повторной отправкой импульса.

Вот в чем проблема.

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

Таймер на 15 минут с большей продолжительностью работает нормально, проблема возникает только с циклами на 7 и 9 секунд.


Фотографии тестирования печатной платы


Полный эскиз [обновлено с предложениями]

#include <stdint.h>
#include <avr/io.h>
#include <util/delay.h>

#define DATA_PIN PB2//цифровой контакт 2
#define ALARM_TRIGGER_PIN PB3//цифровой контакт 3
#define LEARN_PIN PB4//цифровой контакт 4

//15мин= 900000 мс
//16мин = 960000 мс
//20мин= 500000 мс
const unsigned long DELAY_TIME_ZONE_HEARTBEAT = 900000;//15мин
const unsigned long DELAY_TIME_CHECKALARM_TIMEOUT = 7000;//7сек
const unsigned long DELAY_TIME_CHECKLEARN_TIMEOUT = 9000;//9сек

unsigned long Delay_Start_Check_Alarm = 0;
unsigned long Delay_Start_Heartbeat = 0;
unsigned long Delay_Start_Learn = 0;

char LEARN[] ="269369a6924d36da6c800";//сжатый шестнадцатеричный сигнал; LearnState/подделать тот же сигнал
char ALARM[] = "da4da69a4934db69b200";//сжатый шестнадцатеричный сигнал
char HEARTBEAT[] ="1269b4d349269b6d36400";//сжатый шестнадцатеричный сигнал
uint16_t pulse = 1100;//чипу Attiny85 требуется больше времени задержки
uint8_t i;

//пользовательские состояния
enum State 
{
  ALARMTIMEOUT,
  CHECKING,
  HEARTBEATTIMEOUT,
  HEARTBEATSEND,
  LEARNTIMEOUT,
  LEARNSEND,
  ALARMSEND
};

enum State state;

void setup() 
{    
  DDRB |= (1 << DATA_PIN);// заменяет pinMode(DATA_PIN, OUTPUT);
  DDRB &= ~(1 << ALARM_TRIGGER_PIN); // заменяет pinMode(ALARM_TRIGGER_PIN, INPUT);
  DDRB &= ~(1 << LEARN_PIN); // заменяет pinMode(LEARN_PIN, INPUT);

  PORTB |= (1 << ALARM_TRIGGER_PIN);//инициируем поднятие тревожного контакта
  PORTB |= (1 << LEARN_PIN);// инициируем подтягивание штыря обучения

  ADCSRA &= ~(1 << ADEN); //Отключаем АЦП, экономим ~230мкА

  Delay_Start_Check_Alarm = Delay_Start_Heartbeat = Delay_Start_Learn  = millis();//установлен для запуска миллис
  state = CHECKING;//начальное состояние
}

void Check_Alarm_And_Heartbeat() 
{
  unsigned long Mill = millis();//устанавливаем текущее время

  uint8_t alarmState = PINB&8;//читаем контакт 3 сигнализации
  uint8_t learnState = PINB&16;//читаем вывод обучения 4

  //цикл главного переключателя
    switch (state) 
    {
    case CHECKING:
         //постоянная проверка здесь
         if((alarmState) && (!learnState))
          {
           state = ALARMSEND;//устанавливаем состояние
           Delay_Start_Check_Alarm = Mill;//сохраняем время
          }
         //цикл обучения
         if((learnState) && (!alarmState))
         {
           state = LEARNSEND;//устанавливаем состояние
           Delay_Start_Learn = Mill;//сохраняем время
         }
         //продолжаем пульс
         if((!alarmState) && (!learnState))
         {
           state = HEARTBEATTIMEOUT;//устанавливаем состояние
         }
         //если оба имеют высокий приоритет, ставим LearnState/сигнал тампера
         if((alarmState) && (learnState))
         {
           state = LEARNSEND;//установить состояние; LearnState/подделать тот же сигнал
           Delay_Start_Learn = Mill;//сохраняем время
         }
        break;
    case LEARNSEND:
         {
           state = LEARNTIMEOUT;//устанавливаем состояние
           char LEARNFINAL[168];//создаем переменную LEARNFINAL и определяем длину
           strcpy(LEARNFINAL, LEARN);//копируем LEARN в новую переменную LEARNFINAL
           for (uint8_t i = 0; i < 7; i++) 
           {
             strcat(LEARNFINAL, LEARN);// добавляем/объединяем LEARN 7 раз в LEARNFINAL
           }
             for (i = 0; i < sizeof(LEARNFINAL); i++)
             {  
               Send_Hex_Digit(DATA_PIN, LEARNFINAL[i], pulse);//отправляем законченное LEARNFINAL var
             }
             //PORTB &= ~(1 << DATA_PIN);//запись низкого уровня
         }
        break;
    case ALARMSEND:
         {
           state = ALARMTIMEOUT;//устанавливаем состояние
           char ALARMFINAL[168];//создаем переменную ALARMFINAL и определяем длину
           strcpy(ALARMFINAL, ALARM);//копируем ALARM в новую переменную ALARMFINAL
            for (uint8_t i = 0; i < 7; i++) 
            {
               strcat(ALARMFINAL, ALARM);//добавляем/объединяем ALARM 7 раз в ALARMFINAL
            }
             for (i = 0; i < sizeof(ALARMFINAL); i++)
             {
               Send_Hex_Digit(DATA_PIN, ALARMFINAL[i], pulse);//отправляем завершенный ALARMFINAL var
             }
             //PORTB &= ~(1 << DATA_PIN);//запись низкого уровня
         }
        break;
    case HEARTBEATSEND:
         {
             state = CHECKING;//изменяем состояние
             char HEARTBEATPREFINAL[160];//создаем HEARTBEATPREFINAL var и определяем длину
             strcpy(HEARTBEATPREFINAL, HEARTBEAT);//копируем HEARTBEAT в новую переменную HEARTBEATPREFINAL
              for (uint8_t i = 0; i < 7; i++)
              {
                 strcat(HEARTBEATPREFINAL, HEARTBEAT);// добавляем/объединяем HEARTBEAT 7 раз с HEARTBEATPREFINAL
              }
             for (i = 0; i < sizeof(HEARTBEATPREFINAL); i++)
             {
               Send_Hex_Digit(DATA_PIN, HEARTBEATPREFINAL[i], pulse);//отправляем завершенное HEARTBEATPREFINAL var
             }
         }
        break;
    case HEARTBEATTIMEOUT:
             state = CHECKING;//изменяем состояние
             if ((Mill - Delay_Start_Heartbeat) >= DELAY_TIME_ZONE_HEARTBEAT)
             {
                 Delay_Start_Heartbeat += DELAY_TIME_ZONE_HEARTBEAT;//предотвращает дрейф
                 state = HEARTBEATSEND;//изменяем состояние по истечении времени
             }
        break;
    case ALARMTIMEOUT:
             state = ALARMTIMEOUT;//изменение состояния
             if ((Mill - Delay_Start_Check_Alarm) >= DELAY_TIME_CHECKALARM_TIMEOUT)
             {
                 state = CHECKING;//изменяем состояние по истечении времени
             } 
        break;
    case LEARNTIMEOUT:
             state = LEARNTIMEOUT;//изменяем состояние
             if ((Mill - Delay_Start_Learn) >= DELAY_TIME_CHECKLEARN_TIMEOUT)
             {
                 state = CHECKING;//изменяем состояние по истечении времени
             } 
        break;
    default:
        //ничего не делать
        break;
    }
}

void Send_Hex_Digit(uint8_t pin, char c, uint16_t pulse) 
{
  byte bc = c - 48 ; // преобразуем c в число от 0 до 15
  for (int i = 3; i >= 0; i--)
  {
    bitWrite(PORTB, pin, bitRead(bc, i));
    delayMicroseconds(pulse);
  }
}

void loop() 
{
  Check_Alarm_And_Heartbeat();
}

, 👍2

Обсуждение

Итак, после срабатывания тревоги вам нужен тайм-аут в 7 секунд, прежде чем код снова проверит триггер? В этом случае я ожидаю, что вы установите Delay_Start_Check_Alarm, если сработал сигнал тревоги, но я не вижу такой строки. Вы устанавливаете его в настройках, а затем только увеличиваете его по тайм-ауту. Фактически он никогда не устанавливается на временную метку срабатывания сигнализации., @chrisl

У вас включены предупреждения компилятора? Вы объявляете переменную в регистре unsigned char HEARTBEATPREFINAL[160] без переноса кода регистра в { } для создания блока., @6v6gt

В случае CHECKING: вы не учли комбинацию if((alarmState) && (learnState)) . Даже если этого никогда не произойдет, может быть, с этим стоит разобраться?, @6v6gt

@chrisl Спасибо за предложение! Мне нужно только установить его в тайм-ауте, поскольку Переключатель ALARMTIMEOUT будет циклически повторяться до тех пор, пока не пройдут 7 секунд, но я присмотрюсь повнимательнее, когда буду наводить порядок., @Jonas

@6v6gt Да, предупреждения компилятора были отключены. Я знал, что там что-то не так. Я реализовал ваши предложения, но, к сожалению, случайные всплески все еще происходят. Возможно, мне придется просто использовать блокирующую delay(), но я бы предпочел этого не делать. Буду продолжать думать об этом..., @Jonas

@chrisl Я еще немного подумал об этом и думаю, что понимаю, что ты имеешь в виду. Тестирование сейчас показывает гораздо лучшие результаты, которые мы увидим со временем. Я добавил Delay_Start_Check_Alarm = Mill;, чтобы обновить время до таймаута., @Jonas

Что кстати ты подключил к контактам 3 и 4? У вас активны подтягивающие резисторы, но, судя по всему, они используют положительную логику uint8_t AlarmState = PINB&8;//прочитайте сигнальный контакт 3, затем переходите к выполнению теста типа if((alarmState) . . .. Я бы ожидал что-то вроде этого: bool AlarmState = !(PINB&8);//читаем сигнальный контакт 3, поскольку «нормальное» (неактивированное) состояние при подтягивании ВЫСОКОЕ. Если устройства, подключенные к контактам 3 и 4, имеют push- вытяните выход, то вам не следует использовать встроенные подтягивающие резисторы., @6v6gt

@6v6gt Просто транзистор клемм сигнализации PIR, датчик открыт для заземления, когда он не срабатывает, при срабатывании переключается на 5 В с внутренним подтягиванием. Я, вероятно, перейду на ATTINY412, как только прибудет мой заказ, а затем соответствующим образом изменю скетч. Пока скетч в текущем состоянии работает хорошо. Я просто периодически проверяю это, подтягивая контакты 3 и 4 к GND. Я надеюсь проверить надежность/таймер в течение 2 месяцев, а затем сделаю то же самое с ATTINY412., @Jonas

Основная проблема, по-видимому, заключалась в отсутствии Delay_Start_Check_Alarm = Mill;//keep time перед циклом тайм-аута, как прокомментировал @chrisl. со временем нежелательные TX в начале, похоже, исчезли, но проверим еще раз, чтобы подтвердить 100%., @Jonas

Если основная проблема кажется решенной, т.е. ее не так-то просто повторить. тогда, я думаю, вы можете написать ответ самостоятельно, чтобы закрыть проблему, подведя итог тому, что вы сделали, и признать вклад @chrisl, который, похоже, обнаружил серьезную логическую проблему в вашем коде. Или, может быть, подождать 24 часа, чтобы дать Крислу возможность написать ответ и получить за это несколько баллов., @6v6gt

Конечно, подожду немного, а потом закрою., @Jonas

Вы также можете сократить эту функцию примерно до 10 строк кода: void Send_Hex_Digit (uint8_t pin, char c, uint16_tpulse) { байт bc = c - 48 ; // преобразуем c в число от 0 до 15 bitWrite(PORTB, pin, bitRead(bc, 3) ; задержкаМикросекунды(импульс) ; bitWrite(PORTB, pin, bitRead(bc, 2) ; задержкаМикросекунды(импульс) ; bitWrite( PORTB, pin, bitRead(bc, 1) ; задержкаМикросекунды(импульс) ; bitWrite(PORTB, pin, bitRead(bc, 0) ; задержкаМикросекунды(импульс) ; } (здесь не очень красиво отформатировано), @6v6gt

Никакой защиты от перегрузки по току я не вижу. Такая батарея может обеспечить вашему проекту силу тока более 50 ампер в течение достаточно долгого времени, чтобы превратить его в неоптимальное взаимодействие с пользователем. Даже если вы просто присматриваете и возитесь с этим на столе, было бы лучше иметь встроенный держатель предохранителя или что-то в этом роде., @timemage

@6v6gt Awesome удалил 4% скидки на место для хранения программы в моем скетче., @Jonas

@timemage У меня на макетной плате есть место для дополнительных схем, так что обязательно попробую., @Jonas


1 ответ


Лучший ответ:

1

После реализации предложений мне удалось уменьшить эскиз, а также устранить нежелательную передачу для двух таймаутов (pin3, pin4).

Основной проблемой был тайм-аут в 7 секунд, прежде чем скетч проверит максимум/минимум.

Это было решено путем установки Delay_Start_Check_Alarm и Delay_Start_Learn в моем операторе переключения, если сработал сигнал тревоги.

    Delay_Start_Check_Alarm = Mill;//Это решило проблему

Для Heartbeat это изменение не потребовалось, так как оно устанавливается каждый раз при вызове HEARTBEATTIMEOUT, поэтому все работает нормально.

Код в вопросе также обновлен.

Большое спасибо @chrisl, @6v6gt и @timemage.

,

void Send_Hex_Digit() мог бы быть ещё меньше с помощью for цикла, но вскоре вступает в силу закон убывающей отдачи., @6v6gt

@6v6gt Добавлен цикл for, он стал на 1% меньше, но по-прежнему выглядит хорошо, и скетч ATiny412 тоже будет доволен., @Jonas

for (uint8_t i = 4; i > 0; i--) внимательно проверьте этот цикл. Я думал, что i имеет значения от 3 до 0. Но будьте очень осторожны при использовании целого числа без знака в качестве индекса цикла уменьшения. Например, for (uint8_t i = 3; i >= 0; i--) дает бесконечный цикл., @6v6gt

@6v6gt снова изменился на int, поэтому он должен работать правильно. for (int i = 3; i >= 0; i--)Проведем еще несколько тестов, @Jonas