LoRa перестает работать через несколько часов. Кнопка сброса Arduino не работает

Я разрабатываю LoRa-канал ведущий-ведомый между двумя Arduino Pro Mini. Цель - управлять отопителем автомобиля в нескольких сотнях метров от моего дома. Мастер отправляет команду ведомому по мере необходимости. Подчиненное устройство подтверждает команду.

Если ACK не получено в течение 5 секунд, команда отправляется повторно. В дополнение к этому мастер каждую минуту опрашивает подчиненный для проверки связи.

Эта библиотека используется для управления DRF1278F

Я предполагаю, что обратный вызов приема или вызов LoRa.parsePacket() либо ничего не возвращает, либо возвращает полный пакет, поэтому мне никогда не приходится обрабатывать частичные пакеты.

Используются китайские платы Arduino Pro Mini 3.3V 8MHz.
Я использую модуль модуля DRF1278F, который содержит RFIC SX1278

Подключения

Arduino --> LoRa module
----------------------------------
  SS 10 --> NSEL 10k pullup to Vcc
 SCK 13 --> SCK      
MISO 12 --> SDO 100k pulldown to GND  
MOSI 11 --> SDI  
 INT  3 --> IO0  
 RST  6 --> RST  

Устройство построено на плате, выступающей в качестве заземляющего слоя, все соединения GND выполняются на этом слое.
Он построен с короткой прямой проводкой. Подчиненное устройство Проблема.

После нескольких часов работы ведущий или подчиненный зависает. Больше никаких радиоопераций. Есть активность на SPI, но она сильно отличается от нормальной работы. Я проверял в течение нескольких недель и еще не выдержал одну ночь.

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

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

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

Проверено.

Пульсация источника питания 3,3 В составляет менее 50 мВ пик-пик, что было проверено с помощью осциллографа, настроенного на бесконечное послесвечение в течение 8 часов, пока произошло зависание.

Интерфейс может работать как по запросу, так и по прерыванию, без разницы. Строковые объекты не используются.

Использование различных Vcc, таких как 3,0 В и 3,6 В, не имеет значения.
Дополнительная развязка Vcc не принесла улучшений.
Снижение скорости SPI с 8 МГц до 4 МГц не дало никаких результатов.
Удален светодиод Arduino на контакте 13.

Мои симптомы проблемы похожи на этот старый вопрос

Что я могу сделать, чтобы решить эту проблему?

Подчиненный код:

/*
Relay controller, remote end of link
*/
#include <SPI.h>             
#include <LoRa.h>
#include <avr/wdt.h>

//#определить ТТТ

#define csPin 10
#define resetPin 6
#define irqPin 3         // должен быть выводом аппаратного прерывания
#define myLED 8
#define GNDRET 7
#define powerPin 9

unsigned long LEDtime;
const unsigned long LEDonTime = 500;

char outgoing[40];
byte msgCount = 0;
unsigned long lastLoopTime = 0;
unsigned long heaterStarted = 0l;
const unsigned long heaterTime = 60 * 60 * 1000L; // Один час в мс
unsigned interval = 2000;
int temp = 2310;
byte localAddress = 0x11, destination = 0xAA;
unsigned long ttt;

volatile char incoming[40];
volatile int recipient;
volatile byte sender, incomingMsgId,
incomingLength, gotMsg, ovrn;

