Components and supplies
Standard LCD - 16x2 White on Blue
Arduino UNO
Photodiode, 45 °
Tools and machines
Mastech MS8217 Autorange Digital Multimeter
Soldering iron (generic)
Apps and platforms
Arduino IDE
Project description
Code
Chronograph v2.0
arduino
Sketch provides basic chronograph functionality by measuring time between trigerring both sensors and displaying calculated m/s and fps. Menu functionality has been added for setig up pellet weight needed for energy calculation. TO DO, add additional functionalities, such as average speed based on no. of shots etc.
1Chronograph 2 3Version 2.00 4 5Sketch provides basic chronograph functionality by measuring time between trigerring both sensors and displaying calculated m/s and fps. 6Menu functionality has been added for setig up pellet weight needed for energy calculation. 7TO DO, add additional functionalities, such as average speed based on no. of shots etc. 8 9 10Thanks to Phil Grant, polepole for providing parts of the code 11 12Petar Janevski 27/04/2020 13 14*/ 15 16 17#include <EEPROM.h> 18//#include <EEPROMAnything.h> 19#include <avr/sleep.h> 20#include <LiquidCrystal.h> 21#define Trip_in_1 8 //set light Trigger 1 to pin 8 22#define Trip_in_2 9 //set light Trigger 2 to pin 9 23#define UP 14 //Set the menu up button to input 14 24#define DOWN 15 //Set the menu down button to input 15 25#define OK_BTN 16 //Set menu button OK to input 16 26#define WAIT_STATE 0 27#define MONITOR_STATE 1 28#define OUTPUT_STATE 2 29#define MENU_STATE 3 30 31unsigned long Trigger_1 = 0; 32unsigned long Trigger_2 = 0; 33//volatile bool adcDone; 34//volatile boolean is_timeout = false; 35volatile unsigned int current_state = WAIT_STATE; 36//unsigned long my_timeout = 5000000; 37//char my_oldSREG; //to hold status register while ints are disabled 38unsigned long SensDist = 100000; //Distance between sensors in meters*1000000 (328084) 39float pWeight; //Pellet weight in grains 40int weightTens; //first two digits of pWeight 41int weightDec; //Decimal part of Pellet weight 42float mps = 0; //storage for meter per sec value 43float fps = 0; //storage for feet per sec value 44float joules = 0; //storage for feet per sec value 45unsigned long duration = 0; // time between triger 1 and 2 46//boolean firstShot = false; //flag to detect that a shot has been fired. remove adj. pellet weight 47unsigned long timeout = 0; //timer to return to main screen after measurement 48const int menuTimeout1 = 20000; // time of inactivity to return to main screen after measurement 49 50/* 51MENU REQUIRED GLOBAL VARIABLES & DEFINITIONS 52*/ 53#define MOVECURSOR 1 // constants for indicating whether cursor should be redrawn 54#define MOVELIST 2 // constants for indicating whether cursor should be redrawn 55byte totalRows = 2; // total rows of LCD 56byte totalCols = 16; // total columns of LCD 57unsigned long timeoutTime = 0; // this is set and compared to millis to see when the user last did something. 58const int menuTimeout = 5000; // time to timeout in a menu when user doesn't do anything. 59volatile unsigned int menu_state; 60unsigned long lastButtonPressed; // this is when the last button was pressed. It's used to debounce. 61const int debounceTime = 300; //this is the debounce and hold delay. 62 63 64// initialize the LCD library with the numbers of the interface pins 65 66LiquidCrystal lcd(5, 6, 7, 10, 11, 12); 67 68void setup () { 69 Serial.begin(9600); 70 Serial.println("Chrono v2.00"); 71 //Retreive pellet weight 72 weightTens = EEPROM.read(0); 73 weightDec = EEPROM.read(1); 74 pWeight = weightTens + float(weightDec)/100; 75 76// EEPROM_readAnything(0, Config); 77 Serial.print("EEPROM 0 = "); 78 Serial.println(EEPROM.read(0)); 79 Serial.print("EEPROM 1/100 = "); 80 Serial.println(float(EEPROM.read(1))/100); 81 Serial.print("pWeight = "); 82 Serial.println(pWeight); 83 Serial.print("weightTens = "); 84 Serial.println(weightTens); 85 Serial.print("weightDec = "); 86 Serial.println(weightDec); 87 88 pinMode(Trip_in_1, INPUT); //declare pins in use 89 pinMode(Trip_in_2, INPUT); 90 pinMode(UP, INPUT); 91 pinMode(DOWN, INPUT); 92 pinMode(OK_BTN, INPUT); 93 digitalWrite(UP, HIGH); //turn on pullup resistor for buttons 94 digitalWrite(DOWN, HIGH); 95 digitalWrite(OK_BTN, HIGH); 96 97 lcd.begin(16, 2); //2 line 16 character LCD 98 lcd.clear(); 99 100 lcd.setCursor(5, 0); //Draw splash screen 101 lcd.print("Chrono"); 102 lcd.setCursor(4, 1); 103 lcd.print("Ver 2.00"); 104 delay (3000); //show splash screen for 5 seconds 105 defaultScreen(); 106 reset_variables(); 107 108} 109 110 111void loop () { 112 113 switch (current_state) { 114 case WAIT_STATE: 115 if(digitalRead(Trip_in_1)==LOW) { 116 Trigger_1 = micros(); 117 current_state = MONITOR_STATE; 118 } 119 120 if (digitalRead(OK_BTN)==LOW) { 121 122 basicMenu(); 123 delay(200); 124 Serial.println("back in main loop"); 125 defaultScreen(); 126 127 } 128 129 130 if (timeout != 0 && timeout<millis()){ // user hasn't done anything in awhile 131 defaultScreen(); 132 timeout = 0; 133 } 134 135 break; 136 137 case MONITOR_STATE: 138 while(digitalRead(Trip_in_2)==HIGH); //loop until the Trigger goes LOW && !is_timeout 139 140 Trigger_2 = micros(); 141 //Serial.println("Detected exit"); 142 current_state = OUTPUT_STATE; 143 break; 144 case OUTPUT_STATE: 145 146 output_serial_info(); 147 reset_variables(); 148 timeout = millis() + menuTimeout1; 149 current_state = WAIT_STATE; 150 151 break; 152 } 153} 154 155void output_serial_info() { 156 157 //firstShot = true; //First shot has been fired so clear pellet weight adj. message 158 lcd.clear(); 159 mps = float(SensDist)/(Trigger_2 - Trigger_1); 160 mps = constrain(mps,0,999); //limit mps tp plus value upto 999 161 fps = mps*3.28084; 162 joules = ((pWeight*0.00006479)*(mps*mps))/2; 163 //Send data to LCD 164 165 if (mps<999.9) { //Check for large no. 166 167 Serial.print("m/s: "); 168 Serial.println(int(mps)); 169 Serial.print("fps: "); 170 Serial.println(int(fps)); 171 172 lcd.setCursor(0, 0); 173 lcd.print("m/s:"); 174 lcd.print(int(mps)); 175 176 lcd.setCursor(0, 1); 177 lcd.print("fps:"); 178 lcd.print(int(fps)); 179 180 181 lcd.setCursor(9, 0); 182 lcd.print("joules:"); 183 lcd.setCursor(11, 1); 184 lcd.print(joules); 185 } 186 187} 188 189void reset_variables() { 190 Trigger_1 = 0; 191 Trigger_2 = 0; 192 193} 194 195/* 196MENU ROUTINE 197*/ 198void basicMenu(){ 199 200 byte topItemDisplayed = 0; // stores menu item displayed at top of LCD screen 201 byte cursorPosition = 0; // where cursor is on screen, from 0 --> totalRows. 202 203 // redraw = 0 - don't redraw 204 // redraw = 1 - redraw cursor 205 // redraw = 2 - redraw list 206 byte redraw = MOVELIST; // triggers whether menu is redrawn after cursor move. 207 byte i=0; // temp variable for loops. 208 byte totalMenuItems = 0; //a while loop below will set this to the # of menu items. 209 210// Put the menu items here. Remember, the first item will have a 'position' of 0. 211 const char* menuItems[]={ 212 "Set Pweight", 213 "Exit", 214 "", 215 }; 216 217 while (menuItems[totalMenuItems] != ""){ 218 totalMenuItems++; // count how many items are in list. 219 } 220 totalMenuItems--; //subtract 1 so we know total items in array. 221 222 lcd.clear(); // clear the screen so we can paint the menu. 223 224 boolean stillSelecting = true; // set because user is still selecting. 225 boolean set_pweight = true; 226 boolean enter_menu = true; 227 228 timeoutTime = millis() + menuTimeout; // set initial timeout limit. 229 230 do { // loop while waiting for user to select. 231 232 233 /* 234 IF YOU WANT OTHER CODE GOING ON IN THE BACKGROUND 235 WHILE WAITING FOR THE USER TO DO SOMETHING, PUT IT HERE 236 */ 237 238 239 switch(read_buttons()) { // analyze button pressed response. Default is 0. 240 241 242 243 case 1: // EQUIVALENT OF 'UP' BUTTON PUSHED 244 245 timeoutTime = millis()+menuTimeout; // reset timeout timer 246 // if cursor is at top and menu is NOT at top 247 // move menu up one. 248 if(cursorPosition == 0 && topItemDisplayed > 0) // Cursor is at top of LCD, and there are higher menu items still to be displayed. 249 { 250 topItemDisplayed--; // move top menu item displayed up one. 251 redraw = MOVELIST; // redraw the entire menu 252 } 253 254 // if cursor not at top, move it up one. 255 if(cursorPosition>0) 256 { 257 cursorPosition--; // move cursor up one. 258 redraw = MOVECURSOR; // redraw just cursor. 259 } 260 break; 261 262 case 2: // EQUIVALENT OF 'DOWN' BUTTON PUSHED 263 264 timeoutTime = millis()+menuTimeout; // reset timeout timer 265 // this sees if there are menu items below the bottom of the LCD screen & sees if cursor is at bottom of LCD 266 if((topItemDisplayed + (totalRows-1)) < totalMenuItems && cursorPosition == (totalRows-1)) 267 { 268 topItemDisplayed++; // move menu down one 269 redraw = MOVELIST; // redraw entire menu 270 } 271 if(cursorPosition<(totalRows-1)) // cursor is not at bottom of LCD, so move it down one. 272 { 273 cursorPosition++; // move cursor down one 274 redraw = MOVECURSOR; // redraw just cursor. 275 } 276 break; 277 278 case 4: // EQUIVALENT TO 'SELECT' OR 'OKAY' BEING PUSHED 279 280 timeoutTime = millis()+menuTimeout; // reset timeout timer 281 282 switch(topItemDisplayed + cursorPosition){ // adding these values together = where on menuItems cursor is. 283 284 // put code to be run when specific item is selected in place of the Serial.print filler. 285 // the Serial.print code can be removed, but DO NOT change the case & break structure. 286 // (Obviously, you should have as many case instances as you do menu items.) 287 288 case 0: // menu item 1 selected 289 Serial.print("Menu item "); 290 Serial.print(topItemDisplayed + cursorPosition); 291 Serial.print(" selected - "); 292 Serial.println(menuItems[topItemDisplayed + cursorPosition]); 293 if(enter_menu == false){ 294 lcd.clear(); 295 lcd.setCursor(0,0); 296 lcd.print("Pellet weight:"); 297 lcd.setCursor(1,1); 298 lcd.print(pWeight); 299 Serial.println(pWeight); 300 do{ 301 switch(read_buttons()){ 302 case 1: 303 pWeight = pWeight + 0.01 ; 304 weightDec = (pWeight - int(pWeight))*100; 305 lcd.setCursor(1,1); 306 lcd.print(pWeight); 307 break; 308 309 case 2: 310 pWeight = pWeight - 0.01 ; 311 weightDec = (pWeight - int(pWeight))*100; 312 lcd.setCursor(1,1); 313 lcd.print(pWeight); 314 break; 315 316 case 4: 317 delay(200); 318 EEPROM.write(0,int(pWeight)); 319 EEPROM.write(1,weightDec); 320 set_pweight = false; 321 break; 322 } 323 }while (set_pweight == true); 324 } 325 326 327 // to have nested menus,copy this function(i.e. all of basicMenu) into a new function 328 329 enter_menu = false; 330 break; 331 332 case 1: // menu item 2 selected 333 Serial.print("Menu item "); 334 Serial.print(topItemDisplayed + cursorPosition); 335 Serial.print(" selected - "); 336 Serial.println(menuItems[topItemDisplayed + cursorPosition]); 337 stillSelecting = false; 338 Serial.println("exit"); 339 340 341 // add as many "case #:" as items you have. You could put 342 // line separators in menuList and leave out the 343 // corresponding case, which would mean that nothing 344 // would be triggered when user selected the line separator. 345 346 break; 347 348 } 349 350 case 8: // button was pushed for long time. This corresponds to "Back" or "Cancel" being pushed. 351// stillSelecting = false; 352// Serial.println("Button held for a long time"); 353 break; 354 355 } 356 357 358 if(stillSelecting == true){ 359 switch(redraw){ // checks if menu should be redrawn at all. 360 case MOVECURSOR: // Only the cursor needs to be moved. 361 redraw = false; // reset flag. 362 if (cursorPosition > totalMenuItems) // keeps cursor from moving beyond menu items. 363 cursorPosition = totalMenuItems; 364 for(i = 0; i < (totalRows); i++){ // loop through all of the lines on the LCD 365 lcd.setCursor(0,i); 366 lcd.print(" "); // and erase the previously displayed cursor 367 lcd.setCursor((totalCols-1), i); 368 lcd.print(" "); 369 } 370 lcd.setCursor(0,cursorPosition); // go to LCD line where new cursor should be & display it. 371 lcd.print(">"); 372 lcd.setCursor((totalCols-1), cursorPosition); 373 lcd.print("<"); 374 break; // MOVECURSOR break. 375 376 case MOVELIST: // the entire menu needs to be redrawn 377 redraw=MOVECURSOR; // redraw cursor after clearing LCD and printing menu. 378 lcd.clear(); // clear screen so it can be repainted. 379 if(totalMenuItems>((totalRows-1))){ // if there are more menu items than LCD rows, then cycle through menu items. 380 for (i = 0; i < (totalRows); i++){ 381 lcd.setCursor(1,i); 382 lcd.print(menuItems[topItemDisplayed + i]); 383 } 384 } 385 else{ // if menu has less items than LCD rows, display all available menu items. 386 for (i = 0; i < totalMenuItems+1; i++){ 387 lcd.setCursor(1,i); 388 lcd.print(menuItems[topItemDisplayed + i]); 389 } 390 } 391 break; // MOVELIST break 392 } 393 394 if (timeoutTime<millis()){ // user hasn't done anything in awhile 395 stillSelecting = false; // tell loop to bail out. 396 defaultScreen(); 397 398 /* 399 in my main code, I had a function that 400 displayed a default screen on the LCD, so 401 I would put that function here, and it would 402 bail out to the default screen. 403 defaultScreen(); 404 */ 405 } 406 } 407 } 408 409 410 while (stillSelecting == true); // 411 Serial.println("EXIT"); 412 413} 414 415void defaultScreen(){ 416lcd.clear(); 417lcd.setCursor(0,0); 418lcd.print("Ready"); 419lcd.setCursor(7,1); 420lcd.print("OK > menu"); 421 422} 423 424 425int read_buttons(){ // you may need to swap "void" with "int" or "byte" 426 byte returndata = 0; 427 int buttonState; 428 // *** REMEMBER to declare buttonUp, buttonDown, buttonSelect, & buttonCancel pins 429 430 if ((lastButtonPressed + debounceTime) < millis()){ // see if it's time to check the buttons again 431 432 // read Up button 433 buttonState = digitalRead(UP); 434 if (buttonState == LOW){ 435 returndata = returndata + 1; 436 lastButtonPressed = millis(); 437 } 438 439 // read Down button 440 buttonState = digitalRead(DOWN); 441 if (buttonState == LOW){ 442 returndata = returndata + 2; 443 lastButtonPressed = millis(); 444 } 445 446 // read Select button 447 buttonState = digitalRead(OK_BTN); 448 if (buttonState == LOW){ 449 returndata = returndata + 4; 450 lastButtonPressed = millis(); 451 } 452 453 // read Cancel button 454// buttonState = digitalRead(buttonCancel); 455// if (buttonState == LOW){ 456// returndata = returndata + 8; 457// lastButtonPressed = millis(); 458// } 459 } 460 return returndata; // this spits back to the function that calls it the variable returndata. 461}
Chronograph v2.0
arduino
Sketch provides basic chronograph functionality by measuring time between trigerring both sensors and displaying calculated m/s and fps. Menu functionality has been added for setig up pellet weight needed for energy calculation. TO DO, add additional functionalities, such as average speed based on no. of shots etc.
1Chronograph 2 3Version 2.00 4 5Sketch provides basic chronograph 6 functionality by measuring time between trigerring both sensors and displaying calculated 7 m/s and fps. 8Menu functionality has been added for setig up pellet weight needed 9 for energy calculation. 10TO DO, add additional functionalities, such as average 11 speed based on no. of shots etc. 12 13 14Thanks to Phil Grant, polepole for 15 providing parts of the code 16 17Petar Janevski 27/04/2020 18 19*/ 20 21 22#include 23 <EEPROM.h> 24//#include <EEPROMAnything.h> 25#include <avr/sleep.h> 26#include 27 <LiquidCrystal.h> 28#define Trip_in_1 8 //set light Trigger 1 to pin 8 29#define 30 Trip_in_2 9 //set light Trigger 2 to pin 9 31#define UP 14 //Set the menu up 32 button to input 14 33#define DOWN 15 //Set the menu down button to input 15 34#define 35 OK_BTN 16 //Set menu button OK to input 16 36#define WAIT_STATE 0 37#define MONITOR_STATE 38 1 39#define OUTPUT_STATE 2 40#define MENU_STATE 3 41 42unsigned long Trigger_1 43 = 0; 44unsigned long Trigger_2 = 0; 45//volatile bool adcDone; 46//volatile boolean 47 is_timeout = false; 48volatile unsigned int current_state = WAIT_STATE; 49//unsigned 50 long my_timeout = 5000000; 51//char my_oldSREG; //to hold status register while 52 ints are disabled 53unsigned long SensDist = 100000; //Distance between sensors 54 in meters*1000000 (328084) 55float pWeight; //Pellet weight in grains 56int weightTens; 57 //first two digits of pWeight 58int weightDec; //Decimal part of Pellet weight 59float 60 mps = 0; //storage for meter per sec value 61float fps = 0; //storage for feet 62 per sec value 63float joules = 0; //storage for feet per sec value 64unsigned 65 long duration = 0; // time between triger 1 and 2 66//boolean firstShot = false; 67 //flag to detect that a shot has been fired. remove adj. pellet weight 68unsigned 69 long timeout = 0; //timer to return to main screen after measurement 70const int 71 menuTimeout1 = 20000; // time of inactivity to return to main screen after measurement 72 73/* 74MENU 75 REQUIRED GLOBAL VARIABLES & DEFINITIONS 76*/ 77#define MOVECURSOR 1 // constants 78 for indicating whether cursor should be redrawn 79#define MOVELIST 2 // constants 80 for indicating whether cursor should be redrawn 81byte totalRows = 2; // total 82 rows of LCD 83byte totalCols = 16; // total columns of LCD 84unsigned long timeoutTime 85 = 0; // this is set and compared to millis to see when the user last did something. 86const 87 int menuTimeout = 5000; // time to timeout in a menu when user doesn't do anything. 88volatile 89 unsigned int menu_state; 90unsigned long lastButtonPressed; // this is when the 91 last button was pressed. It's used to debounce. 92const int debounceTime = 300; 93 //this is the debounce and hold delay. 94 95 96// initialize the LCD library 97 with the numbers of the interface pins 98 99LiquidCrystal lcd(5, 6, 7, 10, 11, 100 12); 101 102void setup () { 103 Serial.begin(9600); 104 Serial.println("Chrono 105 v2.00"); 106 //Retreive pellet weight 107 weightTens = EEPROM.read(0); 108 weightDec 109 = EEPROM.read(1); 110 pWeight = weightTens + float(weightDec)/100; 111 112// 113 EEPROM_readAnything(0, Config); 114 Serial.print("EEPROM 0 = "); 115 Serial.println(EEPROM.read(0)); 116 117 Serial.print("EEPROM 1/100 = "); 118 Serial.println(float(EEPROM.read(1))/100); 119 120 Serial.print("pWeight = "); 121 Serial.println(pWeight); 122 Serial.print("weightTens 123 = "); 124 Serial.println(weightTens); 125 Serial.print("weightDec = "); 126 127 Serial.println(weightDec); 128 129 pinMode(Trip_in_1, INPUT); //declare pins 130 in use 131 pinMode(Trip_in_2, INPUT); 132 pinMode(UP, INPUT); 133 pinMode(DOWN, 134 INPUT); 135 pinMode(OK_BTN, INPUT); 136 digitalWrite(UP, HIGH); //turn on pullup 137 resistor for buttons 138 digitalWrite(DOWN, HIGH); 139 digitalWrite(OK_BTN, HIGH); 140 141 142 lcd.begin(16, 2); //2 line 16 character LCD 143 lcd.clear(); 144 145 146 lcd.setCursor(5, 0); //Draw splash screen 147 lcd.print("Chrono"); 148 lcd.setCursor(4, 149 1); 150 lcd.print("Ver 2.00"); 151 delay (3000); //show splash screen for 5 152 seconds 153 defaultScreen(); 154 reset_variables(); 155 156} 157 158 159void loop 160 () { 161 162 switch (current_state) { 163 case WAIT_STATE: 164 if(digitalRead(Trip_in_1)==LOW) 165 { 166 Trigger_1 = micros(); 167 current_state = MONITOR_STATE; 168 } 169 170 171 if (digitalRead(OK_BTN)==LOW) { 172 173 basicMenu(); 174 delay(200); 175 176 Serial.println("back in main loop"); 177 defaultScreen(); 178 179 180 } 181 182 183 if (timeout != 0 && timeout<millis()){ // user hasn't done 184 anything in awhile 185 defaultScreen(); 186 timeout = 0; 187 } 188 189 190 break; 191 192 case MONITOR_STATE: 193 while(digitalRead(Trip_in_2)==HIGH); 194 //loop until the Trigger goes LOW && !is_timeout 195 196 Trigger_2 = 197 micros(); 198 //Serial.println("Detected exit"); 199 current_state = OUTPUT_STATE; 200 201 break; 202 case OUTPUT_STATE: 203 204 output_serial_info(); 205 reset_variables(); 206 207 timeout = millis() + menuTimeout1; 208 current_state = WAIT_STATE; 209 210 211 break; 212 } 213} 214 215void output_serial_info() { 216 217 //firstShot = 218 true; //First shot has been fired so clear pellet weight adj. message 219 lcd.clear(); 220 221 mps = float(SensDist)/(Trigger_2 - Trigger_1); 222 mps = constrain(mps,0,999); 223 //limit mps tp plus value upto 999 224 fps = mps*3.28084; 225 joules = ((pWeight*0.00006479)*(mps*mps))/2; 226 227 //Send data to LCD 228 229 if (mps<999.9) { //Check for large no. 230 231 232 Serial.print("m/s: "); 233 Serial.println(int(mps)); 234 Serial.print("fps: 235 "); 236 Serial.println(int(fps)); 237 238 lcd.setCursor(0, 0); 239 lcd.print("m/s:"); 240 241 lcd.print(int(mps)); 242 243 lcd.setCursor(0, 1); 244 lcd.print("fps:"); 245 246 lcd.print(int(fps)); 247 248 249 lcd.setCursor(9, 0); 250 lcd.print("joules:"); 251 252 lcd.setCursor(11, 1); 253 lcd.print(joules); 254 } 255 256} 257 258void 259 reset_variables() { 260 Trigger_1 = 0; 261 Trigger_2 = 0; 262 263} 264 265/* 266MENU 267 ROUTINE 268*/ 269void basicMenu(){ 270 271 byte topItemDisplayed = 0; // stores 272 menu item displayed at top of LCD screen 273 byte cursorPosition = 0; // where 274 cursor is on screen, from 0 --> totalRows. 275 276 // redraw = 0 - don't redraw 277 278 // redraw = 1 - redraw cursor 279 // redraw = 2 - redraw list 280 byte redraw 281 = MOVELIST; // triggers whether menu is redrawn after cursor move. 282 byte i=0; 283 // temp variable for loops. 284 byte totalMenuItems = 0; //a while loop below 285 will set this to the # of menu items. 286 287// Put the menu items here. Remember, 288 the first item will have a 'position' of 0. 289 const char* menuItems[]={ 290 "Set 291 Pweight", 292 "Exit", 293 "", 294 }; 295 296 while (menuItems[totalMenuItems] 297 != ""){ 298 totalMenuItems++; // count how many items are in list. 299 } 300 301 totalMenuItems--; //subtract 1 so we know total items in array. 302 303 lcd.clear(); 304 // clear the screen so we can paint the menu. 305 306 boolean stillSelecting 307 = true; // set because user is still selecting. 308 boolean set_pweight = true; 309 310 boolean enter_menu = true; 311 312 timeoutTime = millis() + menuTimeout; // set 313 initial timeout limit. 314 315 do { // loop while waiting for user to select. 316 317 318 319 /* 320 IF YOU WANT OTHER CODE GOING ON IN THE BACKGROUND 321 WHILE 322 WAITING FOR THE USER TO DO SOMETHING, PUT IT HERE 323 */ 324 325 326 switch(read_buttons()) 327 { // analyze button pressed response. Default is 0. 328 329 330 331 case 332 1: // EQUIVALENT OF 'UP' BUTTON PUSHED 333 334 timeoutTime = millis()+menuTimeout; 335 // reset timeout timer 336 // if cursor is at top and menu is NOT at top 337 338 // move menu up one. 339 if(cursorPosition == 0 && topItemDisplayed 340 > 0) // Cursor is at top of LCD, and there are higher menu items still to be displayed. 341 342 { 343 topItemDisplayed--; // move top menu item displayed up one. 344 345 redraw = MOVELIST; // redraw the entire menu 346 } 347 348 349 // if cursor not at top, move it up one. 350 if(cursorPosition>0) 351 352 { 353 cursorPosition--; // move cursor up one. 354 redraw 355 = MOVECURSOR; // redraw just cursor. 356 } 357 break; 358 359 case 360 2: // EQUIVALENT OF 'DOWN' BUTTON PUSHED 361 362 timeoutTime = millis()+menuTimeout; 363 // reset timeout timer 364 // this sees if there are menu items below the 365 bottom of the LCD screen & sees if cursor is at bottom of LCD 366 if((topItemDisplayed 367 + (totalRows-1)) < totalMenuItems && cursorPosition == (totalRows-1)) 368 { 369 370 topItemDisplayed++; // move menu down one 371 redraw = MOVELIST; 372 // redraw entire menu 373 } 374 if(cursorPosition<(totalRows-1)) 375 // cursor is not at bottom of LCD, so move it down one. 376 { 377 cursorPosition++; 378 // move cursor down one 379 redraw = MOVECURSOR; // redraw just cursor. 380 381 } 382 break; 383 384 case 4: // EQUIVALENT TO 'SELECT' OR 'OKAY' 385 BEING PUSHED 386 387 timeoutTime = millis()+menuTimeout; // reset timeout 388 timer 389 390 switch(topItemDisplayed + cursorPosition){ // adding 391 these values together = where on menuItems cursor is. 392 393 // 394 put code to be run when specific item is selected in place of the Serial.print 395 filler. 396 // the Serial.print code can be removed, but DO NOT change 397 the case & break structure. 398 // (Obviously, you should have as many 399 case instances as you do menu items.) 400 401 case 0: // 402 menu item 1 selected 403 Serial.print("Menu item "); 404 Serial.print(topItemDisplayed 405 + cursorPosition); 406 Serial.print(" selected - "); 407 Serial.println(menuItems[topItemDisplayed 408 + cursorPosition]); 409 if(enter_menu == false){ 410 lcd.clear(); 411 412 lcd.setCursor(0,0); 413 lcd.print("Pellet weight:"); 414 415 lcd.setCursor(1,1); 416 lcd.print(pWeight); 417 Serial.println(pWeight); 418 419 do{ 420 switch(read_buttons()){ 421 case 422 1: 423 pWeight = pWeight + 0.01 ; 424 weightDec 425 = (pWeight - int(pWeight))*100; 426 lcd.setCursor(1,1); 427 428 lcd.print(pWeight); 429 break; 430 431 432 case 2: 433 pWeight 434 = pWeight - 0.01 ; 435 weightDec = (pWeight - int(pWeight))*100; 436 437 lcd.setCursor(1,1); 438 lcd.print(pWeight); 439 440 break; 441 442 case 443 4: 444 delay(200); 445 EEPROM.write(0,int(pWeight)); 446 447 EEPROM.write(1,weightDec); 448 set_pweight 449 = false; 450 break; 451 } 452 }while 453 (set_pweight == true); 454 } 455 456 457 458 // to have nested menus,copy this function(i.e. all of basicMenu) 459 into a new function 460 461 enter_menu = false; 462 break; 463 464 465 case 1: // menu item 2 selected 466 Serial.print("Menu 467 item "); 468 Serial.print(topItemDisplayed + cursorPosition); 469 470 Serial.print(" selected - "); 471 Serial.println(menuItems[topItemDisplayed 472 + cursorPosition]); 473 stillSelecting = false; 474 Serial.println("exit"); 475 476 477 478 // add as many "case #:" as items 479 you have. You could put 480 // line separators in menuList and leave 481 out the 482 // corresponding case, which would mean that nothing 483 484 // would be triggered when user selected the line separator. 485 486 487 break; 488 489 } 490 491 case 492 8: // button was pushed for long time. This corresponds to "Back" or "Cancel" 493 being pushed. 494// stillSelecting = false; 495// Serial.println("Button 496 held for a long time"); 497 break; 498 499 } 500 501 502 if(stillSelecting 503 == true){ 504 switch(redraw){ // checks if menu should be redrawn at all. 505 506 case MOVECURSOR: // Only the cursor needs to be moved. 507 redraw = false; 508 // reset flag. 509 if (cursorPosition > totalMenuItems) // keeps cursor from 510 moving beyond menu items. 511 cursorPosition = totalMenuItems; 512 for(i 513 = 0; i < (totalRows); i++){ // loop through all of the lines on the LCD 514 lcd.setCursor(0,i); 515 516 lcd.print(" "); // and erase the previously displayed 517 cursor 518 lcd.setCursor((totalCols-1), i); 519 lcd.print(" "); 520 521 } 522 lcd.setCursor(0,cursorPosition); // go to LCD line where new 523 cursor should be & display it. 524 lcd.print(">"); 525 lcd.setCursor((totalCols-1), 526 cursorPosition); 527 lcd.print("<"); 528 break; // MOVECURSOR break. 529 530 531 case MOVELIST: // the entire menu needs to be redrawn 532 redraw=MOVECURSOR; 533 // redraw cursor after clearing LCD and printing menu. 534 lcd.clear(); // 535 clear screen so it can be repainted. 536 if(totalMenuItems>((totalRows-1))){ 537 // if there are more menu items than LCD rows, then cycle through menu items. 538 539 for (i = 0; i < (totalRows); i++){ 540 lcd.setCursor(1,i); 541 542 lcd.print(menuItems[topItemDisplayed + i]); 543 } 544 } 545 546 else{ // if menu has less items than LCD rows, display all available menu 547 items. 548 for (i = 0; i < totalMenuItems+1; i++){ 549 lcd.setCursor(1,i); 550 551 lcd.print(menuItems[topItemDisplayed + i]); 552 } 553 } 554 555 break; // MOVELIST break 556 } 557 558 if (timeoutTime<millis()){ // 559 user hasn't done anything in awhile 560 stillSelecting = false; // tell loop 561 to bail out. 562 defaultScreen(); 563 564 /* 565 in my main 566 code, I had a function that 567 displayed a default screen on the LCD, so 568 569 I would put that function here, and it would 570 bail out to the default 571 screen. 572 defaultScreen(); 573 */ 574 } 575 } 576 } 577 578 579 580 while (stillSelecting == true); // 581 Serial.println("EXIT"); 582 583} 584 585void 586 defaultScreen(){ 587lcd.clear(); 588lcd.setCursor(0,0); 589lcd.print("Ready"); 590lcd.setCursor(7,1); 591lcd.print("OK 592 > menu"); 593 594} 595 596 597int read_buttons(){ // you may need to swap "void" 598 with "int" or "byte" 599 byte returndata = 0; 600 int buttonState; 601 // 602 *** REMEMBER to declare buttonUp, buttonDown, buttonSelect, & buttonCancel pins 603 604 605 if ((lastButtonPressed + debounceTime) < millis()){ // see if it's time to check 606 the buttons again 607 608 // read Up button 609 buttonState = digitalRead(UP); 610 611 if (buttonState == LOW){ 612 returndata = returndata + 1; 613 lastButtonPressed 614 = millis(); 615 } 616 617 // read Down button 618 buttonState = digitalRead(DOWN); 619 620 if (buttonState == LOW){ 621 returndata = returndata + 2; 622 lastButtonPressed 623 = millis(); 624 } 625 626 // read Select button 627 buttonState = digitalRead(OK_BTN); 628 629 if (buttonState == LOW){ 630 returndata = returndata + 4; 631 lastButtonPressed 632 = millis(); 633 } 634 635 // read Cancel button 636// buttonState = digitalRead(buttonCancel); 637// 638 if (buttonState == LOW){ 639// returndata = returndata + 8; 640// lastButtonPressed 641 = millis(); 642// } 643 } 644 return returndata; // this spits back to the 645 function that calls it the variable returndata. 646}
Downloadable files
Sensor circuit
The design is for 4 x 8 diodes, I used one lm 339 so I made just one 1/2 :)
Sensor circuit
Comments
Only logged in users can leave comments
Pepisan
0 Followers
•0 Projects
Table of contents
Intro
3
0