Revision 0d84cfd8

View differences:

PiBox/imlightsw.ino
12 12

  
13 13
/* Core libraries */
14 14
#include <stdio.h>
15
#include <string.h>
15 16
#include <FS.h>
16 17
// #include <EEPROM.h>
17 18
#include <ESP8266WiFi.h>
......
42 43
#endif
43 44

  
44 45
/*
46
 * Blue LED pin id.
47
 */
48
#define BLUE_LED_PIN    1
49

  
50
/*
45 51
 * Blink Patterns
46 52
 */
47 53
#define BP_CONNECTED            blinkPattern(4,1000)
48 54
#define BP_CONFIG_MODE          blinkPattern(2,500)
49 55
#define BP_CONNECT_MODE         blinkPattern(5,125)
50
#define BP_PAIR1_MODE           blinkPattern(4,150)
51
#define BP_PAIR2_MODE           blinkPattern(4,750)
56
#define BP_PAIR1_MODE           blinkPattern(2,150)
57
#define BP_PAIR2_MODE           blinkPattern(2,750)
58
#define BP_NOT_REGISTERED       blinkPattern(1,1500)
52 59

  
53 60
/*
54 61
 * Filename for saving credentials.
55 62
 */
56
#define WIFI_CREDENTIALS        "/wifi.txt"
63
char *registration_path     = "/registration.txt";
57 64

  
58 65
/*
59 66
 * Config Mode AP SSID
......
63 70
/*
64 71
 * In the case were setup fails, we don't want to do anything in our main loop.
65 72
 */
66
boolean no_run = false;
73
boolean resetMode = false;
67 74

  
68 75
/*
69 76
 * AP Config mode disabled by default.
......
87 94
 * Incrememented with each multicast message we send, to identify
88 95
 * async responses to them.
89 96
 */
90
static unsigned int multicastID = 0;
97
static unsigned long multicastID = 0;
91 98

  
92 99
/*
93 100
 * Timer object
......
96 103
 * The other is the server, which handles inbound REST API requests.
97 104
 */
98 105
Timer t;
99
int registerID;
100
int serverID;
106
int registerID = -1;
107
int serverID   = -1;
101 108

  
102 109
/*
103 110
 * Registration and encryption
......
119 126
 * These aren't really used yet but may be in the future as part of
120 127
 * the pairing process.
121 128
 */
122
static char deviceType = 0x02;
123
static char version    = 0x01;
129
#define MT_IRONMAN      7
130
#define MA_PAIR_IOT     1
131
static char msgType     = MT_IRONMAN;
132
static char msgAction   = MA_PAIR_IOT;
124 133

  
125 134
/*
126 135
 * This is the web server that accepts inbound messages.
......
148 157
#define FL_SPIFFS_OK        0x04   // If this flag is set then SPIFFS is available.
149 158

  
150 159
/*
151
 * GPIO Pins
152
 */
153
const int   led = 8;        // Pin connected to LED that is used for pairing operations
154
const int   pairButton = 9; // Pin connected to switch that enables pair mode.
155

  
156
/*
157 160
 * Function Prototypes
158 161
 */
159
int handleDevicePOST();
160
int handleDeviceGET();
161
int handleRegistration();
162
int handleNotFound();
163
int paired();
162
int     handleDevicePOST();
163
int     handleDeviceGET();
164
int     handleRegistration();
165
int     handleNotFound();
166
int     paired();
167
void    saveFile( char *path, String data );
168
String  readFile( char *path );
169
void    blinkPattern(int count, int interval);
164 170

  
165 171
/*
166 172
 * ========================================================================
......
333 339
 *
334 340
 * Notes:
335 341
 * See https://github.com/spaniakos/AES/blob/master/examples_Rpi/aes.cpp
342
 * For random number of iv: https://www.tutorialspoint.com/arduino/arduino_random_numbers.htm
343
 * Random seed should come from TX or RX in operational mode, but won't matter otherwise.
336 344
 * ========================================================================
337 345
 */
338 346
int handleDeviceGET()
......
346 354
    byte            *keyPtr;
347 355

  
348 356
    /*
349
     * Validate the request.
357
     * Reject requests when we're not paired.
350 358
     */
351 359
    if ( ! paired() )
352 360
    {
353
        server.send(409, "text/plain", "Failed");
361
        server.send(401, "text/plain", "Failed");
354 362
        return(1);
355 363
    }
356 364

  
......
365 373
    aJson.addNumberToObject(fmt,"state",  state);
366 374
    json_String = aJson.print(root);
367 375
    message = String(json_String);
376
    aJson.deleteItem(root);
