From f7df106272ccb5263d9affaf3f3e2c05ebfbd60c Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Wed, 8 May 2013 13:24:29 +0300 Subject: [PATCH] Add speed sensor implementation for Arduino Leonardo. Adapt simulation code to use data from Arduino. --- README.md | 7 +--- arduino/arduino.ino | 67 ++++++++++++++++++++++++++++++ client/bicyclesim-meteor.html | 10 ++--- client/main.sim.js | 78 ++++++++++++++++++++--------------- 4 files changed, 118 insertions(+), 44 deletions(-) create mode 100644 arduino/arduino.ino diff --git a/README.md b/README.md index 12e695e..6579642 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/arduino/arduino.ino b/arduino/arduino.ino new file mode 100644 index 0000000..580dbe8 --- /dev/null +++ b/arduino/arduino.ino @@ -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); +} diff --git a/client/bicyclesim-meteor.html b/client/bicyclesim-meteor.html index 7fbe388..26e177f 100644 --- a/client/bicyclesim-meteor.html +++ b/client/bicyclesim-meteor.html @@ -66,18 +66,18 @@

Ekokumppanit Oy:n työntekijä Juho Teperi on toteuttanut tämän prototyyppi web-sovelluksen Energia 2012 messuja varten. - Ohjelmassa voit liikkua (polkea) ympäri maailmaa (tai ainakin valmiita reittejä). + Ohjelmassa voit liikkua polkemalla ympäri maailmaa tai ainakin valmiita reittejä.

Käyttö

- Omalla koneellasi voit kokeilla toimintaa valitsemalla Simulaatio-välilehdeltä reitin ja hakkaamalla välilyöntiä. - Parhaan kokemuksen saat kun liität tietokoneeseen polkupyörän telineen (traineri tai rolleri) ja nopeusanturin (laite joka lähettää näppäinpainalluksen kun rengas on pyörähtänyt kierroksen) avulla. + Omalla koneellasi voit kokeilla toimintaa valitsemalla Simulaatio-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.

Toteutus

- Ohjelma on toteutettu käyttäen Meteor JavaSript sovelluskehystä (käyttää palvelinpuolella Node.js). + Ohjelma on toteutettu käyttäen Meteor JavaSript sovelluskehystä. Kuvat tulevat Google Streetview palvelusta Googlen tarjoaman JavaScript rajapinnan kautta.

@@ -85,7 +85,7 @@

Voit joko käyttää valmista ohjelmaa osoitteessa bicyclesim.ekokumppanit.fi tai hakea ohjelman lähdekoodin Githubista, - 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.

diff --git a/client/main.sim.js b/client/main.sim.js index 53da237..0fb1de7 100644 --- a/client/main.sim.js +++ b/client/main.sim.js @@ -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); } });