Самодельный велокомпьютер, или как Arduino на улицу попал

#include #include

//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] = { { 0×00, 0×00, 0×00, 0×00, 0×00 } // 20 ,{ 0×00, 0×00, 0×5f, 0×00, 0×00 } // 21! ,{ 0×00, 0×07, 0×00, 0×07, 0×00 } // 22 » ,{ 0×14, 0×7f, 0×14, 0×7f, 0×14 } // 23 # ,{ 0×24, 0×2a, 0×7f, 0×2a, 0×12 } // 24 $ ,{ 0×23, 0×13, 0×08, 0×64, 0×62 } // 25% ,{ 0×36, 0×49, 0×55, 0×22, 0×50 } // 26 & ,{ 0×00, 0×05, 0×03, 0×00, 0×00 } // 27 ' ,{ 0×00, 0×1c, 0×22, 0×41, 0×00 } // 28 ( ,{ 0×00, 0×41, 0×22, 0×1c, 0×00 } // 29) ,{ 0×14, 0×08, 0×3e, 0×08, 0×14 } // 2a * ,{ 0×08, 0×08, 0×3e, 0×08, 0×08 } // 2b + ,{ 0×00, 0×50, 0×30, 0×00, 0×00 } // 2c, ,{ 0×08, 0×08, 0×08, 0×08, 0×08 } // 2d - ,{ 0×00, 0×60, 0×60, 0×00, 0×00 } // 2e . ,{ 0×20, 0×10, 0×08, 0×04, 0×02 } // 2f / ,{ 0×3e, 0×51, 0×49, 0×45, 0×3e } // 30 0 ,{ 0×00, 0×42, 0×7f, 0×40, 0×00 } // 31 1 ,{ 0×42, 0×61, 0×51, 0×49, 0×46 } // 32 2 ,{ 0×21, 0×41, 0×45, 0×4b, 0×31 } // 33 3 ,{ 0×18, 0×14, 0×12, 0×7f, 0×10 } // 34 4 ,{ 0×27, 0×45, 0×45, 0×45, 0×39 } // 35 5 ,{ 0×3c, 0×4a, 0×49, 0×49, 0×30 } // 36 6 ,{ 0×01, 0×71, 0×09, 0×05, 0×03 } // 37 7 ,{ 0×36, 0×49, 0×49, 0×49, 0×36 } // 38 8 ,{ 0×06, 0×49, 0×49, 0×29, 0×1e } // 39 9 ,{ 0×00, 0×36, 0×36, 0×00, 0×00 } // 3a: ,{ 0×00, 0×56, 0×36, 0×00, 0×00 } // 3b; ,{ 0×08, 0×14, 0×22, 0×41, 0×00 } // 3c < ,{ 0x14, 0x14, 0x14, 0x14, 0x14 } // 3d = ,{ 0x00, 0x41, 0x22, 0x14, 0x08 } // 3e > ,{ 0×02, 0×01, 0×51, 0×09, 0×06 } // 3f? ,{ 0×32, 0×49, 0×79, 0×41, 0×3e } // 40 @ ,{ 0×7e, 0×11, 0×11, 0×11, 0×7e } // 41 A ,{ 0×7f, 0×49, 0×49, 0×49, 0×36 } // 42 B ,{ 0×3e, 0×41, 0×41, 0×41, 0×22 } // 43 C ,{ 0×7f, 0×41, 0×41, 0×22, 0×1c } // 44 D ,{ 0×7f, 0×49, 0×49, 0×49, 0×41 } // 45 E ,{ 0×7f, 0×09, 0×09, 0×09, 0×01 } // 46 F ,{ 0×3e, 0×41, 0×49, 0×49, 0×7a } // 47 G ,{ 0×7f, 0×08, 0×08, 0×08, 0×7f } // 48 H ,{ 0×00, 0×41, 0×7f, 0×41, 0×00 } // 49 I ,{ 0×20, 0×40, 0×41, 0×3f, 0×01 } // 4a J ,{ 0×7f, 0×08, 0×14, 0×22, 0×41 } // 4b K ,{ 0×7f, 0×40, 0×40, 0×40, 0×40 } // 4c L ,{ 0×7f, 0×02, 0×0c, 0×02, 0×7f } // 4d M ,{ 0×7f, 0×04, 0×08, 0×10, 0×7f } // 4e N ,{ 0×3e, 0×41, 0×41, 0×41, 0×3e } // 4f O ,{ 0×7f, 0×09, 0×09, 0×09, 0×06 } // 50 P ,{ 0×3e, 0×41, 0×51, 0×21, 0×5e } // 51 Q ,{ 0×7f, 0×09, 0×19, 0×29, 0×46 } // 52 R ,{ 0×46, 0×49, 0×49, 0×49, 0×31 } // 53 S ,{ 0×01, 0×01, 0×7f, 0×01, 0×01 } // 54 T ,{ 0×3f, 0×40, 0×40, 0×40, 0×3f } // 55 U ,{ 0×1f, 0×20, 0×40, 0×20, 0×1f } // 56 V ,{ 0×3f, 0×40, 0×38, 0×40, 0×3f } // 57 W ,{ 0×63, 0×14, 0×08, 0×14, 0×63 } // 58 X ,{ 0×07, 0×08, 0×70, 0×08, 0×07 } // 59 Y ,{ 0×61, 0×51, 0×49, 0×45, 0×43 } // 5a Z ,{ 0×00, 0×7f, 0×41, 0×41, 0×00 } // 5b [ ,{ 0×02, 0×04, 0×08, 0×10, 0×20 } // 5c ¥ ,{ 0×00, 0×41, 0×41, 0×7f, 0×00 } // 5d ] ,{ 0×04, 0×02, 0×01, 0×02, 0×04 } // 5e ^ ,{ 0×40, 0×40, 0×40, 0×40, 0×40 } // 5f _ ,{ 0×00, 0×01, 0×02, 0×04, 0×00 } // 60 ` ,{ 0×20, 0×54, 0×54, 0×54, 0×78 } // 61 a ,{ 0×7f, 0×48, 0×44, 0×44, 0×38 } // 62 b ,{ 0×38, 0×44, 0×44, 0×44, 0×20 } // 63 c ,{ 0×38, 0×44, 0×44, 0×48, 0×7f } // 64 d ,{ 0×38, 0×54, 0×54, 0×54, 0×18 } // 65 e ,{ 0×08, 0×7e, 0×09, 0×01, 0×02 } // 66 f ,{ 0×0c, 0×52, 0×52, 0×52, 0×3e } // 67 g ,{ 0×7f, 0×08, 0×04, 0×04, 0×78 } // 68 h ,{ 0×00, 0×44, 0×7d, 0×40, 0×00 } // 69 i ,{ 0×20, 0×40, 0×44, 0×3d, 0×00 } // 6a j ,{ 0×7f, 0×10, 0×28, 0×44, 0×00 } // 6b k ,{ 0×00, 0×41, 0×7f, 0×40, 0×00 } // 6c l ,{ 0×7c, 0×04, 0×18, 0×04, 0×78 } // 6d m ,{ 0×7c, 0×08, 0×04, 0×04, 0×78 } // 6e n ,{ 0×38, 0×44, 0×44, 0×44, 0×38 } // 6f o ,{ 0×7c, 0×14, 0×14, 0×14, 0×08 } // 70 p ,{ 0×08, 0×14, 0×14, 0×18, 0×7c } // 71 q ,{ 0×7c, 0×08, 0×04, 0×04, 0×08 } // 72 r ,{ 0×48, 0×54, 0×54, 0×54, 0×20 } // 73 s ,{ 0×04, 0×3f, 0×44, 0×40, 0×20 } // 74 t ,{ 0×3c, 0×40, 0×40, 0×20, 0×7c } // 75 u ,{ 0×1c, 0×20, 0×40, 0×20, 0×1c } // 76 v ,{ 0×3c, 0×40, 0×30, 0×40, 0×3c } // 77 w ,{ 0×44, 0×28, 0×10, 0×28, 0×44 } // 78 x ,{ 0×0c, 0×50, 0×50, 0×50, 0×3c } // 79 y ,{ 0×44, 0×64, 0×54, 0×4c, 0×44 } // 7a z ,{ 0×00, 0×08, 0×36, 0×41, 0×00 } // 7b { ,{ 0×00, 0×00, 0×7f, 0×00, 0×00 } // 7c | ,{ 0×00, 0×41, 0×36, 0×08, 0×00 } // 7d } ,{ 0×10, 0×08, 0×08, 0×10, 0×08 } // 7e ← ,{ 0×78, 0×46, 0×41, 0×46, 0×78 } // 7f → };

void LcdCharacter (char character) { LcdWrite (LCD_D, 0×00); 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, 0×21); // LCD Extended Commands. LcdWrite (LCD_C, 0xB1); // Set LCD Vop (Contrast). LcdWrite (LCD_C, 0×04); // Set Temp coefficent. //0×04 LcdWrite (LCD_C, 0×14); // LCD bias mode 1:48. //0×13 LcdWrite (LCD_C, 0×0C); // LCD in normal mode. LcdWrite (LCD_C, 0×20); LcdWrite (LCD_C, 0×0C);

}

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, 0×80 | x); // Column. LcdWrite (0, 0×40 | 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; }

}

© Habrahabr.ru