Statistics
| Branch: | Revision:

imlightsw / PiBox / imlightsw.ino @ 7d332357

History | View | Annotate | Download (26.2 KB)

1
/*******************************************************************************
2
 * PiBox IoT: ESP8266 controller
3
 *
4
 * imlightsw.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 <stdio.h>
13
#include <FS.h>
14
// #include <EEPROM.h>
15

    
16
#include <ESP8266WiFi.h>
17
#include <WiFiClient.h>
18
#include <ESP8266WebServer.h>
19
#include <ESP8266HTTPClient.h>
20
#include <WiFiManager.h>
21
#include <WiFiUdp.h>
22
#include <AES.h>
23
#include <Timer.h>
24
#include <aJSON.h>
25

    
26
/*
27
 * If defined, use the serial output instead of the Blue LED pin.
28
 */
29
#ifdef USE_SERIAL
30
#define PRINTLN(a)      Serial.println(a);
31
#define PRINT(a)        Serial.print(a);
32
#define SERIAL_BEGIN(a) Serial.begin(a);
33
#define SETBLUELED(a)
34
#else
35
#define PRINTLN(a)
36
#define PRINT(a)
37
#define SERIAL_BEGIN(a)
38
#define SETBLUELED(a)   setBlueLed(a)
39
#endif
40

    
41
/*
42
 * Blink Patterns
43
 */
44
#define BP_CONNECTED            blinkPattern(4,1000)
45
#define BP_CONFIG_MODE          blinkPattern(2,500)
46
#define BP_CONNECT_MODE         blinkPattern(5,125)
47
#define BP_PAIR1_MODE           blinkPattern(4,150)
48
#define BP_PAIR2_MODE           blinkPattern(4,750)
49

    
50
/*
51
 * Filename for saving credentials.
52
 */
53
#define WIFI_CREDENTIALS        "/wifi.txt"
54

    
55
/*
56
 * Config Mode AP SSID
57
 */
58
String apname = "imiot";
59

    
60
/*
61
 * In the case were setup fails, we don't want to do anything in our main loop.
62
 */
63
boolean no_run = false;
64

    
65
/*
66
 * AP Config mode disabled by default.
67
 * If Config button is ON at boot, we go into AP config mode.
68
 * After config is complete, button must be set OFF and device power cycled.
69
 * If Config button is OFF at boot, but turned on after then we go into PAIR mode (LED solid).
70
 * After PAIRING (LED blinks) the button must be turned off for normal operation.
71
 */
72
boolean configMode = false;
73

    
74
/*
75
 * Multicast setup
76
 * This is the address/port we use to send a multicast message asking
77
 * for monitor's to respond.  The first response we get is the one we
78
 * try to pair with.
79
 */
80
#define MULTICAST_PORT          13911
81
IPAddress ipMulti( 234, 1, 42, 3 );
82

    
83
/*
84
 * Incrememented with each multicast message we send, to identify
85
 * async responses to them.
86
 */
87
static unsigned int multicastID = 0;
88

    
89
/*
90
 * Timer object
91
 * To functions are "scheduled" to run periodically.
92
 * One is a registration function.  This runs only while we aren't paired.
93
 * The other is the server, which handles inbound REST API requests.
94
 */
95
Timer t;
96
int registerID;
97
int serverID;
98

    
99
/*
100
 * Registration and encryption
101
 * The uuid is provided by the monitor as part of the pairing process.
102
 * The uuid identifies this device to the monitor and is used, in part, as the key.
103
 * The key is used to encrypt/decrypt messages to/from the monitor.
104
 */
105
String uuid           = "";
106
String key            = "";
107
String monitorAddress = "";
108

    
109
/*
110
 * Object used to handle encryption/decryption.
111
 */
112
AES aes;
113

    
114
/*
115
 * Device type and version
116
 * These aren't really used yet but may be in the future as part of
117
 * the pairing process.
118
 */
