This repository has been archived by the owner on Apr 30, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
RF24Node.cpp
363 lines (294 loc) · 12.1 KB
/
RF24Node.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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
/*
*
* NRF24L01+ to Message Protocol gateway
* Copyright (C) 2013 Dustin Brewer
* License: MIT
*/
#include <algorithm>
#include <unordered_map>
#include <string>
#include <sstream>
#include <vector>
#include <ctime>
#include "libs/csiphash/csiphash.c"
#include "StringSplit.h"
#include "IMessageProtocol.h"
#include "IRadioNetwork.h"
#include "RF24Node.h"
RF24Node::RF24Node(IRadioNetwork& _network, IMessageProtocol& _msg_proto, std::vector<char> _key) :
msg_proto(_msg_proto), network(_network), key(_key), topic_separator('/') { }
void RF24Node::begin(void) {
this->msg_proto.set_on_message_callback([this](std::string subject, std::string body) { this->handle_receive_message(subject, body); });
this->msg_proto.begin();
this->network.begin();
}
void RF24Node::end(void) {
this->msg_proto.end();
}
void RF24Node::loop(void) {
this->network.update();
while (this->network.available()) {
auto header = RF24NetworkHeader();
this->network.peek(header);
switch (header.type) {
case PKT_POWER:
this->handle_receive_power(header);
break;
case PKT_TEMP:
this->handle_receive_temp(header);
break;
case PKT_HUMID:
this->handle_receive_humidity(header);
break;
case PKT_SWITCH:
this->handle_receive_switch(header);
break;
case PKT_MOISTURE:
this->handle_receive_moisture(header);
break;
case PKT_ENERGY:
this->handle_receive_energy(header);
break;
case PKT_RGB:
this->handle_receive_rgb(header);
break;
case PKT_TIME:
this->handle_receive_timesync(header);
break;
case PKT_CHALLENGE:
this->handle_receive_challenge(header);
break;
default:
break;
}
}
this->msg_proto.loop();
}
bool RF24Node::write(RF24NetworkHeader& header, const void* message, size_t len) {
const auto max_retries = 1;
auto ok = false;
auto retries = max_retries;
while (!ok && retries-- > 0) {
ok = this->network.write(header, message, len);
}
return ok;
}
/**
* Upon receiving a command via the C++/Python IPC topic, queue the message
*/
void RF24Node::handle_receive_message(std::string subject, std::string body) {
if (this->debug) printf("Received '%s' via topic '%s' from MQTT\n", subject.c_str(), body.c_str());
auto elements = split(subject, this->topic_separator);
uint16_t to_node = std::stoul("0" + elements[3], nullptr, 0);
uint8_t type_command = std::stoi(elements[4], nullptr);
uint8_t type = type_command % 64;
// Asking for sensor data is unsupported
if (type_command < 64) {
return;
}
this->queued_payloads[to_node][type] = body;
if (this->debug) printf("Queuing: '%s' for node 0%o, payload type %d\n", body.c_str(), to_node, type);
auto payload = pkt_challenge_t { 0, type };
RF24NetworkHeader header(to_node, PKT_CHALLENGE);
this->write(header, &payload, sizeof(payload));
}
/*
* Upon receiving a header specifying a challenge response, store the challenge
*/
void RF24Node::handle_receive_challenge(RF24NetworkHeader& header) {
if (this->debug) printf("Handling challenge request for node 0%o.\n", header.from_node);
// Read the challenge request response
auto payload = pkt_challenge_t();
this->network.read(header, &payload, sizeof(payload));
if (this->queued_payloads.find(header.from_node) == this->queued_payloads.end() ||
this->queued_payloads[header.from_node].find(payload.type) == this->queued_payloads[header.from_node].end()) {
if (this->debug) printf("No queued payload for for node 0%o of payload type %d.\n", header.from_node, payload.type);
return;
}
switch (payload.type) {
case PKT_SWITCH:
this->handle_send_switch(header.from_node, this->queued_payloads[header.from_node][payload.type], payload.challenge);
break;
case PKT_RGB:
this->handle_send_rgb(header.from_node, this->queued_payloads[header.from_node][payload.type], payload.challenge);
break;
}
this->queued_payloads[header.from_node].erase(payload.type);
}
/*
* Upon receiving a header specifying a timesync request, send the time to the node
*/
void RF24Node::handle_receive_timesync(RF24NetworkHeader& header) {
if (this->debug) printf("Handling timesync request for node 0%o.\n", header.from_node);
// Read in the packet; not used
this->network.read(header, nullptr, 0);
// Set the current timestamp
auto payload = pkt_time_t { time(0) };
// Send the packet (timestamp) to the desired node
RF24NetworkHeader new_header(header.from_node, PKT_TIME);
this->write(new_header, &payload, sizeof(payload));
}
/*
* Publish temps on MQTT
*/
void RF24Node::handle_receive_temp(RF24NetworkHeader& header) {
auto payload = pkt_temp_t();
this->network.read(header, &payload, sizeof(payload));
std::stringstream s_value;
s_value << payload.id << "|" << (double)(payload.temp / 10.0);
auto topic = this->generate_msg_proto_subject(header);
auto value = s_value.str();
if (this->debug) printf("Republishing Temp: %s:%s\n", topic.c_str(), value.c_str());
this->msg_proto.send_message(topic, value);
}
/*
* Publish humidity on MQTT
*/
void RF24Node::handle_receive_humidity(RF24NetworkHeader& header) {
auto payload = pkt_humid_t();
this->network.read(header, &payload, sizeof(payload));
std::stringstream s_value;
s_value << payload.id << "|" << ((double)(payload.humidity / 10.0));
auto topic = this->generate_msg_proto_subject(header);
auto value = s_value.str();
if (this->debug) printf("Republishing Humidity: %s:%s\n", topic.c_str(), value.c_str());
this->msg_proto.send_message(topic, value);
}
/*
* Publish power on MQTT
*/
void RF24Node::handle_receive_power(RF24NetworkHeader& header) {
auto payload = pkt_power_t();
this->network.read(header, &payload, sizeof(payload));
std::stringstream s_value;
s_value << payload.battery << "|" << payload.solar << "|" << payload.vcc << "|" << payload.vs << "|" << payload.id;
auto topic = this->generate_msg_proto_subject(header);
auto value = s_value.str();
if (this->debug) printf("Republishing Power: %s:%s\n", topic.c_str(), value.c_str());
this->msg_proto.send_message(topic, value);
}
/*
* Publish moisture on MQTT
*/
void RF24Node::handle_receive_moisture(RF24NetworkHeader& header) {
auto payload = pkt_moisture_t();
this->network.read(header, &payload, sizeof(payload));
std::stringstream s_value;
s_value << payload.id << "|" << payload.moisture;
auto topic = this->generate_msg_proto_subject(header);
auto value = s_value.str();
if (this->debug) printf("Republishing Moisture: %s:%s\n", topic.c_str(), value.c_str());
this->msg_proto.send_message(topic, value);
}
/*
* Publish energy on MQTT
*/
void RF24Node::handle_receive_energy(RF24NetworkHeader& header) {
auto payload = pkt_energy_t();
this->network.read(header, &payload, sizeof(payload));
std::stringstream s_value;
s_value << payload.id << "|" << payload.energy;
auto topic = this->generate_msg_proto_subject(header);
auto value = s_value.str();
if (this->debug) printf("Republishing Energy: %s:%s\n", topic.c_str(), value.c_str());
this->msg_proto.send_message(topic, value);
}
/*
* Publish rgb on MQTT
*/
void RF24Node::handle_receive_rgb(RF24NetworkHeader& header) {
auto payload = pkt_rgb_t();
this->network.read(header, &payload, sizeof(payload));
std::stringstream s_value;
s_value << payload.id << "|" << payload.rgb[0] << "|" << payload.rgb[1]
<< "|" << payload.rgb[2] << "|" << payload.timer;
auto topic = this->generate_msg_proto_subject(header);
auto value = s_value.str();
if (this->debug) printf("Republishing RGB: %s:%s\n", topic.c_str(), value.c_str());
this->msg_proto.send_message(topic, value);
}
/*
* Publish switch on MQTT
*/
void RF24Node::handle_receive_switch(RF24NetworkHeader& header) {
auto payload = pkt_switch_t();
this->network.read(header, &payload, sizeof(payload));
std::stringstream s_value;
s_value << payload.id << "|" << payload.state << "|" << payload.timer;
auto topic = this->generate_msg_proto_subject(header);
auto value = s_value.str();
if (this->debug) printf("Republishing Switch: %s:%s\n", topic.c_str(), value.c_str());
this->msg_proto.send_message(topic, value);
}
void RF24Node::handle_send_rgb(uint16_t node, std::string queued_payload, time_t challenge) {
auto siphash = this->generate_siphash(node, challenge);
auto elements = split(queued_payload.c_str(), '|');
auto payload = pkt_rgb_t();
payload.id = std::stoi(elements[0], nullptr, 0);
payload.rgb[0] = std::stoi(elements[1], nullptr, 0);
payload.rgb[1] = std::stoi(elements[2], nullptr, 0);
payload.rgb[2] = std::stoi(elements[3], nullptr, 0);
payload.timer = std::stoi(elements[4], nullptr, 0);
std::copy(siphash.begin(), siphash.end(), payload.hash);
if (this->debug) {
printf("Republishing RGB Command: 0%o:%s\n", node, queued_payload.c_str());
printf("-- payload: %d, (%d, %d, %d), %d\n", payload.id, payload.rgb[0], payload.rgb[1], payload.rgb[2], payload.timer);
printf("-- using siphashed (%d, %d, %d, %d, %d, %d, %d, %d) challenge %lu\n",
payload.hash[0], payload.hash[1], payload.hash[2], payload.hash[3], payload.hash[4],
payload.hash[5], payload.hash[6], payload.hash[7], challenge);
}
// Send a challenge request to the appropriate node
RF24NetworkHeader header(node, PKT_SWITCH);
this->write(header, &payload, sizeof(payload));
}
void RF24Node::handle_send_switch(uint16_t node, std::string queued_payload, time_t challenge) {
auto siphash = this->generate_siphash(node, challenge);
auto elements = split(queued_payload.c_str(), '|');
auto payload = pkt_switch_t();
payload.id = std::stoi(elements[0], nullptr, 0);
payload.state = std::stoi(elements[1], nullptr, 0);
payload.timer = std::stoi(elements[2], nullptr, 0);
std::copy(siphash.begin(), siphash.end(), payload.hash);
if (this->debug) {
printf("Republishing Switch Command: 0%o:%s\n", node, queued_payload.c_str());
printf("-- payload: %d, %d, %d\n", payload.id, payload.state, payload.timer);
printf("-- using siphashed (%d, %d, %d, %d, %d, %d, %d, %d) challenge %lu\n",
payload.hash[0], payload.hash[1], payload.hash[2], payload.hash[3], payload.hash[4],
payload.hash[5], payload.hash[6], payload.hash[7], challenge);
}
// Send a challenge request to the appropriate node
RF24NetworkHeader header(node, PKT_SWITCH);
this->write(header, &payload, sizeof(payload));
}
std::string RF24Node::generate_msg_proto_subject(RF24NetworkHeader& header) {
char from_node_oct[] = { 0, 0, 0, 0, 0 };
sprintf(from_node_oct, "%o", header.from_node);
std::stringstream s_topic;
s_topic << this->topic_separator
<< "sensornet"
<< this->topic_separator
<< "out"
<< this->topic_separator
<< from_node_oct
<< this->topic_separator
<< std::to_string(header.type);
return s_topic.str();
}
std::vector<uint8_t> RF24Node::generate_siphash(uint16_t node, time_t challenge) {
// Convert the challenge into a byte array
auto data = std::vector<char>({ 0, 1, 2, 3 });
for (auto &d : data) {
d = (challenge >> (8 * d) ) & 0xFF;
}
// Add in the node adrress as a byte array
data.push_back((node >> (8 * 0)) & 0xFF);
data.push_back((node >> (8 * 1)) & 0xFF);
// Generate the hash
auto siphash = siphash24(&data[0], data.size(), &this->key[0]);
// Convert the siphash into a byte array
auto hash = std::vector<uint8_t>({ 0, 1, 2, 3, 4, 5, 6, 7 });
for (auto &h : hash) {
h = (siphash >> (8 * h)) & 0xFF;
}
return hash;
}