arduino-emulator
Loading...
Searching...
No Matches
Ethernet.h
1
2/*
3 Ethernet.h
4 Copyright (c) 2025 Phil Schatzmann. All right reserved.
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20#pragma once
21
22#include <arpa/inet.h> // for inet_pton
23#include <netdb.h> // for gethostbyname, struct hostent
24#include <unistd.h> // for close
25#include <memory> // This is the include you need
26
27#include "ArduinoLogger.h"
28#include "RingBufferExt.h"
29#include "SocketImpl.h"
30#include "SignalHandler.h"
31#include "api/Client.h"
32#include "api/Common.h"
33#include "api/IPAddress.h"
34
35namespace arduino {
36
37#define ETHERNET_DEFAULT_READ_TIMEOUT 200
38
39typedef enum {
40 WL_NO_SHIELD = 255,
41 WL_IDLE_STATUS = 0,
42 WL_NO_SSID_AVAIL,
43 WL_SCAN_COMPLETED,
44 WL_CONNECTED,
45 WL_CONNECT_FAILED,
46 WL_CONNECTION_LOST,
47 WL_DISCONNECTED
48} wl_status_t;
49
51public:
52 EthernetImpl() {
53 // Set some defaults
54 _macAddress[0] = 0xDE; _macAddress[1] = 0xAD; _macAddress[2] = 0xBE; _macAddress[3] = 0xEF; _macAddress[4] = 0xFE; _macAddress[5] = 0xED;
55 _localIP = IPAddress(192,168,1,177);
56 _subnetMask = IPAddress(255,255,255,0);
57 _gatewayIP = IPAddress(192,168,1,1);
58 _dnsServerIP = IPAddress(8,8,8,8);
59 _hardwareStatus = 1; // Assume present
60 _linkStatus = 2; // Assume linkON
61 _retransmissionCount = 8;
62 _retransmissionTimeout = 2000;
63 }
64
65 // Begin with MAC only
66 bool begin(uint8_t macAddress[6]) {
67 setMACAddress(macAddress);
68 return true;
69 }
70 // Begin with full config
71 bool begin(uint8_t macAddress[6], IPAddress localIP, IPAddress dnsServerIP, IPAddress gateway, IPAddress subnet) {
72 setMACAddress(macAddress);
73 setLocalIP(localIP);
74 setDnsServerIP(dnsServerIP);
75 setGatewayIP(gateway);
76 setSubnetMask(subnet);
77 return true;
78 }
79
80 // Returns the local IP address
81 IPAddress localIP() { return _localIP; }
82 // Returns the subnet mask
83 IPAddress subnetMask() { return _subnetMask; }
84 // Returns the gateway IP
85 IPAddress gatewayIP() { return _gatewayIP; }
86 // Returns the DNS server IP
87 IPAddress dnsServerIP() { return _dnsServerIP; }
88 // Returns the MAC address
89 void MACAddress(uint8_t* mac) { for (int i=0; i<6; ++i) mac[i] = _macAddress[i]; }
90 // Set the MAC address
91 void setMACAddress(const uint8_t* mac) { for (int i=0; i<6; ++i) _macAddress[i] = mac[i]; }
92 // Set the local IP
93 void setLocalIP(const IPAddress& ip) { _localIP = ip; }
94 // Set the subnet mask
95 void setSubnetMask(const IPAddress& mask) { _subnetMask = mask; }
96 // Set the gateway IP
97 void setGatewayIP(const IPAddress& ip) { _gatewayIP = ip; }
98 // Set the DNS server IP
99 void setDnsServerIP(const IPAddress& ip) { _dnsServerIP = ip; }
100 // Set retransmission count
101 void setRetransmissionCount(uint8_t count) { _retransmissionCount = count; }
102 // Set retransmission timeout
103 void setRetransmissionTimeout(uint16_t timeout) { _retransmissionTimeout = timeout; }
104
105 // Returns hardware status (1 = present, 0 = absent)
106 int hardwareStatus() { return _hardwareStatus; }
107 // Returns link status (2 = linkON, 1 = linkOFF, 0 = unknown)
108 int linkStatus() { return _linkStatus; }
109 // Init (returns true for compatibility)
110 bool init(uint8_t socketCount = 4) { _hardwareStatus = 1; return true; }
111 // Maintain (returns 0 for compatibility)
112 int maintain() { return 0; }
113
114protected:
115 uint8_t _macAddress[6];
116 IPAddress _localIP;
117 IPAddress _subnetMask;
118 IPAddress _gatewayIP;
119 IPAddress _dnsServerIP;
120 int _hardwareStatus = 1; // 1 = present, 0 = absent
121 int _linkStatus = 2; // 2 = linkON, 1 = linkOFF, 0 = unknown
122 uint8_t _retransmissionCount = 8;
123 uint16_t _retransmissionTimeout = 2000;
124};
125
126inline EthernetImpl Ethernet;
127
128class EthernetClient : public Client {
129 private:
130 static std::vector<EthernetClient*>& active_clients() {
131 static std::vector<EthernetClient*> clients;
132 return clients;
133 }
134 static void cleanupAll(int sig) {
135 for (auto* client : active_clients()) {
136 if (client) {
137 client->stop();
138 }
139 }
140 }
141
142 public:
145 p_sock = std::make_shared<SocketImpl>();
146 readBuffer = RingBufferExt(bufferSize);
147 writeBuffer = RingBufferExt(bufferSize);
148 registerCleanup();
149 active_clients().push_back(this);
150 }
151 EthernetClient(std::shared_ptr<SocketImpl> sock, int bufferSize = 256, long timeout = ETHERNET_DEFAULT_READ_TIMEOUT) {
152 if (sock) {
153 setTimeout(timeout);
154 this->bufferSize = bufferSize;
155 readBuffer = RingBufferExt(bufferSize);
156 writeBuffer = RingBufferExt(bufferSize);
157 p_sock = sock;
158 is_connected = p_sock->connected();
159 registerCleanup();
160 active_clients().push_back(this);
161 }
162 }
165 readBuffer = RingBufferExt(bufferSize);
166 writeBuffer = RingBufferExt(bufferSize);
167 p_sock = std::make_shared<SocketImpl>(socket);
168 is_connected = p_sock->connected();
169 }
170
171 // checks if we are connected - using a timeout
172 virtual uint8_t connected() override {
173 if (!is_connected) return false; // connect has failed
174 if (p_sock->connected()) return true; // check socket
175 long timeout = millis() + getConnectionTimeout();
176 uint8_t result = p_sock->connected();
177 while (result <= 0 && millis() < timeout) {
178 delay(200);
179 result = p_sock->connected();
180 }
181 return result;
182 }
183
184 // support conversion to bool
185 operator bool() override { return connected(); }
186
187 // opens a conection
188 virtual int connect(IPAddress ipAddress, uint16_t port) override {
189 String str = String(ipAddress[0]) + String(".") + String(ipAddress[1]) +
190 String(".") + String(ipAddress[2]) + String(".") +
191 String(ipAddress[3]);
192 this->address = ipAddress;
193 this->port = port;
194 return connect(str.c_str(), port);
195 }
196
197 // opens a connection
198 virtual int connect(const char* address, uint16_t port) override {
199 Logger.info(WIFICLIENT, "connect");
200 this->port = port;
201 if (connectedFast()) {
202 p_sock->close();
203 }
204 IPAddress adr = resolveAddress(address, port);
205 if (adr == IPAddress(0, 0, 0, 0)) {
206 is_connected = false;
207 return 0;
208 }
209 // performs the actual connection
210 String str = adr.toString();
211 Logger.info("Connecting to ", str.c_str());
212 p_sock->connect(str.c_str(), port);
213 is_connected = true;
214 return 1;
215 }
216
217 virtual size_t write(char c) { return write((uint8_t)c); }
218
219 // writes an individual character into the buffer. We flush the buffer when it
220 // is full
221 virtual size_t write(uint8_t c) override {
222 if (writeBuffer.availableToWrite() == 0) {
223 flush();
224 }
225 return writeBuffer.write(c);
226 }
227
228 virtual size_t write(const char* str, int len) {
229 return write((const uint8_t*)str, len);
230 }
231
232 // direct write - if we have anything in the buffer we write that out first
233 virtual size_t write(const uint8_t* str, size_t len) override {
234 flush();
235 return p_sock->write(str, len);
236 }
237
238 virtual int print(const char* str = "") {
239 int len = strlen(str);
240 return write(str, len);
241 }
242
243 virtual int println(const char* str = "") {
244 int len = strlen(str);
245 int result = write(str, len);
246 char eol[1];
247 eol[0] = '\n';
248 write(eol, 1);
249 return result;
250 }
251
252 // flush write buffer
253 virtual void flush() override {
254 Logger.debug(WIFICLIENT, "flush");
255
256 int flushSize = writeBuffer.available();
257 if (flushSize > 0) {
259 writeBuffer.read(rbuffer, flushSize);
260 p_sock->write(rbuffer, flushSize);
261 }
262 }
263
264 // provides the available bytes from the read buffer or from the socket
265 virtual int available() override {
266 Logger.debug(WIFICLIENT, "available");
267 if (readBuffer.available() > 0) {
268 return readBuffer.available();
269 }
270 long timeout = millis() + getTimeout();
271 int result = p_sock->available();
272 while (result <= 0 && millis() < timeout) {
273 delay(200);
274 result = p_sock->available();
275 }
276 return result;
277 }
278
279 // read via ring buffer
280 virtual int read() override {
281 int result = -1;
282 uint8_t c;
283 if (readBytes(&c, 1) == 1) {
284 result = c;
285 }
286 return result;
287 }
288
289 virtual size_t readBytes(char* buffer, size_t len) {
290 return read((uint8_t*)buffer, len);
291 }
292
293 virtual size_t readBytes(uint8_t* buffer, size_t len) {
294 return read(buffer, len);
295 }
296
297 // peeks one character
298 virtual int peek() override {
299 return p_sock->peek();
300 return -1;
301 }
302
303 // close the connection
304 virtual void stop() override { p_sock->close(); }
305
306 virtual void setInsecure() {}
307
308 int fd() { return p_sock->fd(); }
309
310 uint16_t remotePort() { return port; }
311
312 IPAddress remoteIP() { return address; }
313
314 virtual void setCACert(const char* cert) {
315 Logger.error(WIFICLIENT, "setCACert not supported");
316 }
317
318 void setConnectionTimeout(int32_t timeout) {
319 connectTimeout = timeout;
320 }
321
322 int32_t getConnectionTimeout() {
323 return connectTimeout;
324 }
325
326 protected:
327 const char* WIFICLIENT = "EthernetClient";
328 int32_t connectTimeout = 5000; // default timeout 5 seconds
329 std::shared_ptr<SocketImpl> p_sock = nullptr;
330 int bufferSize = 256;
331 RingBufferExt readBuffer;
332 RingBufferExt writeBuffer;
333 bool is_connected = false;
334 IPAddress address{0, 0, 0, 0};
335 uint16_t port = 0;
336
337 // resolves the address and returns sockaddr_in
338 IPAddress resolveAddress(const char* address, uint16_t port) {
339 struct sockaddr_in serv_addr4;
340 memset(&serv_addr4, 0, sizeof(serv_addr4));
341 serv_addr4.sin_family = AF_INET;
342 serv_addr4.sin_port = htons(port);
343 if (::inet_pton(AF_INET, address, &serv_addr4.sin_addr) <= 0) {
344 // Not an IP, try to resolve hostname
345 struct hostent* he = ::gethostbyname(address);
346 if (he == nullptr || he->h_addr_list[0] == nullptr) {
347 Logger.error(WIFICLIENT, "Hostname resolution failed");
348 serv_addr4.sin_addr.s_addr = 0;
349 } else {
350 memcpy(&serv_addr4.sin_addr, he->h_addr_list[0], he->h_length);
351 }
352 }
353 return IPAddress(serv_addr4.sin_addr.s_addr);
354 }
355
356 void registerCleanup() {
357 static bool signal_registered = false;
358 if (!signal_registered) {
359 SignalHandler::registerHandler(SIGINT, cleanupAll);
360 SignalHandler::registerHandler(SIGTERM, cleanupAll);
361 signal_registered = true;
362 }
363 }
364
365 int read(uint8_t* buffer, size_t len) override {
366 Logger.debug(WIFICLIENT, "read");
367 int result = 0;
368 long timeout = millis() + getTimeout();
369 result = p_sock->read(buffer, len);
370 while (result <= 0 && millis() < timeout) {
371 delay(200);
372 result = p_sock->read(buffer, len);
373 }
374 //}
375 char lenStr[16];
376 sprintf(lenStr, "%d", result);
377 Logger.debug(WIFICLIENT, "read->", lenStr);
378
379 return result;
380 }
381
382 bool connectedFast() { return is_connected; }
383};
384
385} // namespace arduino
Definition Client.h:27
Definition DMAPool.h:103
Definition Ethernet.h:128
Definition Ethernet.h:50
Definition IPAddress.h:43
Implementation of a Simple Circular Buffer. Instead of comparing the position of the read and write p...
Definition RingBufferExt.h:35
Definition String.h:53
We provide the WiFi class to simulate the Arduino WIFI. In in Linux we can expect that networking is ...
Definition CanMsg.cpp:31