posting
This commit is contained in:
commit
d2868eeff3
4 changed files with 1589 additions and 0 deletions
669
MasterTransceiverNode/MasterTransceiverNode.ino
Normal file
669
MasterTransceiverNode/MasterTransceiverNode.ino
Normal file
|
|
@ -0,0 +1,669 @@
|
|||
|
||||
#include <Adafruit_EEPROM_I2C.h>
|
||||
#include <RHReliableDatagram.h>
|
||||
#include <RH_RF95.h>
|
||||
#include <Adafruit_EEPROM_I2C.h>
|
||||
|
||||
#define BROADCAST_ADDRESS 255
|
||||
#define MASTER_NODE_ID 0
|
||||
#define RFM95_CS 8
|
||||
#define RFM95_INT 3
|
||||
#define RFM95_RST 4
|
||||
#define RF95_FREQ 915.0
|
||||
#define EEPROM_START_ADDRESS 0
|
||||
Adafruit_EEPROM_I2C EEPROM;
|
||||
|
||||
#define EEPROM_ADDR 0x50
|
||||
|
||||
|
||||
#define MAX_NEIGHBORS 254
|
||||
#define MAX_HOPS 20
|
||||
|
||||
|
||||
#define MESSAGE_TYPE_BROADCAST 0
|
||||
#define MESSAGE_TYPE_COMMAND 1
|
||||
#define MESSAGE_TYPE_SENSOR_DATA 2
|
||||
#define MESSAGE_TYPE_ACK 4
|
||||
#define MESSAGE_TYPE_NETWORK_ADDITION_PROPOSAL 10
|
||||
#define MESSAGE_TYPE_NETWORK_NEIGHBOUR_UPDATE 11
|
||||
#define MESSAGE_TYPE_NETWORK_ROUTE_REQUEST 12
|
||||
|
||||
|
||||
|
||||
|
||||
#define MESSAGE_CONSUMED 5
|
||||
#define MESSAGE_NOT_CONSUMED 6
|
||||
|
||||
|
||||
|
||||
#define MAX_NODES 75
|
||||
#define MAX_NODE_NEIGHBORS 15
|
||||
#define MAX_SAVED_MSGS 100
|
||||
|
||||
struct Node {
|
||||
int nodeId;
|
||||
int neighbors[MAX_NODE_NEIGHBORS];
|
||||
int neighborsCount;
|
||||
};
|
||||
|
||||
struct Message {
|
||||
uint8_t type;
|
||||
uint8_t senderID;
|
||||
uint8_t lastRelayID;
|
||||
uint8_t targetID;
|
||||
char id[10];
|
||||
uint8_t hops;
|
||||
char route[10];
|
||||
char masterRoute[10];
|
||||
char data[20];
|
||||
uint8_t sensorType;
|
||||
uint16_t sensorValue;
|
||||
};
|
||||
struct Neighbor {
|
||||
uint8_t nodeID;
|
||||
unsigned long lastSeen;
|
||||
};
|
||||
|
||||
Node graph[MAX_NODES];
|
||||
Neighbor neighbors[MAX_NEIGHBORS];
|
||||
uint8_t neighborCount = 0;
|
||||
|
||||
RH_RF95 rf95(RFM95_CS, RFM95_INT);
|
||||
RHReliableDatagram manager(rf95, MASTER_NODE_ID);
|
||||
Message incomingMsg;
|
||||
uint8_t masterIdList[MAX_NEIGHBORS];
|
||||
int masterIdListLength = 0;
|
||||
char consumedMessageIds[MAX_SAVED_MSGS][10];
|
||||
char relayedMessageIds[MAX_SAVED_MSGS][10];
|
||||
int consumedMsgCounter = 0;
|
||||
int relayedMsgCounter = 0;
|
||||
void updateGraph(Node graph[], int nodeId, int neighbors[], int neighborsCount, bool toRemove);
|
||||
|
||||
//TODO MAster should Broadcast prescence
|
||||
void setup() {
|
||||
|
||||
Serial.begin(4800);
|
||||
delay(10);
|
||||
Serial.println("LoRa MASTER NODE");
|
||||
if (EEPROM.begin(EEPROM_ADDR)) { // you can stick the new i2c addr in here, e.g. begin(0x51);
|
||||
Serial.println("Found I2C EEPROM");
|
||||
} else {
|
||||
Serial.println("I2C EEPROM not identified ... check your connections?\r\n");
|
||||
while (1) delay(10);
|
||||
}
|
||||
|
||||
pinMode(RFM95_RST, OUTPUT);
|
||||
digitalWrite(RFM95_RST, HIGH);
|
||||
|
||||
|
||||
digitalWrite(RFM95_RST, LOW);
|
||||
delay(10);
|
||||
digitalWrite(RFM95_RST, HIGH);
|
||||
delay(10);
|
||||
|
||||
while (!rf95.init()) {
|
||||
while (1);
|
||||
}
|
||||
// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
|
||||
if (!rf95.setFrequency(RF95_FREQ)) {
|
||||
while (1);
|
||||
}
|
||||
// Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
|
||||
// The default transmitter power is 13dBm, using PA_BOOST.
|
||||
// If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then
|
||||
// you can set transmitter powers from 5 to 23 dBm:
|
||||
rf95.setTxPower(20, false);
|
||||
|
||||
// Initialize EEPROM
|
||||
if (EEPROM.read(EEPROM_START_ADDRESS) != 5) {
|
||||
EEPROM.write(EEPROM_START_ADDRESS, 5);
|
||||
EEPROM.write(EEPROM_START_ADDRESS + 1, masterIdListLength);
|
||||
} else {
|
||||
masterIdListLength = EEPROM.read(EEPROM_START_ADDRESS + 1);
|
||||
for(int i = 0; i < masterIdListLength; i++) {
|
||||
masterIdList[i] = EEPROM.read(EEPROM_START_ADDRESS + 3 + i);
|
||||
}
|
||||
}
|
||||
initializeGraph(graph);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (manager.available()) {
|
||||
Message msg;
|
||||
uint8_t len = sizeof(Message);
|
||||
uint8_t from;
|
||||
if (manager.recvfrom((uint8_t*)&msg, &len, &from)) {
|
||||
Serial.println("Msg Recv");
|
||||
handleIncomingMessage(msg);
|
||||
}
|
||||
}
|
||||
// Broadcast presence
|
||||
static signed long lastBroadcast = -1800000; // 30 mins
|
||||
if (accurateMillis() - lastBroadcast >= 1800000) {
|
||||
Serial.println("BroadCasting Presence");
|
||||
broadcastPresence();
|
||||
lastBroadcast = accurateMillis();
|
||||
}
|
||||
// Check and prune neighbors list
|
||||
static signed long lastPrune = 0;
|
||||
if (accurateMillis() - lastPrune >= 7200000) { // 2 hours
|
||||
Serial.println("Prune Neighbours");
|
||||
pruneNeighbors();
|
||||
lastPrune = accurateMillis();
|
||||
}
|
||||
}
|
||||
|
||||
void handleIncomingMessage(Message &msg) {
|
||||
// Check if message ID is in consumed or relayed lists
|
||||
if (isMessageIDPresent(consumedMessageIds, msg.id) || isMessageIDPresent(relayedMessageIds, msg.id)) {
|
||||
return; // Ignore duplicate messages
|
||||
}
|
||||
|
||||
msg.hops++;
|
||||
// Handle different message types
|
||||
switch (msg.type) {
|
||||
case MESSAGE_TYPE_NETWORK_ADDITION_PROPOSAL:
|
||||
handleNetworkAdditionProposal(msg);
|
||||
break;
|
||||
case MESSAGE_TYPE_BROADCAST:
|
||||
addNeighbor(msg.senderID);
|
||||
Message ackMsg;
|
||||
ackMsg.type = MESSAGE_TYPE_ACK;
|
||||
ackMsg.targetID = msg.senderID;
|
||||
ackMsg.senderID = MASTER_NODE_ID;
|
||||
ackMsg.sensorValue = MESSAGE_CONSUMED;
|
||||
ackMsg.hops = 0;
|
||||
sendToTargetOrBroadcast(ackMsg);
|
||||
break;
|
||||
case MESSAGE_TYPE_NETWORK_NEIGHBOUR_UPDATE:
|
||||
handleNetworkNeighbourUpdate(msg);
|
||||
break;
|
||||
case MESSAGE_TYPE_NETWORK_ROUTE_REQUEST:
|
||||
handleNetworkRouteRequest(msg);
|
||||
break;
|
||||
default:
|
||||
if (msg.targetID == MASTER_NODE_ID) {
|
||||
consumeMessage(msg);
|
||||
} else {
|
||||
relayMessage(msg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void handleNetworkRouteRequest(Message receivedMsg) {
|
||||
char* sp = shortestPath(graph, receivedMsg.senderID, receivedMsg.sensorValue);
|
||||
char* rPath = redundantPath(graph, receivedMsg.senderID, receivedMsg.sensorValue);
|
||||
|
||||
// Constructing response
|
||||
Message response;
|
||||
response.type = MESSAGE_TYPE_NETWORK_ROUTE_REQUEST;
|
||||
response.senderID = MASTER_NODE_ID;
|
||||
response.targetID = receivedMsg.senderID;
|
||||
response.hops = 0;
|
||||
|
||||
// Copy shortest path to data
|
||||
for (int i = 0; i < 10; i++) {
|
||||
response.data[i] = sp[i];
|
||||
}
|
||||
|
||||
// Insert 100 as a separator
|
||||
response.data[10] = 100;
|
||||
|
||||
// Copy redundant path to data
|
||||
for (int i = 0; i < 10; i++) {
|
||||
response.data[11 + i] = rPath[i];
|
||||
}
|
||||
|
||||
// Getting the path from master to the sender
|
||||
char* pathToSender = shortestPath(graph, MASTER_NODE_ID, receivedMsg.senderID);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
response.route[i] = pathToSender[i];
|
||||
}
|
||||
|
||||
// Send the message using pathed route
|
||||
manager.sendto((uint8_t*)&response, sizeof(Message), response.route[response.hops]);
|
||||
const uint16_t ACK_TIMEOUT = 2500; // 500 milliseconds
|
||||
// Wait for an acknowledgment with specific values
|
||||
unsigned long startTime = accurateMillis();
|
||||
while (accurateMillis() - startTime < ACK_TIMEOUT) {// 5 secs = 5000 milliseconds
|
||||
if (manager.available()) {
|
||||
// Read the incoming message
|
||||
Message incomingMsg;
|
||||
uint8_t len = sizeof(Message);
|
||||
uint8_t from;
|
||||
manager.recvfrom((uint8_t*)&incomingMsg, &len, &from);
|
||||
|
||||
|
||||
if (incomingMsg.senderID != receivedMsg.senderID && incomingMsg.targetID == MASTER_NODE_ID && strcmp(incomingMsg.data, "300")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void handleNetworkNeighbourUpdate(Message &msg) {
|
||||
// Assuming neighbors are sent as comma-separated values in data
|
||||
int neighbors[MAX_NODE_NEIGHBORS];
|
||||
int neighborsCount = 0;
|
||||
|
||||
char *token = strtok(msg.data, ",");
|
||||
while(token != nullptr && neighborsCount < MAX_NEIGHBORS) {
|
||||
int nodeId;
|
||||
if(sscanf(token, "%d", &nodeId) == 1) {
|
||||
neighbors[neighborsCount++] = nodeId;
|
||||
}
|
||||
token = strtok(nullptr, ",");
|
||||
}
|
||||
updateGraph(graph, msg.senderID, neighbors, neighborsCount, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void handleNetworkAdditionProposal(Message &msg) {
|
||||
int proposedID = atoi(msg.data);
|
||||
bool idExists = false;
|
||||
|
||||
for (int i = 0; i < masterIdListLength; i++) {
|
||||
if (masterIdList[i] == proposedID) {
|
||||
idExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Message responseMsg;
|
||||
responseMsg.senderID = MASTER_NODE_ID;
|
||||
responseMsg.targetID = msg.senderID;
|
||||
responseMsg.type = MESSAGE_TYPE_NETWORK_ADDITION_PROPOSAL;
|
||||
|
||||
if (idExists || masterIdListLength >= 255) {
|
||||
sprintf(responseMsg.data, "500%d", proposedID);
|
||||
} else {
|
||||
masterIdList[masterIdListLength++] = proposedID;
|
||||
updateEEPROM();
|
||||
sprintf(responseMsg.data, "300%d", proposedID);
|
||||
updateGraph(graph, proposedID, {}, 0, false);
|
||||
//, int nodeId, int neighbors[], int neighborsCount, bool toRemove = false
|
||||
}
|
||||
sendToTargetOrBroadcast(responseMsg);
|
||||
}
|
||||
|
||||
void consumeMessage(Message &msg) {
|
||||
// Cache the message ID in the consumed list
|
||||
cacheMessageID(consumedMessageIds, msg.id);
|
||||
// TODO
|
||||
}
|
||||
|
||||
void relayMessage(Message msg) {
|
||||
// Cache the message ID in the relayed list
|
||||
cacheMessageID(relayedMessageIds, msg.id);
|
||||
msg.lastRelayID = MASTER_NODE_ID;
|
||||
|
||||
|
||||
if (msg.targetID != BROADCAST_ADDRESS) {
|
||||
// Send the message using pathed route
|
||||
manager.sendto((uint8_t*)&msg, sizeof(Message), msg.route[msg.hops]);
|
||||
const uint16_t ACK_TIMEOUT = 2500; // 500 milliseconds
|
||||
// Wait for an acknowledgment with specific values
|
||||
unsigned long startTime = accurateMillis();
|
||||
while (accurateMillis() - startTime < ACK_TIMEOUT) {// 5 secs = 5000 milliseconds
|
||||
if (manager.available()) {
|
||||
// Read the incoming message
|
||||
Message incomingMsg;
|
||||
uint8_t len = sizeof(Message);
|
||||
uint8_t from;
|
||||
manager.recvfrom((uint8_t*)&incomingMsg, &len, &from);
|
||||
|
||||
|
||||
if (incomingMsg.senderID != msg.senderID && incomingMsg.targetID == MASTER_NODE_ID && strcmp(incomingMsg.data, "300")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// bool isClose = isNeighbor(msg.targetID);
|
||||
// const uint16_t ACK_TIMEOUT = 2500; // 500 milliseconds
|
||||
// uint64_t startTime = accurateMillis();
|
||||
// if(isClose){
|
||||
// manager.sendto((uint8_t *)&msg, sizeof(Message), msg.targetID);
|
||||
|
||||
// unsigned long startTime = accurateMillis();
|
||||
// while (accurateMillis() - startTime < ACK_TIMEOUT) {// 5 secs = 5000 milliseconds
|
||||
// if (manager.available()) {
|
||||
// // Read the incoming message
|
||||
// Message incomingMsg;
|
||||
// uint8_t len = sizeof(Message);
|
||||
// uint8_t from;
|
||||
// manager.recvfrom((uint8_t*)&incomingMsg, &len, &from);
|
||||
|
||||
|
||||
// if (incomingMsg.type == MESSAGE_TYPE_ACK && incomingMsg.targetID == MASTER_NODE_ID) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }else {
|
||||
|
||||
// for (uint8_t i = 0; i < neighborCount; i++) {
|
||||
// manager.sendto((uint8_t *)&msg, sizeof(Message), neighbors[i].nodeID);
|
||||
|
||||
// unsigned long startTime = accurateMillis();
|
||||
// while (accurateMillis() - startTime < ACK_TIMEOUT) {// 5 secs = 5000 milliseconds
|
||||
// if (manager.available()) {
|
||||
// // Read the incoming message
|
||||
// Message incomingMsg;
|
||||
// uint8_t len = sizeof(Message);
|
||||
// uint8_t from;
|
||||
// manager.recvfrom((uint8_t*)&incomingMsg, &len, &from);
|
||||
// if (incomingMsg.type == MESSAGE_TYPE_ACK && incomingMsg.targetID == MASTER_NODE_ID) {
|
||||
// break;// continue for loop to send to neighbours
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
void cacheMessageID(char list[MAX_SAVED_MSGS][10], const char* id) {
|
||||
for (int i = MAX_SAVED_MSGS - 1; i > 0; i--) {
|
||||
strcpy(list[i], list[i - 1]);
|
||||
}
|
||||
strcpy(list[0], id);
|
||||
}
|
||||
|
||||
bool isMessageIDPresent(char list[100][10], const char* id) {
|
||||
for (int i = 0; i < MAX_SAVED_MSGS; i++) {
|
||||
if (strcmp(list[i], id) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void updateEEPROM() {
|
||||
EEPROM.write(1, masterIdListLength);
|
||||
for (int i = 0; i < masterIdListLength; i++) {
|
||||
EEPROM.write(3 + i, masterIdList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t accurateMillis() {
|
||||
const uint64_t maxMillisValue = 0xFFFFFFFF; // max val of unsigned long
|
||||
const uint64_t overflowIntervalMillis = maxMillisValue + 1;
|
||||
static uint64_t lastMillisValue = 0;
|
||||
static short numOverflows = 0; // 7 overflows a year, 140 in 20 years, if this is still running jesus
|
||||
uint64_t currentMillisValue = millis();
|
||||
|
||||
// rip overflowed
|
||||
if (currentMillisValue < lastMillisValue) {
|
||||
numOverflows++;
|
||||
}
|
||||
|
||||
lastMillisValue = currentMillisValue;
|
||||
|
||||
return (numOverflows * overflowIntervalMillis) + currentMillisValue;
|
||||
}
|
||||
|
||||
|
||||
bool isNeighbor(uint8_t nodeId) {
|
||||
for (uint8_t i = 0; i < neighborCount; i++) {
|
||||
if (neighbors[i].nodeID == nodeId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void broadcastPresence() {
|
||||
Message msg;
|
||||
msg.type = MESSAGE_TYPE_BROADCAST;
|
||||
msg.senderID = MASTER_NODE_ID;
|
||||
msg.targetID = BROADCAST_ADDRESS;
|
||||
strncpy(msg.id, generateMessageID(), sizeof(msg.id));
|
||||
msg.hops = 0;
|
||||
// Fill other data fields if necessary
|
||||
|
||||
manager.sendto((uint8_t*)&msg, sizeof(Message), BROADCAST_ADDRESS);
|
||||
}
|
||||
void addNeighbor(uint8_t id) {
|
||||
for (uint8_t i = 0; i < neighborCount; i++) {
|
||||
if (neighbors[i].nodeID == id) {
|
||||
neighbors[i].lastSeen = accurateMillis();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (neighborCount < MAX_NEIGHBORS) {
|
||||
neighbors[neighborCount].nodeID = id;
|
||||
neighbors[neighborCount].lastSeen = accurateMillis();
|
||||
neighborCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void pruneNeighbors() {
|
||||
for (uint8_t i = 0; i < neighborCount; i++) {
|
||||
if (accurateMillis() - neighbors[i].lastSeen >= 3600000 / 4 * 3) { // 1 hours / 4 * 3 = 45 mins
|
||||
for (uint8_t j = i; j < neighborCount - 1; j++) {
|
||||
neighbors[j] = neighbors[j + 1];
|
||||
}
|
||||
neighborCount--;
|
||||
i--; // Check the same index again
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sendToTargetOrBroadcast(Message& message) {
|
||||
if (isNeighbor(message.targetID)) {
|
||||
manager.sendto((uint8_t*)&message, sizeof(Message), message.targetID);
|
||||
} else {
|
||||
manager.sendto((uint8_t*)&message, sizeof(Message), BROADCAST_ADDRESS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void initializeGraph(Node graph[]) {
|
||||
for (int i = 0; i < MAX_NODES; i++) {
|
||||
graph[i].nodeId = -1;
|
||||
graph[i].neighborsCount = 0;
|
||||
}
|
||||
updateGraph(graph, MASTER_NODE_ID, {}, 0, false); // Initilize master Node
|
||||
}
|
||||
|
||||
int findNodeIndex(Node graph[], int nodeId) {
|
||||
for(int i = 0; i < MAX_NODES; i++) {
|
||||
if(graph[i].nodeId == nodeId) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1; // Node not found
|
||||
}
|
||||
|
||||
void enqueue(int queue[], int val, int &rear) {
|
||||
queue[rear++] = val;
|
||||
}
|
||||
|
||||
int dequeue(int queue[], int &front) {
|
||||
return queue[front++];
|
||||
}
|
||||
|
||||
bool isEmpty(int front, int rear) {
|
||||
return front == rear;
|
||||
}
|
||||
|
||||
char* shortestPath(Node graph[], int start, int end) {
|
||||
int queue[MAX_NODES];
|
||||
int front = 0, rear = 0;
|
||||
int pathLength = 0;
|
||||
char prevNode[MAX_NODES];
|
||||
for(int i = 0; i < MAX_NODES; i++) {
|
||||
prevNode[i] = -1;
|
||||
}
|
||||
|
||||
char* path = (char*) malloc(MAX_NODES * sizeof(char));
|
||||
for(int i = 0; i < MAX_NODES; i++) {
|
||||
path[i] = 100;
|
||||
}
|
||||
|
||||
enqueue(queue, start, rear);
|
||||
while(!isEmpty(front, rear)) {
|
||||
char node = (char)dequeue(queue, front);
|
||||
int index = findNodeIndex(graph, node);
|
||||
for(int i = 0; i < graph[index].neighborsCount; i++) {
|
||||
int neighbor = graph[index].neighbors[i];
|
||||
if(prevNode[neighbor] == -1 && neighbor != start) {
|
||||
enqueue(queue, neighbor, rear);
|
||||
prevNode[neighbor] = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pathLength = 0;
|
||||
for(char at = end; at != start; at = prevNode[at]) {
|
||||
path[pathLength++] = at;
|
||||
}
|
||||
path[pathLength++] = start;
|
||||
|
||||
// Reversing the path
|
||||
for(int i = 0; i < pathLength / 2; i++) {
|
||||
char temp = path[i];
|
||||
path[i] = path[pathLength - 1 - i];
|
||||
path[pathLength - 1 - i] = temp;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
int getNeighborsCount(Node graph[], int nodeId) {
|
||||
int index = findNodeIndex(graph, nodeId);
|
||||
if(index == -1) {
|
||||
// Handle error
|
||||
return 0;
|
||||
}
|
||||
return graph[index].neighborsCount;
|
||||
}
|
||||
|
||||
void resetVisited(bool visited[]) {
|
||||
for(int i = 0; i < MAX_NODES; i++) {
|
||||
visited[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
int mostConnectedNeighbor(Node graph[], bool visited[], int nodeId) {
|
||||
int index = findNodeIndex(graph, nodeId);
|
||||
int maxNeighbors = -1;
|
||||
int chosenNode = -1;
|
||||
for(int i = 0; i < graph[index].neighborsCount; i++) {
|
||||
int neighborId = graph[index].neighbors[i];
|
||||
int count = getNeighborsCount(graph, neighborId);
|
||||
if(!visited[neighborId] && count > maxNeighbors) {
|
||||
maxNeighbors = count;
|
||||
chosenNode = neighborId;
|
||||
}
|
||||
}
|
||||
visited[chosenNode] = true;
|
||||
return chosenNode;
|
||||
}
|
||||
|
||||
bool redundantPathDFS(Node graph[], bool visited[], int start, int end, char path[], int &pathLength) {
|
||||
if(start == end) {
|
||||
path[pathLength++] = end;
|
||||
return true;
|
||||
}
|
||||
|
||||
visited[start] = true;
|
||||
int nextNode = mostConnectedNeighbor(graph, visited, start);
|
||||
while(nextNode != -1) {
|
||||
if(redundantPathDFS(graph, visited, nextNode, end, path, pathLength)) {
|
||||
path[pathLength++] = start;
|
||||
return true;
|
||||
}
|
||||
nextNode = mostConnectedNeighbor(graph, visited, start);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char* redundantPath(Node graph[], int start, int end ) {
|
||||
bool visited[MAX_NODES];
|
||||
resetVisited(visited);
|
||||
|
||||
int pathLength = 0;
|
||||
|
||||
char* path = (char*) malloc(MAX_NODES * sizeof(char));
|
||||
for(int i = 0; i < MAX_NODES; i++) {
|
||||
path[i] = 100;
|
||||
}
|
||||
|
||||
if(!redundantPathDFS(graph, visited, start, end, path, pathLength)) {
|
||||
// Handle case where no path exists
|
||||
pathLength = -1;
|
||||
} else {
|
||||
// Reversing the path to get it from start to end
|
||||
for(int i = 0; i < pathLength / 2; i++) {
|
||||
int temp = path[i];
|
||||
path[i] = path[pathLength - 1 - i];
|
||||
path[pathLength - 1 - i] = temp;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
char* generateMessageID() {
|
||||
static char id[10]; // 9 characters + null terminator
|
||||
const char* charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
for(int i = 0; i < 9; i++) {
|
||||
id[i] = charset[random(0, 62)]; // 62 possible alphanumeric characters
|
||||
}
|
||||
id[9] = '\0'; // Null terminate the string
|
||||
|
||||
return id;
|
||||
}
|
||||
void updateGraph(Node graph[], int nodeId, int neighbors[], int neighborsCount, bool toRemove) {
|
||||
int index = findNodeIndex(graph, nodeId);
|
||||
|
||||
if(toRemove) {
|
||||
if(index == -1) {
|
||||
// Handle error: Node not found to remove
|
||||
return;
|
||||
}
|
||||
// Remove nodeId from all its neighboring nodes' neighbors list
|
||||
for(int i = 0; i < graph[index].neighborsCount; i++) {
|
||||
int neighborIndex = findNodeIndex(graph, graph[index].neighbors[i]);
|
||||
for(int j = 0; j < graph[neighborIndex].neighborsCount; j++) {
|
||||
if(graph[neighborIndex].neighbors[j] == nodeId) {
|
||||
for(int k = j; k < graph[neighborIndex].neighborsCount - 1; k++) {
|
||||
graph[neighborIndex].neighbors[k] = graph[neighborIndex].neighbors[k + 1];
|
||||
}
|
||||
graph[neighborIndex].neighborsCount--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reset the node's data
|
||||
graph[index].nodeId = -1;
|
||||
graph[index].neighborsCount = 0;
|
||||
} else {
|
||||
if(index == -1) {
|
||||
// Add the new node
|
||||
for(index = 0; index < MAX_NODES; index++) {
|
||||
if(graph[index].nodeId == -1) {
|
||||
graph[index].nodeId = nodeId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update the neighbors
|
||||
for(int i = 0; i < neighborsCount; i++) {
|
||||
graph[index].neighbors[i] = neighbors[i];
|
||||
}
|
||||
graph[index].neighborsCount = neighborsCount;
|
||||
}
|
||||
}
|
||||
606
SensorTransceiverNode/SensorTransceiverNode.ino
Normal file
606
SensorTransceiverNode/SensorTransceiverNode.ino
Normal file
|
|
@ -0,0 +1,606 @@
|
|||
#include "Sensors.h"
|
||||
#include <Adafruit_EEPROM_I2C.h>
|
||||
|
||||
#define RFM95_CS 8
|
||||
#define RFM95_INT 3
|
||||
#define RFM95_RST 4
|
||||
#define RF95_FREQ 915.0
|
||||
|
||||
#define TEMPORARY_ID 100
|
||||
#define MASTER_NODE_ID 0
|
||||
#define BROADCAST_ADDRESS 255
|
||||
|
||||
#define MAX_NEIGHBORS 15
|
||||
#define MAX_HOPS 20
|
||||
|
||||
#define MESSAGE_TYPE_BROADCAST 0
|
||||
#define MESSAGE_TYPE_COMMAND 1
|
||||
#define MESSAGE_TYPE_SENSOR_DATA 2
|
||||
#define MESSAGE_TYPE_ACK 4
|
||||
#define MESSAGE_TYPE_NETWORK_ADDITION_PROPOSAL 10
|
||||
#define MESSAGE_TYPE_NETWORK_NEIGHBOUR_UPDATE 11
|
||||
#define MESSAGE_TYPE_NETWORK_ROUTE_REQUEST 12
|
||||
|
||||
|
||||
#define MESSAGE_CONSUMED 5
|
||||
#define MESSAGE_NOT_CONSUMED 6
|
||||
|
||||
Adafruit_EEPROM_I2C EEPROM;
|
||||
|
||||
#define EEPROM_ADDR 0x50
|
||||
|
||||
RH_RF95 rf95(RFM95_CS, RFM95_INT);
|
||||
uint8_t NODE_ID;
|
||||
unsigned long lastHandshakeAttempt = 0;
|
||||
bool inHandshakeProcess = false;
|
||||
RHReliableDatagram manager(rf95, NODE_ID);
|
||||
|
||||
Neighbor neighbors[MAX_NEIGHBORS];
|
||||
uint8_t neighborCount = 0;
|
||||
uint8_t** masterPaths;
|
||||
|
||||
Sensors sensorModule;
|
||||
|
||||
|
||||
uint8_t** getPath(uint8_t nodeID);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(4800);
|
||||
delay(10);
|
||||
Serial.println("LoRa NODE");
|
||||
|
||||
|
||||
if (EEPROM.begin(EEPROM_ADDR)) { // you can stick the new i2c addr in here, e.g. begin(0x51);
|
||||
Serial.println("Found I2C EEPROM");
|
||||
} else {
|
||||
Serial.println("I2C EEPROM not identified ... check your connections?\r\n");
|
||||
while (1) delay(10);
|
||||
}
|
||||
|
||||
|
||||
masterPaths = (uint8_t**) malloc(2 * sizeof(uint8_t*));
|
||||
for (int i = 0; i < 2; i++) {
|
||||
masterPaths[i] = (uint8_t*) malloc(10 + (i * 10) * sizeof(uint8_t));
|
||||
for(int x = 0; x < 10 + (i * 10); x++){
|
||||
masterPaths[i][x] = 100;
|
||||
}
|
||||
}
|
||||
|
||||
pinMode(RFM95_RST, OUTPUT);
|
||||
digitalWrite(RFM95_RST, HIGH);
|
||||
|
||||
Serial.println("Arduino LoRa TX!");
|
||||
|
||||
digitalWrite(RFM95_RST, LOW);
|
||||
delay(10);
|
||||
digitalWrite(RFM95_RST, HIGH);
|
||||
delay(10);
|
||||
|
||||
while (!rf95.init()) {
|
||||
while (1);
|
||||
}
|
||||
// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
|
||||
if (!rf95.setFrequency(RF95_FREQ)) {
|
||||
while (1);
|
||||
}
|
||||
// Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
|
||||
// The default transmitter power is 13dBm, using PA_BOOST.
|
||||
// If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then
|
||||
// you can set transmitter powers from 5 to 23 dBm:
|
||||
rf95.setTxPower(20, false);
|
||||
// while (1);
|
||||
sensorModule.initialize();
|
||||
|
||||
uint8_t eepromID = EEPROM.read(0);
|
||||
|
||||
if(eepromID > 0 && eepromID != TEMPORARY_ID) {
|
||||
NODE_ID = eepromID;
|
||||
manager.setThisAddress(NODE_ID);
|
||||
} else {
|
||||
// Initiate handshake process
|
||||
NODE_ID = TEMPORARY_ID;
|
||||
manager.setThisAddress(NODE_ID);
|
||||
inHandshakeProcess = true;
|
||||
}
|
||||
sensorModule.setAddress(NODE_ID);
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Handle incoming messages
|
||||
if (manager.available()) {
|
||||
Message msg;
|
||||
uint8_t len = sizeof(Message);
|
||||
uint8_t from;
|
||||
if (manager.recvfrom((uint8_t*)&msg, &len, &from)) {
|
||||
Serial.println("Msg Recv");
|
||||
handleReceivedMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Broadcast presence
|
||||
|
||||
static signed long lastBroadcast = -1800000; // 30 mins
|
||||
Serial.print(accurateMillis());
|
||||
Serial.print(" ");
|
||||
Serial.println(lastBroadcast);
|
||||
if (accurateMillis() - lastBroadcast >= 1800000) {
|
||||
Serial.println("BroadCasting Presence");
|
||||
broadcastPresence();
|
||||
lastBroadcast = accurateMillis();
|
||||
}
|
||||
// Check and prune neighbors list
|
||||
static signed long lastPrune = 0;
|
||||
if (accurateMillis() - lastPrune >= 7200000) { // 2 hours
|
||||
Serial.println("Prune Neighbours");
|
||||
pruneNeighbors();
|
||||
lastPrune = accurateMillis();
|
||||
}
|
||||
|
||||
// send sensor report
|
||||
static signed long lastReport = -150000;
|
||||
if(inHandshakeProcess) {
|
||||
if(millis() - lastHandshakeAttempt > 150000) { // Every 2.5 minutes (handshake retries slower)
|
||||
Serial.println("Attempt Handshake");
|
||||
lastHandshakeAttempt = millis();
|
||||
attemptHandshake();
|
||||
}
|
||||
}else {
|
||||
if (accurateMillis() - lastReport >= (60000)) { // 1 min
|
||||
int messageCount = sensorModule.readSensorsAndSend();
|
||||
Serial.println("Broadcast Sensor Reading");
|
||||
for(int i = 0; i < messageCount; i++) {
|
||||
|
||||
// Checking if any of the masterPaths are valid
|
||||
bool* pathValidity = isValidPath(masterPaths);
|
||||
Message msg = sensorModule.messages[i];
|
||||
if (pathValidity[0]) { // Shortest path is valid
|
||||
for (int i = 0; i < 10; i++) {
|
||||
msg.route[i] = masterPaths[0][i];
|
||||
}
|
||||
// Send the message to the first address in the route
|
||||
manager.sendto((uint8_t*)&msg, sizeof(Message), msg.route[0]);
|
||||
} else if (pathValidity[1]) { // Reliable path is valid
|
||||
for (int i = 0; i < 10; i++) {
|
||||
msg.route[i] = masterPaths[1][i];
|
||||
}
|
||||
// Send the message to the first address in the route
|
||||
manager.sendto((uint8_t*)&msg, sizeof(Message), msg.route[0]);
|
||||
}else {
|
||||
sendToMasterOrBroadcast(msg);
|
||||
}
|
||||
}
|
||||
lastReport = accurateMillis();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get Optimized Path to Master
|
||||
static signed long lastOptimized = -3600000 * 3;
|
||||
if (accurateMillis() - lastOptimized >= 3600000 * 3) { // 3 hours
|
||||
uint8_t** paths = getPath(MASTER_NODE_ID);
|
||||
|
||||
if (isValidPath(paths)[0] || isValidPath(paths)[1]) { // if either shortest or reliable path present
|
||||
// Free old memory
|
||||
for (int i = 0; i < 2; i++) {
|
||||
free(masterPaths[i]);
|
||||
}
|
||||
free(masterPaths);
|
||||
|
||||
// Assign new memory
|
||||
masterPaths = paths;
|
||||
lastOptimized = accurateMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleReceivedMessage(Message msg) {
|
||||
//TODO: Add Id to list of last 10 commands, reject duplicates, trim list ever 5 minutes
|
||||
if(msg.senderID == NODE_ID){
|
||||
return;
|
||||
}
|
||||
Message ackMsg;
|
||||
strncpy(ackMsg.id, generateMessageID(), sizeof(ackMsg.id));
|
||||
ackMsg.type = MESSAGE_TYPE_ACK;
|
||||
ackMsg.targetID = msg.senderID;
|
||||
ackMsg.senderID = NODE_ID;
|
||||
ackMsg.hops = 0;
|
||||
|
||||
if (msg.type == MESSAGE_TYPE_BROADCAST) { // Presence Acknolwedgment
|
||||
addNeighbor(msg.senderID);
|
||||
ackMsg.sensorValue = MESSAGE_CONSUMED;
|
||||
sendToTargetOrBroadcast(ackMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.targetID == NODE_ID) { // Direct message
|
||||
if (msg.type == MESSAGE_TYPE_COMMAND) {
|
||||
// Check the data array when processing commands
|
||||
if (strncmp(msg.data, "CMD", 20) == 0) {
|
||||
// Execute the specific command for "CMD"
|
||||
}
|
||||
// Add more command checks as needed
|
||||
|
||||
ackMsg.sensorValue = MESSAGE_CONSUMED;
|
||||
sprintf(ackMsg.data, "300", NULL);
|
||||
} else {
|
||||
ackMsg.sensorValue = MESSAGE_NOT_CONSUMED;
|
||||
sprintf(ackMsg.data, "500", NULL);
|
||||
}
|
||||
|
||||
// Get the path using getPath and set the route for the acknowledgement message
|
||||
uint8_t** paths = getPath(msg.senderID);
|
||||
if (paths) {
|
||||
// Assuming we prefer the shortest path (paths[0]), but you can change this if needed
|
||||
for (int i = 0; i < 10; i++) { // Assuming route size is 10
|
||||
ackMsg.route[i] = paths[0][i];
|
||||
}
|
||||
|
||||
// Freeing up the allocated memory
|
||||
for (int i = 0; i < 2; i++) {
|
||||
free(paths[i]);
|
||||
}
|
||||
free(paths);
|
||||
}
|
||||
|
||||
// Send the message using the new route
|
||||
manager.sendto((uint8_t*)&ackMsg, sizeof(Message), ackMsg.route[0]);
|
||||
|
||||
return;
|
||||
}
|
||||
// Increase hop count
|
||||
msg.hops++;
|
||||
if (msg.hops > MAX_HOPS) return; // Stop Infinite cycle
|
||||
|
||||
if (msg.type == MESSAGE_TYPE_SENSOR_DATA) {
|
||||
msg.lastRelayID = NODE_ID;
|
||||
manager.sendto((uint8_t*)&msg, sizeof(Message), msg.route[msg.hops]);
|
||||
return;
|
||||
}
|
||||
|
||||
relayMessage(msg);
|
||||
}
|
||||
|
||||
void relayMessage(Message msg) {
|
||||
msg.lastRelayID = NODE_ID;
|
||||
if (msg.targetID != BROADCAST_ADDRESS) {
|
||||
|
||||
// Send the message using pathed route
|
||||
manager.sendto((uint8_t*)&msg, sizeof(Message), msg.route[msg.hops]);
|
||||
const uint16_t ACK_TIMEOUT = 2500; // 500 milliseconds
|
||||
// Wait for an acknowledgment with specific values
|
||||
unsigned long startTime = accurateMillis();
|
||||
while (accurateMillis() - startTime < ACK_TIMEOUT) {// 5 secs = 5000 milliseconds
|
||||
if (manager.available()) {
|
||||
// Read the incoming message
|
||||
Message incomingMsg;
|
||||
uint8_t len = sizeof(Message);
|
||||
uint8_t from;
|
||||
manager.recvfrom((uint8_t*)&incomingMsg, &len, &from);
|
||||
|
||||
|
||||
if (incomingMsg.senderID != msg.senderID && incomingMsg.targetID == NODE_ID && strcmp(incomingMsg.data, "300")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bool isClose = isNeighbor(msg.targetID);
|
||||
// const uint16_t ACK_TIMEOUT = 5000; // 5000 milliseconds
|
||||
// uint64_t startTime = accurateMillis();
|
||||
// if(isClose){
|
||||
// manager.sendto((uint8_t *)&msg, sizeof(Message), msg.targetID);
|
||||
|
||||
// unsigned long startTime = accurateMillis();
|
||||
// while (accurateMillis() - startTime < ACK_TIMEOUT) {// 5 secs = 5000 milliseconds
|
||||
// if (manager.available()) {
|
||||
// // Read the incoming message
|
||||
// Message incomingMsg;
|
||||
// uint8_t len = sizeof(Message);
|
||||
// uint8_t from;
|
||||
// manager.recvfrom((uint8_t*)&incomingMsg, &len, &from);
|
||||
|
||||
|
||||
// if (incomingMsg.type == MESSAGE_TYPE_ACK && incomingMsg.targetID == NODE_ID) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }else {
|
||||
// for (uint8_t i = 0; i < neighborCount; i++) {
|
||||
// manager.sendto((uint8_t *)&msg, sizeof(Message), neighbors[i].nodeID);
|
||||
|
||||
// unsigned long startTime = accurateMillis();
|
||||
// while (accurateMillis() - startTime < ACK_TIMEOUT) {// 5 secs = 5000 milliseconds
|
||||
// if (manager.available()) {
|
||||
// // Read the incoming message
|
||||
// Message incomingMsg;
|
||||
// uint8_t len = sizeof(Message);
|
||||
// uint8_t from;
|
||||
// manager.recvfrom((uint8_t*)&incomingMsg, &len, &from);
|
||||
// if (incomingMsg.type == MESSAGE_TYPE_ACK && incomingMsg.targetID == NODE_ID) {
|
||||
// break;// continue for loop to send to neighbours
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
char* convertNeighborsToCharArray(Neighbor neighbors[], int count) {
|
||||
static char result[MAX_NEIGHBORS];
|
||||
for(int i = 0; i < count && i < MAX_NEIGHBORS; i++) {
|
||||
result[i] = (char)neighbors[i].nodeID;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void uploadNeighbours() {
|
||||
Message msg;
|
||||
|
||||
// Fill message details
|
||||
msg.type = MESSAGE_TYPE_NETWORK_NEIGHBOUR_UPDATE;
|
||||
msg.targetID = MASTER_NODE_ID;
|
||||
|
||||
strncpy(msg.id, generateMessageID(), sizeof(msg.id));
|
||||
msg.senderID = NODE_ID;
|
||||
// Get neighbors as char array
|
||||
char* neighborData = convertNeighborsToCharArray(neighbors, neighborCount);
|
||||
for(int i = 0; i < neighborCount && i < MAX_NEIGHBORS; i++) {
|
||||
msg.data[i] = neighborData[i];
|
||||
}
|
||||
|
||||
// Assuming you have a function called sendToMasterOrBroadcast that takes a Message
|
||||
sendToMasterOrBroadcast(msg);
|
||||
}
|
||||
|
||||
|
||||
void broadcastPresence() {
|
||||
Message msg;
|
||||
msg.type = MESSAGE_TYPE_BROADCAST;
|
||||
msg.senderID = NODE_ID;
|
||||
msg.targetID = BROADCAST_ADDRESS;
|
||||
strncpy(msg.id, generateMessageID(), sizeof(msg.id));
|
||||
msg.hops = 0;
|
||||
// Fill other data fields if necessary
|
||||
|
||||
manager.sendto((uint8_t*)&msg, sizeof(Message), BROADCAST_ADDRESS);
|
||||
}
|
||||
|
||||
void addNeighbor(uint8_t id) {
|
||||
for (uint8_t i = 0; i < neighborCount; i++) {
|
||||
if (neighbors[i].nodeID == id) {
|
||||
neighbors[i].lastSeen = accurateMillis();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (neighborCount < MAX_NEIGHBORS) {
|
||||
neighbors[neighborCount].nodeID = id;
|
||||
neighbors[neighborCount].lastSeen = accurateMillis();
|
||||
neighborCount++;
|
||||
}
|
||||
uploadNeighbours();
|
||||
}
|
||||
|
||||
void pruneNeighbors() {
|
||||
for (uint8_t i = 0; i < neighborCount; i++) {
|
||||
if (accurateMillis() - neighbors[i].lastSeen >= 3600000 / 4 * 3) { // 1 hours / 4 * 3 = 45 minsy
|
||||
for (uint8_t j = i; j < neighborCount - 1; j++) {
|
||||
neighbors[j] = neighbors[j + 1];
|
||||
}
|
||||
neighborCount--;
|
||||
i--; // Check the same index again
|
||||
}
|
||||
}
|
||||
uploadNeighbours();
|
||||
}
|
||||
|
||||
|
||||
void sendToMasterOrBroadcast(Message& message) {
|
||||
if (isNeighbor(MASTER_NODE_ID)) {
|
||||
manager.sendto((uint8_t*)&message, sizeof(Message), MASTER_NODE_ID);
|
||||
} else {
|
||||
manager.sendto((uint8_t*)&message, sizeof(Message), BROADCAST_ADDRESS);
|
||||
}
|
||||
}
|
||||
|
||||
void sendToTargetOrBroadcast(Message& message) {
|
||||
if (isNeighbor(message.targetID)) {
|
||||
manager.sendto((uint8_t*)&message, sizeof(Message), message.targetID);
|
||||
} else {
|
||||
manager.sendto((uint8_t*)&message, sizeof(Message), BROADCAST_ADDRESS);
|
||||
}
|
||||
}
|
||||
bool isNeighbor(uint8_t nodeId) {
|
||||
for (uint8_t i = 0; i < neighborCount; i++) {
|
||||
if (neighbors[i].nodeID == nodeId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t accurateMillis() {
|
||||
const uint64_t maxMillisValue = 0xFFFFFFFF; // max val of unsigned long
|
||||
const uint64_t overflowIntervalMillis = maxMillisValue + 1;
|
||||
static uint64_t lastMillisValue = 0;
|
||||
static short numOverflows = 0; // 7 overflows a year, 140 in 20 years, if this is still running jesus
|
||||
uint64_t currentMillisValue = millis();
|
||||
|
||||
// rip overflowed
|
||||
if (currentMillisValue < lastMillisValue) {
|
||||
numOverflows++;
|
||||
}
|
||||
|
||||
lastMillisValue = currentMillisValue;
|
||||
|
||||
return (numOverflows * overflowIntervalMillis) + currentMillisValue;
|
||||
}
|
||||
|
||||
void attemptHandshake() {
|
||||
uint8_t proposedID = generateID();
|
||||
Message msg;
|
||||
msg.type = MESSAGE_TYPE_NETWORK_ADDITION_PROPOSAL;
|
||||
msg.senderID = NODE_ID;
|
||||
msg.targetID = MASTER_NODE_ID;
|
||||
snprintf(msg.data, sizeof(msg.data), "%u", proposedID);
|
||||
strncpy(msg.id, generateMessageID(), sizeof(msg.id));
|
||||
|
||||
static signed long lastProposalTime = 0;
|
||||
if(isNeighbor(MASTER_NODE_ID)) {
|
||||
manager.sendto((uint8_t*)&msg, sizeof(Message), MASTER_NODE_ID);
|
||||
} else {
|
||||
manager.sendto((uint8_t*)&msg, sizeof(Message), BROADCAST_ADDRESS);
|
||||
}
|
||||
|
||||
unsigned long startTime = accurateMillis();
|
||||
while (accurateMillis() - startTime < 60000) {// 1 minute = 60000 milliseconds
|
||||
if (manager.available()) {
|
||||
// Read the incoming message
|
||||
Message incomingMsg;
|
||||
uint8_t len = sizeof(Message);
|
||||
uint8_t from;
|
||||
manager.recvfrom((uint8_t*)&incomingMsg, &len, &from);
|
||||
|
||||
|
||||
if (incomingMsg.type == MESSAGE_TYPE_NETWORK_ADDITION_PROPOSAL && incomingMsg.targetID == NODE_ID) {
|
||||
if(incomingMsg.data[0] == '3' && incomingMsg.data[1] == '0' && incomingMsg.data[2] == '0') {
|
||||
NODE_ID = proposedID;
|
||||
EEPROM.write(0, NODE_ID);
|
||||
manager.setThisAddress(NODE_ID);
|
||||
sensorModule.setAddress(NODE_ID);
|
||||
break;
|
||||
// EEPROM.commit();
|
||||
|
||||
} else if(incomingMsg.data[0] == '5' && incomingMsg.data[1] == '0' && incomingMsg.data[2] == '0') {
|
||||
break;
|
||||
// Nothing specific to do here, a new ID will be proposed in the next attempt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint8_t generateID() {
|
||||
uint8_t proposedID = random(1, 256);
|
||||
while(proposedID == TEMPORARY_ID || proposedID == 0) {
|
||||
proposedID = random(1, 256);
|
||||
}
|
||||
return proposedID;
|
||||
}
|
||||
char* generateMessageID() {
|
||||
static char id[10]; // 9 characters + null terminator
|
||||
const char* charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
for(int i = 0; i < 9; i++) {
|
||||
id[i] = charset[random(0, 62)]; // 62 possible alphanumeric characters
|
||||
}
|
||||
id[9] = '\0'; // Null terminate the string
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
uint8_t** getPath(uint8_t nodeID) {
|
||||
Message msg;
|
||||
msg.type = MESSAGE_TYPE_NETWORK_ROUTE_REQUEST;
|
||||
msg.targetID = MASTER_NODE_ID;
|
||||
msg.senderID = NODE_ID;
|
||||
msg.sensorValue = (uint16_t)nodeID;
|
||||
|
||||
// Send the message
|
||||
|
||||
// Checking if any of the masterPaths are valid
|
||||
bool* pathValidity = isValidPath(masterPaths);
|
||||
if (pathValidity[0]) { // Shortest path is valid
|
||||
for (int i = 0; i < 10; i++) {
|
||||
msg.route[i] = masterPaths[0][i];
|
||||
}
|
||||
// Send the message to the first address in the route
|
||||
manager.sendto((uint8_t*)&msg, sizeof(Message), msg.route[0]);
|
||||
} else if (pathValidity[1]) { // Reliable path is valid
|
||||
for (int i = 0; i < 10; i++) {
|
||||
msg.route[i] = masterPaths[1][i];
|
||||
}
|
||||
// Send the message to the first address in the route
|
||||
manager.sendto((uint8_t*)&msg, sizeof(Message), msg.route[0]);
|
||||
}else {
|
||||
sendToMasterOrBroadcast(msg);
|
||||
}
|
||||
|
||||
free(pathValidity); // Free the validity array
|
||||
|
||||
|
||||
|
||||
uint8_t** paths = (uint8_t**) malloc(2 * sizeof(uint8_t*));
|
||||
for (int i = 0; i < 2; i++) {
|
||||
paths[i] = (uint8_t*) malloc(10 + (i * 10) * sizeof(uint8_t));
|
||||
for(int x = 0; x < 10 + (i * 10); x++){
|
||||
paths[i][x] = 100;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long startTime = millis();
|
||||
unsigned long timeout = 2000; // 2 seconds
|
||||
while (accurateMillis() - startTime < timeout) {
|
||||
if (manager.available()) {
|
||||
// Read the incoming message
|
||||
Message receivedMsg;
|
||||
uint8_t len = sizeof(Message);
|
||||
uint8_t from;
|
||||
manager.recvfrom((uint8_t*)&receivedMsg, &len, &from);
|
||||
|
||||
|
||||
if (receivedMsg.type == MESSAGE_TYPE_NETWORK_ROUTE_REQUEST && receivedMsg.targetID == NODE_ID) {
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if(receivedMsg.route[i] == 100)
|
||||
break;
|
||||
paths[0][i] = (uint8_t)receivedMsg.route[i];
|
||||
}
|
||||
|
||||
int dataIndex = 0;
|
||||
for (int i = 0; i < 20; i++) {
|
||||
if(receivedMsg.data[i] == 100)
|
||||
break;
|
||||
paths[1][dataIndex++] = (uint8_t)receivedMsg.route[i];
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup if no path received
|
||||
for (int i = 0; i < 2; i++) {
|
||||
free(paths[i]);
|
||||
}
|
||||
free(paths);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
bool* isValidPath(uint8_t** path) {
|
||||
static bool validity[2];
|
||||
|
||||
// Initialize to false
|
||||
validity[0] = false;
|
||||
validity[1] = false;
|
||||
|
||||
// Check if path is not NULL
|
||||
if (path != NULL) {
|
||||
// Check the first element of the shortest path dimension
|
||||
if (path[0][0] != 100) {
|
||||
validity[0] = true;
|
||||
}
|
||||
|
||||
// Check the first element of the reliable path dimension
|
||||
if (path[1][0] != 100) {
|
||||
validity[1] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return validity;
|
||||
}
|
||||
187
SensorTransceiverNode/Sensors.cpp
Normal file
187
SensorTransceiverNode/Sensors.cpp
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
#include "Sensors.h"
|
||||
|
||||
OneWire wire(SENSOR_TYPE_SOIL_TEMPERATURE);
|
||||
DallasTemperature soilTempSensor(&wire);
|
||||
Sensors::Sensors() : sensorFlags(0x000) {}
|
||||
|
||||
void Sensors::initialize() {
|
||||
initBarometer();
|
||||
initSCD30();
|
||||
initRainSensor();
|
||||
initWindSensor();
|
||||
initSoilSensors();
|
||||
initLightSensor();
|
||||
int count = 0;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (sensorFlags & (1 << i)) count++;
|
||||
}
|
||||
|
||||
// Allocate memory for messages
|
||||
messageArraySize = count;
|
||||
messages = new Message[messageArraySize];
|
||||
}
|
||||
|
||||
void Sensors::setAddress(uint8_t id) {
|
||||
NODE_ID = id;
|
||||
}
|
||||
|
||||
void Sensors::initBarometer() {
|
||||
if (bme.begin(/*0x76*/)) {
|
||||
sensorFlags |= BAROMETER_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
void Sensors::initSCD30() {
|
||||
|
||||
if (scd30.begin(Wire)) {
|
||||
sensorFlags |= SCD30_TEMPERATURE_BIT;
|
||||
sensorFlags |= SCD30_HUMIDITY_BIT;
|
||||
sensorFlags |= SCD30_CO2_BIT;
|
||||
}
|
||||
|
||||
}
|
||||
void Sensors::initRainSensor() {
|
||||
pinMode(RAIN_PWR_PIN, OUTPUT);
|
||||
pinMode(RAIN_PIN, INPUT);
|
||||
// Initially keep the sensor OFF
|
||||
digitalWrite(RAIN_PWR_PIN, LOW);
|
||||
sensorFlags |= RAIN_BIT;
|
||||
}
|
||||
void Sensors::initWindSensor() {
|
||||
sensorFlags |= WINDSPEED_BIT;
|
||||
}
|
||||
void Sensors::initSoilSensors() {
|
||||
|
||||
pinMode(SOIL_HUMIDITY_PWR_PIN, OUTPUT);
|
||||
pinMode(SOIL_HUMIDITY_PIN, INPUT);
|
||||
// Initially keep the sensor OFF
|
||||
digitalWrite(SOIL_HUMIDITY_PWR_PIN, LOW);
|
||||
|
||||
sensorFlags |= SOIL_HUMIDITY_BIT;
|
||||
soilTempSensor.begin();
|
||||
delay(10);
|
||||
if(soilTempSensor.getDeviceCount() > 0){
|
||||
sensorFlags |= SOIL_TEMPERATURE_BIT;
|
||||
}
|
||||
}
|
||||
void Sensors::initLightSensor() {
|
||||
sensorFlags |= LIGHT_BIT;
|
||||
}
|
||||
|
||||
float Sensors::mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
|
||||
{
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
}
|
||||
int Sensors::readSensorsAndSend() {
|
||||
float value;
|
||||
int messageCount = 0;
|
||||
|
||||
|
||||
if (sensorFlags & BAROMETER_BIT && messageCount < messageArraySize) {
|
||||
value = readBarometer();
|
||||
messages[messageCount++] = sendSensorData((uint8_t) SENSOR_TYPE_BAROMETER,(uint16_t) value * 100);
|
||||
}
|
||||
if (sensorFlags & SCD30_TEMPERATURE_BIT && scd30.dataAvailable() && messageCount < messageArraySize) {
|
||||
value = readSCD30Temperature();
|
||||
messages[messageCount++] = sendSensorData((uint8_t) SENSOR_TYPE_SCD30_TEMPERATURE,(uint16_t) value * 100);
|
||||
}
|
||||
if (sensorFlags & SCD30_HUMIDITY_BIT && scd30.dataAvailable() && messageCount < messageArraySize) {
|
||||
value = readSCD30Humidity();
|
||||
messages[messageCount++] = sendSensorData((uint8_t) SENSOR_TYPE_SCD30_HUMIDITY,(uint16_t) value * 100);
|
||||
}
|
||||
if (sensorFlags & SCD30_CO2_BIT && scd30.dataAvailable() && messageCount < messageArraySize) {
|
||||
value = readSCD30CO2();
|
||||
messages[messageCount++] = sendSensorData((uint8_t) SENSOR_TYPE_SCD30_CO2,(uint16_t) value * 100);
|
||||
}
|
||||
if (sensorFlags & RAIN_BIT && messageCount < messageArraySize) {
|
||||
value = readRain();
|
||||
messages[messageCount++] = sendSensorData((uint8_t) SENSOR_TYPE_RAIN, (uint16_t) value * 100);
|
||||
}
|
||||
if (sensorFlags & WINDSPEED_BIT && messageCount < messageArraySize) {
|
||||
value = readWindSpeed();
|
||||
messages[messageCount++] = sendSensorData((uint8_t) SENSOR_TYPE_WINDSPEED, (uint16_t) value * 100);
|
||||
}
|
||||
if (sensorFlags & SOIL_TEMPERATURE_BIT && messageCount < messageArraySize) {
|
||||
value = readSoilTemperature();
|
||||
messages[messageCount++] = sendSensorData((uint8_t) SENSOR_TYPE_SCD30_TEMPERATURE, (uint16_t) value * 100);
|
||||
}
|
||||
if (sensorFlags & SOIL_HUMIDITY_BIT && messageCount < messageArraySize) {
|
||||
value = readSoilHumidity();
|
||||
messages[messageCount++] = sendSensorData((uint8_t) SENSOR_TYPE_SCD30_HUMIDITY, (uint16_t) value * 100);
|
||||
}
|
||||
|
||||
if (sensorFlags & LIGHT_BIT) {
|
||||
value = readLight();
|
||||
messages[messageCount++] = sendSensorData((uint8_t) SENSOR_TYPE_LIGHT_DIODE, (uint16_t) value * 100);
|
||||
}
|
||||
return messageCount;
|
||||
}
|
||||
|
||||
char* Sensors::generateMessageID() {
|
||||
static char id[10]; // 9 characters + null terminator
|
||||
const char* charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
for(int i = 0; i < 9; i++) {
|
||||
id[i] = charset[random(0, 62)]; // 62 possible alphanumeric characters
|
||||
}
|
||||
id[9] = '\0'; // Null terminate the string
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
Message Sensors::sendSensorData(uint8_t sensorType, uint16_t value) {
|
||||
Message msg;
|
||||
msg.sensorType = sensorType;
|
||||
msg.sensorValue = value;
|
||||
msg.type = MESSAGE_TYPE_SENSOR_DATA;
|
||||
msg.senderID = NODE_ID;
|
||||
strncpy(msg.id, generateMessageID(), sizeof(msg.id));
|
||||
msg.targetID = MASTER_NODE_ID;
|
||||
msg.hops = 0;
|
||||
return msg;
|
||||
}
|
||||
|
||||
float Sensors::readBarometer() {
|
||||
return bme.readPressure();
|
||||
}
|
||||
float Sensors::readSCD30Temperature() {
|
||||
return scd30.getTemperature();
|
||||
}
|
||||
float Sensors::readSCD30Humidity() {
|
||||
return scd30.getHumidity();
|
||||
}
|
||||
float Sensors::readSCD30CO2() {
|
||||
return scd30.getCO2();
|
||||
}
|
||||
float Sensors::readRain() {
|
||||
digitalWrite(RAIN_PWR_PIN, HIGH);
|
||||
delay(10);
|
||||
float val = digitalRead(RAIN_PIN);
|
||||
digitalWrite(RAIN_PWR_PIN, LOW);
|
||||
return val;
|
||||
}
|
||||
float Sensors::readWindSpeed() {
|
||||
float sensorValue = analogRead(WIND_PIN);
|
||||
float voltage = (sensorValue / 1023) * 5;
|
||||
float wind_speed = mapfloat(voltage, 0.4, 2, 0, 32.4);
|
||||
float speed_kph = ((wind_speed *3600));
|
||||
return speed_kph;
|
||||
|
||||
}
|
||||
float Sensors::readSoilTemperature() {
|
||||
soilTempSensor.getDeviceCount();
|
||||
soilTempSensor.requestTemperatures();
|
||||
float temperature = soilTempSensor.getTempCByIndex(0);
|
||||
return temperature;
|
||||
}
|
||||
float Sensors::readSoilHumidity() {
|
||||
digitalWrite(SOIL_HUMIDITY_PWR_PIN, HIGH);
|
||||
delay(10);
|
||||
float sensorValue = analogRead(SOIL_HUMIDITY_PIN);
|
||||
digitalWrite(SOIL_HUMIDITY_PWR_PIN, LOW);
|
||||
return sensorValue;
|
||||
}
|
||||
float Sensors::readLight() {
|
||||
return analogRead(LIGHT_PIN);
|
||||
}
|
||||
|
||||
127
SensorTransceiverNode/Sensors.h
Normal file
127
SensorTransceiverNode/Sensors.h
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
#ifndef SENSORS_H
|
||||
#define SENSORS_H
|
||||
#include <RHReliableDatagram.h>
|
||||
#include <RHDatagram.h>
|
||||
#include <RH_RF95.h>
|
||||
#include <Wire.h>
|
||||
#include <Arduino.h>
|
||||
#include <Adafruit_Sensor.h>
|
||||
#include <Adafruit_BME280.h>
|
||||
#include <Adafruit_SCD30.h>
|
||||
#include <SPI.h>
|
||||
#include <SparkFun_SCD30_Arduino_Library.h>
|
||||
#include <OneWire.h>
|
||||
#include <DallasTemperature.h>
|
||||
#include <DS28E17.h>
|
||||
//https://github.com/hardwario/SoilSensor/tree/master
|
||||
|
||||
|
||||
// Sensor definitions
|
||||
#define MESSAGE_TYPE_SENSOR_DATA 2
|
||||
#define SENSOR_TYPE_BAROMETER 10
|
||||
#define SENSOR_TYPE_SCD30_TEMPERATURE 11
|
||||
#define SENSOR_TYPE_SCD30_HUMIDITY 12
|
||||
#define SENSOR_TYPE_SCD30_CO2 13
|
||||
#define SENSOR_TYPE_RAIN 14
|
||||
#define SENSOR_TYPE_WINDSPEED 15
|
||||
#define SENSOR_TYPE_SOIL_TEMPERATURE 16//ds18b20
|
||||
#define SENSOR_TYPE_SOIL_HUMIDITY A2
|
||||
#define SENSOR_TYPE_LIGHT_DIODE 18
|
||||
|
||||
|
||||
#define MASTER_NODE_ID 0
|
||||
// https://github.com/adafruit/Adafruit_BME280_Library/blob/master/examples/bme280test/bme280test.ino
|
||||
// https://lastminuteengineers.com/rain-sensor-arduino-tutorial/
|
||||
// Add a 4k7 pull-up resistor to this pin
|
||||
// https://how2electronics.com/measure-wind-speed-using-anemometer-arduino/
|
||||
//https://store-usa.arduino.cc/products/gravity-analog-capacitive-soil-moisture-sensor-corrosion-resistant?selectedStore=us
|
||||
// GL5528
|
||||
|
||||
// Sensor bitmask
|
||||
#define BAROMETER_BIT 0x01
|
||||
#define SCD30_TEMPERATURE_BIT 0x02
|
||||
#define SCD30_HUMIDITY_BIT 0x04
|
||||
#define SCD30_CO2_BIT 0x08
|
||||
#define RAIN_BIT 0x10
|
||||
#define WINDSPEED_BIT 0x20
|
||||
#define SOIL_TEMPERATURE_BIT 0x40
|
||||
#define SOIL_HUMIDITY_BIT 0x80
|
||||
#define LIGHT_BIT 0x100
|
||||
|
||||
//Check if Pin is in USe
|
||||
// https://arduino.stackexchange.com/questions/14647/how-can-i-detect-a-disconnected-pin
|
||||
|
||||
#define WIND_PIN A0
|
||||
#define LIGHT_PIN A1
|
||||
|
||||
#define RAIN_PWR_PIN 1
|
||||
#define RAIN_PIN SENSOR_TYPE_RAIN
|
||||
|
||||
#define SOIL_HUMIDITY_PWR_PIN 2
|
||||
#define SOIL_HUMIDITY_PIN SENSOR_TYPE_SOIL_HUMIDITY
|
||||
|
||||
struct Message {
|
||||
uint8_t type;
|
||||
uint8_t senderID;
|
||||
uint8_t lastRelayID;
|
||||
uint8_t targetID;
|
||||
char id[10];
|
||||
uint8_t hops;
|
||||
char route[10];
|
||||
char masterRoute[10];
|
||||
char data[20];
|
||||
uint8_t sensorType;
|
||||
uint16_t sensorValue;
|
||||
};
|
||||
|
||||
struct AckMessage {
|
||||
uint8_t type; // Should be MESSAGE_TYPE_ACK
|
||||
uint8_t consumed; // MESSAGE_CONSUMED or MESSAGE_NOT_CONSUMED
|
||||
};
|
||||
|
||||
struct Neighbor {
|
||||
uint8_t nodeID;
|
||||
unsigned long lastSeen;
|
||||
};
|
||||
class Sensors {
|
||||
public:
|
||||
Sensors();
|
||||
uint8_t NODE_ID;
|
||||
void initialize();
|
||||
int readSensorsAndSend();
|
||||
Message* messages = nullptr;
|
||||
int messageArraySize = 0;
|
||||
~Sensors() {
|
||||
if (messages) delete[] messages;
|
||||
}
|
||||
void setAddress(uint8_t id);
|
||||
|
||||
private:
|
||||
|
||||
Adafruit_BME280 bme;
|
||||
|
||||
|
||||
OneWire wire;
|
||||
SCD30 scd30;
|
||||
uint16_t sensorFlags;
|
||||
void initBarometer();
|
||||
void initSCD30();
|
||||
void initRainSensor();
|
||||
void initWindSensor();
|
||||
void initSoilSensors();
|
||||
void initLightSensor();
|
||||
char* generateMessageID();
|
||||
float readBarometer();
|
||||
float readSCD30Temperature();
|
||||
float readSCD30Humidity();
|
||||
float readSCD30CO2();
|
||||
float readRain();
|
||||
float readWindSpeed();
|
||||
float readSoilTemperature();
|
||||
float readSoilHumidity();
|
||||
float readLight();
|
||||
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max);
|
||||
Message sendSensorData(uint8_t sensorType, uint16_t value);
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Reference in a new issue