Skip to content

Commit dfd5390

Browse files
committed
esp8266 ESC control example
1 parent d4f4bab commit dfd5390

File tree

14 files changed

+1366
-4
lines changed

14 files changed

+1366
-4
lines changed
Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
/********************
2+
Arduino generic menu system
3+
4+
Feb. 2019 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com
5+
6+
IMPORTANT!:
7+
this requires the data folder to be stored on esp8266 spiff
8+
Extra libraries should be present
9+
10+
arduinoWebSockets - https://github.com/Links2004/arduinoWebSockets
11+
ESP8266WiFi - https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WiFi
12+
ESP8266WebServer - https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WebServer
13+
14+
*/
15+
#include <Servo.h>
16+
17+
#include <menu.h>
18+
#include <menuIO/esp8266Out.h>
19+
#include <menuIO/xmlFmt.h>//to write a menu has html page
20+
#include <menuIO/serialIn.h>
21+
#include <menuIO/xmlFmt.h>//to write a menu has xml page
22+
#include <menuIO/jsonFmt.h>//to write a menu has xml page
23+
#ifndef ARDUINO_STREAMING
24+
#include <streamFlow.h>//https://github.com/neu-rah/streamFlow
25+
#else
26+
#include <Streaming.h>//https://github.com/scottdky/Streaming
27+
#endif
28+
//#include <menuIO/jsFmt.h>//to send javascript thru web socket (live update)
29+
#include <FS.h>
30+
#include <Hash.h>
31+
extern "C" {
32+
#include "user_interface.h"
33+
}
34+
35+
using namespace Menu;
36+
37+
#ifdef WEB_DEBUG
38+
// on debug mode I put aux files on external server to allow changes without SPIFF update
39+
// on this mode the browser MUST be instructed to accept cross domain files
40+
String xslt("http://neurux:8080/");
41+
#else
42+
String xslt("");
43+
#endif
44+
45+
menuOut& operator<<(menuOut& o,unsigned long int i) {
46+
o.print(i);
47+
return o;
48+
}
49+
menuOut& operator<<(menuOut& o,endlObj) {
50+
o.println();
51+
return o;
52+
}
53+
54+
//this version numbers MUST be the same as data/1.2
55+
#define CUR_VERSION "1.4"
56+
#define APName "EscControl"
57+
58+
#define ESCPIN 2
59+
Servo myservo;
60+
61+
#ifndef MENU_SSID
62+
#error "need to define WiFi SSID here"
63+
#define MENU_SSID "r-site.net"
64+
#endif
65+
#ifndef MENU_PASS
66+
#error "need to define WiFi password here"
67+
#define MENU_PASS ""
68+
#endif
69+
70+
// char wifiSSID[wifiSSIDLen+1];//=" ";
71+
// char wifiPwd [wifiPwdLen+1];//=" ";
72+
73+
const char* ssid = MENU_SSID;
74+
const char* password = MENU_PASS;
75+
const char* serverName="192.168.1.79";
76+
77+
#define HTTP_PORT 80
78+
#define WS_PORT 81
79+
#define USE_SERIAL Serial
80+
ESP8266WebServer server(80);
81+
WebSocketsServer webSocket(81);
82+
83+
#define MAX_DEPTH 2
84+
idx_t web_tops[MAX_DEPTH];
85+
PANELS(webPanels,{0,0,80,100});
86+
xmlFmt<esp8266_WebServerStreamOut> serverOut(server,web_tops,webPanels);
87+
jsonFmt<esp8266_WebServerStreamOut> jsonOut(server,web_tops,webPanels);
88+
jsonFmt<esp8266BufferedOut> wsOut(web_tops,webPanels);
89+
90+
int speed=0;//0 to 180
91+
result setEsc() {
92+
myservo.write(speed);
93+
}
94+
result armEsc() {
95+
Serial.println("arming ESC");
96+
serverOut<<"arming ESC<br/>";
97+
// arm the speed controller, modify as necessary for your ESC
98+
speed=0;
99+
setEsc();
100+
delay(1000); //delay 1 second, some speed controllers may need longer
101+
return proceed;
102+
}
103+
result stopEsc() {
104+
Serial.println("ESC stop");
105+
serverOut<<"ESC stop<br/>";
106+
speed=0;
107+
setEsc();
108+
return proceed;
109+
}
110+
111+
//the menu
112+
MENU(mainMenu,"Main menu",doNothing,noEvent,wrapStyle
113+
,OP("Stop",stopEsc,enterEvent)
114+
,OP("Arm",armEsc,enterEvent)
115+
,FIELD(speed,"Speed","%",0,100,10,1, setEsc, enterEvent, noStyle)
116+
,EXIT("Exit!")
117+
);
118+
119+
result idle(menuOut& o,idleEvent e) {
120+
//if (e==idling)
121+
Serial.println("suspended");
122+
o<<"suspended..."<<endl<<"press [select]"<<endl<<"to continue"<<endl<<(millis()%1000);
123+
return quit;
124+
}
125+
126+
template<typename T>//some utill to help us calculate array sizes (known at compile time)
127+
constexpr inline size_t len(T& o) {return sizeof(o)/sizeof(decltype(o[0]));}
128+
129+
//serial menu navigation
130+
MENU_OUTLIST(out,&serverOut);
131+
serialIn serial(Serial);
132+
NAVROOT(nav,mainMenu,MAX_DEPTH,serial,out);
133+
134+
//xml+http navigation control
135+
noInput none;//web uses its own API
136+
menuOut* web_outputs[]={&serverOut};
137+
outputsList web_out(web_outputs,len(web_outputs));
138+
navNode web_cursors[MAX_DEPTH];
139+
navRoot webNav(mainMenu, web_cursors, MAX_DEPTH, none, web_out);
140+
141+
//json+http navigation control
142+
menuOut* json_outputs[]={&jsonOut};
143+
outputsList json_out(json_outputs,len(json_outputs));
144+
navNode json_cursors[MAX_DEPTH];
145+
navRoot jsonNav(mainMenu, json_cursors, MAX_DEPTH, none, json_out);
146+
147+
//websockets navigation control
148+
menuOut* ws_outputs[]={&wsOut};
149+
outputsList ws_out(ws_outputs,len(ws_outputs));
150+
navNode ws_cursors[MAX_DEPTH];
151+
navRoot wsNav(mainMenu, ws_cursors, MAX_DEPTH, none, ws_out);
152+
153+
//config myOptions('*','-',defaultNavCodes,false);
154+
155+
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
156+
switch(type) {
157+
case WStype_DISCONNECTED:
158+
//USE_SERIAL.printf("[%u] Disconnected!\n", num);
159+
break;
160+
case WStype_CONNECTED: {
161+
IPAddress ip = webSocket.remoteIP(num);
162+
//USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
163+
webSocket.sendTXT(num, "console.log('ArduinoMenu Connected')");
164+
}
165+
break;
166+
case WStype_TEXT: {
167+
//USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
168+
// nav.async((const char*)payload);//this is slow!!!!!!!!
169+
__trace(Serial.printf("[%u] get Text: %s\n", num, payload));
170+
char*s=(char*)payload;
171+
_trace(Serial<<"serve websocket menu"<<endl);
172+
wsOut.response.remove(0);
173+
wsOut<<"{\"output\":\"";
174+
wsNav.async((const char*)payload);
175+
wsOut<<"\",\n\"menu\":";
176+
wsNav.doOutput();
177+
wsOut<<"\n}";
178+
webSocket.sendTXT(num,wsOut.response);
179+
// wsOut.response.remove(0);
180+
// jsonEnd();
181+
} break;
182+
case WStype_BIN: {
183+
USE_SERIAL<<"[WSc] get binary length:"<<length<<"[";
184+
for(int c=0;c<length;c++) {
185+
USE_SERIAL.print(*(char*)(payload+c),HEX);
186+
USE_SERIAL.write(',');
187+
}
188+
USE_SERIAL<<"]"<<endl;
189+
uint16_t id=*(uint16_t*) payload++;
190+
idx_t len=*((idx_t*)++payload);
191+
idx_t* pathBin=(idx_t*)++payload;
192+
const char* inp=(const char*)(payload+len);
193+
//Serial<<"id:"<<id<<endl;
194+
if (id==nav.active().hash()) {
195+
//Serial<<"id ok."<<endl;Serial.flush();
196+
//Serial<<"input:"<<inp<<endl;
197+
//StringStream inStr(inp);
198+
//while(inStr.available())
199+
nav.doInput(inp);
200+
webSocket.sendTXT(num, "binBusy=false;");//send javascript to unlock the state
201+
} //else Serial<<"id not ok!"<<endl;
202+
//Serial<<endl;
203+
}
204+
break;
205+
default:break;
206+
}
207+
}
208+
209+
void pageStart() {
210+
_trace(Serial<<"pasgeStart!"<<endl);
211+
serverOut<<"HTTP/1.1 200 OK\r\n"
212+
<<"Content-Type: text/xml\r\n"
213+
<<"Connection: close\r\n"
214+
<<"Expires: 0\r\n"
215+
<<"\r\n";
216+
serverOut<<"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n"
217+
"<?xml-stylesheet type=\"text/xsl\" href=\"";
218+
serverOut<<xslt;
219+
serverOut<<CUR_VERSION"/device.xslt";
220+
serverOut<<"\"?>\r\n<menuLib"
221+
#ifdef WEB_DEBUG
222+
<<" debug=\"yes\""
223+
#endif
224+
<<" host=\"";
225+
serverOut.print(APName);
226+
serverOut<<"\">\r\n<sourceURL ver=\"" CUR_VERSION "/\">";
227+
if (server.hasHeader("host"))
228+
serverOut.print(server.header("host"));
229+
else
230+
serverOut.print(APName);
231+
serverOut<<"</sourceURL>";
232+
}
233+
234+
void pageEnd() {
235+
serverOut<<"</menuLib>";
236+
server.client().stop();
237+
}
238+
239+
void jsonStart() {
240+
_trace(Serial<<"jsonStart!"<<endl);
241+
serverOut<<"HTTP/1.1 200 OK\r\n"
242+
<<"Content-Type: application/json; charset=utf-8\r\n"
243+
<<"Connection: close\r\n"
244+
<<"Expires: 0\r\n"
245+
<<"\r\n";
246+
}
247+
248+
void jsonEnd() {
249+
server.client().stop();
250+
}
251+
252+
bool handleMenu(navRoot& nav){
253+
_trace(
254+
uint32_t free = system_get_free_heap_size();
255+
Serial.print(F("free memory:"));
256+
Serial.print(free);
257+
Serial.print(F(" handleMenu "));
258+
Serial.println(server.arg("at").c_str());
259+
);
260+
String at=server.arg("at");
261+
bool r;
262+
r=nav.async(server.hasArg("at")?at.c_str():"/");
263+
return r;
264+
}
265+
266+
//redirect to version folder,
267+
//this allows agressive caching with no need to cache reset on version change
268+
auto mainPage= []() {
269+
_trace(Serial<<"serving main page from root!"<<endl);
270+
server.sendHeader("Location", CUR_VERSION "/index.html", true);
271+
server.send ( 302, "text/plain", "");
272+
if (server.hasArg("at"))
273+
nav.async(server.arg("at").c_str());
274+
};
275+
276+
void setup(){
277+
Serial.begin(115200);
278+
while(!Serial);
279+
280+
myservo.attach(ESCPIN);
281+
282+
webNav.canExit=false;
283+
jsonNav.canExit=false;
284+
wsNav.canExit=false;
285+
286+
for(uint8_t t = 4; t > 0; t--) {
287+
Serial.printf("[SETUP] BOOT WAIT %d...\n", t);
288+
Serial.flush();
289+
delay(1000);
290+
}
291+
292+
Serial.println();
293+
Serial.println("Arduino menu webserver example");
294+
295+
SPIFFS.begin();
296+
297+
Serial.print("Connecting to ");
298+
Serial.println(ssid);
299+
300+
WiFi.begin(ssid, password);
301+
// Wait for connection
302+
while (WiFi.status() != WL_CONNECTED) {
303+
delay(500);
304+
Serial.print(".");
305+
}
306+
webSocket.begin();
307+
Serial.println("");
308+
webSocket.onEvent(webSocketEvent);
309+
Serial.println("Connected.");
310+
Serial.print("IP address: ");
311+
Serial.println(WiFi.localIP());
312+
313+
webSocket.begin();
314+
315+
nav.idleTask=idle;//point a function to be used when menu is suspended
316+
317+
server.on("/",HTTP_GET,mainPage);
318+
319+
//menu xml server over http
320+
server.on("/menu", HTTP_GET, []() {
321+
pageStart();
322+
serverOut<<"<output state=\""<<((int)&webNav.idleTask)<<"\"><![CDATA[";
323+
_trace(Serial<<"output count"<<webNav.out.cnt<<endl);
324+
handleMenu(webNav);//do navigation (read input) and produce output messages or reports
325+
serverOut<<"]]></output>";
326+
webNav.doOutput();
327+
pageEnd();
328+
});
329+
330+
//menu json server over http
331+
server.on("/json", HTTP_GET, []() {
332+
_trace(Serial<<"json request!"<<endl);
333+
jsonStart();
334+
serverOut<<"{\"output\":\"";
335+
handleMenu(jsonNav);
336+
serverOut<<"\",\n\"menu\":";
337+
jsonNav.doOutput();
338+
serverOut<<"\n}";
339+
jsonEnd();
340+
});
341+
342+
server.begin();
343+
Serial.println("HTTP server started");
344+
Serial.println("Serving ArduinoMenu example.");
345+
#ifdef MENU_DEBUG
346+
server.serveStatic("/", SPIFFS, "/","max-age=30");
347+
#else
348+
server.serveStatic("/", SPIFFS, "/","max-age=31536000");
349+
#endif
350+
}
351+
352+
void loop(void){
353+
wsOut.response.remove(0);//clear websocket json buffer
354+
webSocket.loop();
355+
server.handleClient();
356+
delay(1);
357+
}

0 commit comments

Comments
 (0)