Browse Source

Using gulp to build file system files

fastled
Xose Pérez 8 years ago
parent
commit
5666cd5198
26 changed files with 291 additions and 194 deletions
  1. +1
    -0
      .gitignore
  2. +41
    -11
      README.md
  3. BIN
      code/data/checkboxes-min.css.gz
  4. BIN
      code/data/checkboxes-min.js.gz
  5. BIN
      code/data/favicon.ico
  6. BIN
      code/data/grids-responsive-min.css.gz
  7. BIN
      code/data/images/off.png
  8. BIN
      code/data/images/on.png
  9. BIN
      code/data/images/slider.png
  10. BIN
      code/data/images/slider_center.png
  11. BIN
      code/data/images/slider_left.png
  12. BIN
      code/data/images/slider_right.png
  13. BIN
      code/data/index.html.gz
  14. BIN
      code/data/jquery-1.12.3.min.js.gz
  15. BIN
      code/data/pure-min.css.gz
  16. BIN
      code/data/side-menu-min.css.gz
  17. +43
    -0
      code/gulpfile.js
  18. +0
    -1
      code/html/checkboxes-min.css
  19. +0
    -1
      code/html/checkboxes-min.js
  20. +59
    -0
      code/html/custom.css
  21. +108
    -0
      code/html/custom.js
  22. +0
    -0
      code/html/fsversion
  23. +13
    -179
      code/html/index.html
  24. +0
    -1
      code/html/side-menu-min.css
  25. +25
    -0
      code/package.json
  26. +1
    -1
      code/src/defaults.h

+ 1
- 0
.gitignore View File

@ -4,3 +4,4 @@
firmware* firmware*
.pioenvs .pioenvs
.sconsign.dblite .sconsign.dblite
node_modules

+ 41
- 11
README.md View File

@ -30,21 +30,31 @@ You can read about this board and firmware in [my blog][2].
* Support for **DHT22** sensors * Support for **DHT22** sensors
* Command line configuration * Command line configuration
# Flashing a Sonoff
## Installing
*This section only applies to the Sonoff, but pretty much every other ESP8266-based hardware will be similar.*
### Build the web config site
The unpopulated header in the Sonoff has all the required pins. My board has a 5 pins header in-line with the button. They are (from the button outwards):
Normally when you flash an ESP8266 you only flash the firmware, like for any other microcontroller. But the ESP8266 has plenty of room and normally it is split into different partitions. One such partition is used to store web files like a normal webserver. In the "Flash your board" section below you'll know how to flash this special partition, but first we will have to build it.
* 3V3
* RX
* TX
* GND
* GPIO14
The build process read the HTML files, looks for the stylesheet and script files linked there, grabs them, merges them, minifies them and compresses them. Thus, a single HTML with N linked scripts and M linked CSS files is transformed in just 3 files (index.html.gz, style.css.gz and script.js.gz). This way the ESP8266 webserver can serve them really fast. Mind the ESP8266 is just a microcontroller, the webserver has a very limited capacity to hold concurrent requests, so few and lighter files are a must.
Last one is not necessary. Mind it's a **3V3 device**, if connected to 5V you will probably fry it. Button is connected to GPIO0 on the ESP8266 chip, so to enter flash mode you have to hold the button pressed while powering on the board, then you can realease it again.
To build these files we are using **[Gulp][11]**, a build system built in [node.js][13]. So you will need node (and [npm][14], its package manager) first. [Read the documentation][12] on how to install them.
Once you have node and npm installed, go to the 'code' folder and install all the dependencies with:
```
npm install
```
It will take a minute or two. Then you are ready to build the webserver files with:
```
gulp
```
## Firmware
It will create a populate a 'data' folder with all the required files.
### Build the firmware
The project is ready to be build using [PlatformIO][3]. The project is ready to be build using [PlatformIO][3].
Please refer to their web page for instructions on how to install the builder. Please refer to their web page for instructions on how to install the builder.
@ -75,7 +85,23 @@ Once you have all the code, you can check if it's working by:
> platformio run -e node-debug > platformio run -e node-debug
``` ```
If it compiles you are ready to flash, wire your board like in the flashing section above and:
If it compiles you are ready to flash the firmware.
### Flash your board
*This section only applies to the Sonoff, but pretty much every other ESP8266-based hardware will be similar.*
The unpopulated header in the Sonoff has all the required pins. My board has a 5 pins header in-line with the button. They are (from the button outwards):
* 3V3
* RX
* TX
* GND
* GPIO14
Last one is not necessary. Mind it's a **3V3 device**, if connected to 5V you will probably fry it. Button is connected to GPIO0 on the ESP8266 chip, so to enter flash mode you have to hold the button pressed while powering on the board, then you can realease it again.
Wire your board and flash the firmware (with ```upload```) and the file system (with ```uploadfs```):
```bash ```bash
> platformio run --target upload -e node-debug > platformio run --target upload -e node-debug
@ -127,3 +153,7 @@ After flashing the firmware via serial do a hard reset of the device (unplug & p
[8]: https://www.itead.cc/sonoff-rf.html [8]: https://www.itead.cc/sonoff-rf.html
[9]: https://www.itead.cc/slampher-wifi-wireless-light-holder.html [9]: https://www.itead.cc/slampher-wifi-wireless-light-holder.html
[10]: https://www.itead.cc/smart-socket-eu.html [10]: https://www.itead.cc/smart-socket-eu.html
[11]: http://gulpjs.com/
[12]: https://docs.npmjs.com/getting-started/installing-node
[13]: https://nodejs.org/en/
[14]: https://www.npmjs.com/

BIN
code/data/checkboxes-min.css.gz View File


BIN
code/data/checkboxes-min.js.gz View File


BIN
code/data/favicon.ico View File

Before After

BIN
code/data/grids-responsive-min.css.gz View File


BIN
code/data/images/off.png View File

Before After
Width: 289  |  Height: 27  |  Size: 2.5 KiB

BIN
code/data/images/on.png View File

Before After
Width: 289  |  Height: 27  |  Size: 2.4 KiB

BIN
code/data/images/slider.png View File

Before After
Width: 39  |  Height: 27  |  Size: 1.2 KiB

BIN
code/data/images/slider_center.png View File

Before After
Width: 4  |  Height: 27  |  Size: 260 B

BIN
code/data/images/slider_left.png View File

Before After
Width: 4  |  Height: 27  |  Size: 324 B

BIN
code/data/images/slider_right.png View File

Before After
Width: 4  |  Height: 27  |  Size: 321 B

BIN
code/data/index.html.gz View File


BIN
code/data/jquery-1.12.3.min.js.gz View File


BIN
code/data/pure-min.css.gz View File


BIN
code/data/side-menu-min.css.gz View File


+ 43
- 0
code/gulpfile.js View File

@ -0,0 +1,43 @@
var gulp = require('gulp');
var plumber = require('gulp-plumber');
var htmlmin = require('gulp-htmlmin');
var cleancss = require('gulp-clean-css');
var uglify = require('gulp-uglify');
var gzip = require('gulp-gzip');
var del = require('del');
var useref = require('gulp-useref');
var gulpif = require('gulp-if');
/* Clean destination folder */
gulp.task('clean', function() {
return del(['data/*']);
});
/* Copy static files */
gulp.task('files', function() {
return gulp.src([
'html/**/*.{jpg,jpeg,png,ico,gif}',
'html/fsversion'
])
.pipe(gulp.dest('data/'));
});
/* Process HTML, CSS, JS */
gulp.task('html', function() {
return gulp.src('html/*.html')
.pipe(useref())
.pipe(plumber())
.pipe(gulpif('*.css', cleancss()))
.pipe(gulpif('*.js', uglify()))
.pipe(gulpif('*.html', htmlmin({
collapseWhitespace: true,
removeComments: true,
minifyCSS: true,
minifyJS: true
})))
.pipe(gzip())
.pipe(gulp.dest('data'));
});
/* Default Task */
gulp.task('default', ['clean', 'files', 'html']);

+ 0
- 1
code/html/checkboxes-min.css View File

@ -1 +0,0 @@
.iPhoneCheckContainer{-webkit-transform:translate3d(0,0,0);position:relative;height:27px;cursor:pointer;overflow:hidden}.iPhoneCheckContainer input{position:absolute;top:5px;left:30px;filter:alpha(Opacity=0);opacity:0}.iPhoneCheckContainer label,.iPhoneCheckHandle{display:block;height:27px;cursor:pointer;position:absolute;top:0}.iPhoneCheckContainer label{white-space:nowrap;font-size:17px;line-height:17px;font-weight:700;font-family:"Helvetica Neue",Arial,Helvetica,sans-serif;width:auto;padding-top:5px;overflow:hidden}.iPhoneCheckContainer,.iPhoneCheckContainer label{user-select:none;-moz-user-select:none;-khtml-user-select:none}.iPhoneCheckDisabled{filter:alpha(Opacity=50);opacity:.5}label.iPhoneCheckLabelOn{color:#fff;background:url(images/on.png) no-repeat;text-shadow:0 0 2px rgba(0,0,0,.6);left:0;padding-top:5px}label.iPhoneCheckLabelOn span{padding-left:8px}label.iPhoneCheckLabelOff{color:#8b8b8b;background:url(images/off.png) right 0 no-repeat;text-shadow:0 0 2px rgba(255,255,255,.6);text-align:right;right:0}label.iPhoneCheckLabelOff span{padding-right:8px}.iPhoneCheckHandle{left:0;width:0;background:url(images/slider_left.png) no-repeat;padding-left:3px}.iPhoneCheckHandleRight{height:100%;width:100%;padding-right:3px;background:url(images/slider_right.png) right 0 no-repeat}.iPhoneCheckHandleCenter{height:100%;width:100%;background:url(images/slider_center.png)}

+ 0
- 1
code/html/checkboxes-min.js
File diff suppressed because it is too large
View File


+ 59
- 0
code/html/custom.css View File

@ -0,0 +1,59 @@
#menu .pure-menu-heading {
font-size: 100%;
padding: .5em .5em;
}
.header h2 {
font-size: 1em;
}
.panel {
display: none;
}
.content {
margin: 0px;
}
.page {
margin-top: 40px;
}
.center {
text-align: center;
}
.pure-button {
color: white;
padding: 8px 16px;
border-radius: 4px;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
.button-update {
width: 100px;
margin: 50px auto;
background: #1f8dd6;
}
.pure-g {
margin-bottom: 20px;
}
legend {
font-weight: bold;
}
.l-box {
padding-right: 1px;
}
#topics .pure-g {
padding-bottom: 5px;
margin-bottom: 5px;
border-bottom: 1px dashed #e5e5e5;
}
.pure-form input[type=text][disabled] {
color: #777777;
}
div.hint {
font-size: 80%;
color: #ccc;
margin-top: -5px;
}
.iPhoneCheckContainer {
letter-spacing: 0em;
height: 36px;
}
.iPhoneCheckHandle {
top: 8px;
}

+ 108
- 0
code/html/custom.js View File

@ -0,0 +1,108 @@
var update_timer = null;
var relaySlider;
function doUpdate() {
var self = $(this);
self.addClass("loading");
$.ajax({
'method': 'POST',
'url': '/post',
'dataType': 'json',
'data': $("#formSave").serializeArray()
}).done(function(data) {
self.removeClass("loading");
}).fail(function() {
self.removeClass("loading");
});
}
function showPanel() {
$(".panel").hide();
$("#" + $(this).attr("data")).show();
if ($("#layout").hasClass('active')) toggleMenu();
};
function toggleMenu() {
$("#layout").toggleClass('active');
$("#menu").toggleClass('active');
$("#menuLink").toggleClass('active');
}
function parseResponse(data) {
// pre-process
if ("network" in data) data.network = data.network.toUpperCase();
if ("mqttStatus" in data) data.mqttStatus = data.mqttStatus ? "CONNECTED" : "NOT CONNECTED";
// relay
if ("relayStatus" in data) {
$("input[name='relayStatus']")
.prop("checked", data.relayStatus)
.iphoneStyle("refresh");
}
// title
if ("app" in data) {
document.title = data.app;
$(".pure-menu-heading").html(data.app);
}
// automatic assign
Object.keys(data).forEach(function(key) {
var id = "input[name=" + key + "]";
if ($(id).length) $(id).val(data[key]);
});
// WIFI
var groups = $("#panel-wifi .pure-g");
for (var i in data.wifi) {
var wifi = data.wifi[i];
Object.keys(wifi).forEach(function(key) {
var id = "input[name=" + key + "]";
if ($(id, groups[i]).length) $(id, groups[i]).val(wifi[key]);
});
};
if ("updateInterval" in data) {
if (update_timer) clearInterval(update_timer);
if (data.updateInterval > 0) {
update_timer = setInterval(update, data.updateInterval);
}
}
}
function update() {
$.ajax({
'method': 'GET',
'url': '/status',
'dataType': 'json'
}).done(parseResponse);
}
function init() {
$.ajax({
'method': 'GET',
'url': '/get',
'dataType': 'json'
}).done(parseResponse);
}
$(function() {
$("#menuLink").on('click', toggleMenu);
$(".button-update").on('click', doUpdate);
$(".pure-menu-link").on('click', showPanel);
relaySlider = $('#relayStatus').iphoneStyle({
checkedLabel: 'ON',
uncheckedLabel: 'OFF',
onChange: function(elem, value) {
$.ajax({
'method': 'GET',
'url': value ? '/relay/on' : '/relay/off',
'dataType': 'json'
});
setTimeout(update, 200);
}
});
init();
});

code/data/fsversion → code/html/fsversion View File


+ 13
- 179
code/html/index.html View File

@ -6,187 +6,15 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" /> <link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- build:css style.css -->
<link rel="stylesheet" href="pure-min.css" /> <link rel="stylesheet" href="pure-min.css" />
<link rel="stylesheet" href="side-menu-min.css" />
<link rel="stylesheet" href="side-menu.css" />
<link rel="stylesheet" href="grids-responsive-min.css" /> <link rel="stylesheet" href="grids-responsive-min.css" />
<link rel="stylesheet" href="checkboxes-min.css" />
<script src="jquery-1.12.3.min.js"></script>
<script src="checkboxes-min.js"></script>
<style>
#menu .pure-menu-heading {
font-size: 100%;
padding: .5em .5em;
}
.header h2 {
font-size: 1em;
}
.panel {
display: none;
}
.content {
margin: 0px;
}
.page {
margin-top: 40px;
}
.center {
text-align: center;
}
.pure-button {
color: white;
padding: 8px 16px;
border-radius: 4px;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
.button-update {
width: 100px;
margin: 50px auto;
background: #1f8dd6;
}
.pure-g {
margin-bottom: 20px;
}
legend {
font-weight: bold;
}
.l-box {
padding-right: 1px;
}
#topics .pure-g {
padding-bottom: 5px;
margin-bottom: 5px;
border-bottom: 1px dashed #e5e5e5;
}
.pure-form input[type=text][disabled] {
color: #777777;
}
div.hint {
font-size: 80%;
color: #ccc;
margin-top: -5px;
}
.iPhoneCheckContainer {
letter-spacing: 0em;
height: 36px;
}
.iPhoneCheckHandle {
top: 8px;
}
</style>
<script>
var update_timer = null;
var relaySlider;
function doUpdate() {
var self = $(this);
self.addClass("loading");
$.ajax({
'method': 'POST',
'url': '/post',
'dataType': 'json',
'data': $("#formSave").serializeArray()
}).done(function(data) {
self.removeClass("loading");
}).fail(function() {
self.removeClass("loading");
});
}
function showPanel() {
$(".panel").hide();
$("#" + $(this).attr("data")).show();
if ($("#layout").hasClass('active')) toggleMenu();
};
function toggleMenu() {
$("#layout").toggleClass('active');
$("#menu").toggleClass('active');
$("#menuLink").toggleClass('active');
}
function parseResponse(data) {
// pre-process
if ("network" in data) data.network = data.network.toUpperCase();
if ("mqttStatus" in data) data.mqttStatus = data.mqttStatus ? "CONNECTED" : "NOT CONNECTED";
// relay
if ("relayStatus" in data) {
$("input[name='relayStatus']")
.prop("checked", data.relayStatus)
.iphoneStyle("refresh");
}
// title
if ("app" in data) {
document.title = data.app;
$(".pure-menu-heading").html(data.app);
}
// automatic assign
Object.keys(data).forEach(function(key) {
var id = "input[name=" + key + "]";
if ($(id).length) $(id).val(data[key]);
});
// WIFI
var groups = $("#panel-wifi .pure-g");
for (var i in data.wifi) {
var wifi = data.wifi[i];
Object.keys(wifi).forEach(function(key) {
var id = "input[name=" + key + "]";
if ($(id, groups[i]).length) $(id, groups[i]).val(wifi[key]);
});
};
if ("updateInterval" in data) {
if (update_timer) clearInterval(update_timer);
if (data.updateInterval > 0) {
update_timer = setInterval(update, data.updateInterval);
}
}
}
function update() {
$.ajax({
'method': 'GET',
'url': '/status',
'dataType': 'json'
}).done(parseResponse);
}
function init() {
$.ajax({
'method': 'GET',
'url': '/get',
'dataType': 'json'
}).done(parseResponse);
}
$(function() {
$("#menuLink").on('click', toggleMenu);
$(".button-update").on('click', doUpdate);
$(".pure-menu-link").on('click', showPanel);
relaySlider = $('#relayStatus').iphoneStyle({
checkedLabel: 'ON',
uncheckedLabel: 'OFF',
onChange: function(elem, value) {
$.ajax({
'method': 'GET',
'url': value ? '/relay/on' : '/relay/off',
'dataType': 'json'
});
setTimeout(update, 200);
}
});
init();
});
</script>
<link rel="stylesheet" href="checkboxes.css" />
<link rel="stylesheet" href="custom.css" />
<!-- endbuild -->
</head> </head>
<body> <body>
@ -492,4 +320,10 @@
</body> </body>
<!-- build:js script.js -->
<script src="jquery-1.12.3.min.js"></script>
<script src="checkboxes.js"></script>
<script src="custom.js"></script>
<!-- endbuild -->
</html> </html>

+ 0
- 1
code/html/side-menu-min.css View File

@ -1 +0,0 @@
body{color:#777}.pure-img-responsive{max-width:100%;height:auto}#layout,#menu,.menu-link{-webkit-transition:all 0.2s ease-out;-moz-transition:all 0.2s ease-out;-ms-transition:all 0.2s ease-out;-o-transition:all 0.2s ease-out;transition:all 0.2s ease-out}#layout{position:relative;padding-left:0}#layout.active #menu{left:150px;width:150px}#layout.active .menu-link{left:150px}.content{margin:0 auto;padding:0 2em;max-width:800px;margin-bottom:50px;line-height:1.6em}.header{margin:0;color:#333;text-align:center;padding:2.5em 2em 0;border-bottom:1px solid #eee}.header h1{margin:0.2em 0;font-size:3em;font-weight:300}.header h2{font-weight:300;color:#ccc;padding:0;margin-top:0}.content-subhead{margin:50px 0 20px 0;font-weight:300;color:#888}#menu{margin-left:-150px;width:150px;position:fixed;top:0;left:0;bottom:0;z-index:1000;background:#191818;overflow-y:auto;-webkit-overflow-scrolling:touch}#menu a{color:#999;border:none;padding:0.6em 0 0.6em 0.6em}#menu .pure-menu,#menu .pure-menu ul{border:none;background:transparent}#menu .pure-menu ul,#menu .pure-menu .menu-item-divided{border-top:1px solid #333}#menu .pure-menu li a:hover,#menu .pure-menu li a:focus{background:#333}#menu .pure-menu-selected,#menu .pure-menu-heading{background:#1f8dd6}#menu .pure-menu-selected a{color:#fff}#menu .pure-menu-heading{font-size:110%;color:#fff;margin:0}.menu-link{position:fixed;display:block;top:0;left:0;background:#000;background:rgba(0,0,0,0.7);font-size:10px;z-index:10;width:2em;height:auto;padding:2.1em 1.6em}.menu-link:hover,.menu-link:focus{background:#000}.menu-link span{position:relative;display:block}.menu-link span,.menu-link span:before,.menu-link span:after{background-color:#fff;width:100%;height:0.2em}.menu-link span:before,.menu-link span:after{position:absolute;margin-top:-0.6em;content:" "}.menu-link span:after{margin-top:0.6em}@media (min-width: 48em){.header,.content{padding-left:2em;padding-right:2em}#layout{padding-left:150px;left:0}#menu{left:150px}.menu-link{position:fixed;left:150px;display:none}#layout.active .menu-link{left:150px}}@media (max-width: 48em){#layout.active{position:relative;left:150px}}

+ 25
- 0
code/package.json View File

@ -0,0 +1,25 @@
{
"name": "esp8266-filesystem-builder",
"version": "0.1.0",
"description": "Gulp based build system for ESP8266 file system files",
"main": "gulpfile.js",
"repository": {
"type": "git",
"url": ""
},
"author": "Xose Pérez <xose.perez@gmail.com>",
"license": "MIT",
"homepage": "",
"devDependencies": {
"del": "^2.2.1",
"gulp": "^3.9.1",
"gulp-clean-css": "^2.0.10",
"gulp-gzip": "^1.4.0",
"gulp-htmlmin": "^2.0.0",
"gulp-if": "^2.0.1",
"gulp-plumber": "^1.1.0",
"gulp-uglify": "^1.5.3",
"gulp-useref": "^3.1.2"
},
"dependencies": {}
}

+ 1
- 1
code/src/defaults.h View File

@ -41,7 +41,7 @@
#ifdef NODEMCUV2 #ifdef NODEMCUV2
#define MANUFACTURER "NODEMCU" #define MANUFACTURER "NODEMCU"
#define DEVICE "LOLIN" #define DEVICE "LOLIN"
#define LED_PIN 16
#define LED_PIN 4
#endif #endif
#define AP_PASS "fibonacci" #define AP_PASS "fibonacci"


Loading…
Cancel
Save