/*------------------------------------------------ NXT Tachometer by techbricks.nl Displays RPM (rotations per minute) average over the last second and last minute in large digits 18 x 28 pixels 24 september 2009 NXC (build on NXC/NBC firmware version 1.28, Bricxcc 3.3 - build 3.3.7.20, check http://bricxcc.sourceforge.net/nbc/ for more info) Using RCX legacy rotation sensor (which measures 16 ticks for every rotation) Read more about it at www.techbricks.nl/My-NXT-projects/nxttachometerspeedcomputer.html Important: set the deceleration gearing factor!!! The rotation sensor can measure up to 800 rmp. Speeds above that are not acurate. If you need to measure more revolutions per minute you should use gears to gear down the speed. If you set your gearing configuration below in this program, your NXT will display you engine speed coorect. drive_gear = rotation_sensor_gear = -------------------------------------------------*/ // Deceleration gearing factor long drive_gear=1; // is default set to 1; long rotation_sensor_gear=1; // is default set to 1; #define rotation_port S1 #define rotation_sensor SENSOR_1 // variable declaration int row,value,len_string,current,oldest; int decimal_point,current_position,point_position,start_position,end_position; int digit,current_digit,shift_x,shift_y; long rpm=0,rpm_avrg, sum=0,rotations_start,rotations_end,rotations,duration,second; long samples_rotations[61]; string sdiget,srpm,svalue,szeros; bool bit[10][8]; int shift_x_digit[5],shift_y_digit[5]; unsigned int result; byte fileHandle; short fileSize; short bytesWritten; string read; string write; int filenumber,filepart; string filename,sfilenumber,sfile,sfilepart; string text,text1,text2; /*--------------------------------------------------- This function draws the digit on the NXT display x_position is the horizontal position (digit 1,2,3 or 4) y_positopn is the virtical position (row 1 or 2) ----------------------------------------------------*/ void DisplayDigit(int digit, int position_x, int position_y) { // shift digit to the disired position shift_x=shift_x_digit[position_x]; shift_y=shift_y_digit[position_y]; /*---------- Construction of the digits ------------ The digits are made of 7 bits --1-- | | 2 3 |--4--| 5 6 | | --7-- -----------------------------------------------------*/ // display the first bit if (bit[digit][1]==true) { LineOut(1+shift_x,60-shift_y,16+shift_x,60-shift_y); LineOut(2+shift_x,59-shift_y,15+shift_x,59-shift_y); LineOut(3+shift_x,58-shift_y,14+shift_x,58-shift_y); } // display the second bit if (bit[digit][2]==true) { LineOut(0+shift_x,58-shift_y,0+shift_x,48-shift_y); LineOut(1+shift_x,57-shift_y,1+shift_x,49-shift_y); LineOut(2+shift_x,56-shift_y,2+shift_x,50-shift_y); } // display the thirt bit if (bit[digit][3]==true) { LineOut(17+shift_x,58-shift_y,17+shift_x,48-shift_y); LineOut(16+shift_x,57-shift_y,16+shift_x,49-shift_y); LineOut(15+shift_x,56-shift_y,15+shift_x,50-shift_y); } // display the fourth bit if (bit[digit][4]==true) { LineOut(2+shift_x,47-shift_y,15+shift_x,47-shift_y); LineOut(1+shift_x,46-shift_y,16+shift_x,46-shift_y); LineOut(2+shift_x,45-shift_y,15+shift_x,45-shift_y); } // display the fifth bit if (bit[digit][5]==true) { LineOut(0+shift_x,44-shift_y,0+shift_x,34-shift_y); LineOut(1+shift_x,43-shift_y,1+shift_x,35-shift_y); LineOut(2+shift_x,42-shift_y,2+shift_x,36-shift_y); } // display the sixth bit if (bit[digit][6]==true) { LineOut(17+shift_x,44-shift_y,17+shift_x,34-shift_y); LineOut(16+shift_x,43-shift_y,16+shift_x,35-shift_y); LineOut(15+shift_x,42-shift_y,15+shift_x,36-shift_y); } // display the seventh bit if (bit[digit][7]==true) { LineOut(3+shift_x,34-shift_y,14+shift_x,34-shift_y); LineOut(2+shift_x,33-shift_y,15+shift_x,33-shift_y); LineOut(1+shift_x,32-shift_y,16+shift_x,32-shift_y); } } /*-------------------------------------------------------- This function does the initializing of the figures ---------------------------------------------------------*/ void DisplayInitiation() { // digit 0 - bit 1,2,3,5,6,7 bit[0][1]=true; // _ bit[0][2]=true; // | | bit[0][3]=true; // |_| bit[0][4]=false; bit[0][5]=true; bit[0][6]=true; bit[0][7]=true; // digit 1 - bit 3,6 bit[1][1]=false; // | bit[1][2]=false; // | bit[1][3]=true; bit[1][4]=false; bit[1][5]=false; bit[1][6]=true; bit[1][7]=false; // digit 2 - bit 1,3,4,5,7 bit[2][1]=true; // _ bit[3][2]=false; // _| bit[2][3]=true; // |_ bit[2][4]=true; bit[2][5]=true; bit[2][6]=false; bit[2][7]=true; // digit 3 - bit 1,3,4,6,7 bit[3][1]=true; // _ bit[3][2]=false; // _| bit[3][3]=true; // _| bit[3][4]=true; bit[3][5]=false; bit[3][6]=true; bit[3][7]=true; // digit 4 - bit 2,3,4,6 bit[4][1]=false; // bit[4][2]=true; // |_| bit[4][3]=true; // | bit[4][4]=true; bit[4][5]=false; bit[4][6]=true; bit[4][7]=false; // digit 5 - bit 1,2,4,6,7 bit[5][1]=true; // _ bit[5][2]=true; // |_ bit[5][3]=false; // _| bit[5][4]=true; bit[5][5]=false; bit[5][6]=true; bit[5][7]=true; // digit 6 - bit 1,2,4,5,6,7 bit[6][1]=true; // _ bit[6][2]=true; // |_ bit[6][3]=false; // |_| bit[6][4]=true; bit[6][5]=true; bit[6][6]=true; bit[6][7]=true; // digit 7 - bit 1,3,6 bit[7][1]=true; // _ bit[7][2]=false; // | bit[7][3]=true; // | bit[7][4]=false; bit[7][5]=false; bit[7][6]=true; bit[7][7]=false; // digit 8 - bit 1,2,3,4,5,6,7 bit[8][1]=true; // _ bit[8][2]=true; // |_| bit[8][3]=true; // |_| bit[8][4]=true; bit[8][5]=true; bit[8][6]=true; bit[8][7]=true; // digit 9 - bit 1,2,3,4,6,7 bit[9][1]=true; // _ bit[9][2]=true; // |_| bit[9][3]=true; // _| bit[9][4]=true; bit[9][5]=false; bit[9][6]=true; bit[9][7]=true; // this specifices how many pixels to shift horizontal for the next digits shift_x_digit[1]=7; shift_x_digit[2]=32; shift_x_digit[3]=55; shift_x_digit[4]=80; // this specifices how many pixels to shift virtical for the next line shift_y_digit[1]=-1; shift_y_digit[2]=31; // clear the NXT display ClearScreen(); } /*-------------------------------------------------------- This function displayd the digits and decimal point on the correct row ---------------------------------------------------------*/ void DisplayNumber(int value,int decimal_point,int row) { if (row<1) row=1; if (row>2) row=2; if (decimal_point<0) decimal_point=0; if (decimal_point>6) decimal_point=6; // transform integer to string and determine the lenght. svalue=NumToStr(value); len_string=StrLen(svalue); if ((len_string-decimal_point)>4) { svalue="9999"; len_string=4; decimal_point=0; } else { if ((len_string-decimal_point)<-3) { svalue="0000"; len_string=4; decimal_point=0; } else { if ((len_string-decimal_point)<1) { szeros=SubStr("0000",1,(1+decimal_point-len_string)); svalue=StrCat(szeros,svalue); len_string=StrLen(svalue); decimal_point=decimal_point-len_string; if (len_string>4) { len_string=4; } decimal_point=decimal_point+len_string; } } } // Determine most right displayed digit. if (len_string>4) { decimal_point=decimal_point-(len_string-4); len_string=4; } // Determine most left digit position. start_position=5-len_string; current_position=1; while (current_position<=len_string) { // get sub string sdiget=SubStr(svalue,current_position-1,1); current_digit=StrToNum(sdiget); // display digit DisplayDigit(current_digit,(start_position+current_position-1),row); current_position=current_position+1; } point_position=4-decimal_point; if ((point_position>=0)&&(point_position<4)) { CircleOut(shift_x_digit[point_position+1]-4, 33-shift_y_digit[row], 1); CircleOut(shift_x_digit[point_position+1]-4, 33-shift_y_digit[row], 2); } } /*------------------------------------------------- Create a log file named "tachometer0-0.csv". If a file with that name already exists then it creates an other file. It increments the file number: "tachometer-0.csv" This file is 1024 bytes long. ---------------------------------------------------*/ void InitWriteToFile() { filenumber=0; filepart=0; filename="tachometer0-0.csv"; result=CreateFile(filename, 1024, fileHandle); // check if the file already exists while (result==LDR_FILEEXISTS) { CloseFile(fileHandle); filenumber=filenumber+1; filename=NumToStr(filenumber); filename=StrCat("tachometer",filename,"-0.csv"); result=CreateFile(filename, 1024, fileHandle); } // play a tone every time a file is created PlayTone(TONE_B7, 5); write = "RPM (last second),RPM (last minute)"; WriteLnString(fileHandle,write, bytesWritten); } /*------------------------------------------------- You can write strings to this file. Every string (line) is ended with a EOL (end of line). When this file is full a new file is created. The name of that file is "tachometer-.csv" (the file part number is incremented) ---------------------------------------------------*/ void WriteToFile(string write) { // write string to file result=WriteLnString(fileHandle,write, bytesWritten); // if the end of file is reached, close the file and create a new part if (result==LDR_EOFEXPECTED) { // close the current file CloseFile(fileHandle); // create the next file name filepart=filepart+1; sfilenumber=NumToStr(filenumber); sfilepart=NumToStr(filepart); // filename = "tachometer"++"-"++".csv" filename=StrCat("tachometer",sfilenumber,"-",sfilepart ,".csv"); // delete the file if it exists DeleteFile(filename); // create a new file CreateFile(filename, 1024, fileHandle); // play a tone every time a file is created PlayTone(TONE_B7, 5); WriteLnString(fileHandle,write, bytesWritten); } } // Close the file void StopWriteToFile() { // close the file CloseFile(fileHandle); } task main() { SetSensorType(rotation_port,SENSOR_TYPE_ROTATION); SetSensorMode(rotation_port,SENSOR_MODE_ROTATION); DisplayInitiation(); // Create a new log file InitWriteToFile(); // Duration is the sum the duration of all the sample that are processed duration=0; // set the array values to 0 for (int current=0;current<=60;current+=1) { samples_rotations[current]=0; } // The value that indicates the current sample current=0; // Clear the NXT screen ClearScreen(); // here ends the preperation fase. // Here starts a never ending loop // Every time the cyclus starts with a sample of 1000 ms // During that sample the amount of rotation sensor ticks are counted (16 ticks per rotation) // After th sample the amounts of rounds per minute are calculated // At the same time the amounts of rounds per minute each minute are calculated // The cyclus ends by displaying the figures in large digits on the NXT screen. while (true<>ButtonPressed(BTNCENTER,false)&&(FreeMemory()>=2000)) { // reset the rotation sensor to set the rotation counter to zero ResetSensor(rotation_port); // read the internal NXT clock (value in ms) second=CurrentTick(); // be sure that the clock tick value won't reach 4294967296 (2^32) within the next sample period (1000ms) // if so, then wait until it starts at zero again. while (second >4294966000) { second=CurrentTick(); } // read the rotation sensor value // each rotation is 16 ticks rotations_start=rotation_sensor; // wait 1 second (1000ms) and while (CurrentTick()<(second+1000)) { // read the rotation value rotations_end=rotation_sensor; } oldest=current+1; if (oldest>=60) oldest=0; // The average rotations per minute are measured in samples of 1000ms // The first second can only calculate the average over that second // There is not yet a history over the last minute. // After every past second there is more history duration=duration+1000; // Ones it reached 1 minute (60.000 ms) the durations stays 60 seconds. if (duration>=60000) duration=60000; // The last 60 samples (together 1 minute) are save in an array samples_rotations[current]=(rotations_end-rotations_start); // The amount of rotations over the last minute are the sum last 60 samples. // In other words: rotations=rotations+samples_rotations[current]-samples_rotations[oldest]; // calculations of the amount of rotations per minute over the last second. // note that the ratation senser adds 16 rotation ticks for every rotation rpm=(samples_rotations[current]*600*rotation_sensor_gear)/(drive_gear*16); // calculations of the amount of rotations per minute over the last minute. rpm_avrg=(rotations*600*rotation_sensor_gear)/((duration/1000)*16*drive_gear); // create string with comma seperated values text1=NumToStr(rpm); text2=NumToStr(rpm_avrg); text=StrCat(text1,",",text2,"," ); // write the comma seperated values to the csv file. The line will be end with a EOL WriteToFile(text); // clear the NXT display ClearScreen(); // Display Number on NXT screen value=rpm; decimal_point=1; //amount of figures on the right side of the decimal point, can be between 0 and 5 row=1; //row on the NXT screen, can be 1 or 2 DisplayNumber(value,decimal_point,row); // Display Number on NXT screen value=rpm_avrg; row=2; //row on the NXT screen, can be 1 or 2 DisplayNumber(value,decimal_point,row); // start the next sample current=current+1; if (current>=60) current=0; } // close the file StopWriteToFile(); }