368 377

  
369 378
    // Build outbound string.
370 379
    ptr = uuid + message;
......
382 391
    aes.do_aes_encrypt((byte *)bytes, (int)ptr.length(),
383 392
            (byte *)cipher, (byte *)keyChar, 128);
384 393

  
394
    // Encode message as string
395

  
396
    /* Build the JSON response packet.  This is the encrypted version of our message. */
397
    // TBD
398
    // root = aJson.createObject();
399
    // aJson.addStringToObject(root, "iv", fmt);
400
    // aJson.addStringToObject(root, "message", bytes);
401

  
385 402
    // Send response
386 403
    byte message_str[ptr.length()+1];
387 404
    memset(message_str, 0, ptr.length()+1);
......
406 423
 * Since this is our first contact we have to pass this in the open.
407 424
 * Because of that we make it simple and just pass the UUID in
408 425
 * the URI, as such:  /register/<uuid>.
426
 * 
427
 * If the server responds with HTTP 200 then we're registered.
409 428
 *
410 429
 * Notes:
411 430
 * Insecure?  Sure.  But it requires someone listening precisely for two
......
475 494
    /* Ack the request. */
476 495
    server.send(200, "text/plain", "Ok");
477 496

  
478
    /* Now complete the registration by calling the monitor's registration REST API */
479
    /* TBD: POST /pair/iot/register with UUID as JSON data (like Jarvis) */
480
    /* See (ESP8266HTTPClient - easier )
481
       https://techtutorialsx.com/2016/07/21/esp8266-post-requests/ */
482
    /* See (HTTPClient)
483
     * https://developer.ibm.com/recipes/tutorials/use-http-to-send-data-to-the-ibm-iot-foundation-from-an-esp8266/ */
497
    /* 
498
     * Now complete the registration by calling the monitor's registration REST API: POST /pair/iot/register/UUID
499
     * Use ESP8266HTTPClient to contact the monitor:
500
     *      See https://techtutorialsx.com/2016/07/21/esp8266-post-requests/
501
     */
484 502

  
485 503
    String url = "http://" + monitorAddress + "/pair/iot/register/" + uuid;
486 504
    PRINTLN("Registering with url = " + url);
487 505
    http.begin(url);
488
    http.addHeader("Content-Type", "text/json");
506
    http.addHeader("Content-Type", "text/plain");
489 507
    int httpCode = http.POST("{}");
490 508
    PRINTLN(httpCode);
491 509
    if ( httpCode != 200 )
492 510
    {
493
        PRINTLN("Failed registration");
511
        message = "Failed registration: code = " + httpCode;
512
        PRINTLN(message);
494 513
    }
495 514
    else
496 515
    {
497 516
        PRINTLN("Registration OK.");
498 517

  
518
        /* Save registration data. */
519
        String registration_info = String(uuid + ":" + key + ":" + monitorAddress + "\n");
520
        saveFile(registration_path, registration_info);
521

  
499 522
        /* Mark us a registered */
500 523
        flags |= FL_REGISTERED;
501 524

  
502 525
        /* Stop registration multicast */
503
        flags &= FL_IN_REGISTRATION;
504
        t.stop(registerID);
526
        if ( registerID != -1 )
527
        {
528
            t.stop(registerID);
529
            registerID = -1;
530
        }
505 531
    }
506 532
}
507 533

  
......
513 539

  
514 540
/*
515 541
 * ========================================================================
542
 * Name:   serialNotify
543
 * Prototype:  void serialNotify()( void )
544
 *
545
 * Description:
546
 * Just display a message to the serial console.  This is a timed event.
547
 * ========================================================================
548
 */
549
void serialNotify()
550
{
551
    PRINTLN("In Operational Mode.");
552

  
553
    /*
554
     * Let user know if we're not registered yet.
555
     */
556
    if ( !(flags & FL_REGISTERED) )
557
    {
558
        BP_NOT_REGISTERED;
559
    }
560

  
561
}
562

  
563
/*
564
 * ========================================================================
516 565
 * Name:   paired
517 566
 * Prototype:  int paired()( void )
518 567
 *
......
520 569
 * Check if this device is already paired.
521 570
 *
522 571
 * Returns:
523
 * 0 if we are paired and the request comes from the saved monitor IP.
524
 * 1 if we are not paird or the request comes from the wrong monitor IP.
572
 * 0 if we are not paired or the request comes from the wrong monitor IP.
573
 * 1 if we are paired and the request comes from the saved monitor IP.
525 574
 * ========================================================================
526 575
 */
527 576
int paired()
......
529 578
    /*
530 579
     * Are we registered?  That's the basic requirement.
531 580
     */
532
    if ( flags & FL_REGISTERED )
581
    if ( !(flags & FL_REGISTERED) )
533 582
    {
534
        return(1);
583
        return(0);
535 584
    }
536 585

  
537 586
    /*
......
540 589
    String clientIP = server.client().remoteIP().toString();;
541 590
    if ( !clientIP.equals(monitorAddress) )
542 591
    {
543
        return(1);
592
        return(0);
544 593
    }
545 594

  
546 595
    /* Looks good. */
547
    return(0);
596
    return(1);
548 597
}
549 598

  
550 599
/*
......
563 612

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

  
578
/*
579
 * ========================================================================
580 615
 * Name:   deviceRegister
581 616
 * Prototype:  void deviceRegister()( void )
582 617
 *
......
597 632
        return;
598 633

  
599 634
    /*
600
     * Send multicast request.  We send the device type and version.
635
     * Send multicast request.  The outbound message has the following format.
636
     * Byte 0:      Message type  (MT_IRONMAN)
637
     * Byte 1:      Action type (MA_PAIR_IOT)
638
     * Byte 3-4:    Unused
639
     * Byte 5-8:    Integer size of payload (0, but payload size is 4 bytes long)
601 640
     * If the server accepts those, it responds with an UUID via the web interface.
602 641
     */
642

  
603 643
    WiFiUDP Udp;
604 644
    Udp.beginPacketMulticast(ipMulti, MULTICAST_PORT, WiFi.localIP());
605
    Udp.write(deviceType);
606
    Udp.write(version);
645
    Udp.write(msgType);
646
    Udp.write(msgAction);
647
    char buffer = 0;
648
    for(int i=0; i<6; i++)
649
    {
650
        Udp.write(buffer);
651
    }
607 652
    Udp.endPacket();
608 653

  
609 654
    message  = "Sent multicast announcement: \n";
