lights.html
  1  <!DOCTYPE html>
  2  <html lang="en">
  3  <head>
  4    <meta charset="utf-8">
  5    <title>ESP8266 MicroPython Smart Holiday Lights</title>
  6    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
  7  </head>
  8  
  9  <body>
 10    <div class="container">
 11      <div class="row">
 12        <div class="col-lg-8 col-lg-offset-2">
 13          <div class="jumbotron">
 14            <h1>MicroPython Smart Holiday Lights</h1>
 15            <p class="lead">Control your holiday lights using MicroPython and the
 16            ESP8266 WiFi microcontroller.</p>
 17          </div>
 18        </div>
 19      </div>
 20      <div class="row">
 21        <div class="col-lg-4 col-lg-offset-4">
 22          <form>
 23            <div class="form-group">
 24              <label for="boardURL">Board URL</label>
 25              <input type="text" class="form-control" id="boardURL" value="ws://192.168.4.1:8266">
 26            </div>
 27            <div class="form-group">
 28              <label for="password">Password</label>
 29              <input type="password" class="form-control" id="password" placeholder="Password">
 30            </div>
 31            <div class="form-group">
 32              <label for="animation">Animation</label>
 33              <select class="form-control" id="animation">
 34                <option value="solid">Solid</option>
 35                <option value="chase">Chase</option>
 36                <option value="smooth">Smooth</option>
 37                <option value="blank">Blank (off)</option>
 38              </select>
 39            </div>
 40            <div class="form-group">
 41              <label for="periodMS">Animation Period (milliseconds)</label>
 42              <input type="text" class="form-control" id="periodMS" value="250">
 43            </div>
 44            <div class="form-group">
 45              <label for="colors">Colors</label>
 46              <select class="form-control" id="colors">
 47              </select>
 48            </div>
 49            <div class="checkbox">
 50              <label>
 51                <input type="checkbox" id="mirror" checked>
 52                Mirror Colors
 53              </label>
 54            </div>
 55            <button class="btn btn-lg btn-primary" type="button" id="update">Update Lights</button>
 56          </form>
 57        </div>
 58      </div>
 59    </div>
 60  </body>
 61  
 62  <script
 63    src="https://code.jquery.com/jquery-3.1.1.min.js"
 64    integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
 65    crossorigin="anonymous"></script>
 66  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
 67  <script>
 68    // Global configuration:
 69    var colors = [
 70      { name: 'Blue - Red',   value: [[0,0,255], [16,0,128], [32,0,64], [64,0,32], [128,0,16], [255,0,0]] },
 71      { name: 'Red - Green',  value: [[255,0,0], [128,16,0], [64,32,0], [32,64,0], [16,128,0], [0,255,0]] },
 72      { name: 'Blue - White', value: [[0,0,255], [16,16,255], [32,32,255], [64,64,255], [128,128,255], [255,255,255]] }
 73    ];
 74  
 75    // Function to connect to the WebREPL on the specified board URL and with the
 76    // specified password.  Once connected the onConnect callback will be called
 77    // and a websocket object passed in.  If an error occurs the onError callback
 78    // is called with the error event object as a parameter.
 79    function webreplConnect(url, password, onConnect, onError) {
 80      var ws = new WebSocket(url);
 81      ws.onopen = function() {
 82        // Print messages received from the WebREPL.  Uncomment this to help with
 83        // debugging.
 84        ws.onmessage = function(e) {
 85          console.log(e.data);
 86        }
 87        // First send the password immediately since it's required to use WebREPL.
 88        ws.send(password + '\r\n');  // Send password and CR LF (like pressing enter).
 89        // Next send a Ctrl-C to stop any running main loop.
 90        ws.send('\x03');
 91        // Fire the connected callback.
 92        onConnect(ws);
 93      };
 94      ws.onerror = function(e) {
 95        // Something went wrong!  Call the user's error handler.
 96        onError(e);
 97      };
 98    }
 99  
100    // Send a file to a board using the WebREPL.  WS should be a websocket connection
101    // to a board, filename is the name of the file to save on the board, and data
102    // should be a Uint8Array of data for the file.
103    function putFile(ws, fileName, data) {
104      // Construct a header to specify a file put operation.
105      var size = data.length;
106      var rec = new Uint8Array(2 + 1 + 1 + 8 + 4 + 2 + 64);
107      rec[0] = 'W'.charCodeAt(0);
108      rec[1] = 'A'.charCodeAt(0);
109      rec[2] = 1; // put
110      rec[3] = 0;
111      rec[4] = 0; rec[5] = 0; rec[6] = 0; rec[7] = 0; rec[8] = 0; rec[9] = 0; rec[10] = 0; rec[11] = 0;
112      rec[12] = size & 0xff; rec[13] = (size >> 8) & 0xff; rec[14] = (size >> 16) & 0xff; rec[15] = (size >> 24) & 0xff;
113      rec[16] = fileName.length & 0xff; rec[17] = (fileName.length >> 8) & 0xff;
114      for (var i = 0; i < 64; ++i) {
115          if (i < fileName.length) {
116              rec[18 + i] = fileName.charCodeAt(i);
117          } else {
118              rec[18 + i] = 0;
119          }
120      }
121      // Send put file request header that was constructed above.
122      ws.send(rec);
123      // Next send the file data.
124      ws.send(data);
125    }
126  
127    $(document).ready(function() {
128      // Main code that runs when the page is loaded.
129      // Populate the color selection list.
130      colors.forEach(function(color, i) {
131        $('#colors').append($('<option></option>').attr('value', i).text(color.name));
132      });
133  
134      // Update the board state when the update button is clicked.
135      $('#update').click(function() {
136        // Build the board's new configuration.
137        var config = {
138          colors: colors[$('#colors').val()].value,
139          period_ms: Number($('#periodMS').val()),
140          mirror_colors: $('#mirror').prop('checked'),
141          animation: $('#animation').val()
142        }
143        var boardURL = $('#boardURL').val();
144        var password = $('#password').val();
145        // Connect to the board's REPL.
146        webreplConnect(boardURL, password, function(ws) {
147          console.log('Connected!');
148          // Connected, now convert config state to JSON bytes.
149          var configJSON = JSON.stringify(config);
150          var configBytes = new Uint8Array(configJSON.length);
151          for (var i = 0; i < configJSON.length; ++i) {
152            configBytes[i] = configJSON.charCodeAt(i);
153          }
154          // Send the new configuration file.
155          putFile(ws, 'config.json', configBytes);
156          // Reset the board so the new configuration takes effect.
157          // Note that sending a Ctrl-B doesn't seem to work as the WebREPL won't
158          // restart on soft reset.
159          ws.send('import machine\r\nmachine.reset()\r\n');
160          // Close the connection.
161          ws.close();
162          console.log('Updated lights!');
163        },
164        function(e) {
165          console.log('Error connecting to MicroPython board!');
166        })
167      });
168    });
169  </script>
170  </html>