commit d2868eeff3328f40086c76f92418d4a0c0e25f18 Author: 2ManyProjects Date: Mon Mar 10 18:58:58 2025 -0500 posting diff --git a/MasterTransceiverNode/MasterTransceiverNode.ino b/MasterTransceiverNode/MasterTransceiverNode.ino new file mode 100644 index 0000000..d46d837 --- /dev/null +++ b/MasterTransceiverNode/MasterTransceiverNode.ino @@ -0,0 +1,669 @@ + +#include +#include +#include +#include + +#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; + } +} diff --git a/SensorTransceiverNode/SensorTransceiverNode.ino b/SensorTransceiverNode/SensorTransceiverNode.ino new file mode 100644 index 0000000..32ee6b5 --- /dev/null +++ b/SensorTransceiverNode/SensorTransceiverNode.ino @@ -0,0 +1,606 @@ +#include "Sensors.h" +#include + +#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; +} diff --git a/SensorTransceiverNode/Sensors.cpp b/SensorTransceiverNode/Sensors.cpp new file mode 100644 index 0000000..6eabdd3 --- /dev/null +++ b/SensorTransceiverNode/Sensors.cpp @@ -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); +} + diff --git a/SensorTransceiverNode/Sensors.h b/SensorTransceiverNode/Sensors.h new file mode 100644 index 0000000..e1214e0 --- /dev/null +++ b/SensorTransceiverNode/Sensors.h @@ -0,0 +1,127 @@ +#ifndef SENSORS_H +#define SENSORS_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//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