119
static char deviceType = 0x02;
120
static char version    = 0x01;
121

    
122
/*
123
 * This is the web server that accepts inbound messages.
124
 * HTTPS is too taxing on an ESP-01 so we encrypt the body of messages instead.
125
 */
126
ESP8266WebServer server(80);
127

    
128
/*
129
 * Handle auto-connection
130
 * Globally scoped so API can reset, if necessary.
131
 */
132
WiFiManager wifiManager;
133

    
134
/* 
135
 * NOTE: WiFi object is always available because we included ESP8266WiFi.h 
136
 * See https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/station-class.html
137
 */
138

    
139
/*
140
 * Operational Flags
141
 */
142
char flags = 0;
143
#define FL_REGISTERED       0x01   // If this flag is set then we're paired already.
144
#define FL_IN_REGISTRATION  0x02   // If this flag is set then we're currently trying to register.
145

    
146
/*
147
 * GPIO Pins
148
 */
149
const int   led = 8;        // Pin connected to LED that is used for pairing operations
150
const int   pairButton = 9; // Pin connected to switch that enables pair mode.
151

    
152
/*
153
 * Function Prototypes
154
 */
155
int handleDevicePOST();
156
int handleDeviceGET();
157
int handleRegistration();
158
int handleNotFound();
159
int paired();
160

    
161
/*
162
 * ========================================================================
163
 * Device Specific Functions and variables
164
 * ========================================================================
165
 */
166

    
167
/*
168
 * Device state
169
 * Off: 0
170
 * On:  1
171
 */
172
int state = 0;
173

    
174
/*
175
 * ========================================================================
176
 * Name:   pairEnabled
177
 * Prototype:  int pairEnabled()( void )
178
 *
179
 * Description:
180
 * Checks GPIO pins to see if the pair button is enabled.
181
 *
182
 * Returns:
183
 * 1 if the the button is enabled or 0 (zero) if it is not.
184
 * ========================================================================
185
 */
186
int pairEnabled()
187
{
188
    boolean pairMode = false;
189

    
190
    /* Set pin modes */
191
    pinMode(0, OUTPUT);
192
    pinMode(2, INPUT);
193

    
194
    /* Set GPIO0 output low */
195
    // PRINTLN("PAIR Mode: Set GPIO0 low.");
196
    digitalWrite(0, LOW);
197

    
198
    /* check GPIO2 input to see if push button pressed connecting it to GPIO0 */
199
    // PRINTLN("PairMode: Checking GPIO2 state.");
200
    pairMode = (digitalRead(2) == LOW);
201

    
202
    /* Reset GPIO0 HIGH to turn off the relay */
203
    // PRINTLN("PairMode: Resetting GPIO0.");
204
    digitalWrite(0, HIGH);
205

    
206
    if ( pairMode )
207
        return(1);
208
    else
209
        return(0);
210
}
211

    
212
/*
213
 * ========================================================================
214
 * Name:   setupURIHandlers
215
 * Prototype:  void setupURIHandlers()( void )
216
 *
217
 * Description:
218
 * Sets the callbacks for specific URI patterns.
219
 * ========================================================================
220
 */
221
void setupURIHandlers()
222
{
223
    server.on("/device",   HTTP_POST, handleDevicePOST);
224
    server.on("/device",   HTTP_GET,  handleDeviceGET);
225
    server.on("/register", HTTP_POST, handleRegistration);
226
    server.onNotFound(handleNotFound);
227
}
228

    
229
/*
230
 * ========================================================================
231
 * Name:   handleDevicePOST
232
 * Prototype:  int handleDevicePOST()( void )
233
 *
234
 * Description:
235
 * A POST is used to change device state.  The body of the message is AES
236
 * encoded in the following format:
237
 *    UUID+JSON
238
 * where state is OFF or ON (as text strings).
239
 * ========================================================================
240
 */
241
int handleDevicePOST()
242
{
243
    String          body;
244
    char            *json_string;
245
    char            keyChar[sizeof(key)+1];
246
    byte            *keyPtr;
247
    byte            *bodyPtr;
248
    aJsonObject*    root;
249
    aJsonObject*    device;
250

    
251
    /*
252
     * Validate the request.
253
     */
254
    if ( ! paired() )
255
    {
256
        server.send(401, "text/plain", "Failed");
257
        return(1);
258
    }
259

    
260
    /*
261
     * Extract body of message.
262
     */
263
    if (server.hasArg("plain")== false)
264
    {
265
        server.send(400, "text/plain", "Body not received");
266
        return(1);
267
    }
268
    body = server.arg("plain");
269

    
270
    // Convert key to byte *keyPtr
271
    key.toCharArray(keyChar, sizeof(key));
272
    keyPtr = (unsigned byte *)keyChar;
273

    
274
    // Convert body to byte *bodyPtr
275
    char bodyChar[body.length()+1];
276
    body.toCharArray(bodyChar, body.length());
277
    bodyPtr = (unsigned byte *)bodyChar;
278

    
279
    /*
280
     * Decrypt the message, extracting the JSON.
281
     */
282
    byte json[body.length()];
283
    char *ptr = (char *)json;
284
    aes.do_aes_decrypt(bodyPtr, body.length(), (byte *)ptr, (byte *)keyChar, 128);
285

    
286
    /*
287
     * Pull the UUID and make sure it matches.
288
     */
289
    if ( strncmp((char *)uuid.c_str(), (char *)ptr, uuid.length()) != 0 )
290
    {
291
        server.send(409, "text/plain", "Failed");
292
        return(1);
293
    }
294

    
295
    /*
296
     * Move past the UUIDtogetthe real JSON request.
297
     */
298
    ptr += uuid.length();
299

    
300
    /*
301
     * Parse the JSON.
302
     */
303
    root = aJson.parse( (char *)ptr );
304
    device = aJson.getObjectItem(root, "device");
305

    
306
    /*
307
     * Process request.
308
     */
309
    // field = aJson.getObjectItem(root, "fieldName");
310
    // PRINTLN(field->valuestring);
311

    
312
    // Ack the request.
313
    server.send(200, "text/plain", "Ok");
314

    
315
    // Cleanup
316
    aJson.deleteItem(root);
317
}
318

    
319
/*
320
 * ========================================================================
321
 * Name:   handleDeviceGET
322
 * Prototype:  int handleDeviceGET()( void )
323
 *
324
 * Description:
325
 * A GET is used to retrieve device state.  The body of the message is AES
326
 * encoded in the following format:
327
 *    UUID+JSON
328
 * where state is OFF or ON (as text strings).
329
 *
330
 * Notes:
331
 * See https://github.com/spaniakos/AES/blob/master/examples_Rpi/aes.cpp
332
 * ========================================================================
333
 */
334
int handleDeviceGET()
335
{
336
    String          message;
337
    String          ptr;
338
    aJsonObject     *root,*fmt;
339
    char            *json_String;
340
    char            *uuidChar;
341
    char            keyChar[sizeof(key)+1];
342
    byte            *keyPtr;
343

    
344
    /*
345
     * Validate the request.
346
     */
347
    if ( ! paired() )
348
    {
349
        server.send(409, "text/plain", "Failed");
350
        return(1);
351
    }
352

    
353
    // Convert key to byte *keyPtr
354
    key.toCharArray(keyChar, sizeof(key));
355
    keyPtr = (unsigned byte *)keyChar;
356

    
357
    // Build our JSON response
358
    root = aJson.createObject();
359
    fmt = aJson.createObject();
360
    aJson.addItemToObject(root, "device", fmt);
361
    aJson.addNumberToObject(fmt,"state",  state);
362
    json_String = aJson.print(root);
363
    message = String(json_String);
364

    
365
    // Build outbound string.
366
    ptr = uuid + message;
367

    
368
    // Retrieve byte array from our constructed message.
369
    byte bytes[ptr.length()+1];
370
    memset(bytes, 0, ptr.length()+1);
371
    ptr.getBytes(bytes, ptr.length());
372

    
373
    // Need properly sized buffers for output from AES encryption
374
    byte plain_p[ptr.length() + (N_BLOCK - (ptr.length() % 16)) - 1];
375
    byte cipher[sizeof(plain_p)];
376

    
377
    // Encrypt the message.
378
    aes.do_aes_encrypt((byte *)bytes, (int)ptr.length(),
379
            (byte *)cipher, (byte *)keyChar, 128);
380

    
381
    // Send response
382
    byte message_str[ptr.length()+1];
383
    memset(message_str, 0, ptr.length()+1);
384
    ptr.getBytes( message_str, ptr.length() );
385
    PRINTLN((char *)message_str);
386
    server.send(200, "text/plain", (char *)message_str);
387

    
388
    // Cleanup
389
    aJson.deleteItem(root);
390
}
391

    
392
/*
393
 * ========================================================================
394
 * Name:   handleRegistration
395
 * Prototype:  int handleRegistration()( void )
396
 *
397
 * Description:
398
 * A UUID is passed from the monitor in response to a multicast 
399
 * registration request.  This is the server's way of saying
400
 * "You are UUID".
401
 *
402
 * Since this is our first contact we have to pass this in the open.
403
 * Because of that we make it simple and just pass the UUID in
404
 * the URI, as such:  /register/<uuid>.
405
 *
406
 * Notes:
407
 * Insecure?  Sure.  But it requires someone listening precisely for two
408
 * specific, non-repeated messages to know exactly how to hack this device.
409
 * The uuid is never passed in clear text on-the-wire again.
410
 *
411
 * And yes, I thought about HTTPS, but ESP8266's (specifically ESP-01's)
412
 * really can't do those.  AES encrypted and encoded JSON is good enough.
413
 *
414
 * Imporant:
415
 * Once registered if you move your monitor (re: server) IP then you
416
 * have to re-pair!
417
 * ========================================================================
418
 */
419
int handleRegistration()
420
{
421
    String message   = "";
422
    String path      = server.uri();
423
    int imIdx        = -1;
424
    int registerIdx  = -1;
425
    int uuidIdx      = -1;
426
    HTTPClient http;
427

    
428
    /*
429
     * Do we already have a UUID?  If so, ignore this request.
430
     * But don't tell the requester why.  They should know already.
431
     */
432
    if ( flags & FL_REGISTERED )
433
    {
434
        message = "UUID already set; registration request will be quietly ignored.";
435
        PRINTLN(message);
436
        server.send(200, "text/plain", "Okay");
437
        return(0);
438
    }
439

    
440
    /* Get URI path separator indices */
441
    imIdx = path.indexOf("/", 1);
442
    if ( imIdx != -1 )
443
        registerIdx = path.indexOf("/", imIdx+1);
444
    if ( registerIdx != -1 )
445
        uuidIdx = path.indexOf("/", registerIdx+1);
446
    if ( uuidIdx == -1 )
447
    {
448
        message = "Missing required UUID component: uri = ";
449
        message += server.uri();
450
        PRINTLN(message);
451
        server.send(400, "text/plain", "Failed");
452
        return(1);
453
    }
454

    
455
    /* Parse the UUID that will be used until the next pairing. */
456
    uuid = path.substring(uuidIdx+1);
457
    if ( uuid.length() < 16 )
458
    {
459
        message = "UUID length is too short (<16 characters)";
460
        PRINTLN(message);
461
        server.send(406, "text/plain", "Failed");
462
        return(1);
463
    }
464

    
465
    /* Gen AES key from UUID (like Jarvis) */
466
    key = uuid.substring(0,16);
467

    
468
    /* Save the monitor's IP address */
469
    monitorAddress = server.client().remoteIP().toString();
470

    
471
    /* Ack the request. */
472
    server.send(200, "text/plain", "Ok");
473

    
474
    /* Now complete the registration by calling the monitor's registration REST API */
475
    /* TBD: POST /pair/iot/register with UUID as JSON data (like Jarvis) */
476
    /* See (ESP8266HTTPClient - easier )
477
       https://techtutorialsx.com/2016/07/21/esp8266-post-requests/ */
478
    /* See (HTTPClient)
479
     * https://developer.ibm.com/recipes/tutorials/use-http-to-send-data-to-the-ibm-iot-foundation-from-an-esp8266/ */
480

    
481
    String url = "http://" + monitorAddress + "/pair/iot/register/" + uuid;
482
    PRINTLN("Registering with url = " + url);
483
    http.begin(url);
484
    http.addHeader("Content-Type", "text/json");
485
    int httpCode = http.POST("{}");
486
    PRINTLN(httpCode);
487
    if ( httpCode != 200 )
488
    {
489
        PRINTLN("Failed registration");
490
    }
491
    else
492
    {
493
        PRINTLN("Registration OK.");
494

    
495
        /* Mark us a registered */
496
        flags |= FL_REGISTERED;
497

    
498
        /* Stop registration multicast */
499
        flags &= FL_IN_REGISTRATION;
500
        t.stop(registerID);
501
    }
502
}
503

    
504
/*
505
 * ========================================================================
506
 * Support Functions
507
 * ========================================================================
508
 */
