mirror of
https://github.com/Ekokumppanit/Bicyclesim.git
synced 2026-01-26 03:04:09 +00:00
Add speed sensor implementation for Arduino Leonardo. Adapt simulation code to use data from Arduino.
This commit is contained in:
@@ -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
67
arduino/arduino.ino
Normal 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);
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user