Часть 33: Создание своего интернет-протокола на LoRa

Мы создадим Mesh-сеть на модулях LoRa (SX1278), которая будет работать без интернета. Это идеально для удаленных районов или катастроф.

Протокол ARH-NET (ARDUINOHACKS Network):

cpp

#include <SPI.h>
#include <LoRa.h>

#define NODE_ID 1
#define BROADCAST_ADDR 255

// Структура пакета
struct Packet {
    uint8_t src;
    uint8_t dst;
    uint8_t ttl;
    uint8_t type;     // 0=данные, 1=маршрутизация, 2=приветствие
    uint8_t payload[32];
    uint8_t checksum;
};

// Таблица маршрутизации (до 8 узлов)
struct RouteEntry {
    uint8_t dest;
    uint8_t next_hop;
    uint8_t hop_count;
    uint8_t seq;
    uint32_t last_seen;
};

RouteEntry routingTable[8];
uint8_t routeCount = 0;

// Отправка пакета
void sendPacket(Packet* pkt) {
    // Вычисляем контрольную сумму
    uint8_t checksum = 0;
    uint8_t* data = (uint8_t*)pkt;
    for (uint8_t i = 0; i < sizeof(Packet) - 1; i++) {
        checksum ^= data[i];
    }
    pkt->checksum = checksum;
    
    // Отправляем через LoRa
    LoRa.beginPacket();
    LoRa.write((uint8_t*)pkt, sizeof(Packet));
    LoRa.endPacket();
}

// Обработка полученного пакета
void handlePacket(Packet* pkt) {
    // Проверяем контрольную сумму
    uint8_t checksum = 0;
    uint8_t* data = (uint8_t*)pkt;
    for (uint8_t i = 0; i < sizeof(Packet) - 1; i++) {
        checksum ^= data[i];
    }
    if (checksum != pkt->checksum) {
        return;  // Пакет поврежден
    }
    
    // Если пакет для нас
    if (pkt->dst == NODE_ID || pkt->dst == BROADCAST_ADDR) {
        // Обрабатываем
        if (pkt->type == 0) {  // Данные
            processPayload(pkt->payload);
        } else if (pkt->type == 1) {  // Маршрутизация
            updateRoutingTable(pkt);
        } else if (pkt->type == 2) {  // Приветствие
            sendHelloResponse();
        }
    }
    
    // Пересылаем, если TTL > 0
    if (pkt->ttl > 0 && pkt->dst != NODE_ID) {
        pkt->ttl--;
        sendPacket(pkt);
    }
}

// Обновление таблицы маршрутизации
void updateRoutingTable(Packet* pkt) {
    uint8_t src = pkt->src;
    uint8_t hop = pkt->payload[0];  // Количество хопов
    
    // Ищем запись в таблице
    uint8_t idx = 255;
    for (uint8_t i = 0; i < routeCount; i++) {
        if (routingTable[i].dest == src) {
            idx = i;
            break;
        }
    }
    
    // Добавляем или обновляем
    if (idx == 255) {
        if (routeCount < 8) {
            idx = routeCount++;
        } else {
            // Заменяем самую старую запись
            uint32_t oldest = routingTable[0].last_seen;
            idx = 0;
            for (uint8_t i = 1; i < 8; i++) {
                if (routingTable[i].last_seen < oldest) {
                    oldest = routingTable[i].last_seen;
                    idx = i;
                }
            }
        }
    }
    
    routingTable[idx].dest = src;
    routingTable[idx].hop_count = hop + 1;
    routingTable[idx].last_seen = millis();
    routingTable[idx].seq = pkt->payload[1];
}

// Поиск маршрута к узлу
uint8_t findRoute(uint8_t dest) {
    for (uint8_t i = 0; i < routeCount; i++) {
        if (routingTable[i].dest == dest) {
            return routingTable[i].next_hop;
        }
    }
    return 255;  // Маршрут не найден
}

// Отправка сообщения с маршрутизацией
void sendMessage(uint8_t dest, uint8_t* data, uint8_t len) {
    Packet pkt;
    pkt.src = NODE_ID;
    pkt.dst = dest;
    pkt.ttl = 10;
    pkt.type = 0;
    memcpy(pkt.payload, data, len);
    
    // Находим маршрут
    uint8_t next_hop = findRoute(dest);
    if (next_hop != 255) {
        // Отправляем напрямую следующему узлу
        sendPacket(&pkt);
    } else {
        // Широковещательный поиск маршрута
        pkt.dst = BROADCAST_ADDR;
        pkt.type = 1;  // Запрос маршрута
        pkt.payload[0] = dest;  // Ищем маршрут к этому узлу
        sendPacket(&pkt);
    }
}

void setup() {
    Serial.begin(115200);
    
    // Инициализация LoRa
    if (!LoRa.begin(868E6)) {  // 868 МГц (Европа)
        Serial.println("Ошибка LoRa!");
        while (1);
    }
    
    LoRa.setSpreadingFactor(12);
    LoRa.setSignalBandwidth(125E3);
    LoRa.setCodingRate4(5);
    LoRa.setTxPower(20);
    
    // Отправляем приветствие в сеть
    sendHello();
}

void loop() {
    // Проверяем входящие пакеты
    if (LoRa.parsePacket()) {
        Packet pkt;
        LoRa.readBytes((uint8_t*)&pkt, sizeof(Packet));
        handlePacket(&pkt);
    }
    
    // Отправляем heartbeat каждые 10 секунд
    static uint32_t lastHeartbeat = 0;
    if (millis() - lastHeartbeat > 10000) {
        Packet pkt;
        pkt.src = NODE_ID;
        pkt.dst = BROADCAST_ADDR;
        pkt.ttl = 1;
        pkt.type = 2;  // Heartbeat
        pkt.payload[0] = NODE_ID;
        sendPacket(&pkt);
        lastHeartbeat = millis();
    }
}

Вам также может понравиться

About the Author: ардуинчиков

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *