MySensors Library & Examples  2.3.2-62-ge298769
EthernetLink.h
1 /* EthernetLink and Ethernet strategies contributed by Fred Larsen
2 
3  This EthernetLink class transfers packets of data over
4  TCP/IP Ethernet connections.
5 
6  It may accept incoming connections and send ACK back for each incoming
7  package, it may connect to another object in another device and deliver a
8  packet and get an ACK back, or it may do both -- connect to another device
9  for delivering packages and accept incoming connections and packages for
10  bidirectional transfer.
11 
12  When establishing connections, the target must be registered with the add_node
13  function for setting the IP and port along with a unique id that is used for
14  referring to the target in all other functions.
15 
16  An EthernetLink may work in multiple ways when bidirectional:
17 
18  1. One-to-one connection with another EthernetLink. In this mode, using the
19  keep_connection is recommended because it reduces network activity and
20  speeds things up dramatically. In this mode, there are three options:
21 
22  A. When running on a firewall-free network one socket can be created in each
23  direction, allowing for bidirectional "full-duplex" (as far as that goes
24  for single-threaded code), with data being buffered in each direction. It
25  requires each side to know about each other's address, but delivers
26  higher throughput than a single_socket approach. An EthernetLink object
27  in this mode has one node added by add_node, and both sides calls
28  start_listening to accept incoming connections. If there are multiple
29  objects on one device, each has to listen to a unique port number
30  (specified in the start_listening function call).
31 
32  B. Set single_socket to mave a master-slave scenario where one side, the
33  initiator, creates a socket connection and sends any packages it may
34  have and then receives any package waiting to be sendt in the opposite
35  direction. This requires the initiator to know where to connect, but not
36  the other way around. This is suitable for connections through the
37  Internet because a firewall opening only has to be opened in one place.
38  An EthernetLink object in this mode has one node added by add_node
39  (the initiator side), or calls the start_listening function to start
40  listening (the receiver side). If there are multiple receiving objects
41  on one device, each has to listen to a unique port number
42  (specified in the start_listening function call).
43 
44  C. Set single_initiate_direction to have one device create two sockets to
45  the other device, using one for each packet direction. This is a variant
46  of B) that is firewall friendly and more efficient, but uses one more
47  socket, and cannot be run on the simplest Ethernet cards with only one
48  available socket. This mode has 3-4 times the speed of B) and does not
49  do polling so it will not generate network traffic when idle.
50 
51  2. If running a one-to-many or many-to-many scenario the keep_connection
52  should be deactivated because then one incoming socket would block others
53  from connecting. Not using keep_connection drops the transfer speed some, though.
54 
55  A. When running on a firewall-free network each packet is delivered through
56  a dedicated connection created for delivering that package. After
57  delivery and ACK the socket is closed. All devices can send to and
58  receive from each other.
59 
60  B. Using the single_socket approach will use a master-slave like approach
61  where only the initiators need to know the address of the receiver(s),
62  for easier firewall traversal. After the two-way transfer of packets on
63  the same connection, the connection will be closed so that the receiver
64  is ready for another connection. This requires only _one firewall
65  opening_ for bidirectional transfer with 255 others.
66 
67  Limitations for the different modes when using the W5100 based Ethernet shield
68  with a 4 socket limit:
69 
70  1A. Two EthernetLink objects can be used for bidirectional communication
71  with two sites.
72 
73  1B. Four EthernetLink objects can be used for bidirectional communication
74  with four sites.
75 
76  2A. As the sockets are only used temporarily each object can accept
77  connections from 255 others, and deliver packets to 255 others. Up to
78  two objects can be used in one device, if multiple listening ports are
79  needed. Each object can have 1 to 255 nodes added by add_node, and can
80  deliver to all. There can be up to three objects in each device if needed.
81  One socket must remain free for outgoing connections.
82  2B. As 2A, each receiver can receive packets from and send packets to 255
83  initiators, using a _single listening port_. There can be four receiver
84  objects in on device, or three receiver objects and an unlimited number
85  of initiator objects that will be able to send to and receive from to up
86  to 255 sites each.
87 
88  Limitations for the different modes when using the ENC28J60 based Ethernet
89  shield with a 1 socket limit:
90 
91  1A. Not available.
92  1B. One EthernetLink object can be used for fast bidirectional communication
93  with one site.
94  1C. Not available.
95  2A. Not available.
96  2B. One receiver can receive from and deliver packets to unlimited initiators,
97  _or_ unlimited initator objects can send to and receive from to up to
98  255 sites each
99 
100  Note: To use the ENC28J60 shield instead of the WIZ5100 shield, include
101  <UIPEthernet.h> before EthernetLink.h.
102 
103  PJON and EthernetLink
104  The EthernetLink can be used standalone for simple delivery between two
105  devices. But the EthernetLink is also used as a tool by the EthernetTCP strategy
106  of PJON. A site consisting of one or more buses of devices communicating
107  through PJON, wired and/or wirelessly, can be connected seamlessly with
108  multiple other similar sites in other places of the world, communicating
109  through Internet with minimal firewall configuration.
110 
111  NOTE: The W5100 full-size Ethernet shields come in multiple variants. If you
112  get a problem where the card is not starting after plugging in the
113  power, check if there is small resistor network behind the Ethernet
114  outlet, with one resistor having printed "511" on it. If so, try
115  another brand. Even startup delays do not fix this, but it can be solved
116  with a capacitor+resistor, search for it.
117 
118  NOTE: If needing single_socket functionality with ACK (polling mode),
119  define ETCP_SINGLE_SOCKET_WITH_ACK. The program size has been reduced by
120  only including this when needed.
121 
122  The same goes for ETCP_SINGLE_DIRECTION. It is not included by default
123  to reduce program size. Define this when needed.
124  */
125 
126 #pragma once
127 
128 #ifdef ARDUINO
129 #include "../../interfaces/ARDUINO/TCPHelper_ARDUINO.h"
130 #else
131 #include "../../interfaces/LINUX/TCPHelper_POSIX.h"
132 #ifndef F
133 #define F(x) (x)
134 #endif
135 #define Serial DummyPrint
136 struct DummyPrint {
137  static void print(const char *s)
138  {
139  printf("%s", s);
140  }
141  static void print(int n)
142  {
143  printf("%d", n);
144  }
145  static void println(const char *s)
146  {
147  printf("%s\n", s);
148  }
149  static void println(int n)
150  {
151  printf("%d\n", n);
152  }
153 } DummyPrint;
154 #endif
155 
156 // Constants
157 #ifndef PJON_ACK
158 #define PJON_ACK 6
159 #endif
160 
161 // Internal constants
162 #ifndef PJON_FAIL
163 #define PJON_FAIL 0x100
164 #endif
165 
166 #ifndef ETCP_MAX_REMOTE_NODES
167 #define ETCP_MAX_REMOTE_NODES 10
168 #endif
169 
170 #define ETCP_DEFAULT_PORT 7000
171 
172 /* The maximum packet size to be transferred, this protects again buffer overflow.
173  Set this to a size that is guaranteed to be available in RAM during runtime,
174  depending on the hardware and software. */
175 #ifndef ETCP_MAX_PACKET_SIZE
176 #define ETCP_MAX_PACKET_SIZE 300
177 #endif
178 
179 
180 /* If an incoming packet has not arrived for some time, disconnect the socket so
181  it will be reconnected on demand. The timeout is in ms.
182  The reason for this is that in some cases an idle socket may have gotten
183  disconnected without it being detected, unless trying to write to it.
184  So we could be waiting for data that never arrives. */
185 #ifndef ETCP_IDLE_TIMEOUT
186 #define ETCP_IDLE_TIMEOUT 30000ul
187 #endif
188 
189 // Magic number to verify that we are aligned with telegram start and end
190 #define ETCP_HEADER 0x18ABC427ul
191 #define ETCP_FOOTER 0x9ABE8873ul
192 #define ETCP_SINGLE_SOCKET_HEADER 0x4E92AC90ul
193 #define ETCP_SINGLE_SOCKET_FOOTER 0x7BB1E3F4ul
194 #define ETCP_CONNECTION_HEADER_A 0xFEDFED67ul // Primary socket, packets in initiated direction
195 #define ETCP_CONNECTION_HEADER_A_ACK 0xFEDFED68ul // Same, but request ACK for all packets
196 #define ETCP_CONNECTION_HEADER_B 0xFEDFED77ul // Reverse socket, packets in reverse direction
197 
198 /* UIPEthernet library used for the ENC28J60 based Ethernet shields has the
199  correct return value from the read call, while the standard Ethernet library
200  does not follow the standard! */
201 #if defined(ARDUINO) && !defined(UIPETHERNET_H)
202 #define ETCP_ERROR_READ 0
203 #else
204 #define ETCP_ERROR_READ -1
205 #endif
206 
207 typedef void (*link_receiver)(
208  uint8_t id,
209  const uint8_t *payload,
210  uint16_t length,
211  void *callback_object
212 );
213 
214 typedef void (*link_error)(
215  uint8_t code,
216  uint8_t data
217 );
218 
220 {
221  uint8_t *buf = NULL;
222  uint16_t len = 0;
223 public:
224  TmpBuffer(uint16_t size)
225  {
226  len = size;
227  buf = new uint8_t[size];
228  }
229  ~TmpBuffer()
230  {
231  if (buf) {
232  delete buf;
233  }
234  }
235  uint8_t* operator()()
236  {
237  return buf;
238  }
239  uint16_t size()
240  {
241  return len;
242  }
243 };
244 
246 {
247 private:
248  // ********* Dynamic members ************
249 
250  TCPHelperServer *_server = NULL;
251 
252  // Connection for writing outgoing packets
253  TCPHelperClient _client_out;
254 
255  // Connection for reading incoming packets
256  TCPHelperClient _client_in;
257 
258  // The id of the remove device/node that we have connected to
259  int16_t _current_device = -1;
260 
261  // When a socket is received, the connection header specifies if ACKs are wanted
262  bool _ack_requested = false;
263 
264  // Remember the connection statistics
265  uint32_t _connection_time = 0;
266  uint32_t _connection_count = 0;
267  uint32_t _last_receive_time = 0;
268 
269  // ********* Configuration ************
270 
271  link_receiver _receiver = NULL;
272  link_error _error = NULL;
273  void *_callback_object = NULL;
274 
275  // Local node
276  uint8_t _local_id = 0;
277  uint8_t _local_ip[4];
278  uint16_t _local_port = ETCP_DEFAULT_PORT;
279 
280  // Remote nodes
281  uint8_t _remote_node_count = 0;
282  uint8_t _remote_id[ETCP_MAX_REMOTE_NODES];
283  uint8_t _remote_ip[ETCP_MAX_REMOTE_NODES][4];
284  uint16_t _remote_port[ETCP_MAX_REMOTE_NODES];
285 
286  // Keep sockets permanently open instead of reconnecting for each transfer
287  bool _keep_connection = false;
288 
289  // Do bidirectional transfer on a single socket
290  bool _single_socket = false;
291 
292  // Request an immediate ACK for eack packet delivery to ensure guaranteed delivery
293  bool _request_ack = false;
294 
295  // To avoid deadlocks while connecting (if receiver tries to connect back simultanously),
296  // receive and discard packets while doing non-blocking connect. This should resolve deadlocks,
297  // and the missing ACK should make the packet being resent later.
298  bool _receive_and_discard = false;
299 
300  // With single_socket = false, there is one socket for each packet direction.
301  // Normally the sockets are initiated from the side sending the packet.
302  // By setting initiate_both_sockets_in_same direction, both sockets can be
303  // initiated from one of the devices, to simplify firewall setup, or for letting
304  // only one of the devices have a static IP address.
305  // This should only be used with _keep_connection = true, and is meant for permanent
306  // one-to-one links.
307  bool _initiate_both_sockets_in_same_direction = false;
308  // Whether to be the side initiating both sockets or not
309  bool _initiator = true;
310 
311 public:
312 
313  void init()
314  {
315  memset(_local_ip, 0, 4);
316  };
317 
318 
319  int16_t find_remote_node(uint8_t id)
320  {
321  for(uint8_t i = 0; i < _remote_node_count; i++)
322  if(_remote_id[i] == id) {
323  return i;
324  }
325  return -1;
326  };
327 
328 
329  int16_t read_bytes(
330  TCPHelperClient &client,
331  uint8_t *contents,
332  uint16_t length,
333  uint16_t timeout_ms = 2000
334  )
335  {
336  int32_t total_bytes_read = 0, bytes_read = ETCP_ERROR_READ;
337  uint32_t start_ms = PJON_MILLIS();
338  /* NOTE: The Arduino standard recv/read functions returns
339  -1 if no data waiting
340  0 if socket closed
341  This is the opposite of POSIX. */
342  do {
343 #ifdef HAS_ETHERNETUDP // Avoid using blocking read until there is data present
344  int16_t avail = 0;
345  while(
346  client.connected() &&
347  (avail = client.available()) <= 0 &&
348  (uint32_t)(PJON_MILLIS() - start_ms) < min(1000, timeout_ms)
349  ) {
350  PJON_DELAY_MICROSECONDS(250);
351  }
352  if (avail <= 0) {
353  continue;
354  }
355 #endif
356 
357  bytes_read = client.read(
358  &contents[total_bytes_read],
359  length - total_bytes_read
360  );
361 
362  if(bytes_read > 0) {
363  total_bytes_read += bytes_read;
364  }
365  } while(
366  bytes_read != ETCP_ERROR_READ &&
367  total_bytes_read < length &&
368  (uint32_t)(PJON_MILLIS() - start_ms) < timeout_ms
369  );
370 
371  if(bytes_read == ETCP_ERROR_READ) {
372  stop(client); // Lost connection
373 #ifdef ETCP_ERROR_PRINT
374  Serial.println(F("Read failed, closing cl"));
375 #endif
376  }
377  return total_bytes_read;
378  };
379 
380 
381  // Read a package from a connected client (incoming or outgoing socket) and send ACK
382  uint16_t receive(TCPHelperClient &client, bool wait)
383  {
384  uint16_t return_value = PJON_FAIL;
385  uint32_t start_ms = PJON_MILLIS(), avail;
386  if (wait) {
387  while(
388  client.connected() &&
389  (avail = client.available()) <= 0 &&
390  (uint32_t)(PJON_MILLIS() - start_ms) < 1000
391  ) {
392  PJON_DELAY_MICROSECONDS(250);
393  }
394  } else {
395  avail = client.connected() ? client.available() : 0;
396  }
397 
398  if(avail > 0) {
399 #ifdef ETCP_DEBUG_PRINT
400  Serial.println(F("Recv from cl"));
401 #endif
402 
403  // Locate and read encapsulation header (4 bytes magic number)
404  bool ok = read_until_header(client, ETCP_HEADER);
405 #ifdef ETCP_DEBUG_PRINT
406  Serial.print(F("Read header, stat "));
407  Serial.println(ok);
408 #endif
409 
410  // Read sender device id (1 byte) and length of contents (4 bytes)
411  int16_t bytes_read = 0;
412  uint8_t sender_id = 0;
413  uint32_t content_length = 0;
414  if(ok) {
415  uint8_t buf[5];
416  bytes_read = read_bytes(client, buf, 5);
417  if(bytes_read != 5) {
418  ok = false;
419  } else {
420  memcpy(&sender_id, buf, 1);
421  memcpy(&content_length, &buf[1], 4);
422  content_length = ntohl(content_length);
423  if(content_length == 0) {
424  ok = 0;
425  }
426  }
427  }
428 
429  // Protect against too large packets
430  if (content_length > ETCP_MAX_PACKET_SIZE) {
431  return PJON_FAIL;
432  }
433 
434  // Read contents and footer
435  TmpBuffer buf(content_length);
436  if(ok) {
437  bytes_read = read_bytes(client, (uint8_t*)buf(), content_length);
438  if((uint32_t)bytes_read != content_length) {
439  ok = false;
440  }
441  }
442 
443  // Read footer (4 bytes magic number)
444  if(ok) {
445  uint32_t foot = 0;
446  bytes_read = read_bytes(client, (uint8_t*) &foot, 4);
447  if(bytes_read != 4 || foot != htonl(ETCP_FOOTER)) {
448  ok = false;
449  }
450  }
451 
452 #ifdef ETCP_DEBUG_PRINT
453  Serial.print(F("Stat rec: "));
454  Serial.print(ok);
455  Serial.print(" len: ");
456  Serial.println(content_length);
457 #else
458 #if defined(ETCP_ERROR_PRINT)
459  if (!ok) {
460  Serial.print(F("FAIL rec: "));
461  Serial.println(bytes_read);
462  }
463 #endif
464 #endif
465 
466  return_value = ok ? PJON_ACK : PJON_FAIL;
467  if (ok) {
468  _last_receive_time = PJON_MILLIS();
469  }
470  if (_ack_requested && !_receive_and_discard) {
471  // Write PJON_ACK
472  int8_t acklen = 0;
473  if(ok) {
474  uint16_t r = htons(return_value);
475  acklen = client.write((uint8_t*) &r, 2);
476  client.flush();
477  }
478 
479 #ifdef ETCP_DEBUG_PRINT
480  Serial.print("Sent ");
481  Serial.print(ok ? "PJON_ACK: " : "PJON_FAIL: ");
482  Serial.println(acklen);
483 #else
484  (void)acklen; // Avoid "set but not used" warning
485 #if defined(ETCP_ERROR_PRINT)
486  if (!ok) {
487  Serial.println("FAILURE sending ACK");
488  }
489 #endif
490 #endif
491  }
492 
493  // Call receiver callback function
494  if(ok && !_receive_and_discard && content_length > 0) {
495  _receiver(sender_id, (uint8_t*)buf(), content_length, _callback_object);
496  }
497 
498  if (!ok) {
499  disconnect_in();
500  }
501  }
502  return return_value;
503  };
504 
505 
506  bool connect(uint8_t id)
507  {
508  // Locate the node's IP address and port number
509  int16_t pos = find_remote_node(id);
510 
511  // Determine if to disconnect
512  bool disconnect = !_keep_connection,
513  reverse = _initiate_both_sockets_in_same_direction && _initiator;
514 
515  // Break existing connection if not connected to the wanted server
516  if(!disconnect && _client_out && _current_device != id) {
517  // Connected, but to the wrong device
518 #ifdef ETCP_DEBUG_PRINT
519  Serial.println(F("Switch conn to another srv"));
520 #endif
521  disconnect = true;
522  _current_device = -1;
523  }
524 
525  // Check if established sockets have been disconnected
526  if (!disconnect && _client_out && !_client_out.connected()) {
527  disconnect = true;
528  }
529 #ifdef ETCP_SINGLE_DIRECTION
530  if (!disconnect && reverse && _client_in && !_client_in.connected()) {
531  disconnect = true;
532  }
533  if (!disconnect && reverse && (!_client_in || !_client_out)) {
534  disconnect = true;
535  }
536  if (!disconnect && reverse && _client_in && got_receive_timeout()) {
537 #ifdef ETCP_ERROR_PRINT
538  Serial.println(F("Receive timeout, disconn."));
539 #endif
540  disconnect = true;
541  }
542 #endif
543  if (disconnect) {
544  disconnect_out();
545  }
546  // NOTE: From this point we can avoid using the expensive connected() call
547 
548  bool connected = _client_out;
549 #ifdef ETCP_DEBUG_PRINT
550  if (!connected) {
551  Serial.print(F("Conn to srv pos="));
552  Serial.println(pos);
553  }
554 #endif
555 
556  if(pos < 0) {
557  return false;
558  }
559 
560  // Try to connect to server if not already connected
561  bool did_connect = false;
562  if(!connected) {
563 #ifdef ETCP_DEBUG_PRINT
564  Serial.println("Conn..");
565 #endif
566 #ifdef HAS_ETHERNETUDP // Arduino, otherwise POSIX
567  connected = _client_out.connect(_remote_ip[pos], _remote_port[pos]);
568 #else
569  // Use non-blocking calls, and receive and discard incoming packets
570  if (_client_out.prepare_connect(_remote_ip[pos], _remote_port[pos])) {
571  int8_t status = 0;
572  uint32_t start = PJON_MILLIS();
573  do {
574  if (!_initiate_both_sockets_in_same_direction && !_single_socket && _server) {
575  TCPHelperClient client = _server->available();
576  if (client) {
577  client.stop();
578  }
579  }
580  status = _client_out.try_connect();
581  if (status == 0) {
582  PJON_DELAY_MICROSECONDS(PJON_RANDOM(250)); // Avoid misusing CPU while waiting
583  }
584  } while (status == 0 && (uint32_t)(PJON_MILLIS()-start)<4000);
585  connected = (status == 1);
586  }
587 #endif
588 
589 #ifdef ETCP_DEBUG_PRINT
590  Serial.println(connected ? "Conn to srv" : "Failed conn to srv");
591 #endif
592  if(connected) {
593  // Adjust connection header A to include ACK request flag
594  uint32_t conn_header = htonl(_request_ack ? ETCP_CONNECTION_HEADER_A_ACK :
595  ETCP_CONNECTION_HEADER_A);
596  if (_client_out.write((uint8_t*) &conn_header, 4) != 4) {
597  connected = false;
598 #ifdef ETCP_ERROR_PRINT
599  Serial.println(F("Disconn. failed write connhead"));
600 #endif
601  }
602  }
603  if (!connected) {
604  stop(_client_out);
605  } else {
606  did_connect = true;
607  }
608  }
609 
610  bool connected_rev = false;
611 #ifdef ETCP_SINGLE_DIRECTION
612  if (connected && reverse) {
613  connected_rev = _client_in;
614  if (!connected_rev) {
615 #ifdef ETCP_DEBUG_PRINT
616  Serial.println("Conn rev..");
617 #endif
618  uint32_t start = PJON_MILLIS();
619  do {
620  connected_rev = _client_in.connect(_remote_ip[pos], _remote_port[pos]);
621  } while (!connected_rev && (uint32_t)(PJON_MILLIS()-start) < 2000);
622 #ifdef ETCP_DEBUG_PRINT
623  Serial.println(connected_rev ? F("Conn rev to srv") : F("Failed rev conn to srv"));
624 #endif
625  if(connected_rev) {
626  uint32_t conn_header = htonl(ETCP_CONNECTION_HEADER_B);
627  if (_client_in.write((uint8_t*) &conn_header, 4) != 4) {
628  connected_rev = false;
629 #ifdef ETCP_ERROR_PRINT
630  Serial.println(F("Disconn rev. failed write connhead"));
631 #endif
632  }
633  }
634  if (connected_rev) {
635  // ACK active on both sockets or none in this mode
636  _ack_requested = _request_ack;
637  did_connect = true;
638  _last_receive_time = PJON_MILLIS(); // Count the connection as a receive action
639  }
640  }
641  }
642 #endif
643 
644  if (did_connect) { // Gather a litte connection information
645  _connection_time = PJON_MILLIS();
646  _connection_count++;
647  if (_single_socket) {
648  _ack_requested = _request_ack; // Same mode in both directions
649  }
650  }
651 
652  if (!connected || (reverse && !connected_rev)) {
653 #ifdef ETCP_ERROR_PRINT
654  Serial.print(F("Fail conn, closing "));
655  Serial.print(connected);
656  Serial.println(connected_rev);
657 #endif
658  disconnect_out();
659  _current_device = -1;
660  PJON_DELAY(10); // Slow down if failure
661  return false; // Server is unreachable or busy
662  } else {
663  _current_device = id; // Remember who we are connected to
664  }
665 
666  return true;
667  };
668 
669 
670  void stop(TCPHelperClient &client)
671  {
672  client.stop();
673  };
674 
675  void disconnect_in()
676  {
677 #ifdef ETCP_DEBUG_PRINT
678  if (_client_in || (_initiate_both_sockets_in_same_direction && _client_out)) {
679  Serial.println(F("Disconn in&rev"));
680  }
681 #endif
682 #ifdef ETCP_SINGLE_DIRECTION
683  if (_initiate_both_sockets_in_same_direction && _client_out) {
684  stop(_client_out);
685  }
686 #endif
687  if (_client_in) {
688  stop(_client_in);
689  }
690  }
691 
692 
693  void disconnect_out()
694  {
695 #ifdef ETCP_DEBUG_PRINT
696  if (_client_out || (_initiate_both_sockets_in_same_direction && _client_in)) {
697  Serial.println(F("Disconn out&rev"));
698  }
699 #endif
700  if (_client_out) {
701  stop(_client_out);
702  }
703 #ifdef ETCP_SINGLE_DIRECTION
704  if (_initiate_both_sockets_in_same_direction && _client_in) {
705  stop(_client_in);
706  }
707 #endif
708  }
709 
710 
711  bool accept()
712  {
713  // Determine if to disconnect
714  bool disconnect = !_keep_connection;
715 #ifdef ETCP_SINGLE_DIRECTION
716  bool reverse = _initiate_both_sockets_in_same_direction && !_initiator;
717 #endif
718  if (!disconnect && _client_in && !_client_in.connected()) {
719  disconnect = true;
720  }
721 #ifdef ETCP_SINGLE_DIRECTION
722  if (!disconnect && reverse && _client_out && !_client_out.connected()) {
723  disconnect = true;
724  }
725  if (!disconnect && reverse && (!_client_in || !_client_out)) {
726  disconnect = true;
727  }
728 #endif
729  if (!disconnect && _client_in && got_receive_timeout()) {
730 #ifdef ETCP_ERROR_PRINT
731  Serial.println(F("Receive timeout, disconn."));
732 #endif
733  disconnect = true;
734  }
735  if (disconnect) {
736  disconnect_in();
737  }
738  // NOTE: From this point we can avoid using the expensive connected() call
739 
740  // Accept new incoming connection
741  bool did_connect = false, connected = _client_in;
742  if (!connected) {
743  _client_in = _server->available();
744  connected = _client_in;
745 #ifdef ETCP_DEBUG_PRINT
746  if(connected) {
747  Serial.println(F("Accepted"));
748  }
749 #endif
750  if (connected) {
751  uint32_t connection_header = 0;
752  bool header_ok = false;
753  if (read_bytes(_client_in, (uint8_t*) &connection_header, 4) == 4) {
754  if (connection_header == htonl(ETCP_CONNECTION_HEADER_A)) {
755  header_ok = true;
756  _ack_requested = false;
757  } else if (connection_header == htonl(ETCP_CONNECTION_HEADER_A_ACK)) {
758  header_ok = true;
759  _ack_requested = true;
760  }
761  }
762  if (header_ok) {
763  did_connect = true;
764  } else {
765  disconnect_in();
766  connected = false;
767 #ifdef ETCP_ERROR_PRINT
768  Serial.println(F("Disconn. no connhead"));
769 #endif
770  }
771  }
772  }
773 
774  // Accept reverse connection if relevant
775 #ifdef ETCP_SINGLE_DIRECTION
776  if (connected && reverse && !_client_out) {
777 #ifdef ETCP_DEBUG_PRINT
778  Serial.println(F("Lst rev"));
779 #endif
780  // ACK active on both sockets or none in this mode
781  _request_ack = _ack_requested;
782 
783  bool connected_reverse = false;
784  uint32_t start = PJON_MILLIS();
785  do {
786  _client_out = _server->available();
787  } while (!_client_out && (uint32_t)(PJON_MILLIS()-start) < 2000);
788 
789  if(_client_out) {
790 #ifdef ETCP_DEBUG_PRINT
791  Serial.println("Accept rev OK");
792 #endif
793  uint32_t connection_header = 0;
794  if (read_bytes(_client_out, (uint8_t*) &connection_header, 4) == 4 &&
795  connection_header == htonl(ETCP_CONNECTION_HEADER_B)) {
796  connected_reverse = true;
797  } else {
798 #ifdef ETCP_ERROR_PRINT
799  Serial.println(F("Disconn. rev no connhead"));
800 #endif
801  }
802  } else {
803 #ifdef ETCP_ERROR_PRINT
804  Serial.println(F("Accept rev timed out"));
805 #endif
806  }
807 
808  if (connected_reverse) {
809  did_connect = true;
810  } else {
811 #ifdef ETCP_ERROR_PRINT
812  Serial.print(F("Fail accept, closing, "));
813  Serial.print(connected);
814  Serial.println(connected_reverse);
815 #endif
816  connected = false;
817  disconnect_in();
818  }
819  }
820 #endif
821 
822  if (did_connect) {
823  _connection_time = PJON_MILLIS();
824  _connection_count++;
825  _last_receive_time = PJON_MILLIS(); // Count the connection as a receive action
826  }
827  return connected;
828  };
829 
830 
831  void disconnect_out_if_needed(uint16_t result)
832  {
833  //bool connected = _client_out.connected();
834  if(_client_out && (result == PJON_FAIL || !_keep_connection)) {
835  stop(_client_out);
836 #ifdef ETCP_DEBUG_PRINT
837  Serial.print("Disconn outcl. OK=");
838  Serial.println(result == PJON_ACK);
839 #endif
840  }
841  };
842 
843  bool got_receive_timeout()
844  {
845  return (uint32_t)(PJON_MILLIS() - _last_receive_time) > ETCP_IDLE_TIMEOUT;
846  }
847 
848  bool disconnect_in_if_needed()
849  {
850  if(_client_in && !_keep_connection) {
851 #ifdef ETCP_DEBUG_PRINT
852  Serial.println("Disc. inclient.");
853 #endif
854  stop(_client_in);
855  }
856  return true;
857  };
858 
859 
860  uint16_t send(
861  TCPHelperClient &client,
862  uint8_t id,
863  const uint8_t *packet,
864  uint16_t length
865  )
866  {
867  // Assume we are connected. Try to deliver the package
868  uint32_t head = htonl(ETCP_HEADER), foot = htonl(ETCP_FOOTER), len = htonl(length);
869  uint8_t buf[9];
870  memcpy(buf, &head, 4);
871  memcpy(&buf[4], &id, 1);
872  memcpy(&buf[5], &len, 4);
873 
874  if (!_single_socket && _client_in) if (_client_in.available() > 0) {
875  return PJON_BUSY;
876  }
877 
878 #ifdef HAS_ETHERNETUDP
879  bool ok = client.write((uint8_t*) buf, 9) == 9;
880  if(ok) {
881  ok = client.write((uint8_t*) packet, length) == length;
882  }
883  if(ok) {
884  ok = client.write((uint8_t*) &foot, 4) == 4;
885  }
886 #else
887  // On a POSIX capable device we expect to have enough memory to collect all into one buffer
888  // so that it will not be sent as 3 separate packets when TCP_NODELAY is active.
889  TmpBuffer totalbuf(9+length+4);
890  memcpy(totalbuf(), buf, 9);
891  memcpy(&(totalbuf()[9]), packet, length);
892  memcpy(&(totalbuf()[9+length]), &foot, 4);
893  bool ok = client.write((uint8_t*)totalbuf(), 9+length+4) == (9+length+4);
894 #endif
895  if(ok) {
896  client.flush();
897  }
898 #ifdef ETCP_DEBUG_PRINT
899  Serial.print("Write stat: ");
900  Serial.println(ok);
901 #endif
902 
903  uint16_t result = PJON_FAIL;
904  if (_request_ack) {
905  // Read ACK
906  if(ok) {
907  uint16_t code = 0;
908  int16_t status = read_bytes(client, (uint8_t*) &code, 2);
909  code = ntohs(code);
910  if(status == 2 && code == PJON_ACK) {
911  result = code;
912  } else {
913  ok = false;
914  }
915  }
916 #ifdef ETCP_DEBUG_PRINT
917  Serial.print("PJON_ACK stat: ");
918  Serial.print(result == PJON_ACK);
919  Serial.println(ok ? " OK" : " FAIL");
920 #endif
921  } else {
922  result = ok ? PJON_ACK : PJON_FAIL;
923  }
924  return result; // PJON_FAIL, PJON_ACK
925  };
926 
927 
928  /* Do ACKed bidirectional transfer of packets over a single socket connection by
929  using a master-slave mode where the master connects and delivers packets
930  or a placeholder, then reads packets or placeholder back before closing
931  the connection (unless letting it stay open). */
932 #ifdef ETCP_SINGLE_SOCKET_WITH_ACK
933  uint16_t single_socket_transfer(
934  TCPHelperClient &client,
935  int16_t id,
936  bool master,
937  const uint8_t *contents,
938  uint16_t length
939  )
940  {
941  if(master) { // Creating outgoing connections
942  // Connect or check that we are already connected to the correct server
943  bool connected = connect(id);
944 #ifdef ETCP_DEBUG_PRINT
945  //Serial.println(connected ? "Out conn" : "No out conn");
946 #endif
947  if(!connected) {
948  return PJON_FAIL;
949  }
950 
951  // Send singlesocket header and number of outgoing packets
952  bool ok = true;
953  uint32_t head = htonl(ETCP_SINGLE_SOCKET_HEADER);
954  uint8_t numpackets_out = length > 0 ? 1 : 0;
955  uint8_t buf[5];
956  memcpy(buf, &head, 4);
957  memcpy(&buf[4], &numpackets_out, 1);
958  if(ok) {
959  ok = client.write((uint8_t*) &buf, 5) == 5;
960  }
961  if(ok) {
962  client.flush();
963  }
964 
965  // Send the packet and read PJON_ACK
966  if(ok && numpackets_out > 0) {
967  ok = send(client, id, contents, length) == PJON_ACK;
968 #ifdef ETCP_DEBUG_PRINT
969  Serial.print(F("++++Sent p, ok="));
970  Serial.println(ok);
971 #endif
972  }
973 
974  // Read number of incoming messages
975  uint8_t numpackets_in = 0;
976  if(ok) {
977  ok = read_bytes(client, &numpackets_in, 1) == 1;
978  }
979 #ifdef ETCP_DEBUG_PRINT
980  if (!ok || numpackets_in > 0) {
981  Serial.print("Read np_in: ");
982  Serial.print(numpackets_in);
983  Serial.println(ok ? " OK" : " FAIL");
984  }
985 #endif
986 
987  // Read incoming packages if any
988  for(uint8_t i = 0; ok && i < numpackets_in; i++) {
989  while(client.available() < 1 && client.connected()) ;
990  ok = receive(client, true) == PJON_ACK;
991 #ifdef ETCP_DEBUG_PRINT
992  Serial.print(F("------->Read p "));
993  Serial.print(i);
994  Serial.print(F(", ok="));
995  Serial.println(ok);
996 #endif
997  }
998 
999  // Write singlesocket footer for the whole thing
1000  uint32_t foot = htonl(ETCP_SINGLE_SOCKET_FOOTER);
1001  if(ok) {
1002  ok = client.write((uint8_t*) &foot, 4) == 4;
1003  }
1004  if(ok) {
1005  client.flush();
1006  }
1007 #ifdef ETCP_DEBUG_PRINT
1008  // Serial.print("Sent ss foot, ok=");
1009  // Serial.println(ok);
1010 #endif
1011 
1012  // Disconnect
1013  uint16_t result = ok ? PJON_ACK : PJON_FAIL;
1014  disconnect_out_if_needed(result);
1015 #ifdef ETCP_DEBUG_PRINT
1016  if (numpackets_in > 0 || numpackets_out > 0) {
1017  Serial.print("INOUTm ");
1018  Serial.print(numpackets_in);
1019  Serial.print(numpackets_out);
1020  Serial.println(ok ? " OK" : " FAIL");
1021  }
1022 #endif
1023 
1024  // Return PJON_ACK if successfully sent or received a packet
1025  return contents == NULL ? (numpackets_in > 0 ? result : PJON_FAIL) : result;
1026 
1027  } else { // Receiving incoming connections and packets and request
1028  if (client && got_receive_timeout()) {
1029 #ifdef ETCP_ERROR_PRINT
1030  Serial.println(F("Receive timeout, disconn."));
1031 #endif
1032  stop(client);
1033  }
1034 
1035  // Wait for and accept connection
1036  bool connected = accept();
1037 #ifdef ETCP_DEBUG_PRINT
1038  //Serial.println(connected ? "In conn" : "No in conn");
1039 #endif
1040  if(!connected) {
1041  return PJON_FAIL;
1042  }
1043 
1044  // Read singlesocket header
1045  bool ok = read_until_header(client, ETCP_SINGLE_SOCKET_HEADER);
1046  if (ok) {
1047  _last_receive_time = PJON_MILLIS();
1048  }
1049 #ifdef ETCP_DEBUG_PRINT
1050  //Serial.print("Read ss head, ok=");
1051  //Serial.println(ok);
1052 #endif
1053 
1054  // Read number of incoming packets
1055  uint8_t numpackets_in = 0;
1056  if(ok) {
1057  ok = read_bytes(client, (uint8_t*) &numpackets_in, 1) == 1;
1058  }
1059 #ifdef ETCP_DEBUG_PRINT
1060  if (!ok || numpackets_in > 0) {
1061  Serial.print("Read np_in: ");
1062  Serial.print(numpackets_in);
1063  Serial.println(ok ? " OK" : " FAIL");
1064  }
1065 #endif
1066 
1067  // Read incoming packets if any, send ACK for each
1068  for(uint8_t i = 0; ok && i < numpackets_in; i++) {
1069  while(client.available() < 1 && client.connected()) ;
1070  ok = receive(client, true) == PJON_ACK;
1071 #ifdef ETCP_DEBUG_PRINT
1072  Serial.print(F("Read p "));
1073  Serial.print(i);
1074  Serial.print(F(", ok="));
1075  Serial.println(ok);
1076 #endif
1077  }
1078 
1079  // Write number of outgoing packets
1080  uint8_t numpackets_out = length > 0 ? 1 : 0;
1081  if(ok) {
1082  ok = client.write((uint8_t*) &numpackets_out, 1) == 1;
1083  }
1084  if(ok) {
1085  client.flush();
1086  }
1087 
1088  // Write outgoing packets if any
1089  if(ok && numpackets_out > 0) {
1090  ok = send(client, id, contents, length) == PJON_ACK;
1091 #ifdef ETCP_DEBUG_PRINT
1092  Serial.print(F("Sent p, ok="));
1093  Serial.println(ok);
1094 #endif
1095  }
1096 
1097  // Read singlesocket footer
1098  if(ok) {
1099  uint32_t foot = 0;
1100  ok = read_bytes(client, (uint8_t*) &foot, 4) == 4;
1101  if(foot != htonl(ETCP_SINGLE_SOCKET_FOOTER)) {
1102  ok = 0;
1103  }
1104 #ifdef ETCP_DEBUG_PRINT
1105  //Serial.print(F("Read ss foot, ok="));
1106  //Serial.println(ok);
1107 #endif
1108  }
1109 
1110  // Disconnect
1111  disconnect_in_if_needed();
1112 
1113 #ifdef ETCP_DEBUG_PRINT
1114  if (numpackets_in > 0 || numpackets_out > 0) {
1115  Serial.print("INOUT ");
1116  Serial.print(numpackets_in);
1117  Serial.print(numpackets_out);
1118  Serial.println(ok ? " OK" : " FAIL");
1119  }
1120 #endif
1121 
1122  if (!ok) {
1123  stop(client);
1124 #ifdef ETCP_ERROR_PRINT
1125  Serial.print(F("Failure, disconnecting."));
1126 #endif
1127  }
1128 
1129  // Return PJON_ACK if successfully sent or received a packet
1130  uint16_t result = ok ? PJON_ACK : PJON_FAIL;
1131  return contents == NULL ? (numpackets_in > 0 ? result : PJON_FAIL) : result;
1132  }
1133  return PJON_FAIL;
1134  };
1135 #endif
1136 
1137  /* Read until a specific 4 byte value is found.
1138  This will resync if stream position is lost. */
1139 
1140  bool read_until_header(TCPHelperClient &client, uint32_t header)
1141  {
1142  header = htonl(header); // Network byte order
1143  uint32_t head = 0;
1144  int8_t bytes_read = 0;
1145  bytes_read = (uint8_t)read_bytes(client, (uint8_t*) &head, 4);
1146  if(bytes_read != 4 || head != header) {
1147  // Did not get header. Lost position in stream?
1148  do {
1149  /* Try to resync if we lost position in the stream
1150  (throw avay all until ETCP_HEADER found) */
1151  head = head >> 8;
1152  // Make space for 8 bits to be read into the most significant byte
1153  bytes_read = (uint8_t)read_bytes(client, &((uint8_t*) &head)[3], 1);
1154  if(bytes_read != 1) {
1155  break;
1156  }
1157  } while(head != header);
1158  }
1159  return head == header;
1160  };
1161 
1162 public:
1163 
1164  EthernetLink()
1165  {
1166  init();
1167  };
1168 
1169 
1170  EthernetLink(uint8_t id)
1171  {
1172  init();
1173  set_id(id);
1174  };
1175 
1176 #ifndef ARDUINO
1177  // Destructor not needed on Arduino, uses a lot of program space
1178  ~EthernetLink()
1179  {
1180  if (_server) {
1181  delete _server;
1182  }
1183  }
1184 #endif
1185 
1186  int16_t add_node(
1187  uint8_t remote_id,
1188  const uint8_t remote_ip[],
1189  uint16_t port_number = ETCP_DEFAULT_PORT
1190  )
1191  {
1192  if (_remote_node_count == ETCP_MAX_REMOTE_NODES) {
1193  return -1;
1194  }
1195  _remote_id[_remote_node_count] = remote_id;
1196  memcpy(_remote_ip[_remote_node_count], remote_ip, 4);
1197  _remote_port[_remote_node_count] = port_number;
1198  _remote_node_count++;
1199  return _remote_node_count - 1;
1200  };
1201 
1202 
1203  /* This function must be called after configuration before running,
1204  for ALL configurations EXCEPT when initiator in single_socket
1205  and single_initiate_direction modes.
1206  In single_socket and single_initiate_direction modes, calling this
1207  function sets it as receiver, NOT initiator. */
1208 
1209  void start_listening(uint16_t port_number = ETCP_DEFAULT_PORT)
1210  {
1211  _local_port = port_number;
1212  _initiator = false; // If we are listening, we are not the initiator
1213  if(_server == NULL && (!_initiate_both_sockets_in_same_direction || !_initiator)) {
1214 #ifdef ETCP_DEBUG_PRINT
1215  Serial.print("Lst on port ");
1216  Serial.println(port_number);
1217 #endif
1218  _server = new TCPHelperServer(port_number);
1219  _server->begin();
1220  }
1221  };
1222 
1223 
1224  /* Whether to keep outgoing connection live until we need connect to
1225  another EthernetLink node */
1226 
1227  void keep_connection(bool keep)
1228  {
1229  _keep_connection = keep;
1230  };
1231 
1232 
1233  /* Whether to do bidirectional data transfer on a single socket or use one
1234  socket for each direction. Single socket transfer is slower but more
1235  firewall-friendly. Client connects to server, and packets are exchanged in
1236  both directions on the same socket. If using this, _only one_ of the sides
1237  should call start_listening, and that side will be receiver with the other
1238  initiator (establishing connections) */
1239 #ifdef ETCP_SINGLE_SOCKET_WITH_ACK
1240  void single_socket(bool single_socket)
1241  {
1242  _single_socket = single_socket;
1243  keep_connection(true);
1244  request_ack(true);
1245  };
1246 #endif
1247 
1248  /* With single_socket = false, there is one socket for each packet direction.
1249  Normally the sockets are initiated from the side sending the packet.
1250  By setting initiate_both_sockets_in_same direction, both sockets can be
1251  initiated from one of the devices, to simplify firewall setup, or for letting
1252  only one of the devices have a static IP address.
1253  This should only be used with _keep_connection = true, and is meant for permanent
1254  one-to-one links. */
1255 #ifdef ETCP_SINGLE_DIRECTION
1256  void single_initiate_direction(bool single_initiate_direction)
1257  {
1258  _initiate_both_sockets_in_same_direction = single_initiate_direction;
1259  keep_connection(true);
1260  request_ack(true);
1261  };
1262 #endif
1263 
1264  /* Request an explicit immediate ACK for each packet being sent.
1265  This make it possible with guearanteed delivery.
1266  Without ACK, a packet may be lost before a dead socket is discovered,
1267  but for some shemes this is acceptable. */
1268 
1269  void request_ack(bool request_ack)
1270  {
1271  _request_ack = request_ack;
1272  }
1273 
1274  /* Some connection statistics */
1275  uint32_t get_connection_time() const
1276  {
1277  return _connection_time;
1278  }
1279  uint32_t get_connection_count() const
1280  {
1281  return _connection_count;
1282  }
1283 
1284 
1285  // Keep trying to send for a maximum duration
1286 
1287  uint16_t send_with_duration(
1288  uint8_t id,
1289  const uint8_t *packet,
1290  uint16_t length,
1291  uint32_t duration_us
1292  )
1293  {
1294  uint32_t start = PJON_MICROS();
1295  uint16_t result = PJON_FAIL;
1296  do {
1297  result = send(id, packet, length);
1298  } while(
1299  result != PJON_ACK &&
1300  (uint32_t)(PJON_MICROS() - start) <= duration_us
1301  );
1302  return result;
1303  };
1304 
1305 
1306  /* In single-socket mode and acting as initiator, connect and check for
1307  incoming packets from a specific device */
1308 
1309  uint16_t poll_receive(uint8_t remote_id)
1310  {
1311  /* Create connection if needed but only poll for incoming packet
1312  without delivering any */
1313  if(_single_socket) {
1314  if(!_server) {
1315 #ifdef ETCP_SINGLE_SOCKET_WITH_ACK
1316  return single_socket_transfer(_client_out, remote_id, true, NULL, 0);
1317 #else
1318  (void)remote_id; // Avoid "unused parameter" warning
1319  return PJON_FAIL;
1320 #endif
1321  }
1322  } else { // Just do an ordinary receive without using the id
1323  return receive();
1324  }
1325  return PJON_FAIL;
1326  };
1327 
1328 
1329  uint8_t get_id() const
1330  {
1331  return _local_id;
1332  };
1333 
1334 
1335  // Overridden functions below -----------------------------------------------
1336 
1337  // Connect to a server if needed, then read incoming package and send ACK
1338 
1339  uint16_t receive()
1340  {
1341  if(_server == NULL) { // Not listening for incoming connections
1342 #if defined(ETCP_SINGLE_SOCKET_WITH_ACK) || defined(ETCP_SINGLE_DIRECTION)
1343  int16_t remote_id = _remote_node_count == 1 ? _remote_id[0] : -1;
1344 #endif
1345  if(_single_socket) { // Single-socket mode.
1346  /* Only read from already established outgoing socket, or create
1347  connection if there is only one remote node configured (no doubt about
1348  which node to connect to). */
1349 #ifdef ETCP_SINGLE_SOCKET_WITH_ACK
1350  return single_socket_transfer(_client_out, remote_id, true, NULL, 0);
1351 #else
1352  return PJON_FAIL;
1353 #endif
1354  }
1355 #ifdef ETCP_SINGLE_DIRECTION
1356  else if (_initiate_both_sockets_in_same_direction && _initiator) {
1357  connect(remote_id);
1358  uint16_t result = receive(_client_in, false);
1359  disconnect_out_if_needed(PJON_ACK); // Do not disconnect if nothing to read
1360  disconnect_in_if_needed();
1361  return result;
1362  }
1363 #endif
1364  } else {
1365  // Accept new incoming connection if connection has been lost
1366  if(_single_socket) {
1367 #ifdef ETCP_SINGLE_SOCKET_WITH_ACK
1368  return single_socket_transfer(_client_in, -1, false, NULL, 0);
1369 #else
1370  return PJON_FAIL;
1371 #endif
1372  } else {
1373  // Accept incoming connection(s)
1374  if(!accept()) {
1375  return PJON_FAIL;
1376  }
1377 
1378  uint16_t result = receive(_client_in, !_keep_connection);
1379 
1380  disconnect_in_if_needed();
1381  return result;
1382  }
1383  }
1384  return PJON_FAIL;
1385  };
1386 
1387 
1388  uint16_t receive(uint32_t duration_us)
1389  {
1390  uint32_t start = PJON_MICROS();
1391  uint16_t result = PJON_FAIL;
1392  do {
1393  result = receive();
1394  if (result != PJON_ACK) {
1395  PJON_DELAY_MICROSECONDS(250);
1396  }
1397  } while(
1398  result != PJON_ACK &&
1399  (uint32_t)(PJON_MICROS() - start) <= duration_us
1400  );
1401  return result;
1402  };
1403 
1404 
1405  uint16_t send(
1406  uint8_t id,
1407  const uint8_t *packet,
1408  uint16_t length,
1409  uint32_t = 0 // timing_us
1410  )
1411  {
1412  // Special algorithm for single-socket transfers
1413  if(_single_socket)
1414 #ifdef ETCP_SINGLE_SOCKET_WITH_ACK
1415  return single_socket_transfer(
1416  _server ? _client_in : _client_out,
1417  id,
1418  _server ? false : true,
1419  packet,
1420  length
1421  );
1422 #else
1423  return PJON_FAIL;
1424 #endif
1425 
1426  // Connect or check that we are already connected to the correct server
1427  bool connected = false;
1428 #ifdef ETCP_SINGLE_DIRECTION
1429  if (_initiate_both_sockets_in_same_direction && !_initiator) {
1430  connected = accept();
1431  } else
1432 #endif
1433  connected = connect(id);
1434 
1435  // Send the packet and read PJON_ACK
1436  uint16_t result = PJON_FAIL;
1437  if(connected) {
1438  result = send(_client_out, id, packet, length);
1439  }
1440 
1441  // Disconnect
1442  disconnect_out_if_needed(result);
1443 
1444  return result;
1445  };
1446 
1447 
1448  uint8_t device_id()
1449  {
1450  return _local_id;
1451  };
1452 
1453 
1454  uint8_t acquire_id() // Not supported yet
1455  {
1456  return 0;
1457  };
1458 
1459 
1460  void set_error(link_error e)
1461  {
1462  _error = e;
1463  };
1464 
1465 
1466  void set_id(uint8_t id)
1467  {
1468  _local_id = id;
1469  };
1470 
1471 
1472  void set_receiver(link_receiver r, void *callback_object)
1473  {
1474  _receiver = r;
1475  _callback_object = callback_object;
1476  };
1477 
1478 
1479  void update() { };
1480 };
1481 
1482 #undef Serial
data
char data[MAX_PAYLOAD_SIZE+1]
Buffer for raw payload data.
Definition: MyMessage.h:654
TCPHelperClient
Definition: TCPHelper_POSIX.h:31
DummyPrint
Definition: EthernetLink.h:136
min
#define min(a, b)
min
Definition: MySensors.h:100
wait
void wait(const uint32_t waitingMS)
TmpBuffer
Definition: EthernetLink.h:219
TCPHelperServer
Definition: TCPHelper_POSIX.h:350