以下是基于ESP32-C2和ESP-IDF平台实现ADHOC(动态源路由DSR)自组网的代码实现,分为核心模块和函数实现。假设使用ESP-NOW作为底层通信协议,并简化了部分路由逻辑。
数据结构定义
// dsr_route.h
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "esp_now.h"
#define MAX_NODES 10
#define MAX_HOPS 5
#define BROADCAST_MAC {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
typedef struct {
uint8_t mac[6]; // 节点MAC地址
uint8_t seq_num; // 序列号用于路由新鲜度
uint16_t metric; // 路由度量(如跳数)
} DSRRouteEntry;
typedef struct {
uint8_t src[6]; // 源地址
uint8_t dst[6]; // 目标地址
uint8_t path[MAX_HOPS][6]; // 路径记录
uint8_t hop_count; // 当前跳数
uint8_t payload[100]; // 数据负载
} DSRPacket;
typedef struct {
DSRRouteEntry routes[MAX_NODES];
uint8_t node_count;
} RoutingTable;
路由表管理
// routing_table.c
#include "dsr_route.h"
void init_routing_table(RoutingTable *table) {
memset(table, 0, sizeof(RoutingTable));
}
int add_route(RoutingTable *table, const uint8_t *mac, uint8_t seq_num, uint16_t metric) {
if (table->node_count >= MAX_NODES) return -1;
memcpy(table->routes[table->node_count].mac, mac, 6);
table->routes[table->node_count].seq_num = seq_num;
table->routes[table->node_count].metric = metric;
table->node_count++;
return 0;
}
DSRRouteEntry* find_route(RoutingTable *table, const uint8_t *dst_mac) {
for (int i = 0; i < table->node_count; i++) {
if (memcmp(table->routes[i].mac, dst_mac, 6) == 0) {
return &table->routes[i];
}
}
return NULL;
}
DSR路由请求(RREQ)
// dsr_rreq.c
void send_rreq(RoutingTable *table, const uint8_t *dst_mac) {
DSRPacket pkt;
memset(&pkt, 0, sizeof(DSRPacket));
memcpy(pkt.src, table->local_mac, 6);
memcpy(pkt.dst, dst_mac, 6);
pkt.hop_count = 0;
// 广播RREQ
esp_now_send(BROADCAST_MAC, (uint8_t *)&pkt, sizeof(DSRPacket));
}
void handle_rreq(RoutingTable *table, const DSRPacket *pkt) {
// 检查是否已处理过该请求(通过序列号)
DSRRouteEntry *entry = find_route(table, pkt->src);
if (entry && entry->seq_num >= pkt->seq_num) return;
// 添加反向路径到路由表
add_route(table, pkt->src, pkt->seq_num, pkt->hop_count);
// 如果是目标节点,发送RREP
if (memcmp(pkt->dst, table->local_mac, 6) == 0) {
send_rrep(table, pkt);
} else {
// 转发RREQ(增加跳数)
DSRPacket forwarded_pkt = *pkt;
forwarded_pkt.hop_count++;
esp_now_send(BROADCAST_MAC, (uint8_t *)&forwarded_pkt, sizeof(DSRPacket));
}
}
DSR路由回复(RREP)
// dsr_rrep.c
void send_rrep(RoutingTable *table, const DSRPacket *rreq) {
DSRPacket pkt;
memcpy(pkt.src, table->local_mac, 6);
memcpy(pkt.dst, rreq->src, 6);
pkt.hop_count = 0;
// 单播RREP
esp_now_send(rreq->src, (uint8_t *)&pkt, sizeof(DSRPacket));
}
void handle_rrep(RoutingTable *table, const DSRPacket *pkt) {
// 添加正向路径到路由表
add_route(table, pkt->src, pkt->seq_num, pkt->hop_count);
// 如果不是本机发起,则转发RREP
if (memcmp(pkt->dst, table->local_mac, 6) != 0) {
DSRPacket forwarded_pkt = *pkt;
forwarded_pkt.hop_count++;
esp_now_send(pkt->dst, (uint8_t *)&forwarded_pkt, sizeof(DSRPacket));
}
}
数据包转发
// dsr_forward.c
void forward_data(RoutingTable *table, const DSRPacket *pkt) {
// 检查是否为目标节点
if (memcmp(pkt->dst, table->local_mac, 6) == 0) {
deliver_payload(pkt->payload);
return;
}
// 查找路由
DSRRouteEntry *route = find_route(table, pkt->dst);
if (!route) {
send_rreq(table, pkt->dst);
return;
}
// 转发数据包(需更新路径记录)
DSRPacket forwarded_pkt = *pkt;
memcpy(forwarded_pkt.path[forwarded_pkt.hop_count], table->local_mac, 6);
forwarded_pkt.hop_count++;
esp_now_send(route->mac, (uint8_t *)&forwarded_pkt, sizeof(DSRPacket));
}
ESP-NOW初始化
// main.c
#include "esp_now.h"
#include "dsr_route.h"
RoutingTable g_table;
void esp_now_recv_cb(const uint8_t *mac, const uint8_t *data, int len) {
if (len == sizeof(DSRPacket)) {
DSRPacket *pkt = (DSRPacket *)data;
switch (pkt->type) {
case PKT_RREQ: handle_rreq(&g_table, pkt); break;
case PKT_RREP: handle_rrep(&g_table, pkt); break;
case PKT_DATA: forward_data(&g_table, pkt); break;
}
}
}
void app_main() {
esp_now_init();
esp_now_register_recv_cb(esp_now_recv_cb);
init_routing_table(&g_table);
}
关键点说明
- 路由发现:通过广播RREQ和单播RREP实现。
- 路由维护:使用序列号避免环路,跳数限制路径长度。
- 数据转发:根据路由表选择下一跳,支持动态路径更新。
- 扩展性:可通过增加路由超时机制、链路质量评估等优化。
实际部署时需处理以下问题:
- ESP-NOW的广播包可能丢失,需添加重传机制
- 动态网络拓扑变化时需路由过期策略
- 安全机制(如MAC地址验证)
ESP32-C2实现DSR自组网


被折叠的 条评论
为什么被折叠?



