MySensors Library & Examples  2.3.2-62-ge298769
GlobalUDP.h
1 
2 /* EthernetUDP is a Strategy for the PJON framework.
3  It supports delivering PJON packets through Ethernet UDP to a registered list
4  of devices on the LAN, WAN or Internet. Each device must be registered with
5  its device id, IP address and listening port number.
6  ___________________________________________________________________________
7 
8  EthernetUDP strategy proposed and developed by Fred Larsen 01/09/2017
9 
10  Licensed under the Apache License, Version 2.0 (the "License");
11  you may not use this file except in compliance with the License.
12  You may obtain a copy of the License at
13 
14  http://www.apache.org/licenses/LICENSE-2.0
15 
16  Unless required by applicable law or agreed to in writing, software
17  distributed under the License is distributed on an "AS IS" BASIS,
18  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  See the License for the specific language governing permissions and
20  limitations under the License. */
21 
22 #pragma once
23 
24 #ifdef HAS_ETHERNETUDP
25 #include "../../interfaces/ARDUINO/UDPHelper_ARDUINO.h"
26 #else
27 #include "../../interfaces/LINUX/UDPHelper_POSIX.h"
28 #endif
29 
30 #include <PJONDefines.h>
31 
32 // Timeout waiting for an ACK. This can be increased if the latency is high.
33 #ifndef GUDP_RESPONSE_TIMEOUT
34 #define GUDP_RESPONSE_TIMEOUT 100000ul
35 #endif
36 
37 #ifndef GUDP_MAX_REMOTE_NODES
38 #define GUDP_MAX_REMOTE_NODES 10
39 #endif
40 
41 // Recommended receive time for this strategy, in microseconds
42 #ifndef GUDP_RECEIVE_TIME
43 #define GUDP_RECEIVE_TIME 0
44 #endif
45 
46 #define GUDP_DEFAULT_PORT 7000
47 #define GUDP_MAGIC_HEADER (uint32_t) 0x0DFAC3FF
48 
49 class GlobalUDP
50 {
51  bool _udp_initialized = false;
52  uint16_t _port = GUDP_DEFAULT_PORT;
53  bool _auto_registration = true;
54 
55  // Remote nodes
56  uint8_t _remote_node_count = 0;
57  uint8_t _remote_id[GUDP_MAX_REMOTE_NODES];
58  uint8_t _remote_ip[GUDP_MAX_REMOTE_NODES][4];
59  uint16_t _remote_port[GUDP_MAX_REMOTE_NODES];
60 
61  UDPHelper udp;
62 
63  bool check_udp()
64  {
65  if(!_udp_initialized) {
66  udp.set_magic_header(htonl(GUDP_MAGIC_HEADER));
67  if (udp.begin(_port)) {
68  _udp_initialized = true;
69  }
70  }
71  return _udp_initialized;
72  };
73 
74  int16_t find_remote_node(uint8_t id)
75  {
76  for(uint8_t i = 0; i < _remote_node_count; i++)
77  if(_remote_id[i] == id) {
78  return i;
79  }
80  return -1;
81  };
82 
83  void autoregister_sender(const uint8_t *message, uint16_t length)
84  {
85  // Add the last sender to the node table
86  if (_auto_registration && length>4) {
87  // First get PJON sender id from incoming packet
88  PJON_Packet_Info packet_info;
89  PJONTools::parse_header(message, packet_info);
90  uint8_t sender_id = packet_info.tx.id;
91  if (sender_id == 0) {
92  return; // If parsing fails, it will be 0
93  }
94 
95  // Then get the IP address and port number of the sender
96  uint8_t sender_ip[4];
97  uint16_t sender_port;
98  udp.get_sender(sender_ip, sender_port);
99 
100  // See if PJON id is already registered, add if not
101  int16_t pos = find_remote_node(sender_id);
102  if (pos == -1) {
103  add_node(sender_id, sender_ip, sender_port);
104  } else {
105  // Update IP and port of existing node
106  memcpy(_remote_ip[pos], sender_ip, 4);
107  _remote_port[pos] = sender_port;
108  }
109  }
110  }
111 
112 public:
113 
114  /* Register each device we want to send to */
115 
116  int16_t add_node(
117  uint8_t remote_id,
118  const uint8_t remote_ip[],
119  uint16_t port_number = GUDP_DEFAULT_PORT
120  )
121  {
122  if (_remote_node_count == GUDP_MAX_REMOTE_NODES) {
123  return -1;
124  }
125  _remote_id[_remote_node_count] = remote_id;
126  memcpy(_remote_ip[_remote_node_count], remote_ip, 4);
127  _remote_port[_remote_node_count] = port_number;
128  _remote_node_count++;
129  return _remote_node_count - 1;
130  };
131 
132 
133  /* Select if incoming packets should automatically add their sender as a node */
134 
135  void set_autoregistration(bool enabled)
136  {
137  _auto_registration = enabled;
138  }
139 
140 
141  /* Returns the suggested delay related to attempts passed as parameter: */
142 
143  uint32_t back_off(uint8_t attempts)
144  {
145 #ifdef PJON_ESP
146  return 10000ul * attempts + PJON_RANDOM(10000);
147 #elif _WIN32
148  (void)attempts; // Avoid "unused parameter" warning
149  return 1000ul + PJON_RANDOM(1000);
150 #else
151  (void)attempts; // Avoid "unused parameter" warning
152  return 1;
153 #endif
154  };
155 
156 
157  /* Begin method, to be called on initialization:
158  (returns always true) */
159 
160  bool begin(uint8_t /*did*/ = 0)
161  {
162  return check_udp();
163  };
164 
165 
166  /* Check if the channel is free for transmission */
167 
168  bool can_start()
169  {
170  return check_udp();
171  };
172 
173 
174  /* Returns the maximum number of attempts for each transmission: */
175 
176  static uint8_t get_max_attempts()
177  {
178  return 10;
179  };
180 
181 
182  /* Returns the recommended receive time for this strategy: */
183 
184  static uint16_t get_receive_time()
185  {
186  return GUDP_RECEIVE_TIME;
187  };
188 
189 
190  /* Handle a collision (empty because handled on Ethernet level): */
191 
192  void handle_collision() { };
193 
194 
195  /* Receive a frame: */
196 
197  uint16_t receive_frame(uint8_t *data, uint16_t max_length)
198  {
199  uint16_t length = udp.receive_frame(data, max_length);
200  if (length != PJON_FAIL) {
201  autoregister_sender(data, length);
202  }
203  return length;
204  }
205 
206 
207  /* Receive byte response */
208 
209  uint16_t receive_response()
210  {
211  /* TODO: Improve robustness by ignoring packets not from the previous
212  receiver (Perhaps not that important as long as ACK/NAK responses are
213  directed, not broadcast) */
214  uint32_t start = PJON_MICROS();
215  uint8_t result[8];
216  uint16_t reply_length = 0;
217  do {
218  reply_length = receive_frame(result, sizeof result);
219 
220  // We expect 1, if packet is larger it is not our ACK
221  if(reply_length == 1)
222  if (result[0] == PJON_ACK) {
223  return result[0];
224  }
225 
226  } while ((uint32_t)(PJON_MICROS() - start) < GUDP_RESPONSE_TIMEOUT);
227  return PJON_FAIL;
228  };
229 
230 
231  /* Send byte response to package transmitter.
232  We have the IP so we can reply directly. */
233 
234  void send_response(uint8_t response) // Empty, PJON_ACK is always sent
235  {
236  udp.send_response(response);
237  };
238 
239 
240  /* Send a frame: */
241 
242  void send_frame(uint8_t *data, uint16_t length)
243  {
244  if(length > 0) {
245  uint8_t id = data[0]; // Package always starts with a receiver id
246  if (id == 0) { // Broadcast, send to all receivers
247  for(uint8_t pos = 0; pos < _remote_node_count; pos++) {
248  udp.send_frame(data, length, _remote_ip[pos], _remote_port[pos]);
249  }
250  } else { // To a specific receiver
251  int16_t pos = find_remote_node(id);
252  if (pos != -1) {
253  udp.send_frame(data, length, _remote_ip[pos], _remote_port[pos]);
254  }
255  }
256  }
257  };
258 
259 
260  /* Set the UDP port: */
261 
262  void set_port(uint16_t port = GUDP_DEFAULT_PORT)
263  {
264  _port = port;
265  };
266 };
data
char data[MAX_PAYLOAD_SIZE+1]
Buffer for raw payload data.
Definition: MyMessage.h:654
PJON_Packet_Info
Definition: PJONDefines.h:207
GlobalUDP
Definition: GlobalUDP.h:49
UDPHelper
Definition: UDPHelper_ARDUINO.h:21