......
653 698
int handleNotFound()
654 699
{
655 700
    /*
656
     * Validate the request.  That's more important than telling them it's
657
     * a bad request.
701
     * Requests when we're not paired are rejected.
658 702
     */
659 703
    if ( ! paired() )
660 704
    {
......
662 706
        return(1);
663 707
    }
664 708

  
709
    /*
710
     * Validate the request.  That's more important than telling them it's
711
     * a bad request.
712
     */
665 713
    String message = "Invalid command\n\n";
666 714
    message += "URI: ";
667 715
    message += server.uri();
......
715 763
 */
716 764
void setBlueLed(boolean enabled)
717 765
{
718
    pinMode(1, OUTPUT);
766
    pinMode(BLUE_LED_PIN, OUTPUT);
719 767
    if ( enabled )
720 768
    {
721 769
        digitalWrite(1, LOW);
......
741 789
    pinMode(2, INPUT);
742 790
    pinMode(0, OUTPUT);
743 791
    digitalWrite(0, LOW);
744
    // wifiTurnRelayON = false;
745 792
}
746 793

  
747 794
/*
......
759 806
    pinMode(0, INPUT);
760 807
    pinMode(2, OUTPUT);
761 808
    digitalWrite(2, LOW);
762
    // wifiTurnRelayOFF = false;
763 809
}
764 810

  
765 811
/*
......
790 836
/*
791 837
 * ========================================================================
792 838
 * Name:   saveFile
793
 * Prototype:  void saveFile( char *path, char *data )
839
 * Prototype:  void saveFile( char *path, String data )
794 840
 *
795 841
 * Description:
796 842
 * Saves data to the specified path.  The contents of the file associated 
797 843
 * with path will be replaced by the new data.
798 844
 * ========================================================================
799 845
 */
800
void saveFile( char *path, char *data )
846
void saveFile( char *path, String data )
801 847
{
802 848
    String message;
803 849
    if ( ! (flags & FL_SPIFFS_OK) )
......
814 860
    }
815 861
    else
816 862
    {
817
        f.println(data);
863
        f.println(data.c_str());
818 864
        f.close();
819 865
        message = "Saved data to " + String(path);
820 866
        PRINTLN(message);
......
845 891
    if ( f ) 
846 892
    {
847 893
        PRINTLN("Reading from SPIFFS");
848

  
849
        // we could open the file
850 894
        while(f.available()) 
851 895
        {
852 896
            //Lets read line by line from the file
......
934 978
    PRINTLN("Resetting WiFiManager.");
935 979
    reset();
936 980
    PRINTLN("Done. Now reflash the board.");
937
    no_run = true;
981
    resetMode = true;
938 982
    return;
939 983

  
940 984
#endif // RESET
......
1031 1075
    /* Set schedule for checking timed events. */
1032 1076
    PRINTLN("Scheduling webLoop.");
1033 1077
    serverID = t.every(25, webLoop);
1034
    PRINTLN("Scheduling Serial Notification.");
1035
    serverID = t.every(3000, serialNotify);
1078

  
1079
    /* Load registration info, if any. */
1080
    if ( SPIFFS.exists(registration_path) )
1081
    {
1082
        // split into: uuid, key and monitorAddress
1083
        String registration = readFile(registration_path);
1084
        char *reg_copy = strdup(registration.c_str());
1085

  
1086
        char *ptr = strtok(reg_copy, ":");
1087
        uuid = String(strdup(ptr));
1088

  
1089
        ptr = strtok(NULL, ":");
1090
        key = String(strdup(ptr));
1091

  
1092
        ptr = strtok(NULL, ":");
1093
        monitorAddress = String(strdup(ptr));
1094

  
1095
        free(reg_copy);
1096
    }
1097
    else
1098
    {
1099
        PRINTLN("Device registration not found.");
1100
    }
1036 1101
}
1037 1102

  
1038 1103
/*
......
1048 1113
 */
1049 1114
void loop(void)
1050 1115
{
1116
    /* Do nothing here if we're in RESET mode. */
1117
    if ( resetMode )
1118
    {
1119
        return;
1120
    }
1121

  
1051 1122
    /*
1052 1123
     * We don't do anything until user has reset from Config Mode.
1053 1124
     * That requires a power cycle.
1054
     * Or if we failed setup we don't really want to do anything either.
1055 1125
     */
1056
    if (configMode || no_run )
1126
    if ( configMode )
1057 1127
    {
1128
        BP_CONNECTED;
1129
        delay(3000);
1058 1130
        return;
1059 1131
    }
1060 1132

  
1061 1133
    /*
1062 1134
     * If we're not registered AND PAIR mode button is set, go into pairing mode.
1063 1135
     */
1064
    if ( !(flags & FL_REGISTERED) && pairEnabled() )
1136
    if ( pairEnabled() )
1065 1137
    {
1066
        /* Check for registration events */
1067
        if ( !(flags & FL_IN_REGISTRATION) )
1068
        {
1069
            PRINTLN("Attempting pair mode.");
1070
            PRINTLN("Scheduling deviceRegister.");
1071
            registerID = t.every(3000, deviceRegister);
1072
        }
1073
        BP_PAIR1_MODE;
1074
        BP_PAIR2_MODE;
1138
        /*
1139
         * We should notify existing monitor that we're going away here.
1140
         * TBD: requires new REST API.
1141
         */
1075 1142

  
1076
        t.update();
1143
        /* Assume we're no longer registered. */
1144
        flags &= !FL_REGISTERED;
1145
        SPIFFS.remove(registration_path);
1146

  
1147
        do
1148
        {
1149
            /*
1150
             * Disable notification that we're in operational mode.
1151
             */
1152
            if ( serverID != -1 )
1153
            {
1154
                t.stop(serverID);
1155
                serverID = -1;
1156
            }
1157
    
1158
            /* Schedule multicast query for every 3 seconds. */
1159
            if ( registerID == -1 )
1160
            {
1161
                PRINTLN("Attempting pair mode.");
1162
                PRINTLN("Scheduling deviceRegister.");
1163
                registerID = t.every(3000, deviceRegister);
1164
            }
1165
    
1166
            /* Show blink pattern in Pair Mode */
1167
            BP_PAIR1_MODE;
1168
            BP_PAIR2_MODE;
1169
            t.update();
1170
    
1171
        } while ( !(flags & FL_REGISTERED) && pairEnabled() );
1077 1172
    }
1078 1173
    else
1079 1174
    {
......
1081 1176
         * Operational Mode.
1082 1177
         */
1083 1178

  
1179
        /*
1180
         * If we return from pair mode we may need to disable deviceRegister() calls.
1181
         */
1182
        if ( registerID != -1 )
1183
        {
1184
            t.stop(registerID);
1185
            registerID = -1;
1186
        }
1187

  
1188
        /*
1189
         * If not started, setup notification that we're in operational mode.
1190
         */
1191
        if ( serverID == -1 )
1192
        {
1193
            PRINTLN("Scheduling Serial Notification.");
1194
            serverID = t.every(3000, serialNotify);
1195
        }
1196

  
1084 1197
        /* Check for timer events */
1085 1198
        t.update();
1086 1199
    }

Also available in: Unified diff