-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMainNode.cpp
248 lines (218 loc) · 8.06 KB
/
MainNode.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#include "MainNode.h"
#include "MainNode_config.h"
/// @brief Constructor for the MainNode class.
/// @param node The node ID of the main node.
/// @param channel The RF24 channel to use.
/// @param ssid The WiFi SSID to connect to.
/// @param wifiPassword The WiFi password to use.
/// @param server The MQTT server to connect to.
/// @param port The MQTT server port to connect to.
/// @param topic The MQTT topic to publish to.
MainNode::MainNode(uint16_t node, int channel)
: PreInstalledNode(channel, node), client(espClient), _ssid(SSID), _wifiPassword(WIFI_PASSWORD), _server(SERVER), _port(PORT), _topic(TOPIC) {}
/// @brief Initializes the main node.
/// @details This function initializes the main node by setting up the WiFi connection, the RF24 network, and the MQTT connection.
void MainNode::init()
{
delay(INIT_DELAY); // delay 2-5s to prevent from running the code twice
setupRF24Network();
setupWiFi();
client.setServer(_server, _port);
log(F(": Node ID set to "), _node);
}
/// @brief Sets up the WiFi connection.
void MainNode::setupWiFi()
{
log(F(": Attempting WiFi connection..."));
WiFi.begin(_ssid, _wifiPassword); // Attempt to connect
while (WiFi.status() != WL_CONNECTED) // Loop until we're reconnected
{
log(F(": WiFi connection failed, rc="), WiFi.status(), F(" try again in 5 seconds"));
delay(WIFI_CONNECT_DELAY); // Wait 5 seconds before retrying
}
}
/// @brief Checks the MQTT connection.
void MainNode::checkMQTTConnection()
{
if (!client.connected())
{
setupMQTT();
}
client.loop();
}
/// @brief Sets up the MQTT connection.
void MainNode::setupMQTT()
{
while (!client.connected()) // Loop until we're reconnected
{
log(F(": Attempting MQTT connection..."));
if (!client.connect("arduinoClient")) // Attempt to connect
{
log(F(": MQTT connection failed, rc="), client.state(), F(" try again in 5 seconds"));
delay(MQTT_CONNECT_DELAY); // Wait 5 seconds before retrying
}
}
}
/// @brief Receives a payload from a specific node.
/// @details This function updates the network and checks if there is any payload available.
/// If there is, it reads the header and processes the payload.
void MainNode::receivePayload()
{
network.update(); // Pump the network regularly
while (network.available())
{ // Is there anything ready for us?
RF24NetworkHeader header; // If so, take a look at it
network.peek(header);
// the use of switch case is not recommended
if (header.type == KEEP_ALIVE)
{
receiveKeepAlive(header);
}
if (header.type == READINGS_REQUEST)
{
receiveReadings(header);
}
if (header.type == BEGIN_FLAG)
{
receiveBeginFlag(header);
}
if (header.type == ACTIVE_NODES)
{
receiveNodeID(header);
}
if (header.type != KEEP_ALIVE && header.type != READINGS_REQUEST && header.type != BEGIN_FLAG && header.type != ACTIVE_NODES)
{
log(F("*** WARNING *** Unknown message type "), header.type);
network.read(header, 0, 0);
}
}
}
/// @brief Receives a keep alive message from a specific node.
/// @details This function updates the network status and its timestamp.
/// @param header The RF24Network header.
void MainNode::receiveKeepAlive(RF24NetworkHeader &header)
{
network.read(header, 0, 0);
network_status[header.from_node - 1].status = true;
network_status[header.from_node - 1].time = millis();
log(F(": Keep alive received from "), header.from_node);
}
/// @brief Receives readings from a specific node.
/// @details This function updates the sensor node temperature, phototransistor and name with the information received.
/// @param header The RF24Network header.
void MainNode::receiveReadings(RF24NetworkHeader &header)
{
uint8_t buffer[NAME_LENGTH + 4];
network.read(header, &buffer, sizeof(buffer));
Sensor_Node temp = deserializeSensorNode(buffer);
network_status[header.from_node - 1].data.temperature = temp.temperature;
network_status[header.from_node - 1].data.phototransistor = temp.phototransistor;
strcpy(network_status[header.from_node - 1].data.name, temp.name);
log(F(": Readings received from "), temp.name, F(" - [temp: "), temp.temperature, F("; light: "), temp.phototransistor, F("]"));
}
/// @brief Deserializes a buffer into a Sensor_Node.
/// @param buffer The buffer that is going to be deserialized
/// @return The Sensor_Node it got from the buffer
Sensor_Node MainNode::deserializeSensorNode(uint8_t *buffer)
{
Sensor_Node temp;
memcpy(temp.name, buffer, NAME_LENGTH);
temp.temperature = buffer[NAME_LENGTH] | (buffer[NAME_LENGTH + 1] << 8);
temp.phototransistor = buffer[NAME_LENGTH + 2] | (buffer[NAME_LENGTH + 3] << 8);
return temp;
}
/// @brief Receives a begin flag from a specific node.
/// @details This function resets the connected nodes list of the node that sent the begin flag.
/// @param header The RF24Network header.
void MainNode::receiveBeginFlag(RF24NetworkHeader &header)
{
network.read(header, 0, 0);
for (int i = 0; i < MAX_STUDENT_NODES; i++)
{
network_status[header.from_node - 1].connected_nodes[i].nodeID = 0;
}
log(F(": Begin flag received from "), header.from_node);
}
/// @brief Receives a node ID from a specific node.
/// @details This function updates the connected nodes list of the node that sent the node ID.
/// @param header The RF24Network header.
void MainNode::receiveNodeID(RF24NetworkHeader &header)
{
Student_Node message;
network.read(header, &message, sizeof(message));
for (int i = 0; i < MAX_STUDENT_NODES; i++)
{
if (network_status[header.from_node - 1].connected_nodes[i].nodeID == 0)
{
network_status[header.from_node - 1].connected_nodes[i].nodeID = message.nodeID;
strcpy(network_status[header.from_node - 1].connected_nodes[i].name, message.name);
break;
}
}
log(F(": Node ID received from "), header.from_node, F(" ("), message.name, F(")"));
}
/// @brief Checks the connection of the sensor nodes.
/// @details This function inactivates the sensor nodes that are not active.
void MainNode::checkNodesConnection()
{
for (int i = 0; i < MAX_SENSOR_NODES; i++)
{
if (network_status[i].status && millis() - network_status[i].time > NODE_CONNECTION_CHECK_INTERVAL)
{
network_status[i].status = false;
log(F(": Node "), i + 1, F(" removed from active nodes list"));
}
}
}
/// @brief Publishes the network status to the MQTT server.
/// @details This function sends the temperature, phototransistor, name, and connected nodes of each sensor node.
void MainNode::publishNetworkStatus()
{
unsigned long now = millis();
if (now - last_sent > NETWORK_STATUS_SEND_INTERVAL)
{
last_sent = now;
for (int i = 0; i < MAX_SENSOR_NODES; i++)
{
if (network_status[i].status)
{
DynamicJsonDocument jsonDoc(512);
JsonObject root = jsonDoc.to<JsonObject>();
root["Temp"] = network_status[i].data.temperature;
root["Light"] = network_status[i].data.phototransistor;
JsonArray nodes = root.createNestedArray("Nodes");
JsonObject node = nodes.createNestedObject();
node["id"] = "0" + String(i + 1);
node["name"] = network_status[i].data.name;
for (int j = 0; j < MAX_STUDENT_NODES; j++)
{
if (network_status[i].connected_nodes[j].nodeID != 0)
{
JsonObject node = nodes.createNestedObject();
node["id"] = "0" + String(decimalToOctal(network_status[i].connected_nodes[j].nodeID));
node["name"] = network_status[i].connected_nodes[j].name;
}
}
String jsonString;
serializeJson(root, jsonString);
log(F(": Sent network status: "), jsonString);
client.publish(_topic, jsonString.c_str());
}
}
}
}
/// @brief Converts a decimal number to an octal number.
/// @param decimalNumber The decimal number to convert.
/// @return The octal number.
int MainNode::decimalToOctal(uint16_t decimalNumber)
{
int octalNumber = 0;
int base = 1;
while (decimalNumber != 0)
{
octalNumber += (decimalNumber % 8) * base;
decimalNumber /= 8;
base *= 10;
}
return octalNumber;
}