Last active
March 13, 2016 19:39
-
-
Save synaptiko/6097791 to your computer and use it in GitHub Desktop.
Servo PWM controller as Node.js service (requires kernel and modules from Adafruit's Occidentalis: http://learn.adafruit.com/adafruit-raspberry-pi-educational-linux-distro/occidentalis-v0-dot-2)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<style> | |
label { | |
display: inline-block; width: 100px; height: 20px; float: left; text-align: left; | |
} | |
input { | |
display: inline-block; width: 25%; height: 20px; | |
} | |
input.value { | |
width: 60px; | |
} | |
input.checkbox { | |
width: 20px; | |
} | |
strong { | |
margin-left: 10px; | |
height: 20px; | |
} | |
</style> | |
</head> | |
<body> | |
<label for="active">Active</label><input id="active" class="checkbox" type="checkbox" checked="checked"> | |
<br/> | |
<label for="delayed">Delayed</label><input id="delayed" class="checkbox" type="checkbox"> | |
<br/> | |
<label for="mode">Mode</label><select id="mode"> | |
<option value="pwm">pwm</option> | |
<option value="servo">servo</option> | |
<option value="audio">audio</option> | |
</select> | |
<br/> | |
<label for="servo_max">Servo Max</label><input id="servo_max" type="range" min="8" max="360" value="32"><input id="servo_max_value" class="value" type="number" min="8" max="360" value="32"> | |
<br/> | |
<label for="servo">Servo</label><input id="servo" type="range" min="0" max="32" value="0"><input id="servo_value" class="value" type="number" min="0" max="32" value="0"> | |
<br/> | |
<label for="duty">Duty</label><input id="duty" type="range" min="1" max="99" value="50"><input id="duty_value" class="value" type="number" min="1" max="99" value="50"> | |
<br/> | |
<label for="frequency">Frequency</label><input id="frequency" type="range" min="1" max="1000" value="100"><input id="frequency_value" class="value" type="number" min="1" max="1000" value="100"> | |
<br/> | |
<label for="mcf">MCF</label><select id="mcf"> | |
<option value="25">25 Hz</option> | |
<option value="50">50 Hz</option> | |
<option value="100">100 Hz</option> | |
<option value="500">500 Hz</option> | |
<option value="2000">2 kHz</option> | |
<option value="4000">4 kHz</option> | |
<option value="8000">8 kHz</option> | |
<option value="16000" selected="selected">16 kHz</option> | |
<option value="32000">32 kHz</option> | |
<option value="64000">64 kHz</option> | |
</select> | |
<br/> | |
<span>Real frequency<strong id="real_frequency">0</strong></span> | |
<script> | |
var active = document.getElementById('active'); | |
var delayed = document.getElementById('delayed'); | |
var mode = document.getElementById('mode'); | |
var servoMax = document.getElementById('servo_max'); | |
var servoMaxValue = document.getElementById('servo_max_value'); | |
var servo = document.getElementById('servo'); | |
var servoValue = document.getElementById('servo_value'); | |
var duty = document.getElementById('duty'); | |
var dutyValue = document.getElementById('duty_value'); | |
var frequency = document.getElementById('frequency'); | |
var frequencyValue = document.getElementById('frequency_value'); | |
var mcf = document.getElementById('mcf'); | |
var allFields = [active, delayed, mode, servoMax, servo, duty, frequency, mcf]; | |
bindRangeChange(servoMax, servoMaxValue); | |
bindRangeChange(servo, servoValue); | |
bindRangeChange(duty, dutyValue); | |
bindRangeChange(frequency, frequencyValue); | |
function bindRangeChange(rangeField, numericField) { | |
rangeField.addEventListener('change', onRangeChange.bind(rangeField, numericField), false); | |
numericField.addEventListener('change', onRangeChange.bind(numericField, rangeField), false); | |
} | |
function onRangeChange(valueEl) { | |
valueEl.value = this.value; | |
} | |
bindServoMaxChange(servoMax, servoMaxValue, servo, servoValue); | |
function bindServoMaxChange(max, maxValueEl, current, currentValueEl) { | |
maxValueEl.addEventListener('change', onServoMaxChange, false); | |
max.addEventListener('change', onServoMaxChange, false); | |
function onServoMaxChange() { | |
if (parseInt(current.value, 10) > parseInt(max.value, 10)) { | |
current.value = max.value; | |
onRangeChange.call(current, currentValueEl); | |
} | |
current.max = max.value; | |
currentValueEl.max = max.value; | |
} | |
} | |
bindChange(servoMaxValue); | |
bindChange(servoValue); | |
bindChange(dutyValue); | |
bindChange(frequencyValue); | |
function bindChange(field) { | |
field.addEventListener('change', onChange.bind(field), false); | |
} | |
function onChange() { | |
var id = this.id.replace(/_value$/, ''); | |
var value = this.value; | |
var xhr = new XMLHttpRequest(); | |
if (this.type === 'checkbox') { | |
value = (this.checked ? 1 : 0); | |
} | |
xhr.open('GET', ['/set?', id, '=', value].join(''), true); | |
xhr.addEventListener('readystatechange', function() { | |
if (xhr.readyState === 4) { | |
console.log('set:', id, '=', value, '->', 'result:', xhr.responseText); | |
} | |
}, false); | |
xhr.send(); | |
} | |
function initialSettings() { | |
allFields.forEach(function setValue(field) { | |
onChange.call(field); | |
bindChange(field); | |
}); | |
} | |
initialSettings(); | |
</script> | |
<script> | |
var realFrequency = document.getElementById('real_frequency'); | |
function connect() { | |
var source = new EventSource('/listen'); | |
var eventName = 'real_frequency'; | |
source.addEventListener(eventName, function(event) { | |
realFrequency.textContent = JSON.parse(event.data)[eventName]; | |
}, false); | |
source.addEventListener('open', function(event) { | |
console.log('SSE connection opened'); | |
}, false); | |
} | |
connect(); | |
</script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var fs = require('fs'); | |
var path = require('path'); | |
var basePath = '/sys/class/rpi-pwm/pwm0/'; | |
//var basePath = path.join(__dirname, 'test-pwm0'); | |
function set(object, callback) { | |
// code is written only for one key in object!!! | |
Object.keys(object).forEach(function(name) { | |
var value = object[name] + ''; | |
fs.writeFile(path.join(basePath, name), value, { encoding: 'ascii' }, function(error) { | |
if (error) return console.error(error); | |
get(name, callback); | |
}); | |
}); | |
} | |
function get(name, callback) { | |
fs.readFile(path.join(basePath, name), { encoding: 'ascii' }, function(error, data) { | |
if (error) return console.error(error); | |
var result = {}; | |
result[name] = data.trim(); | |
callback(result); | |
}); | |
} | |
module.exports = { | |
set: set, | |
get: get | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
var http = require('http'); | |
var fs = require('fs'); | |
var path = require('path'); | |
var url = require('url'); | |
var query = require('querystring'); | |
var sse = require('./sse'); | |
var rpiPwm = require('./rpiPwm'); | |
var app = http.createServer(function(request, response) { | |
var parsedUrl = url.parse(request.url); | |
var parsedQuery; | |
var listenEvent = 'real_frequency'; | |
if (parsedUrl.pathname === '/') { | |
response.writeHead(200); | |
fs.readFile(path.join(__dirname, 'index.html'), function(error, buffer) { | |
response.write(buffer); | |
response.end(); | |
}); | |
} | |
else if (parsedUrl.pathname === '/set' && parsedUrl.query) { | |
parsedQuery = query.parse(parsedUrl.query); | |
console.log('set', parsedQuery); | |
rpiPwm.set(parsedQuery, function(result) { | |
response.writeHead(200, { 'Content-Type': 'text/json' }); | |
response.end(JSON.stringify(result)); | |
}); | |
} | |
else if (parsedUrl.pathname === '/listen') { | |
response.connection.setTimeout(1000 * 60 * 20); // max idle 20 min. | |
sse.writeHead(request, response, function(request, response) { | |
sse.writeDataContinuosly(request, response, listenEvent, rpiPwm.get, 500); | |
}); | |
} | |
else if (parsedUrl.pathname === '/end') { | |
response.writeHead(200); | |
response.end(); | |
process.nextTick(function() { | |
process.exit(); | |
}); | |
} | |
else { | |
response.writeHead(404); | |
response.end(); | |
} | |
}); | |
app.listen(8080); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function writeHead(request, response, callback) { | |
response.writeHead(200, { | |
'Content-Type': 'text/event-stream', | |
'Cache-Control': 'no-cache', | |
'Connection': 'keep-alive' | |
}); | |
response.write('retry: 500\n'); | |
return callback(request, response); | |
} | |
function writeData(request, response, event, data, callback) { | |
var id = (new Date().getTime()); | |
response.write('id: ' + id + '\n'); | |
response.write('event: ' + event + '\n'); | |
response.write('data: ' + JSON.stringify(data) + '\n\n'); | |
if (callback) { | |
return callback(request, response); | |
} | |
} | |
function writeDataContinuosly(request, response, event, fn, interval) { | |
var timeoutId; | |
var isClosed = false; | |
fn(event, function writeResult(result) { | |
if (isClosed) return; | |
writeData(request, response, event, result, function(request, response) { | |
if (isClosed) return; | |
timeoutId = setTimeout(fn.bind(null, event, writeResult), interval); | |
}); | |
}); | |
request.connection.addListener('close', function() { | |
isClosed = true; | |
if (timeoutId !== undefined) { | |
clearInterval(timeoutId); | |
} | |
}); | |
} | |
module.exports = { | |
writeHead: writeHead, | |
writeData: writeData, | |
writeDataContinuosly: writeDataContinuosly | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment