Радиочастотный пакетный импульс 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();
}
@Jonas, 👍2
Обсуждение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
- Attiny85 с 433 МГц вч, работал отлично нано к нано, не attiny85 к нано
- Радиочастотное дистанционное управление с использованием VirtualWire на ATtiny85, работающем на частоте 8 МГц на внутреннем генераторе
- Программирование сервопривода на ATtiny85
- Digispark ATtiny 85 - не распознается как HID устройство
- Клавиатура Digispark ATtiny85
- nRF24L01 + потеря пакетов, вызванная считыванием Arduino и связью с датчиками
- Digispark Micro (ATTINY85) не работает на Macbook Pro 2016 г.
- ATtiny85 USB Устранение неполадок. Устройство не распознается, когда программатор просит подключить устройство
Итак, после срабатывания тревоги вам нужен тайм-аут в 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