509

    
510
/*
511
 * ========================================================================
512
 * Name:   paired
513
 * Prototype:  int paired()( void )
514
 *
515
 * Description:
516
 * Check if this device is already paired.
517
 *
518
 * Returns:
519
 * 0 if we are paired and the request comes from the saved monitor IP.
520
 * 1 if we are not paird or the request comes from the wrong monitor IP.
521
 * ========================================================================
522
 */
523
int paired()
524
{
525
    /*
526
     * Are we registered?  That's the basic requirement.
527
     */
528
    if ( flags & FL_REGISTERED )
529
    {
530
        return(1);
531
    }
532

    
533
    /*
534
     * Does the request come from the server we expect it to?
535
     */
536
    String clientIP = server.client().remoteIP().toString();;
537
    if ( !clientIP.equals(monitorAddress) )
538
    {
539
        return(1);
540
    }
541

    
542
    /* Looks good. */
543
    return(0);
544
}
545

    
546
/*
547
 * ========================================================================
548
 * Name:   webLoop
549
 * Prototype:  void webLoop()( void )
550
 *
551
 * Description:
552
 * Check the web server for client connections.
553
 * ========================================================================
554
 */
555
void webLoop()
556
{
557
    server.handleClient();
558
}
559

    
560
/*
561
 * ========================================================================
562
 * Name:   serialNotify
563
 * Prototype:  void serialNotify()( void )
564
 *
565
 * Description:
566
 * Just display a message to the serial console.  This is a timed event.
567
 * ========================================================================
568
 */
569
void serialNotify()
570
{
571
    PRINTLN("In Operational Mode.");
572
}
573

    
574
/*
575
 * ========================================================================
576
 * Name:   deviceRegister
577
 * Prototype:  void deviceRegister()( void )
578
 *
579
 * Description:
580
 * Send registration request via multicast until registration completes.
581
 *
582
 * Notes:
583
 * Multicast request is Ack'd by server.  We use that Ack to identify
584
 * the server IP so we can contact it via http to complete registration.
585
 * ========================================================================
586
 */
587
void deviceRegister()
588
{
589
    String message;
590

    
591
    /* Skip if Wifi is not running. */
592
    if (WiFi.status() != WL_CONNECTED)
593
        return;
594

    
595
    /*
596
     * Send multicast request.  We send the device type and version.
597
     * If the server accepts those, it responds with an UUID via the web interface.
598
     */
599
    WiFiUDP Udp;
600
    Udp.beginPacketMulticast(ipMulti, MULTICAST_PORT, WiFi.localIP());
601
    Udp.write(deviceType);
602
    Udp.write(version);
603
    Udp.endPacket();
604

    
605
    message  = "Sent multicast announcement: \n";
606
    message += multicastID;
607
    multicastID++;
608
    PRINTLN(message);
609
}
610

    
611
/*
612
 * ========================================================================
613
 * Command Handlers
614
 * ========================================================================
615
 */
616

    
617
/*
618
 * ========================================================================
619
 * Name:   reset
620
 * Prototype:  int reset( void )
621
 *
622
 * Description:
623
 * Reset WiFi connection, allowing reconfiguration.
624
 *
625
 * Notes:
626
 * Never returns because it calls ESP.reset().
627
 * ========================================================================
628
 */
629
void reset()
630
{
631
    wifiManager.resetSettings();
632
    flags          = 0;
633
    uuid           = "";
634
    key            = "";
635
    monitorAddress = "";
636
    delay(3000);
637
}
638

    
639
/*
640
 * ========================================================================
641
 * Name:   handleNotFound
642
 * Prototype:  int handleNotFound( void )
643
 *
644
 * Description:
645
 * Handle invalid requests.  This sends a text reply, which may not be
646
 * suitable for the remote caller.
647
 * ========================================================================
648
 */
649
int handleNotFound()
650
{
651
    /*
652
     * Validate the request.  That's more important than telling them it's
653
     * a bad request.
654
     */
655
    if ( ! paired() )
656
    {
657
        server.send(401, "text/plain", "Failed");
658
        return(1);
659
    }
660

    
661
    String message = "Invalid command\n\n";
662
    message += "URI: ";
663
    message += server.uri();
664
    message += "\nMethod: ";
665
    message += (server.method() == HTTP_GET)?"GET":"POST";
666
    message += "\nArguments: ";
667
    message += server.args();
668
    message += "\n";
669
    for (uint8_t i=0; i<server.args(); i++)
670
    {
671
        message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
672
    }
673
    server.send(400, "text/plain", message);
674
}
675

    
676
/*
677
 * ========================================================================
678
 * Name:   configModeCallback
679
 * Prototype:  void configModeCallback( void )
680
 *
681
 * Description:
682
 * Print status info if connection is lost.
683
 * ========================================================================
684
 */
685
void configModeCallback(WiFiManager *myWiFiManager)
686
{
687
    PRINTLN("Failed connection to last AP.");
688
    PRINTLN("Entered config mode.");
689
    PRINTLN(WiFi.softAPIP());
690
    PRINTLN(myWiFiManager->getConfigPortalSSID());
691
}
692

    
693
/*
694
 * ========================================================================
695
 * Pin Handlers
696
 * ========================================================================
697
 */
698

    
699
/*
700
 * ========================================================================
701
 * Name:   setBlueLed
702
 * Prototype:  void setBlueLed( boolean enabled )
703
 *
704
 * Description:
705
 * Set the state of the blue LED.  This only works if USE_SERIAL is
706
 * not defined.
707
 *
708
 * Notes:
709
 * LED is lit when the pin is set LOW.
710
 * ========================================================================
711
 */
712
void setBlueLed(boolean enabled)
713
{
714
    pinMode(1, OUTPUT);
715
    if ( enabled )
716
    {
717
        digitalWrite(1, LOW);
718
    }
719
    else
720
    {
721
        digitalWrite(1, HIGH);
722
    }
723
}
724

    
725
/*
726
 * ========================================================================
727
 * Name:   relayOn
728
 * Prototype:  void relayOn( void )
729
 *
730
 * Description:
731
 * Turns the relay on, enabling power to flow to the controlled device.
732
 * ========================================================================
733
 */
