Connect 4 Playing Machine
A 3D printed machine controlled by an Arduino that plays Connect 4.
Components and supplies
LCD 16x2 with serial interface
Microswitch with long lever
Resistor 2k ohm
Stepper Motor with 28BYJ-48 controller board
Breadboard power supply
Nuts, bolts and washers, 2mm and 3mm
Breadboard (generic)
12 key keypad
Resistor 10k ohm
Arduino UNO
Standard sized servo
Resistor 100 ohm
Jumper wires (generic)
Grab and Go Connect 4 Game
Resistor 5k ohm
Buzzer, Piezo
Tools and machines
3D Printer (generic)
Adhesive
Paint, Red and Yellow
Apps and platforms
Arduino IDE
Project description
Code
Code
arduino
1// Connect 4 Game 2// by Mark Bennett 2020 3 4/* Overview 5 * 6 * Plays physical Connect 4 game 7 * 8 * Can be played human v computer, computer v computer or human v human 9 * Control is via a keypad and an LCD screen shows menus and messages 10 * A servo and stepper motor are used to control dropping counters into the grid 11 * Detects wins and draws and shows end game message on the display 12 * 13 * 14 * Hardware 15 * 16 * Switch for column selector referencing - pin 2 17 * Servo for dropping counters - pin 3 18 * Stepper motor to drive column selector - pins 8 - 11 19 * LCD display with serial board for user interface - SDA SCL 20 * Keypad for user data entry - pin A0 21 * Piezo buzzer - pin 4 22 * 23 */ 24 25#include <Servo.h> 26#include <Wire.h> 27#include <LiquidCrystal_I2C.h> 28 29// debug mode 30const bool debug = true; // sends grid state to serial after each turn if true 31 32// stepper motor 33const int motorPin1 = 8; 34const int motorPin2 = 9; 35const int motorPin3 = 10; 36const int motorPin4 = 11; 37int motorSpeed = 1250; 38int motorLookup[8] = {B01001, B00001, B00011, B00010, B00110, B00100, B01100, B01000}; 39int stepsPerColumn = 125; // number of steps to move 1 column 40int referenceSteps = 2; // number of steps from reference position to column 1 41 42// servo 43Servo dropperservo; 44const int servoPin = 3; 45int servoCentre = 99; // servo drop position, centre 46int servoRed = 120; // servo position to pick up red counter 47int servoYellow = 75; // servo position to pick up yellow counter 48int servoCurrent = servoCentre; 49 50// switch 51const int switchPin = 2; 52 53// keypad 54const int keypadPin = A0; 55// voltage values for keys 1 2 3 4 5 6 7 8 9 * 0 # 56// * and # are not used and read as 0 57int keyValues[] = {505, 336, 251, 222, 181, 153, 141, 124, 110, 103, 93, 85, 0}; 58int keys[13]; 59char lastKeyPressed = ' '; 60 61// lcd 62LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); 63 64// led 65const int ledPin = 13; 66 67// buzzer 68const int buzzerPin = 4; 69 70// menu control 71int menu = 0; 72 73// game 74int selectorPosition = 1; 75int seed = 0; 76 77void setup() { 78 // stepper motor 79 pinMode(motorPin1, OUTPUT); 80 pinMode(motorPin2, OUTPUT); 81 pinMode(motorPin3, OUTPUT); 82 pinMode(motorPin4, OUTPUT); 83 84 // servo 85 dropperservo.attach(servoPin); 86 dropperservo.write(servoCurrent); 87 88 // switch 89 pinMode(switchPin, INPUT); 90 91 // keyboard 92 for(int i = 0; i < 12; i++){ 93 keys[i] = ((keyValues[i] - keyValues[i + 1]) / 2) + keyValues[i + 1]; 94 } 95 96 // lcd 97 lcd.init(); 98 lcd.backlight(); 99 showMenu(0); 100 101 // led 102 pinMode(ledPin, OUTPUT); 103 104 // buzzer 105 pinMode(buzzerPin, OUTPUT); 106 107 // game 108 seed = analogRead(3); 109 Serial.begin(9600); 110 111 playTune(1); 112} 113 114void loop(){ 115 // keypad 116 char keyPressed = readKeypad(); 117 118 // menu navigation 119 if(keyPressed != ' '){ 120 processMenuInput(keyPressed); 121 } 122 123 // game 124 seed++; 125 126 delay(50); 127} 128 129void displayMessage(String line1, String line2){ 130 lcd.clear(); 131 lcd.home(); 132 lcd.print(line1); 133 lcd.setCursor(0, 1); 134 lcd.print(line2); 135} 136 137char readKeypad(){ 138 char key = ' '; 139 if(analogRead(keypadPin) > keys[11]){ 140 delay(20); 141 int keyInput = analogRead(keypadPin); 142 if(keyInput > keys[0]) key = '1'; 143 else if(keyInput > keys[1]) key = '2'; 144 else if(keyInput > keys[2]) key = '3'; 145 else if(keyInput > keys[3]) key = '4'; 146 else if(keyInput > keys[4]) key = '5'; 147 else if(keyInput > keys[5]) key = '6'; 148 else if(keyInput > keys[6]) key = '7'; 149 else if(keyInput > keys[7]) key = '8'; 150 else if(keyInput > keys[8]) key = '9'; 151 else if(keyInput > keys[9]) key = '0'; 152 else if(keyInput > keys[10]) key = '0'; 153 else if(keyInput > keys[11]) key = '0'; 154 tone(buzzerPin, 500, 50); 155 } 156 while(analogRead(keypadPin) > keys[11]){ 157 delay(10); 158 } 159 return key; 160} 161 162void showMenu(int menu){ 163 if(menu == 0) displayMessage("Arduino Connect4", "Press any key"); 164 if(menu == 1) displayMessage("1 Play Connect 4", "2 Test 3 Set-up"); 165 if(menu == 11) displayMessage("1 HvC 2 HvH", "3 CvC 0 Exit"); 166 if(menu == 111) displayMessage("1 Human go first", "2 Second 0 Exit"); 167 if(menu == 12) displayMessage("1 Keypad 2 Servo", "3 Select 0 Exit"); 168 if(menu == 13) displayMessage("1 Keypad 2 Servo", "3 Select 0 Exit"); 169} 170 171void processMenuInput(char key){ 172 if(menu == 0){referenceSelector(); menu = 1; showMenu(menu);} 173 // top level menu 174 else if(menu == 1){ 175 if(key == '1'){menu = 11; showMenu(menu);} 176 if(key == '2'){menu = 12; showMenu(menu);} 177 if(key == '3'){menu = 13; showMenu(menu);} 178 } 179 // play menu 180 else if(menu == 11){ 181 if(key == '1'){menu = 111; showMenu(menu);} 182 if(key == '2'){playGame(1, 1);} 183 if(key == '3'){playGame(2, 2);} 184 if(key == '0'){menu = 1; showMenu(menu);} 185 } 186 else if(menu == 111){ 187 if(key == '1'){playGame(1, 2);} 188 if(key == '2'){playGame(2, 1);} 189 if(key == '0'){menu = 11; showMenu(menu);} 190 } 191 // test menu 192 else if(menu == 12){ 193 if(key == '1'){testKeypad();} 194 if(key == '2'){testServo();} 195 if(key == '3'){testMotor();} 196 if(key == '0'){menu = 1; showMenu(menu);} 197 } 198 // set-up menu 199 else if(menu == 13){ 200 if(key == '1'){setupKeypad();} 201 if(key == '2'){setupServo();} 202 if(key == '3'){setupMotor();} 203 if(key == '0'){menu = 1; showMenu(menu);} 204 } 205} 206 207void playGame(int player1Type, int player2Type){ 208 randomSeed(seed); 209 for(int i = 0; i < 100; i++){ 210 int dump = random(50); 211 } 212 213 // type, 1 = human, 2 = computer 214 int players[] = {0, 0}; 215 players[0] = player1Type; 216 players[1] = player2Type; 217 218 byte grid[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 219 bool gameEnd = false; 220 bool aborted = false; 221 int player = 0; 222 int key = 0; 223 int selectorMove = 0; 224 String colour; 225 bool draw = false; 226 int win = 0; 227 int row = 0; 228 int column = 0; 229 int countDown = 2000; 230 char keyPressed = ' '; 231 232 resetGrid(grid); 233 234 while(!gameEnd){ 235 if(player == 0){colour = "Red";} 236 else{colour = "Yellow";} 237 238 if(players[player] == 1){ 239 displayMessage(String("Player ") + (player + 1) + " " + colour, "1-7 col 9 exit"); 240 key = getHumanMove(grid, player, colour); 241 if(key == 9){ 242 gameEnd = true; 243 aborted = true; 244 } 245 } 246 else{ 247 displayMessage(String("Player ") + (player + 1) + " " + colour, "Deciding move..."); 248 key = decideMove(grid, player); 249 delay(200); 250 } 251 252 if(key > 0 && key < 8){ 253 displayMessage("Executing move", "Please wait..."); 254 selectorMove = key - selectorPosition; 255 if(selectorMove > 0){ 256 rotate('R', selectorMove * stepsPerColumn); 257 } 258 if(selectorMove < 0){ 259 rotate('L', selectorMove * -1 * stepsPerColumn); 260 } 261 selectorPosition = key; 262 if(player == 0) drop('R'); 263 if(player == 1) drop('Y'); 264 delay(2000); 265 266 column = key - 1; 267 row = addCounter(grid, player, column); 268 if(debug) sendGrid(grid); 269 270 // check for win 271 win = checkForWin(grid, player, column, row); 272 if(win > 0) gameEnd = true; 273 274 // check for draw, all full and no winner 275 if(!gameEnd){ 276 draw = checkForDraw(grid); 277 if(draw) gameEnd = true; 278 } 279 280 player = abs(player - 1); 281 } 282 // check for abort if not gameEnd 283 if(!gameEnd && players[0] == 2 && players[1] == 2){ 284 displayMessage("Press 0 to abort", "Wait 2s to cont"); 285 countDown = 2000; 286 while(countDown > 0){ 287 keyPressed = readKeypad(); 288 if(keyPressed == '0'){ 289 Serial.println(keyPressed); 290 gameEnd = true; 291 aborted = true; 292 } 293 delay(50); 294 countDown = countDown - 50; 295 } 296 } 297 } 298 // end game messages 299 if(draw){ 300 displayMessage("Result is a Draw", "Press any key"); 301 playTune(1); 302 } 303 if(win == 1){ 304 displayMessage("Winner P1 Red", "Press any key"); 305 playTune(2); 306 } 307 if(win == 2){ 308 displayMessage("Winner P2 Yellow", "Press any key"); 309 playTune(2); 310 } 311 if(aborted) displayMessage("Game aborted", "Press any key"); 312 while(readKeypad() == ' '){ 313 delay(20); 314 } 315 316 showMenu(menu); 317} 318 319void playTune(int tune){ 320 if(tune == 1){ 321 tone(buzzerPin, 262, 200); 322 delay(210); 323 tone(buzzerPin, 523, 400); 324 delay(410); 325 } 326 if(tune == 2){ 327 tone(buzzerPin, 262, 200); 328 delay(210); 329 tone(buzzerPin, 330, 200); 330 delay(210); 331 tone(buzzerPin, 392, 200); 332 delay(210); 333 tone(buzzerPin, 523, 400); 334 delay(410); 335 tone(buzzerPin, 392, 200); 336 delay(210); 337 tone(buzzerPin, 523, 400); 338 delay(410); 339 } 340} 341 342 343int getHumanMove(byte g[], int p, String colour){ 344 bool columnAccepted = true; 345 char keyPressed = ' '; 346 int key = 0; 347 348 do{ 349 columnAccepted = true; 350 keyPressed = ' '; 351 while(keyPressed == ' '){ 352 keyPressed = readKeypad(); 353 delay(10); 354 } 355 key = 0; 356 if(keyPressed == '1') key = 1; 357 if(keyPressed == '2') key = 2; 358 if(keyPressed == '3') key = 3; 359 if(keyPressed == '4') key = 4; 360 if(keyPressed == '5') key = 5; 361 if(keyPressed == '6') key = 6; 362 if(keyPressed == '7') key = 7; 363 364 // exit from game 365 if(keyPressed == '9') key = 9; 366 367 if(key > 0 && key < 8){ 368 // check for full column 369 if(g[toGrid(key - 1, 5)] != 0){ 370 displayMessage("Full, try again", String("P") + (p + 1) + " " + colour + " 1-7"); 371 columnAccepted = false; 372 } 373 } 374 }while(!columnAccepted && key != 9); 375 return key; 376} 377 378int decideMove(byte g[], int p){ 379 int columns[] = {50, 50, 50, 50, 50, 50, 50}; 380 byte tempGrid[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 381 int win = 0; 382 int key = 0; 383 int biggest = 0; 384 int count = 0; 385 int selected = 0; 386 int row = 0; 387 int p2 = abs(p - 1); 388 389 // mark all full columns with 0s 390 for(int i = 0; i < 7; i++){ 391 if(g[toGrid(i, 5)] != 0){ 392 columns[i] = 0; 393 } 394 } 395 396 // add 1000 to winning columns[] 397 for(int i = 0; i < 7; i++){ 398 if(columns[i] > 0){ 399 for(int j = 0; j < 42; j++){ 400 tempGrid[j] = g[j]; 401 } 402 row = addCounter(tempGrid, p, i); 403 win = checkForWin(tempGrid, p, i, row); 404 if(win > 0){ 405 columns[i] += 1000; 406 } 407 } 408 } 409 410 // add 50 to losing columns[] 411 for(int i = 0; i < 7; i++){ 412 if(columns[i] > 0){ 413 for(int j = 0; j < 42; j++){ 414 tempGrid[j] = g[j]; 415 } 416 row = addCounter(tempGrid, p2, i); 417 win = checkForWin(tempGrid, p2, i, row); 418 if(win > 0){ 419 columns[i] += 50; 420 } 421 } 422 } 423 424 // check for give away moves 425 for(int i = 0; i < 7; i++){ 426 if(columns[i] > 0){ 427 for(int k = 0; k < 7; k++){ 428 if(columns[k] > 0){ 429 for(int j = 0; j < 42; j++){ 430 tempGrid[j] = g[j]; 431 } 432 row = addCounter(tempGrid, p, i); 433 if(i != k || row < 5){ 434 row = addCounter(tempGrid, p2, k); 435 win = checkForWin(tempGrid, p2, k, row); 436 if(win > 0){ 437 columns[i] -= 10; 438 } 439 } 440 } 441 } 442 } 443 } 444 445 // find biggest value in columns[] 446 for(int i = 0; i < 7; i++){ 447 if(columns[i] > biggest){ 448 biggest = columns[i]; 449 } 450 } 451 // count biggest in columns[] 452 for(int i = 0; i < 7; i++){ 453 if(columns[i] == biggest){ 454 count++; 455 } 456 } 457 // random select a biggest column 458 selected = random(count) + 1; 459 if(debug){ 460 Serial.println(String("Player ") + p); 461 Serial.println(String("Biggest ") + biggest); 462 Serial.println(String("Count ") + count); 463 Serial.println(String("Selected ") + selected); 464 } 465 for(int i = 0; i < 7; i++){ 466 if(columns[i] == biggest){ 467 selected--; 468 if(selected == 0){ 469 key = i + 1; 470 } 471 } 472 } 473 474 if(debug){ 475 for(int i = 0; i < 7; i++){ 476 Serial.print(columns[i]); 477 Serial.print(" "); 478 } 479 Serial.println(""); 480 Serial.println(""); 481 } 482 483 return key; 484} 485 486int addCounter(byte g[], int p, int c){ 487 // find row 488 int row = 0; 489 while(g[toGrid(c, row)] != 0){ 490 row++; 491 } 492 // update array 493 g[toGrid(c, row)] = p + 1; 494 return row; 495} 496 497int checkForDraw(byte g[]){ 498 bool draw = true; 499 for(int i = 0; i < 7; i++){ 500 if(g[toGrid(i, 5)] == 0){ 501 draw = false; 502 } 503 } 504 return draw; 505} 506 507int checkForWin(byte g[], int p, int c, int r){ 508 p++; 509 int win = 0; 510 511 if(win == 0) win = checkFor4(g, p, c, r, 3, 6, 0, 2, -1, 1, -2, 2, -3, 3); 512 if(win == 0) win = checkFor4(g, p, c, r, 2, 5, 1, 3, -1, 1, -2, 2, 1, -1); 513 if(win == 0) win = checkFor4(g, p, c, r, 1, 4, 2, 4, -1, 1, 1, -1, 2, -2); 514 if(win == 0) win = checkFor4(g, p, c, r, 0, 3, 3, 5, 1, -1, 2, -2, 3, -3); 515 516 if(win == 0) win = checkFor4(g, p, c, r, 0, 3, 0, 2, 1, 1, 2, 2, 3, 3); 517 if(win == 0) win = checkFor4(g, p, c, r, 1, 4, 1, 3, 1, 1, 2, 2, -1, -1); 518 if(win == 0) win = checkFor4(g, p, c, r, 2, 5, 2, 4, 1, 1, -1, -1, -2, -2); 519 if(win == 0) win = checkFor4(g, p, c, r, 3, 6, 3, 5, -1, -1, -2, -2, -3, -3); 520 521 if(win == 0) win = checkFor4(g, p, c, r, 0, 3, 0, 5, 1, 0, 2, 0, 3, 0); 522 if(win == 0) win = checkFor4(g, p, c, r, 1, 4, 0, 5, 1, 0, 2, 0, -1, 0); 523 if(win == 0) win = checkFor4(g, p, c, r, 2, 5, 0, 5, 1, 0, -1, 0, -2, 0); 524 if(win == 0) win = checkFor4(g, p, c, r, 3, 6, 0, 5, -1, 0, -2, 0, -3, 0); 525 526 if(win == 0) win = checkFor4(g, p, c, r, 0, 6, 3, 5, 0, -1, 0, -2, 0, -3); 527 528 return win; 529} 530 531int checkFor4(byte g[], int p, int c, int r, int c1, int c2, int r1, int r2, int sc1, int sr1, int sc2, int sr2, int sc3, int sr3){ 532 int win = 0; 533 if(c >= c1 && c <= c2 && r >= r1 && r <= r2 && g[toGrid(c + sc1, r + sr1)] == p && g[toGrid(c + sc2, r + sr2)] == p && g[toGrid(c + sc3, r + sr3)] == p) win = p; 534 return win; 535} 536 537void resetGrid(byte grid[]){ 538 for(int i = 0; i < 42; i++){ 539 grid[i] = 0; 540 } 541} 542 543void drop(char colour){ 544 if(colour == 'R'){ 545 dropperservo.write(servoRed); 546 } 547 else{ 548 dropperservo.write(servoYellow); 549 } 550 delay(1000); 551 dropperservo.write(servoCentre); 552} 553 554int toGrid(int column, int row){ 555 return column + (row * 7); 556} 557 558int toColumn(int grid){ 559 return grid - (toRow(grid) * 7); 560} 561 562int toRow(int grid){ 563 return grid / 7; 564} 565 566void sendGrid(byte g[]){ 567 for(int r = 5; r >= 0; r--){ 568 for(int c = 0; c < 7; c++){ 569 Serial.print(g[toGrid(c, r)]); 570 Serial.print(" "); 571 } 572 Serial.println(""); 573 } 574 Serial.println(""); 575 576 /* 577 for(int i = 0; i < 42; i++){ 578 Serial.print(g[i]); 579 Serial.print(" "); 580 } 581 Serial.println(""); 582 Serial.println(""); 583 */ 584} 585 586void testKeypad(){ 587 int countDown = 5000; 588 char keyPressed = ' '; 589 displayMessage("Key", "Wait 5s to exit"); 590 while(countDown > 0){ 591 keyPressed = readKeypad(); 592 if(keyPressed != ' '){ 593 countDown = 5000; 594 lcd.setCursor(4, 0); 595 lcd.print(keyPressed); 596 } 597 delay(50); 598 countDown = countDown - 50; 599 } 600 showMenu(menu); 601} 602 603void testServo(){ 604 char keyPressed = ' '; 605 displayMessage("1 Drop Red", "2 Yellow 0 Exit"); 606 while(keyPressed != '0'){ 607 keyPressed = readKeypad(); 608 if(keyPressed == '1'){ 609 dropperservo.write(servoRed); 610 delay(1000); 611 dropperservo.write(servoCentre); 612 } 613 if(keyPressed == '2'){ 614 dropperservo.write(servoYellow); 615 delay(1000); 616 dropperservo.write(servoCentre); 617 } 618 delay(10); 619 } 620 showMenu(menu); 621} 622 623void testMotor(){ 624 char keyPressed = ' '; 625 int key = 0; 626 int selectorMove = 0; 627 showTestMotorControlMenu(); 628 while(keyPressed != '0'){ 629 keyPressed = readKeypad(); 630 selectorMove = 0; 631 key = 0; 632 if(keyPressed != ' '){ 633 if(keyPressed == '1') key = 1; 634 if(keyPressed == '2') key = 2; 635 if(keyPressed == '3') key = 3; 636 if(keyPressed == '4') key = 4; 637 if(keyPressed == '5') key = 5; 638 if(keyPressed == '6') key = 6; 639 if(keyPressed == '7') key = 7; 640 641 if(key > 0){ 642 selectorMove = key - selectorPosition; 643 if(selectorMove > 0){ 644 rotate('R', selectorMove * stepsPerColumn); 645 } 646 if(selectorMove < 0){ 647 rotate('L', selectorMove * -1 * stepsPerColumn); 648 } 649 selectorPosition = key; 650 } 651 652 if(keyPressed == '8'){ 653 referenceSelector(); 654 showTestMotorControlMenu(); 655 } 656 } 657 delay(10); 658 } 659 showMenu(menu); 660} 661 662void showTestMotorControlMenu(){ 663 displayMessage("1-7 Select Col", "8 Ref 0 Exit"); 664} 665 666void setupKeypad(){ 667 int countDown = 5000; 668 int value = 0; 669 displayMessage("Keypad", "Wait 5s to exit"); 670 while(countDown > 0){ 671 value = analogRead(keypadPin); 672 if(value > 40){ 673 countDown = 5000; 674 } 675 lcd.setCursor(7, 0); 676 lcd.print(value); 677 lcd.print(" "); 678 delay(50); 679 countDown = countDown - 50; 680 } 681 showMenu(menu); 682} 683 684void setupServo(){ 685 char keyPressed = ' '; 686 displayMessage("Servo", "1/4+ 2/5- 0 Exit"); 687 while(keyPressed != '0'){ 688 keyPressed = readKeypad(); 689 if(keyPressed == '1'){ 690 servoCurrent += 3; 691 } 692 if(keyPressed == '2'){ 693 servoCurrent -= 3; 694 } 695 if(keyPressed == '4'){ 696 servoCurrent += 1; 697 } 698 if(keyPressed == '5'){ 699 servoCurrent -= 1; 700 } 701 lcd.setCursor(6, 0); 702 lcd.print(servoCurrent); 703 lcd.print(" "); 704 dropperservo.write(servoCurrent); 705 delay(10); 706 } 707 showMenu(menu); 708} 709 710void setupMotor(){ 711 char keyPressed = ' '; 712 int value = 0; 713 displayMessage("Motor 9 Rst", "147L 258R 0 Exit"); 714 while(keyPressed != '0'){ 715 keyPressed = readKeypad(); 716 if(keyPressed == '1'){rotate('L', 50); value -=50;} 717 if(keyPressed == '2'){rotate('R', 50); value +=50;} 718 if(keyPressed == '4'){rotate('L', 10); value -=10;} 719 if(keyPressed == '5'){rotate('R', 10); value +=10;} 720 if(keyPressed == '7'){rotate('L', 1); value -=1;} 721 if(keyPressed == '8'){rotate('R', 1); value +=1;} 722 if(keyPressed == '9') value = 0; 723 lcd.setCursor(6, 0); 724 lcd.print(String(value) + " "); 725 } 726 referenceSelector(); 727 showMenu(menu); 728} 729 730void setOutput(int out){ 731 digitalWrite(motorPin1, bitRead(motorLookup[out], 0)); 732 digitalWrite(motorPin2, bitRead(motorLookup[out], 1)); 733 digitalWrite(motorPin3, bitRead(motorLookup[out], 2)); 734 digitalWrite(motorPin4, bitRead(motorLookup[out], 3)); 735} 736 737int getSwitchState(){ 738 if(digitalRead(switchPin) == HIGH){ 739 return 0; 740 } 741 else{ 742 return 1; 743 } 744} 745 746void referenceSelector(){ 747 displayMessage("Referencing", "Please wait..."); 748 while(getSwitchState() == 1){ 749 rotate('R', 1); 750 } 751 delay(50); 752 while(getSwitchState() == 0){ 753 rotate('L', 1); 754 } 755 delay(50); 756 while(getSwitchState() == 1){ 757 rotate('R', 1); 758 } 759 delay(50); 760 rotate('R', referenceSteps); 761 selectorPosition = 1; 762} 763 764void rotate(char turn, int steps){ 765 for(int j = 0; j < steps; j++){ 766 if(turn == 'L'){ 767 for(int i = 0; i < 8; i++){ 768 setOutput(i); 769 delayMicroseconds(motorSpeed); 770 } 771 } 772 if(turn == 'R'){ 773 for(int i = 7; i >= 0; i--){ 774 setOutput(i); 775 delayMicroseconds(motorSpeed); 776 } 777 } 778 } 779 digitalWrite(motorPin1, 0); 780 digitalWrite(motorPin2, 0); 781 digitalWrite(motorPin3, 0); 782 digitalWrite(motorPin4, 0); 783}
Code
arduino
1// Connect 4 Game 2// by Mark Bennett 2020 3 4/* Overview 5 * 6 7 * Plays physical Connect 4 game 8 * 9 * Can be played human v computer, 10 computer v computer or human v human 11 * Control is via a keypad and an LCD screen 12 shows menus and messages 13 * A servo and stepper motor are used to control dropping 14 counters into the grid 15 * Detects wins and draws and shows end game message 16 on the display 17 * 18 * 19 * Hardware 20 * 21 * Switch for column 22 selector referencing - pin 2 23 * Servo for dropping counters - pin 3 24 * Stepper 25 motor to drive column selector - pins 8 - 11 26 * LCD display with serial board 27 for user interface - SDA SCL 28 * Keypad for user data entry - pin A0 29 * Piezo 30 buzzer - pin 4 31 * 32 */ 33 34#include <Servo.h> 35#include <Wire.h> 36#include 37 <LiquidCrystal_I2C.h> 38 39// debug mode 40const bool debug = true; // sends 41 grid state to serial after each turn if true 42 43// stepper motor 44const int 45 motorPin1 = 8; 46const int motorPin2 = 9; 47const int motorPin3 = 10; 48const 49 int motorPin4 = 11; 50int motorSpeed = 1250; 51int motorLookup[8] = {B01001, B00001, 52 B00011, B00010, B00110, B00100, B01100, B01000}; 53int stepsPerColumn = 125; // 54 number of steps to move 1 column 55int referenceSteps = 2; // number of steps from 56 reference position to column 1 57 58// servo 59Servo dropperservo; 60const int 61 servoPin = 3; 62int servoCentre = 99; // servo drop position, centre 63int servoRed 64 = 120; // servo position to pick up red counter 65int servoYellow = 75; // servo 66 position to pick up yellow counter 67int servoCurrent = servoCentre; 68 69// 70 switch 71const int switchPin = 2; 72 73// keypad 74const int keypadPin = A0; 75// 76 voltage values for keys 1 2 3 4 5 6 7 8 9 * 0 # 77// * and # are not used and read 78 as 0 79int keyValues[] = {505, 336, 251, 222, 181, 153, 141, 124, 110, 103, 93, 80 85, 0}; 81int keys[13]; 82char lastKeyPressed = ' '; 83 84// lcd 85LiquidCrystal_I2C 86 lcd = LiquidCrystal_I2C(0x27, 16, 2); 87 88// led 89const int ledPin = 13; 90 91// 92 buzzer 93const int buzzerPin = 4; 94 95// menu control 96int menu = 0; 97 98// 99 game 100int selectorPosition = 1; 101int seed = 0; 102 103void setup() { 104 // 105 stepper motor 106 pinMode(motorPin1, OUTPUT); 107 pinMode(motorPin2, OUTPUT); 108 109 pinMode(motorPin3, OUTPUT); 110 pinMode(motorPin4, OUTPUT); 111 112 // 113 servo 114 dropperservo.attach(servoPin); 115 dropperservo.write(servoCurrent); 116 117 118 // switch 119 pinMode(switchPin, INPUT); 120 121 // keyboard 122 for(int 123 i = 0; i < 12; i++){ 124 keys[i] = ((keyValues[i] - keyValues[i + 1]) / 2) + 125 keyValues[i + 1]; 126 } 127 128 // lcd 129 lcd.init(); 130 lcd.backlight(); 131 132 showMenu(0); 133 134 // led 135 pinMode(ledPin, OUTPUT); 136 137 // buzzer 138 139 pinMode(buzzerPin, OUTPUT); 140 141 // game 142 seed = analogRead(3); 143 Serial.begin(9600); 144 145 146 playTune(1); 147} 148 149void loop(){ 150 // keypad 151 char keyPressed 152 = readKeypad(); 153 154 // menu navigation 155 if(keyPressed != ' '){ 156 processMenuInput(keyPressed); 157 158 } 159 160 // game 161 seed++; 162 163 delay(50); 164} 165 166void displayMessage(String 167 line1, String line2){ 168 lcd.clear(); 169 lcd.home(); 170 lcd.print(line1); 171 172 lcd.setCursor(0, 1); 173 lcd.print(line2); 174} 175 176char readKeypad(){ 177 178 char key = ' '; 179 if(analogRead(keypadPin) > keys[11]){ 180 delay(20); 181 182 int keyInput = analogRead(keypadPin); 183 if(keyInput > keys[0]) key = '1'; 184 185 else if(keyInput > keys[1]) key = '2'; 186 else if(keyInput > keys[2]) key 187 = '3'; 188 else if(keyInput > keys[3]) key = '4'; 189 else if(keyInput > keys[4]) 190 key = '5'; 191 else if(keyInput > keys[5]) key = '6'; 192 else if(keyInput 193 > keys[6]) key = '7'; 194 else if(keyInput > keys[7]) key = '8'; 195 else 196 if(keyInput > keys[8]) key = '9'; 197 else if(keyInput > keys[9]) key = '0'; 198 199 else if(keyInput > keys[10]) key = '0'; 200 else if(keyInput > keys[11]) 201 key = '0'; 202 tone(buzzerPin, 500, 50); 203 } 204 while(analogRead(keypadPin) 205 > keys[11]){ 206 delay(10); 207 } 208 return key; 209} 210 211void showMenu(int 212 menu){ 213 if(menu == 0) displayMessage("Arduino Connect4", "Press any key"); 214 215 if(menu == 1) displayMessage("1 Play Connect 4", "2 Test 3 Set-up"); 216 217 if(menu == 11) displayMessage("1 HvC 2 HvH", "3 CvC 0 Exit"); 218 if(menu 219 == 111) displayMessage("1 Human go first", "2 Second 0 Exit"); 220 if(menu 221 == 12) displayMessage("1 Keypad 2 Servo", "3 Select 0 Exit"); 222 if(menu == 223 13) displayMessage("1 Keypad 2 Servo", "3 Select 0 Exit"); 224} 225 226void 227 processMenuInput(char key){ 228 if(menu == 0){referenceSelector(); menu = 1; showMenu(menu);} 229 230 // top level menu 231 else if(menu == 1){ 232 if(key == '1'){menu = 11; showMenu(menu);} 233 234 if(key == '2'){menu = 12; showMenu(menu);} 235 if(key == '3'){menu = 13; 236 showMenu(menu);} 237 } 238 // play menu 239 else if(menu == 11){ 240 if(key 241 == '1'){menu = 111; showMenu(menu);} 242 if(key == '2'){playGame(1, 1);} 243 244 if(key == '3'){playGame(2, 2);} 245 if(key == '0'){menu = 1; showMenu(menu);} 246 247 } 248 else if(menu == 111){ 249 if(key == '1'){playGame(1, 2);} 250 if(key 251 == '2'){playGame(2, 1);} 252 if(key == '0'){menu = 11; showMenu(menu);} 253 } 254 255 // test menu 256 else if(menu == 12){ 257 if(key == '1'){testKeypad();} 258 259 if(key == '2'){testServo();} 260 if(key == '3'){testMotor();} 261 if(key 262 == '0'){menu = 1; showMenu(menu);} 263 } 264 // set-up menu 265 else if(menu 266 == 13){ 267 if(key == '1'){setupKeypad();} 268 if(key == '2'){setupServo();} 269 270 if(key == '3'){setupMotor();} 271 if(key == '0'){menu = 1; showMenu(menu);} 272 273 } 274} 275 276void playGame(int player1Type, int player2Type){ 277 randomSeed(seed); 278 279 for(int i = 0; i < 100; i++){ 280 int dump = random(50); 281 } 282 283 // 284 type, 1 = human, 2 = computer 285 int players[] = {0, 0}; 286 players[0] = player1Type; 287 288 players[1] = player2Type; 289 290 byte grid[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 291 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 292 0, 0, 0, 0, 0}; 293 bool gameEnd = false; 294 bool aborted = false; 295 int player 296 = 0; 297 int key = 0; 298 int selectorMove = 0; 299 String colour; 300 bool 301 draw = false; 302 int win = 0; 303 int row = 0; 304 int column = 0; 305 int 306 countDown = 2000; 307 char keyPressed = ' '; 308 309 resetGrid(grid); 310 311 312 while(!gameEnd){ 313 if(player == 0){colour = "Red";} 314 else{colour 315 = "Yellow";} 316 317 if(players[player] == 1){ 318 displayMessage(String("Player 319 ") + (player + 1) + " " + colour, "1-7 col 9 exit"); 320 key = getHumanMove(grid, 321 player, colour); 322 if(key == 9){ 323 gameEnd = true; 324 aborted 325 = true; 326 } 327 } 328 else{ 329 displayMessage(String("Player 330 ") + (player + 1) + " " + colour, "Deciding move..."); 331 key = decideMove(grid, 332 player); 333 delay(200); 334 } 335 336 if(key > 0 && key < 8){ 337 338 displayMessage("Executing move", "Please wait..."); 339 selectorMove 340 = key - selectorPosition; 341 if(selectorMove > 0){ 342 rotate('R', 343 selectorMove * stepsPerColumn); 344 } 345 if(selectorMove < 0){ 346 rotate('L', 347 selectorMove * -1 * stepsPerColumn); 348 } 349 selectorPosition = key; 350 351 if(player == 0) drop('R'); 352 if(player == 1) drop('Y'); 353 delay(2000); 354 355 356 column = key - 1; 357 row = addCounter(grid, player, column); 358 359 if(debug) sendGrid(grid); 360 361 // check for win 362 win 363 = checkForWin(grid, player, column, row); 364 if(win > 0) gameEnd = true; 365 366 367 // check for draw, all full and no winner 368 if(!gameEnd){ 369 370 draw = checkForDraw(grid); 371 if(draw) gameEnd = true; 372 } 373 374 375 player = abs(player - 1); 376 } 377 // check for abort if 378 not gameEnd 379 if(!gameEnd && players[0] == 2 && players[1] == 2){ 380 displayMessage("Press 381 0 to abort", "Wait 2s to cont"); 382 countDown = 2000; 383 while(countDown 384 > 0){ 385 keyPressed = readKeypad(); 386 if(keyPressed == '0'){ 387 388 Serial.println(keyPressed); 389 gameEnd = true; 390 aborted 391 = true; 392 } 393 delay(50); 394 countDown = countDown - 50; 395 396 } 397 } 398 } 399 // end game messages 400 if(draw){ 401 displayMessage("Result 402 is a Draw", "Press any key"); 403 playTune(1); 404 } 405 if(win == 1){ 406 407 displayMessage("Winner P1 Red", "Press any key"); 408 playTune(2); 409 410 } 411 if(win == 2){ 412 displayMessage("Winner P2 Yellow", "Press any key"); 413 414 playTune(2); 415 } 416 if(aborted) displayMessage("Game aborted", "Press 417 any key"); 418 while(readKeypad() == ' '){ 419 delay(20); 420 } 421 422 showMenu(menu); 423} 424 425void 426 playTune(int tune){ 427 if(tune == 1){ 428 tone(buzzerPin, 262, 200); 429 delay(210); 430 431 tone(buzzerPin, 523, 400); 432 delay(410); 433 } 434 if(tune == 2){ 435 436 tone(buzzerPin, 262, 200); 437 delay(210); 438 tone(buzzerPin, 330, 200); 439 440 delay(210); 441 tone(buzzerPin, 392, 200); 442 delay(210); 443 tone(buzzerPin, 444 523, 400); 445 delay(410); 446 tone(buzzerPin, 392, 200); 447 delay(210); 448 449 tone(buzzerPin, 523, 400); 450 delay(410); 451 } 452} 453 454 455int getHumanMove(byte 456 g[], int p, String colour){ 457 bool columnAccepted = true; 458 char keyPressed 459 = ' '; 460 int key = 0; 461 462 do{ 463 columnAccepted = true; 464 keyPressed 465 = ' '; 466 while(keyPressed == ' '){ 467 keyPressed = readKeypad(); 468 469 delay(10); 470 } 471 key = 0; 472 if(keyPressed == '1') key = 1; 473 474 if(keyPressed == '2') key = 2; 475 if(keyPressed == '3') key = 3; 476 if(keyPressed 477 == '4') key = 4; 478 if(keyPressed == '5') key = 5; 479 if(keyPressed == '6') 480 key = 6; 481 if(keyPressed == '7') key = 7; 482 483 // exit from game 484 485 if(keyPressed == '9') key = 9; 486 487 if(key > 0 && key < 8){ 488 // 489 check for full column 490 if(g[toGrid(key - 1, 5)] != 0){ 491 displayMessage("Full, 492 try again", String("P") + (p + 1) + " " + colour + " 1-7"); 493 columnAccepted 494 = false; 495 } 496 } 497 }while(!columnAccepted && key != 9); 498 return 499 key; 500} 501 502int decideMove(byte g[], int p){ 503 int columns[] = {50, 50, 504 50, 50, 50, 50, 50}; 505 byte tempGrid[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 506 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 507 0, 0, 0}; 508 int win = 0; 509 int key = 0; 510 int biggest = 0; 511 int count 512 = 0; 513 int selected = 0; 514 int row = 0; 515 int p2 = abs(p - 1); 516 517 518 // mark all full columns with 0s 519 for(int i = 0; i < 7; i++){ 520 if(g[toGrid(i, 521 5)] != 0){ 522 columns[i] = 0; 523 } 524 } 525 526 // add 1000 to winning 527 columns[] 528 for(int i = 0; i < 7; i++){ 529 if(columns[i] > 0){ 530 for(int 531 j = 0; j < 42; j++){ 532 tempGrid[j] = g[j]; 533 } 534 row = addCounter(tempGrid, 535 p, i); 536 win = checkForWin(tempGrid, p, i, row); 537 if(win > 0){ 538 539 columns[i] += 1000; 540 } 541 } 542 } 543 544 // add 50 to losing 545 columns[] 546 for(int i = 0; i < 7; i++){ 547 if(columns[i] > 0){ 548 for(int 549 j = 0; j < 42; j++){ 550 tempGrid[j] = g[j]; 551 } 552 row = addCounter(tempGrid, 553 p2, i); 554 win = checkForWin(tempGrid, p2, i, row); 555 if(win > 0){ 556 557 columns[i] += 50; 558 } 559 } 560 } 561 562 // check for give 563 away moves 564 for(int i = 0; i < 7; i++){ 565 if(columns[i] > 0){ 566 for(int 567 k = 0; k < 7; k++){ 568 if(columns[k] > 0){ 569 for(int j = 0; j 570 < 42; j++){ 571 tempGrid[j] = g[j]; 572 } 573 row = 574 addCounter(tempGrid, p, i); 575 if(i != k || row < 5){ 576 row 577 = addCounter(tempGrid, p2, k); 578 win = checkForWin(tempGrid, p2, k, 579 row); 580 if(win > 0){ 581 columns[i] -= 10; 582 } 583 584 } 585 } 586 } 587 } 588 } 589 590 // find biggest value 591 in columns[] 592 for(int i = 0; i < 7; i++){ 593 if(columns[i] > biggest){ 594 595 biggest = columns[i]; 596 } 597 } 598 // count biggest in columns[] 599 600 for(int i = 0; i < 7; i++){ 601 if(columns[i] == biggest){ 602 count++; 603 604 } 605 } 606 // random select a biggest column 607 selected = random(count) 608 + 1; 609 if(debug){ 610 Serial.println(String("Player ") + p); 611 Serial.println(String("Biggest 612 ") + biggest); 613 Serial.println(String("Count ") + count); 614 Serial.println(String("Selected 615 ") + selected); 616 } 617 for(int i = 0; i < 7; i++){ 618 if(columns[i] == 619 biggest){ 620 selected--; 621 if(selected == 0){ 622 key = i + 623 1; 624 } 625 } 626 } 627 628 if(debug){ 629 for(int i = 0; i < 7; i++){ 630 631 Serial.print(columns[i]); 632 Serial.print(" "); 633 } 634 Serial.println(""); 635 636 Serial.println(""); 637 } 638 639 return key; 640} 641 642int addCounter(byte 643 g[], int p, int c){ 644 // find row 645 int row = 0; 646 while(g[toGrid(c, row)] 647 != 0){ 648 row++; 649 } 650 // update array 651 g[toGrid(c, row)] = p + 1; 652 653 return row; 654} 655 656int checkForDraw(byte g[]){ 657 bool draw = true; 658 659 for(int i = 0; i < 7; i++){ 660 if(g[toGrid(i, 5)] == 0){ 661 draw = false; 662 663 } 664 } 665 return draw; 666} 667 668int checkForWin(byte g[], int p, int 669 c, int r){ 670 p++; 671 int win = 0; 672 673 if(win == 0) win = checkFor4(g, 674 p, c, r, 3, 6, 0, 2, -1, 1, -2, 2, -3, 3); 675 if(win == 0) win = checkFor4(g, 676 p, c, r, 2, 5, 1, 3, -1, 1, -2, 2, 1, -1); 677 if(win == 0) win = checkFor4(g, 678 p, c, r, 1, 4, 2, 4, -1, 1, 1, -1, 2, -2); 679 if(win == 0) win = checkFor4(g, 680 p, c, r, 0, 3, 3, 5, 1, -1, 2, -2, 3, -3); 681 682 if(win == 0) win = checkFor4(g, 683 p, c, r, 0, 3, 0, 2, 1, 1, 2, 2, 3, 3); 684 if(win == 0) win = checkFor4(g, p, 685 c, r, 1, 4, 1, 3, 1, 1, 2, 2, -1, -1); 686 if(win == 0) win = checkFor4(g, p, c, 687 r, 2, 5, 2, 4, 1, 1, -1, -1, -2, -2); 688 if(win == 0) win = checkFor4(g, p, c, 689 r, 3, 6, 3, 5, -1, -1, -2, -2, -3, -3); 690 691 if(win == 0) win = checkFor4(g, 692 p, c, r, 0, 3, 0, 5, 1, 0, 2, 0, 3, 0); 693 if(win == 0) win = checkFor4(g, p, 694 c, r, 1, 4, 0, 5, 1, 0, 2, 0, -1, 0); 695 if(win == 0) win = checkFor4(g, p, c, 696 r, 2, 5, 0, 5, 1, 0, -1, 0, -2, 0); 697 if(win == 0) win = checkFor4(g, p, c, r, 698 3, 6, 0, 5, -1, 0, -2, 0, -3, 0); 699 700 if(win == 0) win = checkFor4(g, p, c, 701 r, 0, 6, 3, 5, 0, -1, 0, -2, 0, -3); 702 703 return win; 704} 705 706int checkFor4(byte 707 g[], int p, int c, int r, int c1, int c2, int r1, int r2, int sc1, int sr1, int 708 sc2, int sr2, int sc3, int sr3){ 709 int win = 0; 710 if(c >= c1 && c <= c2 && 711 r >= r1 && r <= r2 && g[toGrid(c + sc1, r + sr1)] == p && g[toGrid(c + sc2, r + 712 sr2)] == p && g[toGrid(c + sc3, r + sr3)] == p) win = p; 713 return win; 714} 715 716void 717 resetGrid(byte grid[]){ 718 for(int i = 0; i < 42; i++){ 719 grid[i] = 0; 720 721 } 722} 723 724void drop(char colour){ 725 if(colour == 'R'){ 726 dropperservo.write(servoRed); 727 728 } 729 else{ 730 dropperservo.write(servoYellow); 731 } 732 delay(1000); 733 734 dropperservo.write(servoCentre); 735} 736 737int toGrid(int column, int row){ 738 739 return column + (row * 7); 740} 741 742int toColumn(int grid){ 743 return grid 744 - (toRow(grid) * 7); 745} 746 747int toRow(int grid){ 748 return grid / 7; 749} 750 751void 752 sendGrid(byte g[]){ 753 for(int r = 5; r >= 0; r--){ 754 for(int c = 0; c < 755 7; c++){ 756 Serial.print(g[toGrid(c, r)]); 757 Serial.print(" "); 758 759 } 760 Serial.println(""); 761 } 762 Serial.println(""); 763 764 /* 765 766 for(int i = 0; i < 42; i++){ 767 Serial.print(g[i]); 768 Serial.print(" 769 "); 770 } 771 Serial.println(""); 772 Serial.println(""); 773 */ 774} 775 776void 777 testKeypad(){ 778 int countDown = 5000; 779 char keyPressed = ' '; 780 displayMessage("Key", 781 "Wait 5s to exit"); 782 while(countDown > 0){ 783 keyPressed = readKeypad(); 784 785 if(keyPressed != ' '){ 786 countDown = 5000; 787 lcd.setCursor(4, 788 0); 789 lcd.print(keyPressed); 790 } 791 delay(50); 792 countDown 793 = countDown - 50; 794 } 795 showMenu(menu); 796} 797 798void testServo(){ 799 800 char keyPressed = ' '; 801 displayMessage("1 Drop Red", "2 Yellow 0 Exit"); 802 803 while(keyPressed != '0'){ 804 keyPressed = readKeypad(); 805 if(keyPressed 806 == '1'){ 807 dropperservo.write(servoRed); 808 delay(1000); 809 dropperservo.write(servoCentre); 810 811 } 812 if(keyPressed == '2'){ 813 dropperservo.write(servoYellow); 814 815 delay(1000); 816 dropperservo.write(servoCentre); 817 } 818 delay(10); 819 820 } 821 showMenu(menu); 822} 823 824void testMotor(){ 825 char keyPressed = ' 826 '; 827 int key = 0; 828 int selectorMove = 0; 829 showTestMotorControlMenu(); 830 831 while(keyPressed != '0'){ 832 keyPressed = readKeypad(); 833 selectorMove 834 = 0; 835 key = 0; 836 if(keyPressed != ' '){ 837 if(keyPressed == '1') 838 key = 1; 839 if(keyPressed == '2') key = 2; 840 if(keyPressed == '3') 841 key = 3; 842 if(keyPressed == '4') key = 4; 843 if(keyPressed == '5') 844 key = 5; 845 if(keyPressed == '6') key = 6; 846 if(keyPressed == '7') 847 key = 7; 848 849 if(key > 0){ 850 selectorMove = key - selectorPosition; 851 852 if(selectorMove > 0){ 853 rotate('R', selectorMove * stepsPerColumn); 854 855 } 856 if(selectorMove < 0){ 857 rotate('L', selectorMove 858 * -1 * stepsPerColumn); 859 } 860 selectorPosition = key; 861 } 862 863 864 if(keyPressed == '8'){ 865 referenceSelector(); 866 showTestMotorControlMenu(); 867 868 } 869 } 870 delay(10); 871 } 872 showMenu(menu); 873} 874 875void 876 showTestMotorControlMenu(){ 877 displayMessage("1-7 Select Col", "8 Ref 0 878 Exit"); 879} 880 881void setupKeypad(){ 882 int countDown = 5000; 883 int value 884 = 0; 885 displayMessage("Keypad", "Wait 5s to exit"); 886 while(countDown 887 > 0){ 888 value = analogRead(keypadPin); 889 if(value > 40){ 890 countDown 891 = 5000; 892 } 893 lcd.setCursor(7, 0); 894 lcd.print(value); 895 lcd.print(" 896 "); 897 delay(50); 898 countDown = countDown - 50; 899 } 900 showMenu(menu); 901} 902 903void 904 setupServo(){ 905 char keyPressed = ' '; 906 displayMessage("Servo", "1/4+ 907 2/5- 0 Exit"); 908 while(keyPressed != '0'){ 909 keyPressed = readKeypad(); 910 911 if(keyPressed == '1'){ 912 servoCurrent += 3; 913 } 914 if(keyPressed 915 == '2'){ 916 servoCurrent -= 3; 917 } 918 if(keyPressed == '4'){ 919 920 servoCurrent += 1; 921 } 922 if(keyPressed == '5'){ 923 servoCurrent 924 -= 1; 925 } 926 lcd.setCursor(6, 0); 927 lcd.print(servoCurrent); 928 lcd.print(" 929 "); 930 dropperservo.write(servoCurrent); 931 delay(10); 932 } 933 showMenu(menu); 934} 935 936void 937 setupMotor(){ 938 char keyPressed = ' '; 939 int value = 0; 940 displayMessage("Motor 941 9 Rst", "147L 258R 0 Exit"); 942 while(keyPressed != '0'){ 943 keyPressed 944 = readKeypad(); 945 if(keyPressed == '1'){rotate('L', 50); value -=50;} 946 if(keyPressed 947 == '2'){rotate('R', 50); value +=50;} 948 if(keyPressed == '4'){rotate('L', 10); 949 value -=10;} 950 if(keyPressed == '5'){rotate('R', 10); value +=10;} 951 if(keyPressed 952 == '7'){rotate('L', 1); value -=1;} 953 if(keyPressed == '8'){rotate('R', 1); 954 value +=1;} 955 if(keyPressed == '9') value = 0; 956 lcd.setCursor(6, 0); 957 958 lcd.print(String(value) + " "); 959 } 960 referenceSelector(); 961 showMenu(menu); 962} 963 964void 965 setOutput(int out){ 966 digitalWrite(motorPin1, bitRead(motorLookup[out], 0)); 967 968 digitalWrite(motorPin2, bitRead(motorLookup[out], 1)); 969 digitalWrite(motorPin3, 970 bitRead(motorLookup[out], 2)); 971 digitalWrite(motorPin4, bitRead(motorLookup[out], 972 3)); 973} 974 975int getSwitchState(){ 976 if(digitalRead(switchPin) == HIGH){ 977 978 return 0; 979 } 980 else{ 981 return 1; 982 } 983} 984 985void referenceSelector(){ 986 987 displayMessage("Referencing", "Please wait..."); 988 while(getSwitchState() 989 == 1){ 990 rotate('R', 1); 991 } 992 delay(50); 993 while(getSwitchState() 994 == 0){ 995 rotate('L', 1); 996 } 997 delay(50); 998 while(getSwitchState() 999 == 1){ 1000 rotate('R', 1); 1001 } 1002 delay(50); 1003 rotate('R', referenceSteps); 1004 1005 selectorPosition = 1; 1006} 1007 1008void rotate(char turn, int steps){ 1009 for(int 1010 j = 0; j < steps; j++){ 1011 if(turn == 'L'){ 1012 for(int i = 0; i < 8; i++){ 1013 1014 setOutput(i); 1015 delayMicroseconds(motorSpeed); 1016 } 1017 1018 } 1019 if(turn == 'R'){ 1020 for(int i = 7; i >= 0; i--){ 1021 setOutput(i); 1022 1023 delayMicroseconds(motorSpeed); 1024 } 1025 } 1026 } 1027 digitalWrite(motorPin1, 1028 0); 1029 digitalWrite(motorPin2, 0); 1030 digitalWrite(motorPin3, 0); 1031 digitalWrite(motorPin4, 1032 0); 1033}
Downloadable files
Instructions
Instructions
Circuit diagram
Circuit diagram
Instructions
Instructions
Circuit diagram
Circuit diagram
Documentation
Rack C
Rack C
Base front
Base front
Stepper Motor Mount
Stepper Motor Mount
Servo Mount B
Servo Mount B
Dropper Top Plate A
Dropper Top Plate A
Base rear
Base rear
Control panel (with recesses) (Option 1)
Control panel (with recesses) (Option 1)
Control panel (without recesses) (Option 2)
Control panel (without recesses) (Option 2)
Rack A
Rack A
Selector B
Selector B
Lower Pillar (3 required)
Lower Pillar (3 required)
Dropper Counter Selector B
Dropper Counter Selector B
Magazine Split B (Option 1)
Magazine Split B (Option 1)
Upper Pillar C
Upper Pillar C
Magazine Split A (Option 1)
Magazine Split A (Option 1)
Dropper Top Plate B
Dropper Top Plate B
Servo Adaptor with Horn Retainer (Option 1)
Servo Adaptor with Horn Retainer (Option 1)
Counter Chute Nudger
Counter Chute Nudger
Counter Chute
Counter Chute
Switch mount
Switch mount
Rack B
Rack B
Upper Pillar A
Upper Pillar A
Dropper Base Plate A
Dropper Base Plate A
Dropper Base Plate B
Dropper Base Plate B
Control Panel Support
Control Panel Support
Upper Pillar B
Upper Pillar B
Servo Adaptor without Horn Retainer (Option 2)
Servo Adaptor without Horn Retainer (Option 2)
Guideway B
Guideway B
Cog
Cog
Magazine (Option 2)
Magazine (Option 2)
Selector A
Selector A
Servo Mount A
Servo Mount A
Selector C
Selector C
Dropper Counter Selector A
Dropper Counter Selector A
Servo Mount A
Servo Mount A
Guideway B
Guideway B
Base rear
Base rear
Switch mount
Switch mount
Counter Chute Nudger
Counter Chute Nudger
Dropper Top Plate A
Dropper Top Plate A
Control Panel Support
Control Panel Support
Servo Adaptor without Horn Retainer (Option 2)
Servo Adaptor without Horn Retainer (Option 2)
Counter Chute
Counter Chute
Base front
Base front
Selector C
Selector C
Rack B
Rack B
Stepper Motor Mount
Stepper Motor Mount
Control panel (without recesses) (Option 2)
Control panel (without recesses) (Option 2)
Lower Pillar (3 required)
Lower Pillar (3 required)
Upper Pillar A
Upper Pillar A
Rack A
Rack A
Cog
Cog
Servo Mount B
Servo Mount B
Upper Pillar C
Upper Pillar C
Selector A
Selector A
Magazine (Option 2)
Magazine (Option 2)
Magazine Split A (Option 1)
Magazine Split A (Option 1)
Dropper Base Plate A
Dropper Base Plate A
Dropper Counter Selector A
Dropper Counter Selector A
Dropper Top Plate B
Dropper Top Plate B
Selector B
Selector B
Guideway A
Guideway A
Dropper Base Plate B
Dropper Base Plate B
Upper Pillar B
Upper Pillar B
Servo Adaptor with Horn Retainer (Option 1)
Servo Adaptor with Horn Retainer (Option 1)
Magazine Split B (Option 1)
Magazine Split B (Option 1)
Dropper Counter Selector B
Dropper Counter Selector B
Control panel (with recesses) (Option 1)
Control panel (with recesses) (Option 1)
Rack C
Rack C
Comments
Only logged in users can leave comments