void setup()
{
    Serial.begin(9600);
    while (!Serial);
    pinMode(myLED, OUTPUT);
    digitalWrite(myLED, LOW);
    pinMode(GNDRET, OUTPUT);
    digitalWrite(GNDRET, LOW);
    pinMode(powerPin, OUTPUT);
    digitalWrite(powerPin, LOW);

    Serial.println("LoRa Switch 0.1");

    LoRa.setPins(csPin, resetPin, irqPin);
/*  
    LoRa.setTxPower(10);
    LoRa.setSpreadingFactor(12);
    LoRa.setCodingRate4(8);
    LoRa.setSPIFrequency(4E6);
    LoRa.enableCrc();
*/
    wdt_enable(WDTO_1S);

    if (!LoRa.begin(433E6))
    {
        Serial.println("LoRa init failed. Check your connections.");
        {
            // моргаем в панике
            LEDtime = millis();
            while (millis() - LEDtime < 75)
            {
                digitalWrite(myLED, HIGH);
            }
            while (millis() - LEDtime < 150)
            {
                digitalWrite(myLED, LOW);
            }
        }
    }

    LoRa.onReceive(onReceive);  // Назначение обратного вызова
    LoRa.receive();
    Serial.println("LoRa init succeeded.");
}



void loop()
{
    if (millis() - lastLoopTime > 1000)
    {
        // Цикл один раз в секунду
        lastLoopTime = millis();
    }

// при получении(LoRa.parsePacket()); // Опрос

    if (gotMsg)  // Флаг из процедуры обратного вызова
    {
        digitalWrite(myLED, HIGH);
        LEDtime = millis();

#ifdef TTT
        Serial.print("Received from: 0x");
        Serial.println(sender, HEX);
        Serial.print("Sent to: 0x");
        Serial.println(recipient, HEX);
        Serial.print("Message ID: ");
        Serial.println(incomingMsgId);
        Serial.print("Message length: ");
        Serial.println(incomingLength);
        Serial.print("Message: <<");
        for (int i = 0; incoming[i] != 0; i++)
            Serial.write(incoming[i]);
        Serial.println(">>");
        Serial.print("RSSI: ");
        Serial.println(LoRa.packetRssi());
        Serial.print("Snr: ");
        Serial.println(LoRa.packetSnr());
        Serial.println();
#endif
        // Обработка команд
        if (incomingLength == 4)
        {
            // Запустить нагреватель, он либо истечет по таймауту, либо будет остановлен сообщением
            if ((byte)incoming[0] == 0x55 && (byte)incoming[1] == 0xAA)
            {
                Serial.println("Set on");
                digitalWrite(powerPin, HIGH);
                heaterStarted = millis();
            }

            if ((byte)incoming[0] == 0xCC && (byte)incoming[1] == 0x99) // Останов нагревателя
            {
                Serial.println("Set off");
                digitalWrite(powerPin, LOW);
                heaterStarted = 0L;
            }
            // Строим ответ подтверждения

            if (heaterStarted > 0L)
            {
                // Расчет времени сеанса в минутах
                ttt = millis() - heaterStarted;
                ttt /= (1000L * 60L); // минут с начала
            }
            else
                ttt = 0L;
            outgoing[0] = (heaterStarted > 0L) ? 'T' : 'F'; // Состояние обогревателя
            snprintf(outgoing + 1, 30, "%3lu", ttt);

            incoming[0] = 0; // Готово, сбрасываем флаги
            gotMsg = 0;

            sendAck(outgoing);
        }
        LoRa.receive();

    }

    if (LEDtime) // тайм-аут светодиода
    {
        if (millis() - LEDtime > LEDonTime)
        {
            digitalWrite(myLED, LOW);
            LEDtime = 0;
        }
    }

    if (heaterStarted) // тайм-аут нагревателя
    {
        if (millis() - heaterStarted > heaterTime)
        {
            digitalWrite(powerPin, LOW);
            heaterStarted = 0L;
        }
    }
    wdt_reset();
    // КОНЕЦ цикла
}


void sendMessage(char *outgoing)
{
    LoRa.beginPacket();                   // стартовый пакет
    LoRa.write(destination);              // добавляем адрес назначения
    LoRa.write(localAddress);             // добавляем адрес отправителя
    LoRa.write(msgCount++);                 // добавляем идентификатор сообщения
    LoRa.write(bsize(outgoing));        // добавляем длину полезной нагрузки
    LoRa.print(outgoing);                 // добавляем полезную нагрузку
    LoRa.endPacket();                     // завершаем пакет и отправляем его
}