734
void relayOn(void)
735
{
736
    PRINTLN("Turning relay on.");
737
    pinMode(2, INPUT);
738
    pinMode(0, OUTPUT);
739
    digitalWrite(0, LOW);
740
    // wifiTurnRelayON = false;
741
}
742

    
743
/*
744
 * ========================================================================
745
 * Name:   relayOff
746
 * Prototype:  void relayOff( void )
747
 *
748
 * Description:
749
 * Turns the relay off, disabling power to the controlled device.
750
 * ========================================================================
751
 */
752
void relayOff(void)
753
{
754
    PRINTLN("Turning relay off.");
755
    pinMode(0, INPUT);
756
    pinMode(2, OUTPUT);
757
    digitalWrite(2, LOW);
758
    // wifiTurnRelayOFF = false;
759
}
760

    
761
/*
762
 * ========================================================================
763
 * Name:   blinkPattern
764
 * Prototype:  void blinkPattern( int count, int interval )
765
 *
766
 * Description:
767
 * Blinks the BlueLED in a specified pattern.
768
 * ========================================================================
769
 */
770
void blinkPattern(int count, int interval)
771
{
772
    /* Blink patterns repeat "count" times, with "interval" milliseconds between patterns. */
773
    for(int i=0; i<count; i++)
774
    {
775
        SETBLUELED(true); delay(interval); SETBLUELED(false); delay(interval);
776
        PRINT(".");
777
    }
778
}
779

    
780
/*
781
 * ========================================================================
782
 * Setup
783
 * ========================================================================
784
 */
785

    
786
/*
787
 * ========================================================================
788
 * Name:   setup
789
 * Prototype:  void setup( void )
790
 *
791
 * Description:
792
 * Initialization function; required by Arduino enviroment.
793
 * This function handles setup of WiFi connections and configures
794
 * URI handlers for the web server.
795
 * ========================================================================
796
 */
797
void setup(void)
798
{
799
    String message;
800

    
801
    /*
802
     * Serial port output seems to work better at 9600.
803
     */
804
    SERIAL_BEGIN(115200);
805
    PRINTLN("");
806
    PRINTLN("Waiting on SDK.");
807
#ifdef USE_SERIAL
808
#ifdef DEBUG_CORE
809
    Serial.setDebugOutput(true);
810
#endif
811
#endif
812
    delay(3500);
813

    
814
    /*
815
     * Initial setup for using WifiManager.
816
     */
817
    wifiManager.setDebugOutput(true);
818
    wifiManager.setConnectTimeout(30);
819

    
820
    /*
821
     * Set callback to be notified when connection to last configured
822
     * remote AP fails.
823
     */
824
    wifiManager.setAPCallback(configModeCallback);
825

    
826
#ifdef RESET
827
    /*
828
     * Use this to reset the board to it's default state.
829
     * Set RESET=1 on the GNU Make command line to use this.
830
     * Then after the board resets once, run the build without RESET=1
831
     * build/upload again.
832
     */
833
    PRINTLN("Resetting WiFiManager.");
834
    reset();
835
    PRINTLN("Done. Now reflash the board.");
836
    no_run = true;
837
    return;
838

    
839
#endif // RESET
840

    
841
    /*
842
     * Method to use WITH Config mode button.
843
     */
844

    
845
    /* Set GPIO0 output low */
846
    PRINTLN("Set GPIO0 low.");
847
    pinMode(0, OUTPUT);
848
    digitalWrite(0, LOW);
849

    
850
    /*
851
     * Check GPIO2 input to see if push button is pressed (connecting it to GPIO0)
852
     * placing us in ConfigMode.
853
     */
854
    PRINTLN("Checking boot state.");
855
    configMode = (digitalRead(2) == LOW);
856
    if (configMode)
857
    {
858
        PRINTLN("In Config Mode.");
859
        BP_CONFIG_MODE;
860
        delay(1000);
861
        SETBLUELED(true);
862

    
863
        /*
864
         * Start AP and serve up web page configuration.
865
         */
866
        while ( 1 )
867
        {
868
            wifiManager.startConfigPortal((char *)apname.c_str());
869
            if (WiFi.status() == WL_CONNECTED)
870
            {
871
                break;
872
            }
873
            message = "Failed connection to ";
874
            message += WiFi.SSID();
875
            message += "; trying again.";
876
            PRINTLN(message);
877
        }
878
        BP_CONNECTED;
879
        PRINTLN("");
880
        PRINT("Connected to ");
881
        PRINTLN(WiFi.SSID());
882
        PRINT("IP address: ");
883
        PRINTLN(WiFi.localIP());
884
        return;
885
    }
886
    else
887
    {
888
        /*
889
         * In operational mode we just try to connect to the last configured AP.
890
         * We don't use WiFiManager here because we don't want to drop back into the 
891
         * auto-configure AP on the device.  If connection fails with saved credentials
892
         * then we simply punt in this mode.
893
         */
894
        PRINTLN("In Operational Mode.");
895
        WiFi.begin();
896
        while ( 1 )
897
        {
898
            if (WiFi.status() == WL_CONNECTED)
899
            {
900
                break;
901
            }
902
            message = "Failed connection to ";
903
            message += WiFi.SSID();
904
            message += "; trying again.";
905
            PRINTLN(message);
906
            BP_CONNECT_MODE;
907
            delay(1500);
908
        }
909
        BP_CONNECTED;
910
        PRINTLN("imiot setup:");
911
        PRINT("Connected to: "); PRINTLN(WiFi.SSID());
912
        PRINT("IP address  : "); PRINTLN(WiFi.localIP());
913
    }
914

    
915
    /* Reset GPIO0 HIGH to turn off the relay */
916
    PRINTLN("Resetting GPIO0.");
917
    digitalWrite(0, HIGH);
918

    
919
    /*
920
     * Setup web server URI handlers: this is done in a function to make
921
     * it easy to find where device specific code will go.
922
     */
923
    setupURIHandlers();
924

    
925
    /* Notify user of status, if they're watching the serial console. */
926
    server.begin();
927
    PRINTLN("HTTP server started");
928

    
929
    /* Set schedule for checking timed events. */
930
    PRINTLN("Scheduling webLoop.");
931
    serverID = t.every(25, webLoop);
932
    PRINTLN("Scheduling Serial Notification.");
933
    serverID = t.every(3000, serialNotify);
934
}
935

    
936
/*
937
 * ========================================================================
938
 * Name:   loop
939
 * Prototype:  void loop( void )
940
 *
941
 * Description:
942
 * Main loop function; required by Arduino enviroment.
943
 * This function uses the Timer API to spin, caling handlers at specified
944
 * intervals.
945
 * ========================================================================
946
 */
947
void loop(void)
948
{
949
    /*
950
     * We don't do anything until user has reset from Config Mode.
951
     * That requires a power cycle.
952
     * Or if we failed setup we don't really want to do anything either.
953
     */
954
    if (configMode || no_run )
955
    {
956
        return;
957
    }
958

    
959
    /*
960
     * If we're not registered AND PAIR mode button is set, go into pairing mode.
961
     */
962
    if ( !(flags & FL_REGISTERED) && pairEnabled() )
963
    {
964
        /* Check for registration events */
965
        if ( !(flags & FL_IN_REGISTRATION) )
966
        {
967
            PRINTLN("Attempting pair mode.");
968
            PRINTLN("Scheduling deviceRegister.");
969
            registerID = t.every(3000, deviceRegister);
970
        }
971
        BP_PAIR1_MODE;
972
        BP_PAIR2_MODE;
973

    
974
        t.update();
975
    }
976
    else
977
    {
978
        /*
979
         * Operational Mode.
980
         */
981

    
982
        /* Check for timer events */
983
        t.update();
984
    }
985
}