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 ## Speed sensor
Most basic speed sensor would be "Keyboard" that sends a keypress whenever the wheel has turned one revolution. Check arduino/arduino.ino for sample implementation for Arduino Leonardo.
~~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.
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). It works as a keyboard and sends the number of wheel revolutions once per second.
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).

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> <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.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. <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> </p>
<h1>Käyttö</h1> <h1>Käyttö</h1>
<p> <p>
Omalla koneellasi voit kokeilla toimintaa valitsemalla <em>Simulaatio</em>-välilehdeltä reitin ja hakkaamalla välilyöntiä. 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 <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. 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> </p>
<h1>Toteutus</h1> <h1>Toteutus</h1>
<p> <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. Kuvat tulevat Google Streetview palvelusta Googlen tarjoaman JavaScript rajapinnan kautta.
</p> </p>
@@ -85,7 +85,7 @@
<p> <p>
Voit joko käyttää valmista ohjelmaa osoitteessa <a href="http://bicyclesim.ekokumppanit.fi">bicyclesim.ekokumppanit.fi</a> tai 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>, 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> </p>
</div> </div>

View File

@@ -1,38 +1,64 @@
window.point = null; window.point = null;
window.traveled = 0; window.traveled = 0;
function move () { var createRingBuffer = function (length){
// var dist = localStorage['multiplier'] * c(); var pointer = 0, buffer = [], sum = 0;
var dist = localStorage['multiplier'] * 0.1 * Session.get('speed');
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); Session.set('distance', Session.get('distance') + dist);
window.traveled += dist; window.traveled += localStorage['multiplier'] * dist;
if (window.traveled >= window.point.distance) { if (window.traveled >= window.point.distance) {
var next = Points.findOne({_id: window.point.next}); var next = Points.findOne({_id: window.point.next});
// Go to next point, if one exists // Go to next point, if one exists
if (next) { if (next) {
window.traveled -= next.distance; window.traveled -= localStorage['multiplier'] * next.distance;
maps.travel(next._id, {route: true}); maps.travel(next._id, {route: true});
window.point = next; 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 = ''; setInterval(speedo, 500);
$(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);
}
});
Template.sim.speed = function () { Template.sim.speed = function () {
return Session.get('speed'); return Session.get('speed');
@@ -53,7 +79,6 @@ Template.sim.helpers({
window.init_sim = function init_sim() { window.init_sim = function init_sim() {
var i;
Meteor.autosubscribe(function () { Meteor.autosubscribe(function () {
if (Session.equals('page', 'sim')) { if (Session.equals('page', 'sim')) {
var route = Routes.findOne({_id: Session.get('route')}); var route = Routes.findOne({_id: Session.get('route')});
@@ -74,22 +99,7 @@ Meteor.autosubscribe(function () {
maps.lines.route.add(p.latlng); maps.lines.route.add(p.latlng);
p = Points.findOne({_id: p.next}); 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);
} }
}); });