void sendAck(char *outgoing)
{
    Serial.println("Send ACK");
    LoRa.beginPacket();
    LoRa.write(destination);
    LoRa.write(localAddress);
    LoRa.write(incomingMsgId);            // идентификатор отчета
    LoRa.write(bsize(outgoing));        // добавляем длину полезной нагрузки
    LoRa.print(outgoing);                 // добавляем полезную нагрузку
    LoRa.endPacket();
}

byte bsize(char *ooo)
{
    byte i = 0;
    while (ooo[i++]);
    return i - 1;
}

byte bsize(volatile char *oo)
{
    return bsize((volatile char *)oo);
}


byte insptr;
void onReceive(int packetSize) // должна быть неблокирующей функцией
{
    if (packetSize == 0) return;     // если пакета нет, возвращаем
    if (gotMsg) ovrn = true; // gotMsg уже должен быть сброшен потребителем

    // чтение байтов заголовка пакета:
    recipient = LoRa.read();         // адрес получателя
    sender = LoRa.read();            // адрес отправителя
    incomingMsgId = LoRa.read();     // идентификатор входящего сообщения
    incomingLength = LoRa.read();    // длина входящего сообщения

    insptr = 0;

    while (LoRa.available())
    {
        if (insptr < 38)
            incoming[insptr++] = (char)LoRa.read();
        else
            LoRa.read();  // Румянец
    }
    incoming[insptr] = 0;  // Конец строки C
    gotMsg = true;

}


/*
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
void setFrequency(long frequency);
void setSpreadingFactor(int sf);
void setSignalBandwidth(long sbw);
void setCodingRate4(int denominator);
void setPreambleLength(long length);
void setSyncWord(int sw);
void enableCrc();
void disableCrc();
*/

Мастер-код:

/*
Relay controller, Controller and GUI base
*/
#include <SPI.h>             
#include <LoRa.h>
#include <avr/wdt.h>


#define TTT 

#define csPin  10
#define resetPin 6
#define irqPin 3         // должен быть выводом аппаратного прерывания
#define ackLED 9
#define LEDGND 8
#define remoteLED 7

unsigned long LEDtime;
const unsigned long LEDonTime = 500;

char outgoing[40];
byte msgSequ = 0, mincnt, ack_TO;
unsigned long lastLoopTime = 0;
unsigned interval = 2000;
byte localAddress = 0xAA, destination = 0x11;
unsigned long ttt;

volatile char incoming[40];
volatile int recipient;
volatile byte sender, incomingMsgId, incomingLength, isACKed, gotMsg, ovrn;

char cc;

void setup()
{
    Serial.begin(9600);
    while (!Serial);
    pinMode(ackLED, OUTPUT);
    digitalWrite(ackLED, LOW);
    pinMode(remoteLED, OUTPUT);
    digitalWrite(remoteLED, LOW);
    pinMode(LEDGND, OUTPUT);
    digitalWrite(LEDGND, LOW);
    isACKed = 1;
    Serial.println("LoRa Master 0.1");

    LoRa.setPins(csPin, resetPin, irqPin);
/* no specials
    LoRa.setTxPower(10);
    LoRa.setSPIFrequency(4E6);
    LoRa.setSpreadingFactor(12);
    LoRa.setCodingRate4(8);
    LoRa.enableCrc();
*/
    wdt_enable(WDTO_1S);

    if (!LoRa.begin(433E6))
    {
        Serial.println("LoRa init failed. Check your connections.");
        {
            // моргаем в панике
            LEDtime = millis();
            while (millis() - LEDtime < 75)
            {
                digitalWrite(ackLED, HIGH);
            }
            while (millis() - LEDtime < 150)
            {
                digitalWrite(ackLED, LOW);
            }
        }
    }

    LoRa.onReceive(onReceive);  // Назначение обратного вызова
    LoRa.receive();
    Serial.println("LoRa init succeeded.");
}



void loop()
{
    digitalWrite(ackLED, isACKed == 0);

    if (millis() - lastLoopTime > 1000)
    {
        // Цикл один раз в секунду
        lastLoopTime = millis();

        if (isACKed == 0)
        {
            if (ack_TO++ > 5)
            {
                sendMessage(outgoing); // Отправить
                LoRa.receive();
                ack_TO = 0;
                Serial.println("Resend...");
            }
        }


        if (mincnt++ > 60)
        {
            // Цикл один раз в минуту
            mincnt = 0;
            if (isACKed)  // Свободно спрашивать
            {
                // Сердцебиение
                cmdAsk();
                isACKed = 0;
            }
        }
    }

    if ((cc = Serial.read()) > 0)  // Тестовая версия, cmd
    {
        if (cc == '1')
        {
            cmdOn();
            isACKed = 0;
        }
        else if (cc == '0')
        {
            cmdOff();
            isACKed = 0;
        }
        else if (cc == 'Q')
        {
            cmdAsk();
            isACKed = 0;
        }
    }
// при получении(LoRa.parsePacket()); // Опрос

    if (gotMsg)  // Флаг из подпрограммы onReceive
    {
#ifdef TTT
        Serial.print("Received from: 0x");
        Serial.println(sender, HEX);
        Serial.print("Sent to: 0x");
        Serial.println(recipient, HEX);
        Serial.print("Message ID: ");
        Serial.println(incomingMsgId);
        Serial.print("Message length: ");
        Serial.println(incomingLength);
        Serial.print("Message: <<");
        for (int i = 0; incoming[i] != 0; i++)
            Serial.write(incoming[i]);
        Serial.println(">>");
        Serial.print("RSSI: ");
        Serial.println(LoRa.packetRssi());
        Serial.print("Snr: ");
        Serial.println(LoRa.packetSnr());
        Serial.println();
#endif
        // Обработка команд
        if (incomingLength == 4)
            if (incomingMsgId == msgSequ - 1) // Подтверждение отправлено последним
            {
                if (incoming[0] == 'T') digitalWrite(remoteLED, HIGH);
                if (incoming[0] == 'F') digitalWrite(remoteLED, LOW);
                isACKed = 1;
            }
        incoming[0] = 0; // Сообщение обработано, сбрасываем флаги
        gotMsg = 0;
    }

    if (LEDtime) // тайм-аут светодиода
    {
        if (millis() - LEDtime > LEDonTime)
        {
            LEDtime = 0;
        }
    }
    wdt_reset();
}   // КОНЕЦ цикла


void cmdOn()
{
    outgoing[0] = 0x55; // Нагреватель включен
    outgoing[1] = 0xAA;
    outgoing[2] = 'X';
    outgoing[3] = 'Y';
    sendMessage(outgoing);
    LoRa.receive();
}

void cmdOff()
{
    outgoing[0] = 0xCC; // Нагреватель выключен
    outgoing[1] = 0x99;
    outgoing[2] = 'A';
    outgoing[3] = 'B';
    sendMessage(outgoing);
    LoRa.receive();
}

void cmdAsk()
{
    outgoing[0] = 0x0F;
    outgoing[1] = 0xF0;
    outgoing[2] = '7';
    outgoing[3] = '7';
    sendMessage(outgoing);
    LoRa.receive();
}

void sendMessage(char *outgoing)
{
    LoRa.beginPacket();                   // стартовый пакет
    LoRa.write(destination);              // добавляем адрес назначения
    LoRa.write(localAddress);             // добавляем адрес отправителя
    LoRa.write(msgSequ++);                 // добавляем идентификатор сообщения
    LoRa.write(bsize(outgoing));        // добавляем длину полезной нагрузки
    LoRa.print(outgoing);                 // добавляем полезную нагрузку
    LoRa.endPacket();                     // завершаем пакет и отправляем его
}

byte bsize(char *ooo)
{
    byte i = 0;
    while (ooo[i++]);
    return i - 1;
}

byte bsize(volatile char *oo)
{
    return bsize((volatile char *)oo);
}


unsigned char insptr;
void onReceive(int packetSize) // неблокирующая функция
{
    if (packetSize == 0) return;     // если пакета нет, возвращаем
    if (gotMsg) ovrn = true; // gotMsg уже должен быть сброшен потребителем

    // чтение байтов заголовка пакета:
    recipient = LoRa.read();         // адрес получателя
    sender = LoRa.read();            // адрес отправителя
    incomingMsgId = LoRa.read();     // идентификатор входящего сообщения
    incomingLength = LoRa.read();    // длина входящего сообщения

    insptr = 0;

    while (LoRa.available())
    {
        if (insptr < 38)
            incoming[insptr++] = (char)LoRa.read();
        else
            LoRa.read();  // Румянец
    }
    incoming[insptr] = 0;
    gotMsg = true;
}


/*
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
void setFrequency(long frequency);
void setSpreadingFactor(int sf);
void setSignalBandwidth(long sbw);
void setCodingRate4(int denominator);
void setPreambleLength(long length);
void setSyncWord(int sw);
void enableCrc();
void disableCrc();

RSSI always a value between -30 ( very close) and - 120
SNR 0 is noise floor… max = 10

*/

, 👍2

Обсуждение

Если нажатие кнопки Reset на Arduino не помогает, мне кажется, что проблема заключается в части Lora. Нажатие «Сброс» не сбросит это (обязательно)., @Nick Gammon

он всегда зависает через одинаковое количество времени?, @jsotola

*Отключение питания 3,3 В всегда восстанавливает нормальную работу* - с какой целью? Отправка или получение?, @Nick Gammon

*между двумя Arduino Pro Micros* ... *Используются китайские платы Arduino Pro Mini 3.3V 8MHz* - что это? Мини или Микро?, @Nick Gammon

возможно, это как-то связано с каким-то счетчиком последовательности, достигшим состояния переполнения, @jsotola

@Nick Gammon Как редактировать сегменты кода, как вы?, @Wirewrap

@jsotola Время зависания кажется случайным и довольно долгим. Кроме того, какая сторона, главная или подчиненная, зависает, кажется непредсказуемым., @Wirewrap

DRF1278F имеет контакт сброса. Просто подключите этот контакт к одному из контактов Arduino. Затем в «настройке» установите вывод на ВЫХОД и НИЗКИЙ уровень, подождите мс, затем снова поверните его на ВХОД. Таким образом, DRF1278F также сбрасывается при сбросе Arduino. Например, через событие WDT, которое вы уже используете., @Gerben

@Gerben LoRa.begin() подает импульсы NRST, однако, похоже, он не работает, когда зависает. WDT не запускается зависанием., @Wirewrap

[Как использовать уценку](https://stackoverflow.com/editing-help), @Nick Gammon

я бы отключил Лору на 50 секунд, затем включил на 10, в цикле. экономит электроэнергию на 80 %, обеспечивает "свежую загрузку" с минимальной задержкой для запуска обогревателя. можно также рассмотреть ESPNOW вместо LORA., @dandavis


1 ответ


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

2

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

После долгих испытаний я обнаружил взаимодействие между загрузчиком и сторожевым таймером. Этот пост описывает это хорошо: -the-bootloader/">Запись в блоге о сторожевом таймере
Так что замена загрузчика вроде убирает зависание, пока... Почему срабатывает сторожевой таймер, еще предстоит выяснить, но в настоящее время он почти не причиняет вреда.

Примечание: похоже, что кнопка сброса перезагружает ProMini, если ее нажать достаточно быстро после зависания. Если нажать несколько часов спустя, это не имеет никакого эффекта.

,

Работает уже больше года без зависаний., @Wirewrap