Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
ESPWifiClient.hxx
Go to the documentation of this file.
1
36#ifndef _UTILS_ESPWIFICLIENT_HXX_
37#define _UTILS_ESPWIFICLIENT_HXX_
38
39extern "C" {
40#include "ets_sys.h"
41#include <ip_addr.h>
42#include <user_interface.h>
43#include <osapi.h>
44#include <espconn.h>
45}
46
47#include "utils/Singleton.hxx"
48#include "utils/Hub.hxx"
50
55class ESPWifiClient : public Singleton<ESPWifiClient>, private HubPort
56{
57public:
71 ESPWifiClient(const string &ssid, const string &password, CanHubFlow *hub,
72 const string &hostname, int port, unsigned send_buf_size,
73 std::function<void()> connect_callback)
74 : HubPort(hub->service())
75 , host_(hostname)
76 , port_(port)
77 , sendPending_(0)
78 , sendBlocked_(0)
79 , timerPending_(0)
80 , sendBuf_((uint8_t*)malloc(send_buf_size))
81 , bufSize_(send_buf_size)
82 , hub_(hub)
83 , gcHub_(hub_->service())
84 , connectCallback_(std::move(connect_callback))
85 {
86
87 wifi_set_opmode_current(STATION_MODE);
88 memcpy(&stationConfig_.ssid, ssid.c_str(), ssid.size() + 1);
89 memcpy(&stationConfig_.password, password.c_str(), password.size() + 1);
90 wifi_station_set_config(&stationConfig_);
91 wifi_set_event_handler_cb(&static_wifi_callback);
92 wifi_station_connect(); // may be avoided if the constructor is still
93 // called in the user_init.
94 }
95
98 static void static_wifi_callback(System_Event_t *evt)
99 {
100 os_printf("%s: %d\n", __FUNCTION__, evt->event);
101
102 switch (evt->event)
103 {
104 case EVENT_STAMODE_CONNECTED:
105 {
106 os_printf("connect to ssid %s, channel %d\n",
107 evt->event_info.connected.ssid,
108 evt->event_info.connected.channel);
109 break;
110 }
111
112 case EVENT_STAMODE_DISCONNECTED:
113 {
114 os_printf("disconnect from ssid %s, reason %d\n",
115 evt->event_info.disconnected.ssid,
116 evt->event_info.disconnected.reason);
117
118 system_deep_sleep_set_option(0);
119 system_deep_sleep(60 * 1000 * 1000); // 60 seconds
120 break;
121 }
122
123 case EVENT_STAMODE_GOT_IP:
124 {
125 /*
126 os_printf("ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR,
127 IP2STR(evt->event_info.got_ip.ip),
128 IP2STR(evt->event_info.got_ip.mask),
129 IP2STR(evt->event_info.got_ip.gw));
130 os_printf("\n");*/
131
132 Singleton<ESPWifiClient>::instance()->do_dns_lookup();
133 break;
134 }
135
136 default:
137 {
138 break;
139 }
140 }
141 }
142
145 {
146 espconn_gethostbyname(&conn_, host_.c_str(), &targetIp_, dns_done);
147 }
148
155 static void dns_done(const char *name, ip_addr_t *ipaddr, void *arg)
156 {
157 if (ipaddr == NULL)
158 {
159 os_printf("DNS lookup failed\n");
160 wifi_station_disconnect();
161 return;
162 }
163 Singleton<ESPWifiClient>::instance()->do_connect(ipaddr);
164 }
165
168 void do_connect(ip_addr_t *ipaddr)
169 {
170 os_printf("Connecting to %s:%d...\n", host_.c_str(), port_);
171
172 conn_.type = ESPCONN_TCP;
173 conn_.state = ESPCONN_NONE;
174 conn_.proto.tcp = &tcp_;
175 conn_.proto.tcp->local_port = espconn_port();
176 conn_.proto.tcp->remote_port = port_;
177 memcpy(conn_.proto.tcp->remote_ip, &ipaddr->addr, 4);
178
179 espconn_regist_connectcb(&conn_, static_tcp_connected);
180 espconn_regist_disconcb(&conn_, static_tcp_disconnected);
181
182 espconn_connect(&conn_);
183 }
184
186 static void static_tcp_connected(void *arg)
187 {
188 os_printf("%s\n", __FUNCTION__);
189 Singleton<ESPWifiClient>::instance()->tcp_connected();
190 }
191
194 {
195 gcAdapter_.reset(
197 espconn_regist_recvcb(&conn_, static_data_received);
198 espconn_regist_sentcb(&conn_, static_data_sent);
199 gcHub_.register_port(this);
201 }
202
204 static void static_tcp_disconnected(void *arg)
205 {
206 os_printf("%s\n", __FUNCTION__);
207 Singleton<ESPWifiClient>::instance()->tcp_disconnected();
208 }
209
212 {
214 gcAdapter_->shutdown();
215 wifi_station_disconnect();
216 }
217
223 static void static_data_received(void *arg, char *pdata, unsigned short len)
224 {
225 Singleton<ESPWifiClient>::instance()->data_received(pdata, len);
226 }
227
230 static void static_data_sent(void *arg)
231 {
233 }
234
240 void data_received(char *pdata, unsigned short len)
241 {
242 auto *b = gcHub_.alloc();
243 b->data()->skipMember_ = this;
244 b->data()->assign(pdata, len);
245 gcHub_.send(b);
246 }
247
248private:
250 Action entry() override
251 {
252 if (sendPending_)
253 {
254 sendBlocked_ = 1;
255 // Will call again once the wifi notification comes back.
256 return wait();
257 }
258 if (message()->data()->size() > bufSize_)
259 {
260 if (bufEnd_ > 0)
261 {
262 // Cannot copy the data to the buffer. Must send separately.
263 send_buffer();
264 return again();
265 }
266 else
267 {
268 sendPending_ = 1;
269 sendBlocked_ = 1; // will cause notify.
270 espconn_sent(&conn_, (uint8 *)message()->data()->data(),
271 message()->data()->size());
273 }
274 }
275 if (message()->data()->size() > bufSize_ - bufEnd_)
276 {
277 // Doesn't fit into the current buffer.
278 send_buffer();
279 return again();
280 }
281 // Copies the data into the buffer.
282 memcpy(sendBuf_ + bufEnd_, message()->data()->data(),
283 message()->data()->size());
284 bufEnd_ += message()->data()->size();
285 release();
286 // Decides whether to send off the buffer now.
287 if (!queue_empty())
288 {
289 // let's process more of the queue
290 return exit();
291 }
292 if (!timerPending_)
293 {
294 timerPending_ = 1;
295 bufferTimer_.start(MSEC_TO_NSEC(3));
296 }
297 return exit();
298 }
299
301 void timeout()
302 {
303 timerPending_ = 0;
304 if (!sendPending_) {
305 send_buffer();
306 }
307 }
308
314 {
315 return release_and_exit();
316 }
317
320 {
321 espconn_sent(&conn_, sendBuf_, bufEnd_);
322 sendPending_ = 1;
323 }
324
327 {
328 sendPending_ = 0;
329 bufEnd_ = 0;
330 if (sendBlocked_)
331 {
332 sendBlocked_ = 0;
333 notify();
334 }
335 }
336
339 class BufferTimer : public ::Timer
340 {
341 public:
344 : Timer(parent->service()->executor()->active_timers())
345 , parent_(parent)
346 {
347 }
348
350 long long timeout() override
351 {
352 parent_->timeout();
353 return NONE;
354 }
355 private:
357 } bufferTimer_{this};
358
360 struct station_config stationConfig_;
362 struct espconn conn_;
364 esp_tcp tcp_;
365
367 ip_addr_t targetIp_;
369 string host_;
371 int port_;
373 uint16_t sendPending_ : 1;
376 uint16_t sendBlocked_ : 1;
379 uint16_t timerPending_ : 1;
380
382 uint16_t bufEnd_ : 16;
384 uint8_t *sendBuf_;
386 unsigned bufSize_;
393 std::unique_ptr<GCAdapterBase> gcAdapter_;
396 std::function<void()> connectCallback_;
397};
398
399#endif // _UTILS_ESPWIFICLIENT_HXX_
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
Timer that triggers the parent flow when expiring.
long long timeout() override
callback when the timer expires.
BufferTimer(ESPWifiClient *parent)
Constructor.
ESPWifiClient * parent_
parent who owns *this.
Uses the ESPConn API on the ESP8266 wifi-enabled MCU to connect to a wifi base station,...
uint16_t sendBlocked_
True when we are waiting for a notification from the TCP stack send done callback.
struct espconn conn_
IP (including DNS) connection handle.
void do_dns_lookup()
Initiates the DNS lookup of the target host.
Action send_done()
Callback state when we are sending directly off of the input buffer becuase the data payload is too m...
uint16_t bufEnd_
Offset in sendBuf_ of the first unused byte.
struct station_config stationConfig_
Configuration of the access pint we are connecting to.
static void static_data_received(void *arg, char *pdata, unsigned short len)
Callback for incoming data.
static void dns_done(const char *name, ip_addr_t *ipaddr, void *arg)
Callback when the DNS lookup is completed.
ESPWifiClient(const string &ssid, const string &password, CanHubFlow *hub, const string &hostname, int port, unsigned send_buf_size, std::function< void()> connect_callback)
Creates a wifi+TCP client connection via the ESPConn API.
std::unique_ptr< GCAdapterBase > gcAdapter_
Transcoder bridge from CAN to GridConnect protocol.
uint16_t timerPending_
True when there is a send timer running with the assembly buffer being not full.
unsigned bufSize_
How many bytes are there in the send buffer.
Action entry() override
Sending base state.
void tcp_disconnected()
Callback when the TCP connection is lost.
void send_buffer()
Writes all bytes that are in the send buffer to the TCP socket.
static void static_wifi_callback(System_Event_t *evt)
Callback when an event happens on the wifi port.
int port_
Port numer we are connecting to on the target host.
void timeout()
Called from the timer to signal sending off the buffer's contents.
CanHubFlow * hub_
CAN hub to send incoming packets to and to receive outgoing packets from.
void do_connect(ip_addr_t *ipaddr)
Connects to the specific IP address.
std::function< void()> connectCallback_
Application level callback function to call when the connection has been successfully established.
HubFlow gcHub_
String-typed hub for the gridconnect-rendered packets.
esp_tcp tcp_
TCP connection handle.
void tcp_connected()
Callback when the TCP connection is established.
static void static_tcp_disconnected(void *arg)
Callback when the TCP connection is lost.
uint16_t sendPending_
True when the TCP stack is busy.
string host_
Target host we are trying to connect to.
static void static_tcp_connected(void *arg)
Callback when the TCP connection is established.
static void static_data_sent(void *arg)
Callback when data that was requested to be sent has completed sending.
ip_addr_t targetIp_
IP address of the target host.
uint8_t * sendBuf_
Temporarily stores outgoing data until the TCP stack becomes free.
void data_received(char *pdata, unsigned short len)
Callback when incoming data is received.
void data_sent()
Callback from the TCP stack when the data send has been completed.
MessageType * alloc()
Synchronously allocates a message buffer from the pool of this flow.
static GCAdapterBase * CreateGridConnectAdapter(HubFlow *gc_side, CanHubFlow *can_side, bool double_bytes)
This function connects an ASCII (GridConnect-format) CAN adapter to a binary CAN adapter,...
void register_port(port_type *port)
Adds a new port.
Definition Hub.hxx:167
void unregister_port(port_type *port)
Removes a previously added port.
Definition Hub.hxx:174
Singleton class.
Definition Singleton.hxx:65
static T * instance()
Definition Singleton.hxx:77
Return type for a state flow callback.
Service * service()
Return a pointer to the service I am bound to.
Action wait()
Wait for an asynchronous call.
Action again()
Call the current state again via call_immediately.
Action wait_and_call(Callback c)
Wait for resource to become available before proceeding to next state.
void notify() override
Wakeup call arrived. Schedules *this on the executor.
Action exit()
Terminates the processing of this flow.
Action release_and_exit()
Terminates the processing of the current message.
State flow with a given typed input queue.
A timer that can schedule itself to run on an executor at specified times in the future.
Definition Timer.hxx:134
@ NONE
Do not restart the timer.
Definition Timer.hxx:161
void release() OVERRIDE
Unrefs the current buffer.
void send(MessageType *msg, unsigned priority=UINT_MAX) OVERRIDE
Sends a message to the state flow for processing.
bool queue_empty() OVERRIDE
#define MSEC_TO_NSEC(_msec)
Convert a millisecond value to a nanosecond value.
Definition os.h:268