Project

General

Profile

Action Item #496 ยป ESP8266IoT.ino

Currently named "ESP8266IoT" but will be changed later. - Hammel, 21 Feb 2016 17:17

 
1
/*******************************************************************************
2
 * PiBox IoT: ESP8266 controller
3
 *
4
 * ESP8266IoT.ino:  Provide RESTful interface to control this device.
5
 *
6
 * License: see LICENSE file
7
 ******************************************************************************
8
 * Notes:
9
 * Being a C file (and not Java), please adhere to the 80 character line
10
 * length standard in this file.
11
 ******************************************************************************/
12
#include <ESP8266WiFi.h>
13
#include <WiFiClient.h>
14
#include <ESP8266WebServer.h>
15
#include <ESP8266mDNS.h>
16
#include <WiFiManager.h>
17

    
18
// This is the web server that accepts inbound messages.
19
ESP8266WebServer server(80);
20

    
21
// Handle auto-connection
22
// Globally scoped so API can reset, if necessary.
23
WiFiManager wifiManager;
24

    
25
// Default WiFi config
26
String ssid = "Reset";
27
// const char* ssid = "MuseB";
28
// const char* password = "6953af02c3";
29

    
30
// Device controls stepper motor with 4 settings.
31
const String cmds[] = {
32
    "open",         // Open the controlled device
33
    "close",        // Close the controlled device
34
    "half",         // Open the controlled device half
35
    "quarter",      // Open the controlled device one-quarter
36
    "reset"         // Reset WiFi configuration - must be protected!
37
};
38

    
39
/*
40
 * ========================================================================
41
 * Command Handlers
42
 * ========================================================================
43
 */
44

    
45
/*
46
 * ========================================================================
47
 * Name:   reset
48
 * Prototype:  int reset( void )
49
 *            
50
 * Description:
51
 * Reset WiFi connection, allowing reconfiguration.
52
 *            
53
 * Notes:
54
 * Never returns because it calls ESP.reset().
55
 * ========================================================================
56
 */ 
57
void reset() 
58
{
59
    wifiManager.resetSettings();
60
    delay(3000);
61
    ESP.reset();
62
    delay(5000);
63
}
64

    
65
/*
66
 * ========================================================================
67
 * URI handling
68
 * ========================================================================
69
 */
70

    
71
/*
72
 * ========================================================================
73
 * Name:   validate
74
 * Prototype:  int validate( String domain, String version, String cmd )
75
 *            
76
 * Description:
77
 * Validate inbound request. 
78
 *            
79
 * Input Arguments:
80
 * String  domain     The domain requested.  
81
 * String  version    The API version requested.  
82
 * String  cmd        The command requested.
83
 *
84
 * Returns:
85
 * 1 if the request validates.
86
 * 0 if the request is invalid.
87
 * ========================================================================
88
 */ 
89
int validate(String domain, String version, String cmd) 
90
{
91
    String message;
92
    int    i;
93

    
94
    // This device is in the IronMan (im) domain.
95
    if ( !domain.equals("im") )
96
    {
97
        message = "Invalid domain: ";
98
        message += domain;
99
        Serial.println(message);
100
        return(0);
101
    }
102

    
103
    // We support only the first version of the API (so far).
104
    if ( !version.equals("1") )
105
    {
106
        message = "Unsupported version: ";
107
        message += version;
108
        Serial.println(message);
109
        return(0);
110
    }
111

    
112
    // Run through list of valid commands
113
    for(i=0; i<4; i++)
114
    {
115
        if ( cmd.equals(cmds[i]) )
116
            return(1);
117
    }
118
    message = "Unsupported command: ";
119
    message += cmd;
120
    Serial.println(message);
121
    return(0);
122
}
123

    
124
/*
125
 * ========================================================================
126
 * Name:   handleCommand
127
 * Prototype:  int handleCommand(  String cmd, String args )
128
 *            
129
 * Description:
130
 * Handle valid commands.  This is where motors are controlled.
131
 * ========================================================================
132
 */ 
133
void handleCommand( String cmd, String args ) 
134
{
135
    if ( cmd.equals("reset") )
136
    {
137
        // Should validate authorization here, probably.
138
        reset();
139

    
140
        // Should never get here due to call to ESP.reset() in handler.
141
        return;
142
    }
143

    
144
    String message = "Handled command\n\n";
145
    message  = "Command: ";
146
    message += cmd;
147
    message += "\targs: ";
148
    message += args;
149
    Serial.println(message);
150
    server.send(404, "text/plain", message);
151
}
152

    
153
/*
154
 * ========================================================================
155
 * Name:   handleNotFound
156
 * Prototype:  int handleNotFound( void )
157
 *            
158
 * Description:
159
 * Handle invalid requests.  This sends a text reply, which may not be
160
 * suitable for the remote caller.
161
 * ========================================================================
162
 */ 
163
void handleNotFound() 
164
{
165
    String message = "Invalid command\n\n";
166
    message += "URI: ";
167
    message += server.uri();
168
    message += "\nMethod: ";
169
    message += (server.method() == HTTP_GET)?"GET":"POST";
170
    message += "\nArguments: ";
171
    message += server.args();
172
    message += "\n";
173
    for (uint8_t i=0; i<server.args(); i++)
174
    {
175
        message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
176
    }
177
    server.send(404, "text/plain", message);
178
}
179

    
180
/*
181
 * ========================================================================
182
 * Name:   handleREST
183
 * Prototype:  int handleREST( void )
184
 *            
185
 * Description:
186
 * Handle all RESTful requests.  This parses out the expected 
187
 * domain, version and command (with any arguments), validates them and
188
 * then calls the appropriate command handler.
189
 *
190
 * Notes:
191
 * URI format for this API: /domain/version/cmd/args
192
 * We don't yet handle GET variables.
193
 * We don't support PUT or any other HTTP commands.  The command and 
194
 * it's arguments must exist in the URI only.
195
 * ========================================================================
196
 */ 
