Add speed sensor implementation for Arduino Leonardo. Adapt simulation code to use data from Arduino.

This commit is contained in:
Juho Teperi
2013-05-08 13:24:29 +03:00
parent fab4189347
commit f7df106272
4 changed files with 118 additions and 44 deletions

View File

@@ -16,9 +16,6 @@ a speed sensor.
## Speed sensor
Most basic speed sensor would be "Keyboard" that sends a keypress whenever the wheel has turned one revolution.
~~This kind of sensor can be built from a old keyboard by soldering a reed switch to right conductors at keyboard circuit board. Some instructions here: http://www.instructables.com/id/Hacking-a-USB-Keyboard/.~~
Doesn't work. The reed switch wont be closed long enough for the keyboard controller or computer to register wheel revolutions.
Check arduino/arduino.ino for sample implementation for Arduino Leonardo.
It should be possible to build working device from Arduino Uno (http://mitchtech.net/arduino-usb-hid-keyboard/) or Arduino Due (http://www.i-programmer.info/news/91-hardware/4965-new-powerful-arduino-due-.html).
Device should read the reed switch status every 1ms and send keypress maybe every 500ms (send 'a' if there was one wheel revolution since last keypress, send 'b' if two etc).
It works as a keyboard and sends the number of wheel revolutions once per second.

67
arduino/arduino.ino Normal file
View File

@@ -0,0 +1,67 @@
// Inspiration and parts of code from:
// http://www.instructables.com/id/Arduino-Bike-Speedometer/
// Connect the reed between 5V and A0.
// Add a 10kOhm resistor between ground and A0.
#define reed A0 // pin connected to read switch
// 80ms per wheel rev:
// (28 inch * pi) / (80 millisecond) = 100.543531 km/h
const int DEBOUNCE = 80; // 100 * 1/(1kHz) = 100ms
int val;
int revs = 0;
int counter = 0;
void setup() {
pinMode(reed, INPUT);
cli();
// set timer1 interrupt at 1kHz
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;
// set timer count for 1khz increments
OCR1A = 1999;// = (1/1000) / ((1/(16*10^6))*8) - 1
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS11 bit for 8 prescaler
TCCR1B |= (1 << CS11);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei();
Keyboard.begin();
}
// 1kHz timer
ISR(TIMER1_COMPA_vect) {
val = digitalRead(reed);
if (val) {
if (counter == 0) {
++revs;
counter = DEBOUNCE;
} else if (counter > 0) {
--counter;
}
} else if (counter > 0) {
--counter;
}
}
void output() {
if (revs > 9) {
Keyboard.write('a' + revs - 10);
} else if (revs > 0) {
Keyboard.write('0' + revs);
}
revs = 0;
}
void loop(){
output();
delay(1000);
}

View File

@@ -66,18 +66,18 @@
<p>
<a href="http://www.ekokumppanit.fi" target="new">Ekokumppanit Oy:n</a> työntekijä Juho Teperi on toteuttanut tämän prototyyppi web-sovelluksen
<a href="http://www.expomark.fi/fi/messut/energia2012" target="new">Energia 2012</a> messuja varten.
Ohjelmassa voit liikkua (polkea) ympäri maailmaa (tai ainakin valmiita reittejä).
Ohjelmassa voit liikkua polkemalla ympäri maailmaa tai ainakin valmiita reittejä.
</p>
<h1>Käyttö</h1>
<p>
Omalla koneellasi voit kokeilla toimintaa valitsemalla <em>Simulaatio</em>-välilehdeltä reitin ja hakkaamalla välilyöntiä.
Parhaan kokemuksen saat kun liität tietokoneeseen polkupyörän telineen <small>(traineri tai rolleri)</small> ja nopeusanturin <small>(laite joka lähettää näppäinpainalluksen kun rengas on pyörähtänyt kierroksen)</small> avulla.
Omalla koneellasi voit kokeilla toimintaa valitsemalla <em>Simulaatio</em>-välilehdeltä reitin ja hakkaamalla näppäimistöltä numeroita 1-9 ja kirjamia a-f.
Parhaan kokemuksen saat kun liität tietokoneeseen polkupyörän telineen ja nopeusanturin avulla. Nopeusanturina toimii laite joka lähettää näppäinpainalluksen kun rengas on pyörähtänyt kierroksen.
</p>
<h1>Toteutus</h1>
<p>
Ohjelma on toteutettu käyttäen <a href="http://meteor.com" target="new">Meteor</a> JavaSript sovelluskehystä <small>(käyttää palvelinpuolella Node.js)</small>.
Ohjelma on toteutettu käyttäen <a href="http://meteor.com" target="new">Meteor</a> JavaSript sovelluskehystä.
Kuvat tulevat Google Streetview palvelusta Googlen tarjoaman JavaScript rajapinnan kautta.
</p>
@@ -85,7 +85,7 @@
<p>
Voit joko käyttää valmista ohjelmaa osoitteessa <a href="http://bicyclesim.ekokumppanit.fi">bicyclesim.ekokumppanit.fi</a> tai
hakea ohjelman lähdekoodin <a href="http://github.com/Ekokumppanit/Bicyclesim" target="new">Githubista</a>,
voit myös tehdä vapaasti muutoksia koodiin ja lähettää parannuksesi viralliseen versioon jotta muutkin hyötyvät niistä.
voit myös tehdä vapaasti muutoksia koodiin.
</p>
</div>

View File

@@ -1,38 +1,64 @@
window.point = null;
window.traveled = 0;
function move () {
// var dist = localStorage['multiplier'] * c();
var dist = localStorage['multiplier'] * 0.1 * Session.get('speed');
var createRingBuffer = function (length){
var pointer = 0, buffer = [], sum = 0;
return {
push: function (item) {
if (buffer[pointer] > 0) {
sum -= buffer[pointer];
if (sum <= 0) sum = 0;
}
buffer[pointer] = item;
sum += item;
pointer = (length + pointer + 1) % length;
},
sum: function () {
return sum;
}
};
};
var revs = 0;
$(document).bind('keydown', function (e) {
if (e.keyCode > 48 /* 0 */ && e.keyCode <= 57 /* 9 */) {
revs += e.keyCode - 48;
} else if (e.keyCode >= 65 /* a */ && e.keyCode <= 70 /* f */) {
revs += e.keyCode - 65 + 10;
}
if (revs === 0) return;
var dist = revs * c();
revs = 0;
Session.set('distance', Session.get('distance') + dist);
window.traveled += dist;
window.traveled += localStorage['multiplier'] * dist;
if (window.traveled >= window.point.distance) {
var next = Points.findOne({_id: window.point.next});
// Go to next point, if one exists
if (next) {
window.traveled -= next.distance;
window.traveled -= localStorage['multiplier'] * next.distance;
maps.travel(next._id, {route: true});
window.point = next;
}
}
});
var speed_buffer = createRingBuffer(5 * 2);
var prev = 0;
function speedo() {
speed_buffer.push(Session.get('distance') - prev);
prev = Session.get('distance') || 0;
Session.set('speed', speed_buffer.sum() / 5.0);
}
var line = '';
$(document).on('keydown', function (e) {
if (e.keyCode === 13) { // enter
if (line.length >= 1 && line[0] == 'S') {
var speed = Number(line.slice(1));
Session.set('speed', speed);
$('.speedSlider').slider('value', speed);
}
line = '';
} else {
line += String.fromCharCode(e.keyCode);
}
});
setInterval(speedo, 500);
Template.sim.speed = function () {
return Session.get('speed');
@@ -53,7 +79,6 @@ Template.sim.helpers({
window.init_sim = function init_sim() {
var i;
Meteor.autosubscribe(function () {
if (Session.equals('page', 'sim')) {
var route = Routes.findOne({_id: Session.get('route')});
@@ -74,22 +99,7 @@ Meteor.autosubscribe(function () {
maps.lines.route.add(p.latlng);
p = Points.findOne({_id: p.next});
}
i = setInterval(move, 100);
}
$('.speedSlider').slider({
orientation: 'vertical',
range: 'min',
min: 0,
max: 22.2, // m/s
value: 0,
slide: function (event, ui) {
Session.set('speed', ui.value);
}
});
} else {
i = clearInterval(i);
}
});