Devices & Components
1
Arduino Uno Rev3
12
Red 5mm LEDs
1
7.4V Lithium Ion Battery
1
8 Ohm Speaker
12
470ohm resistor
16
5x2 mm magnets
2
M3 25 mm Hex Carbon Steel Standoffs
1
1000 ohm resistor
1
Black PETG
1
Aluminum Chassis
1
Swivel Ball Caster
1
6x6x6 Momentary Button Switch
1
5V Buck Converter
1
KCD1 Rocker Switch
1
8GB MicroSD card
1
HC-SR04 US Sensor
1
DFRobot DFPlayer Mini
1
M3 nuts and bolts (various sizes)
4
TT Motors + Rubber Wheels
1
L298N Motor DC Dual H-Bridge Motor Driver Controller
Hardware & Tools
3D printer (any brand)
Precision Screwdriver
Soldering iron (+ solder)
Software & Tools
Arduino IDE
Project description
Code
Party-Mouse-Droid
cpp
1/* 2Autonomous Party Mouse Droid 3 4 Copyright (c) 2026 Chaos Theory 5 youtube.com/@heychaostheory 6 7 This project is an unofficial, non-commercial Star Wars fan project. 8 Star Wars, Mouse Droid, Sabine Wren, and related intellectual property belong to 9 their respective rights holders. This project is not affiliated with 10 or endorsed by Lucasfilm Ltd., The Walt Disney Company, or any other 11 Star Wars rights holder. 12 13 The original source code in this file is licensed under the MIT License. 14 See the LICENSE-MIT.txt file for the full license text. */ 15 16#include <SoftwareSerial.h> 17#include <DFRobotDFPlayerMini.h> 18 19const byte ECHO_PIN = 2; 20const byte TRIG_PIN = 3; 21 22const byte BUTTON_PIN = 4; 23 24const byte MOTOR_LEFT_ENABLE_PIN = 5; 25const byte MOTOR_LEFT_IN1_PIN = 6; 26const byte MOTOR_LEFT_IN2_PIN = 7; 27 28const byte MOTOR_RIGHT_IN1_PIN = 8; 29const byte MOTOR_RIGHT_IN2_PIN = 9; 30const byte MOTOR_RIGHT_ENABLE_PIN = 10; 31 32const byte DFPLAYER_TX_PIN = 11; 33const byte DFPLAYER_RX_PIN = 12; 34 35const byte LED_PINS[] = { 36 A0, 37 A1, 38 A2, 39 A3 40}; 41 42const byte LED_COUNT = 43 sizeof(LED_PINS) / sizeof(LED_PINS[0]); 44const byte DRIVE_SPEED = 180; 45const byte TURN_SPEED = 200; 46const byte BACKUP_SPEED = 180; 47 48// obstacle avoidance distance 49const unsigned int WALL_DISTANCE_CM = 40; 50 51// turning clearance 52const unsigned int AVOID_SAFE_DISTANCE_CM = 50; 53 54// scream mode activation maximum distance 55const unsigned int SCREAM_DISTANCE_CM = 25; 56 57const unsigned long ULTRASONIC_INTERVAL_MS = 60; 58const unsigned long ULTRASONIC_TIMEOUT_US = 25000UL; 59 60const byte DISTANCE_SAMPLE_COUNT = 5; 61const unsigned long BUTTON_DEBOUNCE_MS = 30; 62 63const unsigned long AVOID_STOP_DURATION_MS = 120; 64const unsigned long AVOID_BACKUP_DURATION_MS = 550; 65 66const unsigned long AVOID_TURN_CHUNK_MS = 500; 67 68const unsigned long AVOID_LOOK_DELAY_MS = 120; 69 70const byte AVOID_MAX_TURN_CHUNKS = 6; 71 72const unsigned long RANDOM_PAUSE_MIN_INTERVAL_MS = 3500; 73const unsigned long RANDOM_PAUSE_MAX_INTERVAL_MS = 7500; 74 75const unsigned long RANDOM_PAUSE_MIN_DURATION_MS = 250; 76const unsigned long RANDOM_PAUSE_MAX_DURATION_MS = 650; 77 78const unsigned long PERSONALITY_WIGGLE_MIN_INTERVAL_MS = 2500; 79const unsigned long PERSONALITY_WIGGLE_MAX_INTERVAL_MS = 5500; 80const unsigned long PERSONALITY_WIGGLE_DURATION_MS = 140; 81 82const unsigned long SCREAM_PAUSE_MS = 150; 83 84const unsigned long SCREAM_BACKUP_DURATION_MS = 5000; 85 86const unsigned long SCREAM_TURN_DURATION_MS = 0; 87 88const unsigned long SURPRISE_COOLDOWN_MS = 2000; 89 90const unsigned long SCREAM_WIGGLE_INTERVAL_MS = 350; 91 92const byte SCREAM_WIGGLE_SPEED = 85; 93 94const unsigned long LED_BLINK_INTERVAL_MS = 1000; 95 96const byte PARTY_SPIN_SPEED = 255; 97 98// party mode duration in milliseconds 99const unsigned long PARTY_DURATION_MS = 20000; 100 101const byte TRACK_NORMAL = 1; 102const byte TRACK_SCREAM = 2; 103const byte TRACK_PARTY = 3; 104 105const byte DFPLAYER_VOLUME = 30; 106 107enum RobotState { 108 NORMAL, 109 SCREAM, 110 PARTY 111}; 112 113enum AvoidanceStep { 114 AVOID_DRIVING, 115 AVOID_STOPPING, 116 AVOID_BACKING_UP, 117 AVOID_TURNING, 118 AVOID_LOOKING 119}; 120 121enum NormalPauseState { 122 PAUSE_NOT_ACTIVE, 123 PAUSE_ACTIVE 124}; 125 126RobotState robotState = NORMAL; 127 128AvoidanceStep avoidanceStep = 129 AVOID_DRIVING; 130 131NormalPauseState normalPauseState = 132 PAUSE_NOT_ACTIVE; 133 134SoftwareSerial dfPlayerSerial( 135 DFPLAYER_RX_PIN, 136 DFPLAYER_TX_PIN 137); 138 139DFRobotDFPlayerMini dfPlayer; 140 141bool dfPlayerReady = false; 142 143unsigned long stateStartedAt = 0; 144unsigned long avoidanceStepStartedAt = 0; 145unsigned long avoidanceLookStartedAt = 0; 146unsigned long surpriseIgnoreUntil = 0; 147unsigned long lastUltrasonicReadAt = 0; 148unsigned long lastLedChangeAt = 0; 149unsigned long nextRandomPauseAt = 0; 150unsigned long randomPauseStartedAt = 0; 151unsigned long randomPauseDuration = 0; 152unsigned long nextPersonalityWiggleAt = 0; 153unsigned long personalityWiggleEndsAt = 0; 154 155bool personalityWiggleActive = false; 156 157byte avoidanceTurnChunks = 0; 158 159bool turnLeftSelected = true; 160bool nextTurnLeft = true; 161 162unsigned long screamLastWiggleAt = 0; 163 164bool screamWiggleLeft = true; 165bool screamTurnInitialized = false; 166bool partyLedsAlternate = false; 167bool partyTrackFinished = false; 168 169unsigned int rawDistanceCm = 0; 170unsigned int distanceSamples[DISTANCE_SAMPLE_COUNT] = { 171 0 172}; 173 174byte distanceSampleIndex = 0; 175byte distanceSampleTotal = 0; 176 177unsigned long distanceSampleSum = 0; 178unsigned int filteredDistanceCm = 0; 179unsigned int previousFilteredDistanceCm = 0; 180 181bool lastRawButtonState = HIGH; 182bool stableButtonState = HIGH; 183 184unsigned long rawButtonChangedAt = 0; 185 186enum AudioMode { 187 AUDIO_NONE, 188 AUDIO_LOOPING, 189 AUDIO_ONCE 190}; 191 192AudioMode activeAudioMode = AUDIO_NONE; 193 194byte activeAudioTrack = 0; 195 196void setMotorDirection( 197 bool leftForward, 198 bool rightForward 199) { 200 201 digitalWrite( 202 MOTOR_LEFT_IN1_PIN, 203 leftForward ? HIGH : LOW 204 ); 205 206 digitalWrite( 207 MOTOR_LEFT_IN2_PIN, 208 leftForward ? LOW : HIGH 209 ); 210 211 digitalWrite( 212 MOTOR_RIGHT_IN1_PIN, 213 rightForward ? HIGH : LOW 214 ); 215 216 digitalWrite( 217 MOTOR_RIGHT_IN2_PIN, 218 rightForward ? LOW : HIGH 219 ); 220} 221 222void driveForward(byte speed) { 223 224 setMotorDirection( 225 true, 226 true 227 ); 228 229 analogWrite( 230 MOTOR_LEFT_ENABLE_PIN, 231 speed 232 ); 233 234 analogWrite( 235 MOTOR_RIGHT_ENABLE_PIN, 236 speed 237 ); 238} 239 240void driveBackward(byte speed) { 241 242 setMotorDirection( 243 false, 244 false 245 ); 246 247 analogWrite( 248 MOTOR_LEFT_ENABLE_PIN, 249 speed 250 ); 251 252 analogWrite( 253 MOTOR_RIGHT_ENABLE_PIN, 254 speed 255 ); 256} 257 258void turnLeft(byte speed) { 259 260 digitalWrite( 261 MOTOR_LEFT_IN1_PIN, 262 LOW 263 ); 264 265 digitalWrite( 266 MOTOR_LEFT_IN2_PIN, 267 LOW 268 ); 269 270 analogWrite( 271 MOTOR_LEFT_ENABLE_PIN, 272 0 273 ); 274 275 digitalWrite( 276 MOTOR_RIGHT_IN1_PIN, 277 HIGH 278 ); 279 280 digitalWrite( 281 MOTOR_RIGHT_IN2_PIN, 282 LOW 283 ); 284 285 analogWrite( 286 MOTOR_RIGHT_ENABLE_PIN, 287 speed 288 ); 289} 290 291void turnRight(byte speed) { 292 293 digitalWrite( 294 MOTOR_RIGHT_IN1_PIN, 295 LOW 296 ); 297 298 digitalWrite( 299 MOTOR_RIGHT_IN2_PIN, 300 LOW 301 ); 302 303 analogWrite( 304 MOTOR_RIGHT_ENABLE_PIN, 305 0 306 ); 307 308 digitalWrite( 309 MOTOR_LEFT_IN1_PIN, 310 HIGH 311 ); 312 313 digitalWrite( 314 MOTOR_LEFT_IN2_PIN, 315 LOW 316 ); 317 318 analogWrite( 319 MOTOR_LEFT_ENABLE_PIN, 320 speed 321 ); 322} 323 324 325 326void stopMotors() { 327 328 digitalWrite( 329 MOTOR_LEFT_ENABLE_PIN, 330 LOW 331 ); 332 333 digitalWrite( 334 MOTOR_RIGHT_ENABLE_PIN, 335 LOW 336 ); 337 338 digitalWrite( 339 MOTOR_LEFT_IN1_PIN, 340 LOW 341 ); 342 343 digitalWrite( 344 MOTOR_LEFT_IN2_PIN, 345 LOW 346 ); 347 348 digitalWrite( 349 MOTOR_RIGHT_IN1_PIN, 350 LOW 351 ); 352 353 digitalWrite( 354 MOTOR_RIGHT_IN2_PIN, 355 LOW 356 ); 357} 358 359void setAllLeds(bool on) { 360 361 for ( 362 byte i = 0; 363 i < LED_COUNT; 364 i++ 365 ) { 366 367 digitalWrite( 368 LED_PINS[i], 369 on ? HIGH : LOW 370 ); 371 } 372} 373 374void showPartyLeds( 375 bool alternatePattern 376) { 377 378 digitalWrite( 379 LED_PINS[0], 380 alternatePattern ? LOW : HIGH 381 ); 382 383 digitalWrite( 384 LED_PINS[1], 385 alternatePattern ? HIGH : LOW 386 ); 387 388 digitalWrite( 389 LED_PINS[2], 390 alternatePattern ? LOW : HIGH 391 ); 392 393 digitalWrite( 394 LED_PINS[3], 395 alternatePattern ? HIGH : LOW 396 ); 397} 398 399void playLoop(byte track) { 400 401 if ( 402 !dfPlayerReady || 403 ( 404 activeAudioMode == AUDIO_LOOPING && 405 activeAudioTrack == track 406 ) 407 ) { 408 409 return; 410 } 411 412 dfPlayer.loop(track); 413 414 activeAudioMode = 415 AUDIO_LOOPING; 416 417 activeAudioTrack = 418 track; 419} 420 421void playOnce(byte track) { 422 423 Serial.print("playOnce("); 424 Serial.print(track); 425 Serial.println(")"); 426 427 if (!dfPlayerReady) { 428 429 Serial.println( 430 "DFPlayer not ready" 431 ); 432 433 return; 434 } 435 436 if ( 437 activeAudioMode == AUDIO_ONCE && 438 activeAudioTrack == track 439 ) { 440 441 Serial.println( 442 "Already playing this track" 443 ); 444 445 return; 446 } 447 448 Serial.println( 449 "Sending play command" 450 ); 451 452 dfPlayer.playMp3Folder(track); 453 454 activeAudioMode = 455 AUDIO_ONCE; 456 457 activeAudioTrack = 458 track; 459} 460 461void serviceDfPlayerEvents() { 462 463 if ( 464 !dfPlayerReady || 465 !dfPlayer.available() 466 ) { 467 468 return; 469 } 470 471 const uint8_t eventType = 472 dfPlayer.readType(); 473 474 const int eventValue = 475 dfPlayer.read(); 476 477 if ( 478 eventType == DFPlayerPlayFinished && 479 eventValue == TRACK_PARTY && 480 robotState == PARTY 481 ) { 482 483 partyTrackFinished = 484 true; 485 } 486} 487 488unsigned int readDistance() { 489 490 digitalWrite( 491 TRIG_PIN, 492 LOW 493 ); 494 495 delayMicroseconds(2); 496 497 digitalWrite( 498 TRIG_PIN, 499 HIGH 500 ); 501 502 delayMicroseconds(10); 503 504 digitalWrite( 505 TRIG_PIN, 506 LOW 507 ); 508 509 const unsigned long pulseDuration = 510 pulseIn( 511 ECHO_PIN, 512 HIGH, 513 ULTRASONIC_TIMEOUT_US 514 ); 515 516 if ( 517 pulseDuration == 0 518 ) { 519 520 return 0; 521 } 522 523 return ( 524 unsigned int 525 )( 526 pulseDuration / 58UL 527 ); 528} 529 530void addDistanceSample( 531 unsigned int distanceCm 532) { 533 534 if ( 535 distanceCm == 0 536 ) { 537 538 return; 539 } 540 541 if ( 542 distanceSampleTotal == 543 DISTANCE_SAMPLE_COUNT 544 ) { 545 546 distanceSampleSum -= 547 distanceSamples[ 548 distanceSampleIndex 549 ]; 550 551 } else { 552 553 distanceSampleTotal++; 554 } 555 556 distanceSamples[ 557 distanceSampleIndex 558 ] = distanceCm; 559 560 distanceSampleSum += 561 distanceCm; 562 563 distanceSampleIndex = 564 ( 565 distanceSampleIndex + 1 566 ) % 567 DISTANCE_SAMPLE_COUNT; 568 569 filteredDistanceCm = 570 distanceSampleSum / 571 distanceSampleTotal; 572} 573 574void updateUltrasonic() { 575 576 const unsigned long now = 577 millis(); 578 579 if ( 580 now - lastUltrasonicReadAt < 581 ULTRASONIC_INTERVAL_MS 582 ) { 583 584 return; 585 } 586 587 lastUltrasonicReadAt = 588 now; 589 590 const unsigned int measuredDistanceCm = 591 readDistance(); 592 593 rawDistanceCm = 594 measuredDistanceCm; 595 596 if ( 597 measuredDistanceCm == 0 598 ) { 599 600 return; 601 } 602 603 previousFilteredDistanceCm = 604 filteredDistanceCm; 605 606 addDistanceSample( 607 measuredDistanceCm 608 ); 609} 610 611unsigned int getFreshDistance() { 612 613 const unsigned int distance = 614 readDistance(); 615 616 rawDistanceCm = 617 distance; 618 619 if ( 620 distance > 0 621 ) { 622 623 previousFilteredDistanceCm = 624 filteredDistanceCm; 625 626 addDistanceSample( 627 distance 628 ); 629 } 630 631 return distance; 632} 633 634bool buttonWasPressed() { 635 636 const unsigned long now = 637 millis(); 638 639 const bool rawState = 640 digitalRead( 641 BUTTON_PIN 642 ); 643 644 if ( 645 rawState != 646 lastRawButtonState 647 ) { 648 649 lastRawButtonState = 650 rawState; 651 652 rawButtonChangedAt = 653 now; 654 } 655 656 if ( 657 rawState != 658 stableButtonState && 659 now - rawButtonChangedAt >= 660 BUTTON_DEBOUNCE_MS 661 ) { 662 663 stableButtonState = 664 rawState; 665 666 return ( 667 stableButtonState == LOW 668 ); 669 } 670 671 return false; 672} 673 674void changeState( 675 RobotState newState 676) { 677 678 robotState = 679 newState; 680 681 stateStartedAt = 682 millis(); 683 684 switch ( 685 robotState 686 ) { 687 688 case NORMAL: 689 690 avoidanceStep = 691 AVOID_DRIVING; 692 693 personalityWiggleActive = 694 false; 695 696 normalPauseState = 697 PAUSE_NOT_ACTIVE; 698 699 nextPersonalityWiggleAt = 700 stateStartedAt + 701 random( 702 PERSONALITY_WIGGLE_MIN_INTERVAL_MS, 703 PERSONALITY_WIGGLE_MAX_INTERVAL_MS + 1 704 ); 705 706 nextRandomPauseAt = 707 stateStartedAt + 708 random( 709 RANDOM_PAUSE_MIN_INTERVAL_MS, 710 RANDOM_PAUSE_MAX_INTERVAL_MS + 1 711 ); 712 713 setAllLeds( 714 false 715 ); 716 717 playLoop( 718 TRACK_NORMAL 719 ); 720 721 driveForward( 722 DRIVE_SPEED 723 ); 724 725 break; 726 727 case SCREAM: 728 729 stopMotors(); 730 731 setAllLeds(true); 732 733 playOnce( 734 TRACK_SCREAM 735 ); 736 737 screamTurnInitialized = 738 false; 739 740 screamLastWiggleAt = 741 millis(); 742 743 screamWiggleLeft = 744 true; 745 746 break; 747 748 case PARTY: 749 750 stopMotors(); 751 752 playOnce( 753 TRACK_PARTY 754 ); 755 756 partyTrackFinished = 757 false; 758 759 partyLedsAlternate = 760 false; 761 762 lastLedChangeAt = 763 stateStartedAt; 764 765 showPartyLeds( 766 partyLedsAlternate 767 ); 768 769 break; 770 } 771} 772 773void startObstacleAvoidance() { 774 775 avoidanceStep = 776 AVOID_STOPPING; 777 778 avoidanceStepStartedAt = 779 millis(); 780 781 avoidanceTurnChunks = 782 0; 783 784 turnLeftSelected = 785 nextTurnLeft; 786 787 nextTurnLeft = 788 !nextTurnLeft; 789 790 normalPauseState = 791 PAUSE_NOT_ACTIVE; 792 793 personalityWiggleActive = 794 false; 795 796 stopMotors(); 797} 798 799bool runRandomPause( 800 unsigned long now 801) { 802 803 if ( 804 normalPauseState == 805 PAUSE_NOT_ACTIVE && 806 (long)( 807 now - nextRandomPauseAt 808 ) >= 0 809 ) { 810 811 normalPauseState = 812 PAUSE_ACTIVE; 813 814 randomPauseStartedAt = 815 now; 816 817 randomPauseDuration = 818 random( 819 RANDOM_PAUSE_MIN_DURATION_MS, 820 RANDOM_PAUSE_MAX_DURATION_MS + 1 821 ); 822 823 stopMotors(); 824 825 } 826 827 828 if ( 829 normalPauseState == 830 PAUSE_NOT_ACTIVE 831 ) { 832 833 return false; 834 } 835 836 if ( 837 now - randomPauseStartedAt < 838 randomPauseDuration 839 ) { 840 841 stopMotors(); 842 843 return true; 844 } 845 846 normalPauseState = 847 PAUSE_NOT_ACTIVE; 848 849 nextRandomPauseAt = 850 now + 851 random( 852 RANDOM_PAUSE_MIN_INTERVAL_MS, 853 RANDOM_PAUSE_MAX_INTERVAL_MS + 1 854 ); 855 856 driveForward( 857 DRIVE_SPEED 858 ); 859 860 return false; 861} 862 863bool runPersonalityWiggle( 864 unsigned long now 865) { 866 867 if ( 868 !personalityWiggleActive && 869 (long)( 870 now - nextPersonalityWiggleAt 871 ) >= 0 872 ) { 873 874 turnLeftSelected = 875 nextTurnLeft; 876 877 nextTurnLeft = 878 !nextTurnLeft; 879 880 personalityWiggleActive = 881 true; 882 883 personalityWiggleEndsAt = 884 now + 885 PERSONALITY_WIGGLE_DURATION_MS; 886 887 nextPersonalityWiggleAt = 888 now + 889 random( 890 PERSONALITY_WIGGLE_MIN_INTERVAL_MS, 891 PERSONALITY_WIGGLE_MAX_INTERVAL_MS + 1 892 ); 893 } 894 895 if ( 896 !personalityWiggleActive 897 ) { 898 899 return false; 900 } 901 902 if ( 903 (long)( 904 now - personalityWiggleEndsAt 905 ) >= 0 906 ) { 907 908 personalityWiggleActive = 909 false; 910 911 driveForward( 912 DRIVE_SPEED 913 ); 914 915 return false; 916 } 917 918 if ( 919 turnLeftSelected 920 ) { 921 922 turnLeft( 923 TURN_SPEED 924 ); 925 926 } else { 927 928 turnRight( 929 TURN_SPEED 930 ); 931 } 932 933 return true; 934} 935 936void updateNormalState() { 937 938 const unsigned long now = 939 millis(); 940 941 switch ( 942 avoidanceStep 943 ) { 944 945 case AVOID_DRIVING: 946 947 if ( 948 rawDistanceCm > 0 && 949 rawDistanceCm <= 950 SCREAM_DISTANCE_CM && 951 (long)( 952 now - surpriseIgnoreUntil 953 ) >= 0 954 ) { 955 956 changeState( 957 SCREAM 958 ); 959 960 return; 961 } 962 963 if ( 964 rawDistanceCm > 0 && 965 rawDistanceCm < 966 WALL_DISTANCE_CM 967 ) { 968 969 startObstacleAvoidance(); 970 971 return; 972 } 973 974 if ( 975 runRandomPause( 976 now 977 ) 978 ) { 979 980 return; 981 } 982 983 if ( 984 runPersonalityWiggle( 985 now 986 ) 987 ) { 988 989 return; 990 } 991 992 driveForward( 993 DRIVE_SPEED 994 ); 995 996 break; 997 998 case AVOID_STOPPING: 999 1000 if ( 1001 now - 1002 avoidanceStepStartedAt >= 1003 AVOID_STOP_DURATION_MS 1004 ) { 1005 1006 avoidanceStep = 1007 AVOID_BACKING_UP; 1008 1009 avoidanceStepStartedAt = 1010 now; 1011 1012 driveBackward( 1013 BACKUP_SPEED 1014 ); 1015 } 1016 1017 break; 1018 1019 case AVOID_BACKING_UP: 1020 1021 if ( 1022 now - 1023 avoidanceStepStartedAt >= 1024 AVOID_BACKUP_DURATION_MS 1025 ) { 1026 1027 avoidanceStep = 1028 AVOID_TURNING; 1029 1030 avoidanceStepStartedAt = 1031 now; 1032 1033 avoidanceTurnChunks = 1034 0; 1035 1036 if ( 1037 turnLeftSelected 1038 ) { 1039 1040 turnLeft( 1041 TURN_SPEED 1042 ); 1043 1044 } else { 1045 1046 turnRight( 1047 TURN_SPEED 1048 ); 1049 } 1050 } 1051 1052 break; 1053 1054 case AVOID_TURNING: 1055 1056 if ( 1057 now - 1058 avoidanceStepStartedAt >= 1059 AVOID_TURN_CHUNK_MS 1060 ) { 1061 1062 stopMotors(); 1063 1064 avoidanceStep = 1065 AVOID_LOOKING; 1066 1067 avoidanceLookStartedAt = 1068 now; 1069 } 1070 1071 break; 1072 1073 case AVOID_LOOKING: 1074 1075 if ( 1076 now - 1077 avoidanceLookStartedAt < 1078 AVOID_LOOK_DELAY_MS 1079 ) { 1080 1081 return; 1082 } 1083 1084 const unsigned int freshDistance = 1085 getFreshDistance(); 1086 1087 if ( 1088 freshDistance >= 1089 AVOID_SAFE_DISTANCE_CM 1090 ) { 1091 1092 avoidanceStep = 1093 AVOID_DRIVING; 1094 1095 surpriseIgnoreUntil = 1096 millis() + 1097 300; 1098 1099 driveForward( 1100 DRIVE_SPEED 1101 ); 1102 1103 return; 1104 } 1105 1106 avoidanceTurnChunks++; 1107 1108 if ( 1109 avoidanceTurnChunks >= 1110 AVOID_MAX_TURN_CHUNKS 1111 ) { 1112 1113 turnLeftSelected = 1114 !turnLeftSelected; 1115 1116 avoidanceTurnChunks = 1117 0; 1118 } 1119 1120 avoidanceStep = 1121 AVOID_TURNING; 1122 1123 avoidanceStepStartedAt = 1124 now; 1125 1126 if ( 1127 turnLeftSelected 1128 ) { 1129 1130 turnLeft( 1131 TURN_SPEED 1132 ); 1133 1134 } else { 1135 1136 turnRight( 1137 TURN_SPEED 1138 ); 1139 } 1140 1141 break; 1142 } 1143} 1144 1145void updateScreamState() { 1146 1147 const unsigned long now = 1148 millis(); 1149 1150 const unsigned long elapsed = 1151 now - stateStartedAt; 1152 1153 if ( 1154 elapsed < 1155 SCREAM_PAUSE_MS 1156 ) { 1157 1158 stopMotors(); 1159 1160 return; 1161 } 1162 1163 if ( 1164 elapsed < 1165 SCREAM_PAUSE_MS + 1166 SCREAM_BACKUP_DURATION_MS 1167 ) { 1168 1169 if ( 1170 now - 1171 screamLastWiggleAt >= 1172 SCREAM_WIGGLE_INTERVAL_MS 1173 ) { 1174 1175 screamLastWiggleAt = 1176 now; 1177 1178 screamWiggleLeft = 1179 !screamWiggleLeft; 1180 } 1181 1182 setMotorDirection( 1183 false, 1184 false 1185 ); 1186 1187 1188 if ( 1189 screamWiggleLeft 1190 ) { 1191 1192 analogWrite( 1193 MOTOR_LEFT_ENABLE_PIN, 1194 SCREAM_WIGGLE_SPEED 1195 ); 1196 1197 analogWrite( 1198 MOTOR_RIGHT_ENABLE_PIN, 1199 BACKUP_SPEED 1200 ); 1201 1202 } else { 1203 1204 analogWrite( 1205 MOTOR_LEFT_ENABLE_PIN, 1206 BACKUP_SPEED 1207 ); 1208 1209 analogWrite( 1210 MOTOR_RIGHT_ENABLE_PIN, 1211 SCREAM_WIGGLE_SPEED 1212 ); 1213 } 1214 1215 return; 1216 } 1217 1218 if ( 1219 elapsed < 1220 SCREAM_PAUSE_MS + 1221 SCREAM_BACKUP_DURATION_MS + 1222 SCREAM_TURN_DURATION_MS 1223 ) { 1224 1225 if ( 1226 !screamTurnInitialized 1227 ) { 1228 1229 turnLeftSelected = 1230 nextTurnLeft; 1231 1232 nextTurnLeft = 1233 !nextTurnLeft; 1234 1235 screamTurnInitialized = 1236 true; 1237 } 1238 1239 if ( 1240 turnLeftSelected 1241 ) { 1242 1243 turnLeft( 1244 TURN_SPEED 1245 ); 1246 1247 } else { 1248 1249 turnRight( 1250 TURN_SPEED 1251 ); 1252 } 1253 1254 return; 1255 } 1256 1257 stopMotors(); 1258 1259 surpriseIgnoreUntil = 1260 millis() + 1261 SURPRISE_COOLDOWN_MS; 1262 1263 changeState( 1264 NORMAL 1265 ); 1266} 1267 1268void updatePartyState() { 1269 const unsigned long now = millis(); 1270 1271 const unsigned long elapsed = 1272 now - stateStartedAt; 1273 if (elapsed < PARTY_DURATION_MS / 2) { 1274 1275 setMotorDirection( 1276 true, 1277 false 1278 ); 1279 1280 } else { 1281 1282 setMotorDirection( 1283 false, 1284 true 1285 ); 1286 } 1287 1288 analogWrite( 1289 MOTOR_LEFT_ENABLE_PIN, 1290 PARTY_SPIN_SPEED 1291 ); 1292 1293 analogWrite( 1294 MOTOR_RIGHT_ENABLE_PIN, 1295 PARTY_SPIN_SPEED 1296 ); 1297 if ( 1298 now - 1299 lastLedChangeAt >= 1300 LED_BLINK_INTERVAL_MS 1301 ) { 1302 1303 lastLedChangeAt = 1304 now; 1305 1306 partyLedsAlternate = 1307 !partyLedsAlternate; 1308 1309 showPartyLeds( 1310 partyLedsAlternate 1311 ); 1312 } 1313 1314 if ( 1315 elapsed >= 1316 PARTY_DURATION_MS 1317 ) { 1318 1319 stopMotors(); 1320 1321 setAllLeds( 1322 false 1323 ); 1324 1325 changeState( 1326 NORMAL 1327 ); 1328 } 1329} 1330 1331void setup() { 1332 1333 Serial.begin( 1334 115200 1335 ); 1336 1337 pinMode( 1338 TRIG_PIN, 1339 OUTPUT 1340 ); 1341 1342 pinMode( 1343 ECHO_PIN, 1344 INPUT 1345 ); 1346 1347 digitalWrite( 1348 TRIG_PIN, 1349 LOW 1350 ); 1351 1352 pinMode( 1353 BUTTON_PIN, 1354 INPUT_PULLUP 1355 ); 1356 1357 pinMode( 1358 MOTOR_LEFT_ENABLE_PIN, 1359 OUTPUT 1360 ); 1361 1362 pinMode( 1363 MOTOR_LEFT_IN1_PIN, 1364 OUTPUT 1365 ); 1366 1367 pinMode( 1368 MOTOR_LEFT_IN2_PIN, 1369 OUTPUT 1370 ); 1371 1372 pinMode( 1373 MOTOR_RIGHT_ENABLE_PIN, 1374 OUTPUT 1375 ); 1376 1377 pinMode( 1378 MOTOR_RIGHT_IN1_PIN, 1379 OUTPUT 1380 ); 1381 1382 pinMode( 1383 MOTOR_RIGHT_IN2_PIN, 1384 OUTPUT 1385 ); 1386 1387 for ( 1388 byte i = 0; 1389 i < LED_COUNT; 1390 i++ 1391 ) { 1392 1393 pinMode( 1394 LED_PINS[i], 1395 OUTPUT 1396 ); 1397 } 1398 1399 setAllLeds( 1400 false 1401 ); 1402 1403 stopMotors(); 1404 1405 randomSeed( 1406 analogRead(A5) 1407 ); 1408 1409 dfPlayerSerial.begin( 1410 9600 1411 ); 1412 1413 if ( 1414 dfPlayer.begin( 1415 dfPlayerSerial, 1416 true, 1417 true 1418 ) 1419 ) { 1420 1421 dfPlayerReady = 1422 true; 1423 1424 dfPlayer.volume( 1425 DFPLAYER_VOLUME 1426 ); 1427 1428 Serial.println( 1429 "DFPlayer ready" 1430 ); 1431 1432 } else { 1433 1434 Serial.println( 1435 "DFPlayer NOT detected" 1436 ); 1437 } 1438 1439 changeState( 1440 NORMAL 1441 ); 1442} 1443 1444void loop() { 1445 1446 serviceDfPlayerEvents(); 1447 1448 if ( 1449 buttonWasPressed() && 1450 robotState != PARTY 1451 ) { 1452 1453 changeState( 1454 PARTY 1455 ); 1456 } 1457 1458 if ( 1459 robotState == NORMAL 1460 ) { 1461 1462 updateUltrasonic(); 1463 } 1464 1465 switch ( 1466 robotState 1467 ) { 1468 1469 case NORMAL: 1470 updateNormalState(); 1471 break; 1472 1473 1474 case SCREAM: 1475 updateScreamState(); 1476 break; 1477 1478 1479 case PARTY: 1480 updatePartyState(); 1481 break; 1482 } 1483}
Comments
Only logged in users can leave comments