197
int handleREST() 
198
{
199
    String message;
200
    String path = server.uri();
201
    int imIdx   = -1;
202
    int verIdx  = -1;
203
    int cmdIdx  = -1;
204
    String prefix, im, ver, cmd, args;
205

    
206
    prefix = path.substring(0,1);
207
    if ( !path.startsWith("/") )
208
    {
209
        Serial.println("Missing leading /");
210
        handleNotFound();
211
        return(1);
212
    }
213

    
214
    // Get path separator indices
215
    imIdx = path.indexOf("/", 1);
216
    if ( imIdx != -1 )
217
        verIdx = path.indexOf("/", imIdx+1);
218
    if ( verIdx != -1 )
219
        cmdIdx = path.indexOf("/", verIdx+1);
220
    if ( (imIdx == -1) || (verIdx == -1) || (cmdIdx == -1) )
221
    {
222
        message = "Missing required REST components: uri = ";
223
        message += server.uri();
224
        Serial.println(message);
225
        handleNotFound();
226
        return(1);
227
    }
228

    
229
    // Parse 
230
    im = path.substring(1,imIdx);
231
    ver = path.substring(imIdx+1,verIdx);
232
    cmd = path.substring(verIdx+1,cmdIdx);
233
    args = path.substring(cmdIdx+1);
234

    
235
    // Test validity of message
236
    if ( !validate( im, ver, cmd ) )
237
    {
238
        handleNotFound();
239
        return(1);
240
    }
241

    
242
    // Request is valid.  Handle command.
243
    handleCommand( cmd, args );
244
}
245

    
246
// Handle root directory access
247
void handleRoot() 
248
{
249
    server.send(200, "text/plain", "hello from esp8266!");
250
}
251

    
252
/*
253
 * ========================================================================
254
 * Name:   setup
255
 * Prototype:  void setup( void )
256
 *            
257
 * Description:
258
 * Initialization function; required by Arduino enviroment.
259
 * This function handles setup of WiFi connections and configures
260
 * URI handlers for the web server.
261
 * ========================================================================
262
 */ 
263
void configModeCallback(WiFiManager *myWiFiManager)
264
{
265
    Serial.println("Entered config mode");
266
    Serial.println(WiFi.softAPIP());
267
    //if you used auto generated SSID, print it
268
    Serial.println(myWiFiManager->getConfigPortalSSID());
269
}
270

    
271
/*
272
 * ========================================================================
273
 * Name:   setup
274
 * Prototype:  void setup( void )
275
 *            
276
 * Description:
277
 * Initialization function; required by Arduino enviroment.
278
 * This function handles setup of WiFi connections and configures
279
 * URI handlers for the web server.
280
 * ========================================================================
281
 */ 
282
void setup(void)
283
{
284

    
285
    Serial.begin(115200);
286

    
287
    // Artifical wait - to get serial console connected.
288
    delay(5000);
289

    
290
    // Set callback to be notified when connection to last configured
291
    // remote AP fails.
292
    wifiManager.setAPCallback(configModeCallback);
293

    
294
#if 0
295
    // Use this to reset the board to it's default state.
296
    // You have to uncomment it, build and upload, run it,
297
    // Then after the board resets once, comment it out and
298
    // build/upload again.
299
    if ( ssid.equals("Reset") )
300
    {
301
        ssid = "NoReset";
302
        reset();
303
    }
304
#endif
305

    
306
    // Tries to connect with last known settings.
307
    // If connection fails, starts an access point with no password
308
    // and waits for configuration.
309
    if (!wifiManager.autoConnect("imiot")) 
310
    {
311
        Serial.println("Failed connection, we should reset as see if it connects");
312
        delay(3000);
313
        ESP.reset();
314
        delay(5000);
315
    }
316
    Serial.println("Connected to AP.");
317

    
318
    // Without WiFiManager, this would do the connection for us.
319
    // WiFi.begin(ssid, password);
320
    // Serial.println("");
321

    
322
    // Wait for connection
323
    // while (WiFi.status() != WL_CONNECTED) 
324
    // {
325
    //     delay(500);
326
    //     Serial.print(".");
327
    // }
328

    
329
    Serial.println("");
330
    Serial.print("Connected to ");
331
    Serial.println(WiFi.SSID());
332
    Serial.print("IP address: ");
333
    Serial.println(WiFi.localIP());
334

    
335
    // Do we need this for IoT?
336
#if 0
337
    if (MDNS.begin("esp8266")) 
338
    {
339
        Serial.println("MDNS responder started");
340
    }
341
#endif
342

    
343
    // Setup web server URI handlers
344
    server.on("/", handleRoot);
345
    server.onNotFound(handleREST);
346

    
347
    // Sert the web server.
348
    server.begin();
349
    Serial.println("HTTP server started");
350
}
351

    
352
/*
353
 * ========================================================================
354
 * Name:   loop
355
 * Prototype:  void loop( void )
356
 *            
357
 * Description:
358
 * Main loop function; required by Arduino enviroment.
359
 * This function uses the web server API to spin, waiting for inbound
360
 * connections.  It then calls the appropriate handlers.
361
 * ========================================================================
362
 */ 
363
void loop(void)
364
{
365
    server.handleClient();
366
}
    (1-1/1)