- PVSM.RU - https://www.pvsm.ru -
Здравствуй! Мне хотелось бы поделиться историей о создании велокопомпьютера своими руками. После покупки велосипеда я решил как-то фиксировать свой прогресс, но мобильные трекеры оказались не самым удобным решением, а из Китая ко мне уже летел заранее заказанный там BOGEER 823, что делало покупку на месте бессмысленной. Поэтому я решил попытаться сделать велокомп своими руками, заодно заложив в него основу для расширения: управление обвесом велосипеда (передний и задний свет, звонок).
В основе любого велокомпьютера — геркон, который фиксирует оборот колеса, а всё остальное это элементарная математика… Конечно, я поискал в интернете готовые реализации и формулы, которые мне понадобятся.
Как я уже писал, всё завязано на геркон [1]: магнит зафиксирован на спице, а сам геркон на «вилке». Когда магнит на вилке замыкает геркон — это значит, что колесо сделало полный оборот и велосипед проехал расстояние равное:
2*Pi*Rшины
Прежде всего я составил список того, что мне было нужно от велокомпьютера:
К счастью, все нужные модули были под рукой:
Я быстро собрал бутерброд: сверху экран, вторым слоем SD модуль и сама ардуинка, а часы под всем этим. Получилось довольно компактно. Конечно, если бы я использовал не готовые модули, а «рассыпуху» и травил платы, можно было бы выиграть много места.
Подключив геркон, я начал тестировать первую версию скетча, который базировался на раздобытых из сети скетча для работы с дисплеем [5] и англоязычном спидометре [6].
Убедившись в работоспособности конструкции, я решил провести первый тест. Питание я планировал от нескольких АА батареек или аккумулятора, но в тот момент под рукой их не оказалось… Поверьте, кататься на велосипеде с ноутбуком под мышкой то ещё удовольствие.
Тестирование принесло свои плоды. Я обнаружил ошибку в логике: сигнал от геркона всегда приравнивался к обороту колеса (если прошло минимальное время на оборот). Вроде, всё правильно, но если остановиться, зафиксировав магнит напротив геркона, то программа считала, что вы едете очень быстро.
Немного доработав программу, я собрал «блок питания»: набор из пяти АА батареек. До этого я попытался использовать крону, но её для Ардуинки с обвесом оказалось мало.
Так как мой 5110-й был на красной подложке, а значит, подсветка в нём управлялась логическим нулём, а не единицей, от программного управления через аналоговый порт пришлось отказаться и добавить простую кнопку.
Кроме того, аналоговый порт без данных выдавал случайные значения, но это решилось простым резистором. А вот другой сюрприз от аналогового порта заставил меня поломать голову: значение замыкания геркона при питании от батареек были не такими, как при использовании USB порта.
Второй тест показал уже полную работоспособность системы. Единственной проблемой стала ошибка в расчётах: радиус «26 дюймового колеса» оказался «13.5» дюймов. В результате одометр немного врал (спасибо Яндекс.Картам за удобный инструмент для расчёта расстояний).
Устройство работало, но конечно хотелось придать законченность внешнему виду. Так уж получилось, что всё что мне удалось найти — только один корпус, и он оказался «узким».
Пришлось изменить расположение компонентов: в корпус всё влезло, но итоговый размер стал больше. В процессе перепайки пришлось заменить Arduino Nano и SD модуль: паяльником я владею так себе и часть выводов просто привёл в негодность.
Размеры: 72x50x28mm
После того, как я проверил работоспособность всех модулей, я решил зафиксировать их эпоксидным клеем для большей устойчивости к тряскам.
Моя торопливость сыграла злую шутку: я недостаточно хорошо зафиксировал провода под экраном и один из них стал пускать помехи (на самом деле это влияние на параметр «контрастность»).
Ещё пара исправлений в коде, и интерфейс был приведён к финальному (на текущий момент) виду.
Неприятность преподнесли часы: один из контактов отошёл, и время стало скакать (как если бы я перепутал RST и CLK). Но что самое интересное — после поездки часы пришли в норму.
Пожалуй, претензию можно предъявить к углу крепления: во время поездки смотреть неудобно.
Да и способ крепления изолентой неидеален, но у меня не получилось найти отдельно кронштейн для велосипедных гаджетов. Хотя, из Китая ко мне едет паучок [29]. Думаю, что он вполне подойдёт для решения данной проблемы.
Итого: ~1100р
Я получил ещё немного опыта работы с Arduino и сборки небольших работающих устройств. Весело провёл несколько часов, но что самое главное — теперь у меня есть работающий велокомпьютер. В планах написать нормальное сохранение/загрузку «глобальных» данных на карту памяти и добавить кнопку для отображения «глобальной» статистики. Для управления внешними устройствами у меня осталось шесть аналоговых портов (три кнопки & три тиристора) и где-то затерялся один цифровой, например, для дальномера (парктроник) или градусника. Очень хотелось бы решить проблему с питанием, ибо пять АА батареек на раме велосипеда не очень смотрятся.
#include <SD.h>
#include <DS1302.h>
//Function: This procedure applies to the Arduino driver NOKIA 5110 LCD.
//Time:September 4,2012
#define PIN_SCE 3
#define PIN_RESET 2
#define PIN_DC 4
#define PIN_SDIN 5
#define PIN_SCLK 6
#define reed A0//
#define LCD_C LOW
#define LCD_D HIGH
int count=0;
char dat[4];
char disp_tab[]={
'0','1','2','3','4','5','6','7','8','9'};
#define LCD_X 84
#define LCD_Y 48
const int chipSelect = 10;
static const byte ASCII[][5] =
{
{
0x00, 0x00, 0x00, 0x00, 0x00 } // 20
,{
0x00, 0x00, 0x5f, 0x00, 0x00 } // 21 !
,{
0x00, 0x07, 0x00, 0x07, 0x00 } // 22 "
,{
0x14, 0x7f, 0x14, 0x7f, 0x14 } // 23 #
,{
0x24, 0x2a, 0x7f, 0x2a, 0x12 } // 24 $
,{
0x23, 0x13, 0x08, 0x64, 0x62 } // 25 %
,{
0x36, 0x49, 0x55, 0x22, 0x50 } // 26 &
,{
0x00, 0x05, 0x03, 0x00, 0x00 } // 27 '
,{
0x00, 0x1c, 0x22, 0x41, 0x00 } // 28 (
,{
0x00, 0x41, 0x22, 0x1c, 0x00 } // 29 )
,{
0x14, 0x08, 0x3e, 0x08, 0x14 } // 2a *
,{
0x08, 0x08, 0x3e, 0x08, 0x08 } // 2b +
,{
0x00, 0x50, 0x30, 0x00, 0x00 } // 2c ,
,{
0x08, 0x08, 0x08, 0x08, 0x08 } // 2d -
,{
0x00, 0x60, 0x60, 0x00, 0x00 } // 2e .
,{
0x20, 0x10, 0x08, 0x04, 0x02 } // 2f /
,{
0x3e, 0x51, 0x49, 0x45, 0x3e } // 30 0
,{
0x00, 0x42, 0x7f, 0x40, 0x00 } // 31 1
,{
0x42, 0x61, 0x51, 0x49, 0x46 } // 32 2
,{
0x21, 0x41, 0x45, 0x4b, 0x31 } // 33 3
,{
0x18, 0x14, 0x12, 0x7f, 0x10 } // 34 4
,{
0x27, 0x45, 0x45, 0x45, 0x39 } // 35 5
,{
0x3c, 0x4a, 0x49, 0x49, 0x30 } // 36 6
,{
0x01, 0x71, 0x09, 0x05, 0x03 } // 37 7
,{
0x36, 0x49, 0x49, 0x49, 0x36 } // 38 8
,{
0x06, 0x49, 0x49, 0x29, 0x1e } // 39 9
,{
0x00, 0x36, 0x36, 0x00, 0x00 } // 3a :
,{
0x00, 0x56, 0x36, 0x00, 0x00 } // 3b ;
,{
0x08, 0x14, 0x22, 0x41, 0x00 } // 3c <
,{
0x14, 0x14, 0x14, 0x14, 0x14 } // 3d =
,{
0x00, 0x41, 0x22, 0x14, 0x08 } // 3e >
,{
0x02, 0x01, 0x51, 0x09, 0x06 } // 3f ?
,{
0x32, 0x49, 0x79, 0x41, 0x3e } // 40 @
,{
0x7e, 0x11, 0x11, 0x11, 0x7e } // 41 A
,{
0x7f, 0x49, 0x49, 0x49, 0x36 } // 42 B
,{
0x3e, 0x41, 0x41, 0x41, 0x22 } // 43 C
,{
0x7f, 0x41, 0x41, 0x22, 0x1c } // 44 D
,{
0x7f, 0x49, 0x49, 0x49, 0x41 } // 45 E
,{
0x7f, 0x09, 0x09, 0x09, 0x01 } // 46 F
,{
0x3e, 0x41, 0x49, 0x49, 0x7a } // 47 G
,{
0x7f, 0x08, 0x08, 0x08, 0x7f } // 48 H
,{
0x00, 0x41, 0x7f, 0x41, 0x00 } // 49 I
,{
0x20, 0x40, 0x41, 0x3f, 0x01 } // 4a J
,{
0x7f, 0x08, 0x14, 0x22, 0x41 } // 4b K
,{
0x7f, 0x40, 0x40, 0x40, 0x40 } // 4c L
,{
0x7f, 0x02, 0x0c, 0x02, 0x7f } // 4d M
,{
0x7f, 0x04, 0x08, 0x10, 0x7f } // 4e N
,{
0x3e, 0x41, 0x41, 0x41, 0x3e } // 4f O
,{
0x7f, 0x09, 0x09, 0x09, 0x06 } // 50 P
,{
0x3e, 0x41, 0x51, 0x21, 0x5e } // 51 Q
,{
0x7f, 0x09, 0x19, 0x29, 0x46 } // 52 R
,{
0x46, 0x49, 0x49, 0x49, 0x31 } // 53 S
,{
0x01, 0x01, 0x7f, 0x01, 0x01 } // 54 T
,{
0x3f, 0x40, 0x40, 0x40, 0x3f } // 55 U
,{
0x1f, 0x20, 0x40, 0x20, 0x1f } // 56 V
,{
0x3f, 0x40, 0x38, 0x40, 0x3f } // 57 W
,{
0x63, 0x14, 0x08, 0x14, 0x63 } // 58 X
,{
0x07, 0x08, 0x70, 0x08, 0x07 } // 59 Y
,{
0x61, 0x51, 0x49, 0x45, 0x43 } // 5a Z
,{
0x00, 0x7f, 0x41, 0x41, 0x00 } // 5b [
,{
0x02, 0x04, 0x08, 0x10, 0x20 } // 5c ¥
,{
0x00, 0x41, 0x41, 0x7f, 0x00 } // 5d ]
,{
0x04, 0x02, 0x01, 0x02, 0x04 } // 5e ^
,{
0x40, 0x40, 0x40, 0x40, 0x40 } // 5f _
,{
0x00, 0x01, 0x02, 0x04, 0x00 } // 60 `
,{
0x20, 0x54, 0x54, 0x54, 0x78 } // 61 a
,{
0x7f, 0x48, 0x44, 0x44, 0x38 } // 62 b
,{
0x38, 0x44, 0x44, 0x44, 0x20 } // 63 c
,{
0x38, 0x44, 0x44, 0x48, 0x7f } // 64 d
,{
0x38, 0x54, 0x54, 0x54, 0x18 } // 65 e
,{
0x08, 0x7e, 0x09, 0x01, 0x02 } // 66 f
,{
0x0c, 0x52, 0x52, 0x52, 0x3e } // 67 g
,{
0x7f, 0x08, 0x04, 0x04, 0x78 } // 68 h
,{
0x00, 0x44, 0x7d, 0x40, 0x00 } // 69 i
,{
0x20, 0x40, 0x44, 0x3d, 0x00 } // 6a j
,{
0x7f, 0x10, 0x28, 0x44, 0x00 } // 6b k
,{
0x00, 0x41, 0x7f, 0x40, 0x00 } // 6c l
,{
0x7c, 0x04, 0x18, 0x04, 0x78 } // 6d m
,{
0x7c, 0x08, 0x04, 0x04, 0x78 } // 6e n
,{
0x38, 0x44, 0x44, 0x44, 0x38 } // 6f o
,{
0x7c, 0x14, 0x14, 0x14, 0x08 } // 70 p
,{
0x08, 0x14, 0x14, 0x18, 0x7c } // 71 q
,{
0x7c, 0x08, 0x04, 0x04, 0x08 } // 72 r
,{
0x48, 0x54, 0x54, 0x54, 0x20 } // 73 s
,{
0x04, 0x3f, 0x44, 0x40, 0x20 } // 74 t
,{
0x3c, 0x40, 0x40, 0x20, 0x7c } // 75 u
,{
0x1c, 0x20, 0x40, 0x20, 0x1c } // 76 v
,{
0x3c, 0x40, 0x30, 0x40, 0x3c } // 77 w
,{
0x44, 0x28, 0x10, 0x28, 0x44 } // 78 x
,{
0x0c, 0x50, 0x50, 0x50, 0x3c } // 79 y
,{
0x44, 0x64, 0x54, 0x4c, 0x44 } // 7a z
,{
0x00, 0x08, 0x36, 0x41, 0x00 } // 7b {
,{
0x00, 0x00, 0x7f, 0x00, 0x00 } // 7c |
,{
0x00, 0x41, 0x36, 0x08, 0x00 } // 7d }
,{
0x10, 0x08, 0x08, 0x10, 0x08 } // 7e ←
,{
0x78, 0x46, 0x41, 0x46, 0x78 } // 7f →
};
void LcdCharacter(char character)
{
LcdWrite(LCD_D, 0x00);
for (int index = 0; index < 5; index++)
{
LcdWrite(LCD_D, ASCII[character - 0x20][index]);
}
LcdWrite(LCD_D, 0x00);
}
void LcdClear(void)
{
for (int index = 0; index < LCD_X * LCD_Y / 8; index++)
{
LcdWrite(LCD_D, 0x00);
}
}
void LcdInitialise(void)
{
pinMode(PIN_SCE, OUTPUT);
pinMode(PIN_RESET, OUTPUT);
pinMode(PIN_DC, OUTPUT);
pinMode(PIN_SDIN, OUTPUT);
pinMode(PIN_SCLK, OUTPUT);
digitalWrite(PIN_RESET, LOW);
digitalWrite(PIN_RESET, HIGH);
LcdWrite(LCD_C, 0x21 ); // LCD Extended Commands.
LcdWrite(LCD_C, 0xB1 ); // Set LCD Vop (Contrast).
LcdWrite(LCD_C, 0x04 ); // Set Temp coefficent. //0x04
LcdWrite(LCD_C, 0x14 ); // LCD bias mode 1:48. //0x13
LcdWrite(LCD_C, 0x0C ); // LCD in normal mode.
LcdWrite(LCD_C, 0x20 );
LcdWrite(LCD_C, 0x0C );
}
void LcdString(char *characters)
{
while (*characters)
{
LcdCharacter(*characters++);
}
}
void LcdWrite(byte dc, byte data)
{
digitalWrite(PIN_DC, dc);
digitalWrite(PIN_SCE, LOW);
shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data);
digitalWrite(PIN_SCE, HIGH);
}
void gotoXY(int x, int y)
{
LcdWrite( 0, 0x80 | x); // Column.
LcdWrite( 0, 0x40 | y); // Row.
}
void dispcountt(int count)
{
LcdCharacter(disp_tab[count/10000]);
LcdCharacter(disp_tab[count/1000%10]);
LcdCharacter(disp_tab[count/100%10]);
LcdCharacter(disp_tab[count%100/10]);
LcdCharacter(disp_tab[count%10]);
}
//storage variables
float radius = 13.5;//Радиус шины в дюймах. У 26 дюймовой шины, около 13.5 (магия)
boolean reedVal;
long timer = 0;//Время между оборотами в миллисекундах
float kmh = 0.00;//Скорость в км/ч
float circumference;
float distance = 0;//Дистанция поездки в метрах
long totalDistance = 0;//Дистанция всех поездок в метрах
long totalTime = 0;//Время всех поездок в минутах
float distanceBuffer = 0;//Несохранённая дистанция поездки в метрах
long timeBuffer = 0;//Несохранённое время поездки в секундах
float deltaD;//Проезжаем за оборот в метрах
boolean moving = false; //флаг - едем или нет
long time = 0;//Время езды в секундах
long lastTime = millis();
long duration = 0;
DS1302 rtc(9, 8, 7);
int maxReedCounter = 100;//Минимальное время в миллисекундах на оборот
int reedCounter;
void setup(void)
{
rtc.halt(false);
rtc.writeProtect(false);
pinMode(10, OUTPUT);//Магия для карты памяти
reedCounter = maxReedCounter;
deltaD = 2*3.415926535*radius*0.025;//Проезжается за один оборот колеса в метрах
circumference = 2*3.14*radius;//То же самое, но с меньшей точностью (для измерения скорости) и не в метрах
pinMode(A0, INPUT);
// TIMER SETUP- the timer interrupt allows preceise timed measurements of the reed switch
//for mor info about configuration of arduino timers see http://arduino.cc/playground/Code/Timer1
cli();//stop interrupts
//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();//allow interrupts
//END TIMER SETUP
LcdInitialise();
LcdClear();
LcdString("Initializing SD card...");
pinMode(10, OUTPUT);
gotoXY(0, 1);
if (!SD.begin(chipSelect)) {
LcdString("Card failed, or not present");
}else{
LcdString("Card initialized.");
File logFile = SD.open("logfile.txt");
if (logFile)
{
gotoXY(0, 2);
LcdString("reading file");
while (logFile.available()) {
totalDistance = logFile.parseInt();
totalTime = logFile.parseInt();
}
} else {
gotoXY(0, 2);
LcdString("error read file");
}
}
LcdClear();
gotoXY(0, 0);
LcdString("Spd:");//скорость Км/Ч
gotoXY(0, 1);
LcdString("Dst:");//дистанция за поездку в метрах
gotoXY(0, 2);
LcdString("Drt:");//длительность поездки в минутах
gotoXY(0, 3);
LcdString("Clc:");//часы
}
ISR(TIMER1_COMPA_vect) {//Прерывание на частота 1 кГц для проверки геркона
if(analogRead(reed) >= 680 && analogRead(reed) <= 742){//Геркон замкнут при таких показаниях
reedVal = true;
}else{
reedVal = false;
}
if (reedVal){//Геркон замкнут
if (timer > 110){
moving = true;
}
if (reedCounter == 0){//Минимальное время между импульсами прошло
if(moving){
kmh = (56.8*float(circumference))/float(timer)*1.61;//километры в час
distance += deltaD;
distanceBuffer
}
reedCounter = maxReedCounter;//Сбрасываем reedCounter
timer = 0;//Сбрасываем таймер геркона
} else{
if (reedCounter > 0){//Не улетаем в минуса
reedCounter -= 1;//Уменьшаем reedCounter
}
}
}else{//Если геркон не замкнут
if (reedCounter > 0){//Не улетаем в минуса
reedCounter -= 1;//Уменьшаем reedCounter
}
}
if (timer > 2000){
kmh = 0;//Если долго нет сигналов от геркона, мы стоим.
moving = false;
}else{
timer += 1;//Увеличиваем таймер
}
}
void updateDisplay(){
for(int i = 0; i<=3; i++){
gotoXY(28, i);
LcdString(" ");
}
char Sensor1CharMsg[8];
gotoXY(28, 0);
if(moving){
String Sensor1String((int)(kmh), DEC);
Sensor1String.toCharArray(Sensor1CharMsg,(Sensor1String.length()+1));
LcdString(Sensor1CharMsg);
}else{
LcdString("0");
}
LcdString("KM/h");
gotoXY(28, 1);
String Sensor1String2((int)(distance), DEC);
Sensor1String2.toCharArray(Sensor1CharMsg,(Sensor1String2.length()+1));
LcdString(Sensor1CharMsg);
LcdString("m");
gotoXY(28, 2);
String Sensor1String3((int)(duration/60), DEC);
Sensor1String3.toCharArray(Sensor1CharMsg,(Sensor1String3.length()+1));
LcdString(Sensor1CharMsg);
LcdString("min");
gotoXY(28, 3);
String Sensor1String4(rtc.getTimeStr(FORMAT_SHORT));
Sensor1String4.toCharArray(Sensor1CharMsg,(Sensor1String4.length()+1));
LcdString(Sensor1CharMsg);
}
void loop(void)
{
if (reedVal){//Геркон замкнут
if (timer <= 110){
moving = false;//Мы остановились, замкнув геркон
}else{
moving = true;
}
}
updateDisplay();
saveData();
if (moving){
int d = (int)((millis()-lastTime)/1000)
timeBuffer += d;
duration += d;
}
lastTime = millis();
delay(1000);
}
void saveData(){
while(timeBuffer>=60){
timeBuffer-=60;
totalTime++;
}
while(distanceBuffer>=11){
distanceBuffer-=11;
totalDistance+=11;
}
}
Автор: BIanF
Источник [39]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/diy/65945
Ссылки в тексте:
[1] геркон: http://ru.wikipedia.org/wiki/%C3%E5%F0%EA%EE%ED
[2] Image: http://http:http://habrastorage.org/getpro/habr/post_images/2fa/5d9/170/2fa5d91709979f200108bd6efcfc075f.jpg
[3] Image: http://http:http://habrastorage.org/getpro/habr/post_images/428/094/4f2/4280944f269c94035650f36ec8374b92.jpg
[4] Image: http://http:http://habrastorage.org/getpro/habr/post_images/471/937/de2/471937de24b4f1f5d8d204baa7eb8d56.jpg
[5] скетча для работы с дисплеем: http://rizmiestore.blogspot.ru/2014/03/nokia-lcd-5110-using-arduino.html
[6] англоязычном спидометре: http://www.instructables.com/id/Arduino-Bike-Speedometer/?ALLSTEPS
[7] Image: http://http:http://habrastorage.org/getpro/habr/post_images/a64/667/d70/a64667d70132a0bfbf2f1f5eaf8157d9.jpg
[8] Image: http://http:http://habrastorage.org/getpro/habr/post_images/3c9/f3b/903/3c9f3b903f0d868993f33fd3230f87f6.jpg
[9] Image: http://http:http://habrastorage.org/getpro/habr/post_images/070/c46/6ab/070c466ab009ea8e9e6af53d56be139d.jpg
[10] Image: http://http:http://habrastorage.org/getpro/habr/post_images/ab0/735/5ef/ab07355efb5e69a9a6a129ec2afe17f5.jpg
[11] Image: http://http:http://habrastorage.org/getpro/habr/post_images/f63/fc8/d5b/f63fc8d5b4eb323a07787b5109dd42bb.jpg
[12] Image: http://http:http://habrastorage.org/getpro/habr/post_images/da6/adf/7d3/da6adf7d38f6a346eed5a3c45e8716f7.jpg
[13] Image: http://http:http://habrastorage.org/getpro/habr/post_images/4f3/320/44b/4f332044b1d75aa6352aa2744c8eb71a.jpg
[14] Image: http://http:http://habrastorage.org/getpro/habr/post_images/d65/eae/ec4/d65eaeec4eec0dc79ee1de9dccaa921b.jpg
[15] Image: http://http:http://habrastorage.org/getpro/habr/post_images/c81/5d6/3c1/c815d63c12a77b14f3b4f285c9ce3682.jpg
[16] Image: http://http:http://habrastorage.org/getpro/habr/post_images/6c0/c39/39d/6c0c3939d7fb0b9f90c5afdc18d8b634.jpg
[17] Image: http://http:http://habrastorage.org/getpro/habr/post_images/a3d/47b/27e/a3d47b27eb34de8d6837c63893cb0b2c.jpg
[18] Image: http://http:http://habrastorage.org/getpro/habr/post_images/126/f16/464/126f16464922b76030a0e0a882f6b7c6.jpg
[19] Image: http://http:http://habrastorage.org/getpro/habr/post_images/b9d/a55/3cc/b9da553cc27e3ec4bd6ff74b9b63e9ac.jpg
[20] Image: http://http:http://habrastorage.org/getpro/habr/post_images/24f/79f/231/24f79f2314982882d097df41958e233d.jpg
[21] Image: http://http:http://habrastorage.org/getpro/habr/post_images/77e/0ed/639/77e0ed639bbe8dc3abd624db2299befd.jpg
[22] Image: http://http:http://habrastorage.org/getpro/habr/post_images/b58/3d7/74e/b583d774e9b09014dcb4f6e7e0f986d7.jpg
[23] Image: http://http:http://habrastorage.org/getpro/habr/post_images/43d/4d7/705/43d4d770542526944b80aaa302fa876e.jpg
[24] Image: http://http:http://habrastorage.org/getpro/habr/post_images/8fd/060/9b9/8fd0609b92627eba748ee678f4614096.jpg
[25] Image: http://http:http://habrastorage.org/getpro/habr/post_images/edd/e4d/88b/edde4d88bf2ec222f88ebe3660ce37d5.jpg
[26] Image: http://http:http://habrastorage.org/getpro/habr/post_images/430/89f/3bc/43089f3bc2bf3ad8f0570363eee7a648.jpg
[27] Image: http://http:http://habrastorage.org/getpro/habr/post_images/af4/317/f43/af4317f432b3cb142b13650854c8fdbc.jpg
[28] Image: http://http:http://habrastorage.org/getpro/habr/post_images/f0c/b36/cf3/f0cb36cf32a521bf82944ccd7d91155e.jpg
[29] паучок: http://ru.aliexpress.com/item/Bicycle-mobile-rack-multifunctional-mobile-rack-universal-spider-holder/1917747831.html?promotionVersion=1
[30] Image: http://http:http://habrastorage.org/getpro/habr/post_images/61d/177/009/61d1770092d85a6fcda93307da197a13.jpg
[31] Image: http://http:http://habrastorage.org/getpro/habr/post_images/9cc/73a/495/9cc73a495b713b47fe6594e8f4ade470.jpg
[32] Image: http://http:http://habrastorage.org/getpro/habr/post_images/3f2/d22/d01/3f2d22d01656aaca46ebede36f5790e6.jpg
[33] Image: http://http:http://habrastorage.org/getpro/habr/post_images/998/ad7/a5b/998ad7a5b9d0849f7b35d554d2b26759.jpg
[34] Image: http://http:http://habrastorage.org/getpro/habr/post_images/537/b39/a9d/537b39a9d24b5dadbd9c558016f9e4f4.jpg
[35] Image: http://http:http://habrastorage.org/getpro/habr/post_images/013/931/1bf/0139311bfc794740d686b2d3649b97fd.jpg
[36] Ваттметр/велокомп на ардуино: http://electrotransport.ru/ussr/index.php?topic=21742.msg417360#msg417360
[37] Велосипедный спидометр на Arduino: http://cxem.net/arduino/arduino60.php
[38] Image: http://http:http://habrastorage.org/getpro/habr/post_images/7c6/b20/df5/7c6b20df5414dc4c57348e342b5497de.jpg
[39] Источник: http://habrahabr.ru/post/231113/
Нажмите здесь для печати.