Skip to content

Instantly share code, notes, and snippets.

@synaptiko
Last active March 13, 2016 19:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save synaptiko/6097791 to your computer and use it in GitHub Desktop.
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)
<!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>
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
};
#!/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);
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