Skip to content

Instantly share code, notes, and snippets.

@lampeh
Last active December 11, 2015 01:53
Show Gist options
  • Save lampeh/7f49e596d2c10c1eaa5a to your computer and use it in GitHub Desktop.
Save lampeh/7f49e596d2c10c1eaa5a to your computer and use it in GitHub Desktop.
cp-xmas2015
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
server_tokens off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# access_log /dev/null;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
gzip_min_length 100;
gzip_vary on;
gzip_proxied any;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
upstream uv4l {
server 127.0.0.1:8080;
keepalive 1;
}
upstream node {
server 127.0.0.1:3000;
keepalive 1;
}
proxy_http_version 1.1;
proxy_set_header Connection "";
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name xmas.commerce-plus.com;
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
expires +1h;
}
location ~ \.(js|css|svg|png|ico)$ {
expires +1h;
}
location = /stream/video.mjpeg {
proxy_pass http://uv4l;
proxy_buffering off;
}
location /app/ {
proxy_pass http://node;
include /etc/nginx/cors.conf;
}
}
}
{
"name": "cp-xmas2015",
"version": "1.0.0",
"homepage": "http://xmas.commerce-plus.com/",
"license": "UNLICENSED",
"private": true,
"config" : {
"port": "3000",
"switch_host": "192.168.252.64",
"switch_user": "user:kl4cK",
"led_device": "/dev/ttyUSB0"
},
"dependencies": {
"express": "^4.13.3",
"body-parser": "^1.14.1",
"serialport": "^2.0.5"
}
}
// http://xmas.commerce-plus.com/
// Switch: Koukaam NETIO 230A, http://www.koukaam.se/koukaam/downloads/MAN_DE_NETIO-230A_2.20.pdf
// LED: LED stripe, Atmega 168, https://www.arduino.cc/en/Tutorial/ReadASCIIString
var express = require("express");
var bodyParser = require("body-parser");
var serialport = require("serialport");
var http = require("http");
// default config
const appPort = process.env.npm_package_config_port || 3000;
const switchHost = process.env.npm_package_config_switch_host || "192.168.10.100";
const switchUser = process.env.npm_package_config_switch_user || "user:user";
const ledDevice = process.env.npm_package_config_led_device || "/dev/ttyAMA0";
// TODO: unsorted foo
const switchTimeout = 2 * 1000;
const loginDelay = 0.5 * 1000;
var lastSwitchVal = "";
// switch refresh rate
const switchInterval = 1 * 1000;
// color refresh rate
const ledInterval = 0.5 * 1000;
// serial connection to Atmega
var ledPort = new serialport.SerialPort(ledDevice, {
baudrate: 9600,
parser: serialport.parsers.readline("\n")
});
// initial state
var ledColor = { "red": 0x00, "green": 0xFF, "blue": 0x00 }
// auto-reset switch if timeout > 0
var switchState = {
"sw1": { "val": 0, "timeout": 0 },
"sw2": { "val": 0, "timeout": 30 * 1000 },
"sw3": { "val": 1, "timeout": 0 },
"sw4": { "val": 0, "timeout": 10 * 1000 }
};
var app = express();
app.set("etag", false);
app.use(bodyParser.json());
app.route("/app/color")
.get(function(req, res) {
res.send(ledColor);
})
.post(function(req, res) {
console.log("POST color:", req.body);
for (var attr in ledColor) {
if (typeof req.body[attr] !== "undefined") {
var val = Math.max(0, Math.min(255, parseInt(req.body[attr])));
ledColor[attr] = isNaN(val) ? 0 : val;
}
}
console.log("ledColor =", ledColor);
res.send(ledColor);
});
app.route("/app/switch")
.post(function(req, res, next) {
console.log("POST switch:", req.body);
for (var attr in switchState) {
if (typeof req.body[attr] !== "undefined") {
// reset switch after timeout
if (switchState[attr].timeout && req.body[attr] && !switchState[attr].val) {
clearTimeout(switchState[attr].timer);
switchState[attr].timer = setTimeout(function(attr) {
switchState[attr].val = 0;
}, switchState[attr].timeout, attr);
}
switchState[attr].val = (req.body[attr] ? 1 : 0);
}
}
console.log("switchState =", switchState);
next();
})
// TODO: match only GET/POST without code duplication
.all(function(req, res) {
// reformat object
var body = {};
for (var attr in switchState) {
body[attr] = switchState[attr].val;
}
res.send(body);
});
function switchControl(command, cb) {
var options = {
host: switchHost,
path: "/tgi/control.tgi?" + command
};
var req = http.request(options, function(res) {
console.log("switch response code:", res.statusCode);
console.log("switch response headers:", res.headers);
var str = "";
res.on("data", function(chunk) { str += chunk; });
res.on("end", function() { cb(str); });
});
req.on("error", function(e) {
console.error("HTTP client error:", e);
cb(null);
});
req.setTimeout(switchTimeout, req.abort);
req.end();
}
function switchLogin() {
console.log("switchLogin()");
switchControl("login=p:" + switchUser, function(str) {
console.log("login response:", str);
setTimeout(updateSwitches, loginDelay);
});
}
// continously check switchState for updates
function updateSwitches() {
// var val = "" + switchState.sw1.val + switchState.sw2.val + switchState.sw3.val + switchState.sw4.val;
// TODO: performance in main loop?
// TODO: ensure consistent swX order
var val = "";
for (var attr in switchState) {
val += switchState[attr].val;
}
if (val === lastSwitchVal) {
setTimeout(updateSwitches, switchInterval);
return;
}
console.log("updateSwitches(): " + val);
switchControl("port=" + val, function(str) {
console.log("switch response:", str);
if (str === "<html>250 OK</html>\r\n") {
lastSwitchVal = val;
} else if (str === "<html>555 FORBIDDEN</html>\r\n") {
setTimeout(switchLogin, loginDelay);
return;
}
setTimeout(updateSwitches, switchInterval);
});
}
// continously set color through serial port
function updateColor() {
// TODO: driver is active-high. update Atmega code to use non-inverted values
var val = "" + (255-ledColor.red) + "," + (255-ledColor.green) + "," + (255-ledColor.blue);
// console.log("updateColor: " + val);
if (ledPort.isOpen()) {
ledPort.write(val + "\n", function(error, result) {
// console.log("ledPort.write() result:", result);
if (!error) {
ledPort.drain(function(error) {
if (error) {
console.error("ledPort.drain() error:", error);
}
setTimeout(updateColor, ledInterval);
});
} else {
console.error("ledPort.write() error:", error);
setTimeout(updateColor, ledInterval);
}
});
} else {
setTimeout(updateColor, ledInterval);
}
}
function startLED() {
ledPort.on("open", function() {
console.log("ledPort opened");
ledPort.on("data", function(data) {
// console.log("ledPort rx:", data);
});
updateColor();
});
}
function startSwitch() {
updateSwitches();
}
function startServer() {
// TODO: fork?
app.listen(appPort, function() {
console.log("listening at http://%s:%s", this.address().address, this.address().port);
});
}
startLED();
startSwitch();
startServer();
driver = raspicam
auto-video_nr = yes
verbosity = 6
syslog-host = localhost
frame-buffers = 4
drop-bad-frames = yes
encoding = mjpeg
width = 640
height = 480
framerate = 10
quality = 10
stills-denoise = yes
video-denoise = yes
nopreview = yes
fullscreen = no
### image settings options:
# sharpness = 0
# contrast = 0
# brightness = 50
# saturation = 0
# iso = 400
# vstab = yes
# ev = 0
# exposure = auto
# awb = auto
# imgfx = none
# metering = average
# rotation = 0
hflip = no
vflip = no
# shutter-speed = 0
# drc = off
# red-gain = 100
# blue-gain = 100
# text-annotation = HelloWorld!
# text-annotation-background = yes
statistics = yes
# output-buffers = 3
server-option = --port=8080
server-option = --max-streams=30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment