diff --git a/.gitignore b/.gitignore index 300fc22..526891b 100644 --- a/.gitignore +++ b/.gitignore @@ -77,5 +77,4 @@ wokwi-libs/*/.cache/ .claude/settings.local.json .history/* .daveagent/* -example_zip/* data/* diff --git a/example_zip/ServoOverdone.zip b/example_zip/ServoOverdone.zip new file mode 100644 index 0000000..d91345a Binary files /dev/null and b/example_zip/ServoOverdone.zip differ diff --git a/example_zip/extracted/ServoOverdone/ServoOverdone.ino b/example_zip/extracted/ServoOverdone/ServoOverdone.ino new file mode 100644 index 0000000..569860d --- /dev/null +++ b/example_zip/extracted/ServoOverdone/ServoOverdone.ino @@ -0,0 +1,260 @@ +// ServoOverdone.ino +// +// Example for multiple Servo objects in a array. +// +// Version 1, 28 July 2021, by Koepel. +// Version 2, 15 August 2021, by Koepel. +// changed timing, a little slower +// diagram.json has servos in reverse order (I think it is visually better) +// Added fourth sequence: "compass" +// +// Public Domain +// + +#include + +#define NUM_SERVOS 32 +Servo myServo[NUM_SERVOS]; + +void setup() +{ + // Attach pins from the Arduino Mega board to the Servo objects. + // Starting from pin 22, there happen to be exactly 32 pins on the double row pins. + for( int i=0; i=0; r--) + { + for( int i=0; i (NUM_SERVOS / 2)) + d = NUM_SERVOS - d; + + int angle = 90 - (10 * d); + if( angle < 0) + angle = 0; + myServo[j].write( angle); + } + delay(40); + } + } + + // Sequence four. + // A "compass" + // Start by pointing upwards + int pointer = NUM_SERVOS * 3 / 4; + showPointer( pointer); + delay( 1000); // let the viewer get used to new pattern + + for( int i=0; i<5; i++) + { + showPointer( --pointer); + delay( 150); + } + delay( 200); + for( int i=0; i<9; i++) + { + showPointer( ++pointer); + delay( 150); + } + delay( 200); + for( int i=0; i<5; i++) + { + showPointer( --pointer); + delay( 150); + } + delay( 200); + for( int i=0; i<4; i++) + { + showPointer( ++pointer); + delay( 150); + } + delay( 160); + for( int i=0; i<2; i++) + { + showPointer( --pointer); + delay( 150); + } + delay( 80); + for( int i=0; i<1; i++) + { + showPointer( ++pointer); + delay( 150); + } + + delay( 2000); +} + +// This function makes a "pointer" with the servos. +// It is used to create the "compass". +// The parameter 's' is the servo motor that has the pointer. +// It is allowed that 's' is below zero or larger than the numbers of servo motors. +void showPointer( int s) +{ + int pointerA = s % NUM_SERVOS; // Using the '%' (remainder) for valid number + int pointerB = (s + 1) % NUM_SERVOS; // pointer is made with the next servo motor + int tailA = (s + 16) % NUM_SERVOS; + int tailB = (s + 17) % NUM_SERVOS; + + // make pointer with servo motor s and s+1. + myServo[pointerA].write(180-56); + myServo[pointerB].write(56); + + // make tail with servo motor s+16 and s+17. + myServo[tailA].write(95); + myServo[tailB].write(85); + + // Set servos right of pointer + int n = (NUM_SERVOS / 2) - 2; + int start = pointerB + 1; + for( int i=0; i=0; i--) + { + float rotate = float( i) * (360.0 / float( NUM_SERVOS)); + float rad = rotate / 360.0 * 2.0 * M_PI; + float top = (300.0 * sin( rad)) + 300.0; + float left = (300.0 * cos( rad)) + 300.0; + Serial.print( " {\n"); + Serial.print( " \"type\": \"wokwi-servo\",\n"); + Serial.print( " \"id\": \"servo"); + Serial.print( i); + Serial.print( "\",\n"); + Serial.print( " \"top\": "); + Serial.print( top); + Serial.print( ",\n"); + Serial.print( " \"left\": "); + Serial.print( left); + Serial.print( ",\n"); + Serial.print( " \"rotate\": "); + Serial.print( rotate); + Serial.print( ",\n"); + Serial.print( " \"attrs\": { \"hornColor\": \"Red\" }\n"); + Serial.print( " }"); + if( i != 0) + Serial.print( ","); + Serial.print( "\n"); + } + + Serial.print( " ],\n"); + Serial.print( " \"connections\": [\n"); + + for( int i=0; i +#include +#include +#include + +#define UP_BUTTON 2 +#define DOWN_BUTTON 3 + +const unsigned long PADDLE_RATE = 64; +const unsigned long BALL_RATE = 16; +const uint8_t PADDLE_HEIGHT = 12; +const uint8_t SCORE_LIMIT = 9; + +Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire); + +bool game_over, win; + +uint8_t player_score, mcu_score; +uint8_t ball_x = 53, ball_y = 26; +uint8_t ball_dir_x = 1, ball_dir_y = 1; + +unsigned long ball_update; +unsigned long paddle_update; + +const uint8_t MCU_X = 12; +uint8_t mcu_y = 16; + +const uint8_t PLAYER_X = 115; +uint8_t player_y = 16; + +void setup() +{ + display.begin(SSD1306_SWITCHCAPVCC, 0x3C); + + // Display the splash screen (we're legally required to do so) + display.display(); + unsigned long start = millis(); + + pinMode(UP_BUTTON, INPUT_PULLUP); + pinMode(DOWN_BUTTON, INPUT_PULLUP); + + display.clearDisplay(); + drawCourt(); + + while(millis() - start < 2000); + + display.display(); + + ball_update = millis(); + paddle_update = ball_update; +} + +void loop() +{ + bool update_needed = false; + unsigned long time = millis(); + + static bool up_state = false; + static bool down_state = false; + + up_state |= (digitalRead(UP_BUTTON) == LOW); + down_state |= (digitalRead(DOWN_BUTTON) == LOW); + + if(time > ball_update) + { + uint8_t new_x = ball_x + ball_dir_x; + uint8_t new_y = ball_y + ball_dir_y; + + // Check if we hit the vertical walls + if(new_x == 0 || new_x == 127) + { + ball_dir_x = -ball_dir_x; + new_x += ball_dir_x + ball_dir_x; + + if (new_x < 64) + { + player_scoreTone(); + player_score++; + } + else + { + mcu_scoreTone(); + mcu_score++; + } + + if (player_score == SCORE_LIMIT || mcu_score == SCORE_LIMIT) + { + win = player_score > mcu_score; + game_over = true; + } + } + + // Check if we hit the horizontal walls. + if(new_y == 0 || new_y == 53) + { + wallTone(); + ball_dir_y = -ball_dir_y; + new_y += ball_dir_y + ball_dir_y; + } + + // Check if we hit the CPU paddle + if(new_x == MCU_X && new_y >= mcu_y && new_y <= mcu_y + PADDLE_HEIGHT) + { + mcuPaddleTone(); + ball_dir_x = -ball_dir_x; + new_x += ball_dir_x + ball_dir_x; + } + + // Check if we hit the player paddle + if(new_x == PLAYER_X && new_y >= player_y && new_y <= player_y + PADDLE_HEIGHT) + { + playerPaddleTone(); + ball_dir_x = -ball_dir_x; + new_x += ball_dir_x + ball_dir_x; + } + + display.drawPixel(ball_x, ball_y, BLACK); + display.drawPixel(new_x, new_y, WHITE); + ball_x = new_x; + ball_y = new_y; + + ball_update += BALL_RATE; + + update_needed = true; + } + + if(time > paddle_update) + { + paddle_update += PADDLE_RATE; + + // CPU paddle + display.drawFastVLine(MCU_X, mcu_y, PADDLE_HEIGHT, BLACK); + const uint8_t half_paddle = PADDLE_HEIGHT >> 1; + + if(mcu_y + half_paddle > ball_y) + { + int8_t dir = ball_x > MCU_X ? -1 : 1; + mcu_y += dir; + } + + if(mcu_y + half_paddle < ball_y) + { + int8_t dir = ball_x > MCU_X ? 1 : -1; + mcu_y += dir; + } + + if(mcu_y < 1) + { + mcu_y = 1; + } + + if(mcu_y + PADDLE_HEIGHT > 53) + { + mcu_y = 53 - PADDLE_HEIGHT; + } + + // Player paddle + display.drawFastVLine(MCU_X, mcu_y, PADDLE_HEIGHT, WHITE); + display.drawFastVLine(PLAYER_X, player_y, PADDLE_HEIGHT, BLACK); + + if(up_state) + { + player_y -= 1; + } + + if(down_state) + { + player_y += 1; + } + + up_state = down_state = false; + + if(player_y < 1) + { + player_y = 1; + } + + if(player_y + PADDLE_HEIGHT > 53) + { + player_y = 53 - PADDLE_HEIGHT; + } + + display.drawFastVLine(PLAYER_X, player_y, PADDLE_HEIGHT, WHITE); + + update_needed = true; + } + + if(update_needed) + { + if (game_over) + { + const char* text = win ? "YOU WIN!!" : "YOU LOSE!"; + display.clearDisplay(); + display.setCursor(40, 28); + display.print(text); + display.display(); + + delay(5000); + + display.clearDisplay(); + ball_x = 53; + ball_y = 26; + ball_dir_x = 1; + ball_dir_y = 1; + mcu_y = 16; + player_y = 16; + mcu_score = 0; + player_score = 0; + game_over = false; + drawCourt(); + } + + display.setTextColor(WHITE, BLACK); + display.setCursor(0, 56); + display.print(mcu_score); + display.setCursor(122, 56); + display.print(player_score); + display.display(); + } +} + +void playerPaddleTone() +{ + tone(11, 250, 25); + delay(25); + noTone(11); +} + +void mcuPaddleTone() +{ + tone(11, 225, 25); + delay(25); + noTone(11); +} + +void wallTone() +{ + tone(11, 200, 25); + delay(25); + noTone(11); +} + +void player_scoreTone() +{ + tone(11, 200, 25); + delay(50); + noTone(11); + delay(25); + tone(11, 250, 25); + delay(25); + noTone(11); +} + +void mcu_scoreTone() +{ + tone(11, 250, 25); + delay(25); + noTone(11); + delay(25); + tone(11, 200, 25); + delay(25); + noTone(11); +} + +void drawCourt() +{ + display.drawRect(0, 0, 128, 54, WHITE); +} \ No newline at end of file diff --git a/example_zip/extracted/pong/wokwi-project.txt b/example_zip/extracted/pong/wokwi-project.txt new file mode 100644 index 0000000..492e28d --- /dev/null +++ b/example_zip/extracted/pong/wokwi-project.txt @@ -0,0 +1,3 @@ +Downloaded from https://wokwi.com/projects/348849468083274322 + +Simulate this project on https://wokwi.com diff --git a/example_zip/extracted/simon-with-score/README.md b/example_zip/extracted/simon-with-score/README.md new file mode 100644 index 0000000..8de7275 --- /dev/null +++ b/example_zip/extracted/simon-with-score/README.md @@ -0,0 +1,56 @@ +Simon is a simple electronic memory game: the user has to repeat a growing sequence of +colors. The sequence is displayed by lighting up the LEDs. Each color also has a +corresponding tone. + +In each turn, the game will play the sequence, and then wait for the user to repeat +the sequence by pressing the buttons according to the color sequence. If the user +repeated the sequence correctly, the game will play a "leveling-up" sound, add a new +color at the end of the sequence, and move to the next turn. + +The game continues until the user has made a mistake. Then a game over sound is +played, and the game restarts. + +![Simon Game Shield for Arduino Uno](https://i.imgur.com/CBVsVxzh.jpg) + +### Hardware + +| Item | Quantity | Notes | +| ---------------- | -------- | ---------------------------- | +| Arduino Uno R3 | 1 | | +| 5mm LED | 4 | Red, Green, Blue, and Yellow | +| 12mm Push button | 4 | Red, Green, Blue, and Yellow | +| Resistor | 4 | 220Ω | +| Piezo Buzzer | 1 | | + +
+ Simon hardware kit +
+ Hardware for the Simon game, + + kit available on Tindie + +
+
+ +### Diagram + +
+ diagram +
Simon connection diagram
+
+ +### Pin Connections + +| Arduino Pin | Device | +| ----------- | ------------- | +| 12 | Red LED | +| 11 | Green LED | +| 10 | Blue LED | +| 9 | Yellow LED | +| 8 | Buzzer | +| 5 | Red Button | +| 4 | Green Button | +| 3 | Blue Button | +| 2 | Yellow Button | + +- The LEDs are connected through a 220Ω resistor each. diff --git a/example_zip/extracted/simon-with-score/diagram.json b/example_zip/extracted/simon-with-score/diagram.json new file mode 100644 index 0000000..6b9e8d5 --- /dev/null +++ b/example_zip/extracted/simon-with-score/diagram.json @@ -0,0 +1,133 @@ +{ + "version": 1, + "author": "Uri Shaked", + "editor": "wokwi", + "parts": [ + { "type": "wokwi-arduino-uno", "id": "uno", "top": 183, "left": 18.6, "attrs": {} }, + { + "type": "wokwi-buzzer", + "id": "buzzer", + "top": 16, + "left": 124, + "attrs": { "volume": "0.1" } + }, + { "type": "wokwi-led", "id": "led-red", "top": 10, "left": 6, "attrs": { "color": "red" } }, + { + "type": "wokwi-led", + "id": "led-green", + "top": 73, + "left": 6, + "attrs": { "color": "green" } + }, + { + "type": "wokwi-led", + "id": "led-blue", + "top": 10, + "left": 270, + "attrs": { "color": "blue" } + }, + { + "type": "wokwi-led", + "id": "led-yellow", + "top": 73, + "left": 270, + "attrs": { "color": "yellow" } + }, + { + "type": "wokwi-pushbutton", + "id": "btn-red", + "top": 10, + "left": 46, + "attrs": { "color": "red", "key": "1", "label": "1" } + }, + { + "type": "wokwi-pushbutton", + "id": "btn-green", + "top": 76, + "left": 46, + "attrs": { "color": "green", "key": "2", "label": "2" } + }, + { + "type": "wokwi-pushbutton", + "id": "btn-blue", + "top": 10, + "left": 200, + "attrs": { "color": "blue", "key": "3", "label": "3" } + }, + { + "type": "wokwi-pushbutton", + "id": "btn-yellow", + "top": 76, + "left": 200, + "attrs": { "color": "yellow", "key": "4", "label": "4" } + }, + { + "type": "wokwi-74hc595", + "id": "sr1", + "top": 171.8, + "left": 361.16, + "rotate": 180, + "attrs": {} + }, + { + "type": "wokwi-74hc595", + "id": "sr2", + "top": 171.8, + "left": 457.16, + "rotate": 180, + "attrs": {} + }, + { "type": "wokwi-7segment", "id": "sevseg1", "top": 47.16, "left": 379.48, "attrs": {} }, + { "type": "wokwi-7segment", "id": "sevseg2", "top": 47.16, "left": 446.68, "attrs": {} } + ], + "connections": [ + [ "uno:GND.1", "buzzer:1", "black", [ "v-12", "*", "h0" ] ], + [ "uno:2", "btn-yellow:1.l", "gold", [ "v-48", "*", "h-6" ] ], + [ "uno:GND.1", "btn-yellow:2.r", "black", [ "v-12", "*", "h6" ] ], + [ "uno:3", "btn-blue:1.l", "blue", [ "v-44", "*", "h-10" ] ], + [ "uno:GND.1", "btn-blue:2.r", "black", [ "v-12", "*", "h6" ] ], + [ "uno:4", "btn-green:2.r", "green", [ "v-40", "*", "h6" ] ], + [ "uno:GND.1", "btn-green:1.l", "black", [ "v-12", "*", "h-6" ] ], + [ "uno:5", "btn-red:2.r", "orange", [ "v-36", "*", "h10" ] ], + [ "uno:GND.1", "btn-red:1.l", "black", [ "v-12", "*", "h-6" ] ], + [ "uno:8", "buzzer:2", "purple", [ "v-32", "*", "h0" ] ], + [ "uno:9", "led-yellow:A", "gold", [ "v-28", "*", "h0" ] ], + [ "uno:GND.1", "led-yellow:C", "black", [ "v-12", "*", "h-15", "v4" ] ], + [ "uno:10", "led-blue:A", "blue", [ "v-24", "*", "h8" ] ], + [ "uno:GND.1", "led-blue:C", "black", [ "v-12", "*", "h-15", "v4" ] ], + [ "uno:11", "led-green:A", "green", [ "v-20", "*", "h0" ] ], + [ "uno:GND.1", "led-green:C", "black", [ "v-12", "*", "h-8", "v4" ] ], + [ "uno:12", "led-red:A", "orange", [ "v-16", "*", "h6" ] ], + [ "uno:GND.1", "led-red:C", "black", [ "v-12", "*", "h-8", "v4" ] ], + [ "uno:5V", "sr1:VCC", "red", [ "v57.5", "h253.4" ] ], + [ "uno:A2", "sr1:SHCP", "gray", [ "v19.1", "h138.4" ] ], + [ "uno:A1", "sr1:STCP", "purple", [ "v28.7", "h157.5" ] ], + [ "uno:A0", "sr1:DS", "blue", [ "v38.3", "h186.2" ] ], + [ "sr1:SHCP", "sr2:SHCP", "gray", [ "v47", "h106.12" ] ], + [ "sr1:STCP", "sr2:STCP", "purple", [ "v37.4", "h96.52" ] ], + [ "sr1:Q7S", "sr2:DS", "blue", [ "h0.52", "v56.6", "h144" ] ], + [ "sr1:VCC", "sr1:MR", "red", [ "v17", "h-57.6" ] ], + [ "sr1:VCC", "sr2:MR", "red", [ "v17", "h38.4" ] ], + [ "sr1:VCC", "sr2:VCC", "red", [ "v17", "h96" ] ], + [ "sr1:OE", "sr2:OE", "black", [ "v26.6", "h96" ] ], + [ "sr1:MR", "sevseg1:COM.1", "red", [ "v17", "h-57.6", "v-96", "h76.8" ] ], + [ "sevseg1:COM.1", "sevseg2:COM.1", "red", [ "h0", "v9.6", "h57.6" ] ], + [ "sr2:Q0", "sevseg2:A", "green", [ "v7.4", "h28.8", "v-182.4", "h-67.2" ] ], + [ "sr2:Q1", "sevseg2:B", "green", [ "v0", "h9.6", "v-134.4", "h-48" ] ], + [ "sr2:Q2", "sevseg2:C", "green", [ "v-38.4", "h-38.4" ] ], + [ "sr2:Q3", "sevseg2:D", "green", [ "v-33.6", "h-33.6", "v-9.6", "h-14.4" ] ], + [ "sr2:Q4", "sevseg2:E", "green", [ "v-28.8", "h-28.8", "v-9.6", "h-14.4" ] ], + [ "sr2:Q5", "sevseg2:F", "green", [ "v-24", "h-24", "v-9.6", "h-24", "v-110.4", "h19.2" ] ], + [ "sr2:Q6", "sevseg2:G", "green", [ "v-19.2", "h-43.2", "v-115.2", "h14.4" ] ], + [ "sr1:GND", "sr2:GND", "black", [ "v-9.6", "h96" ] ], + [ "sr1:Q1", "sevseg1:B", "green", [ "v-134.4", "h-19.2" ] ], + [ "sr1:Q2", "sevseg1:C", "green", [ "v-38.4", "h-19.2" ] ], + [ "sr1:Q3", "sevseg1:D", "green", [ "v-33.6", "h-24" ] ], + [ "sr1:Q4", "sevseg1:E", "green", [ "v-28.8", "h-28.8" ] ], + [ "uno:GND.3", "sr1:GND", "black", [ "v47.9", "h157.6", "v-259.2", "h9.6" ] ], + [ "sr1:GND", "sr1:OE", "black", [ "v-9.6", "h-9.6", "v67.2", "h172.8" ] ], + [ "sr1:Q0", "sevseg1:A", "green", [ "v65", "h-76.8", "v-240", "h57.6" ] ], + [ "sr1:Q5", "sevseg1:F", "green", [ "v-24", "h-19.2", "v-110.4", "h19.2" ] ], + [ "sr1:Q6", "sevseg1:G", "green", [ "v-19.2", "h-14.4", "v-110.4", "h14.4" ] ] + ] +} \ No newline at end of file diff --git a/example_zip/extracted/simon-with-score/pitches.h b/example_zip/extracted/simon-with-score/pitches.h new file mode 100644 index 0000000..c597c5a --- /dev/null +++ b/example_zip/extracted/simon-with-score/pitches.h @@ -0,0 +1,94 @@ +/************************************************** + This file defines constants with the frequency + of different musical notes. + *************************************************/ + +#define NOTE_B0 31 +#define NOTE_C1 33 +#define NOTE_CS1 35 +#define NOTE_D1 37 +#define NOTE_DS1 39 +#define NOTE_E1 41 +#define NOTE_F1 44 +#define NOTE_FS1 46 +#define NOTE_G1 49 +#define NOTE_GS1 52 +#define NOTE_A1 55 +#define NOTE_AS1 58 +#define NOTE_B1 62 +#define NOTE_C2 65 +#define NOTE_CS2 69 +#define NOTE_D2 73 +#define NOTE_DS2 78 +#define NOTE_E2 82 +#define NOTE_F2 87 +#define NOTE_FS2 93 +#define NOTE_G2 98 +#define NOTE_GS2 104 +#define NOTE_A2 110 +#define NOTE_AS2 117 +#define NOTE_B2 123 +#define NOTE_C3 131 +#define NOTE_CS3 139 +#define NOTE_D3 147 +#define NOTE_DS3 156 +#define NOTE_E3 165 +#define NOTE_F3 175 +#define NOTE_FS3 185 +#define NOTE_G3 196 +#define NOTE_GS3 208 +#define NOTE_A3 220 +#define NOTE_AS3 233 +#define NOTE_B3 247 +#define NOTE_C4 262 +#define NOTE_CS4 277 +#define NOTE_D4 294 +#define NOTE_DS4 311 +#define NOTE_E4 330 +#define NOTE_F4 349 +#define NOTE_FS4 370 +#define NOTE_G4 392 +#define NOTE_GS4 415 +#define NOTE_A4 440 +#define NOTE_AS4 466 +#define NOTE_B4 494 +#define NOTE_C5 523 +#define NOTE_CS5 554 +#define NOTE_D5 587 +#define NOTE_DS5 622 +#define NOTE_E5 659 +#define NOTE_F5 698 +#define NOTE_FS5 740 +#define NOTE_G5 784 +#define NOTE_GS5 831 +#define NOTE_A5 880 +#define NOTE_AS5 932 +#define NOTE_B5 988 +#define NOTE_C6 1047 +#define NOTE_CS6 1109 +#define NOTE_D6 1175 +#define NOTE_DS6 1245 +#define NOTE_E6 1319 +#define NOTE_F6 1397 +#define NOTE_FS6 1480 +#define NOTE_G6 1568 +#define NOTE_GS6 1661 +#define NOTE_A6 1760 +#define NOTE_AS6 1865 +#define NOTE_B6 1976 +#define NOTE_C7 2093 +#define NOTE_CS7 2217 +#define NOTE_D7 2349 +#define NOTE_DS7 2489 +#define NOTE_E7 2637 +#define NOTE_F7 2794 +#define NOTE_FS7 2960 +#define NOTE_G7 3136 +#define NOTE_GS7 3322 +#define NOTE_A7 3520 +#define NOTE_AS7 3729 +#define NOTE_B7 3951 +#define NOTE_C8 4186 +#define NOTE_CS8 4435 +#define NOTE_D8 4699 +#define NOTE_DS8 4978 diff --git a/example_zip/extracted/simon-with-score/simon-with-score.ino b/example_zip/extracted/simon-with-score/simon-with-score.ino new file mode 100644 index 0000000..8529eaf --- /dev/null +++ b/example_zip/extracted/simon-with-score/simon-with-score.ino @@ -0,0 +1,202 @@ +/** + Simon Game for Arduino with Score display + + Copyright (C) 2022, Uri Shaked + + Released under the MIT License. +*/ + +#include "pitches.h" + +/* Constants - define pin numbers for LEDs, + buttons and speaker, and also the game tones: */ +const uint8_t ledPins[] = {9, 10, 11, 12}; +const uint8_t buttonPins[] = {2, 3, 4, 5}; +#define SPEAKER_PIN 8 + +// These are connected to 74HC595 shift register (used to show game score): +const int LATCH_PIN = A1; // 74HC595 pin 12 +const int DATA_PIN = A0; // 74HC595pin 14 +const int CLOCK_PIN = A2; // 74HC595 pin 11 + +#define MAX_GAME_LENGTH 100 + +const int gameTones[] = { NOTE_G3, NOTE_C4, NOTE_E4, NOTE_G5}; + +/* Global variables - store the game state */ +uint8_t gameSequence[MAX_GAME_LENGTH] = {0}; +uint8_t gameIndex = 0; + +/** + Set up the Arduino board and initialize Serial communication +*/ +void setup() { + Serial.begin(9600); + for (byte i = 0; i < 4; i++) { + pinMode(ledPins[i], OUTPUT); + pinMode(buttonPins[i], INPUT_PULLUP); + } + pinMode(SPEAKER_PIN, OUTPUT); + pinMode(LATCH_PIN, OUTPUT); + pinMode(CLOCK_PIN, OUTPUT); + pinMode(DATA_PIN, OUTPUT); + + // The following line primes the random number generator. + // It assumes pin A3 is floating (disconnected): + randomSeed(analogRead(A3)); +} + +/* Digit table for the 7-segment display */ +const uint8_t digitTable[] = { + 0b11000000, + 0b11111001, + 0b10100100, + 0b10110000, + 0b10011001, + 0b10010010, + 0b10000010, + 0b11111000, + 0b10000000, + 0b10010000, +}; +const uint8_t DASH = 0b10111111; + +void sendScore(uint8_t high, uint8_t low) { + digitalWrite(LATCH_PIN, LOW); + shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, low); + shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, high); + digitalWrite(LATCH_PIN, HIGH); +} + +void displayScore() { + int high = gameIndex % 100 / 10; + int low = gameIndex % 10; + sendScore(high ? digitTable[high] : 0xff, digitTable[low]); +} + +/** + Lights the given LED and plays a suitable tone +*/ +void lightLedAndPlayTone(byte ledIndex) { + digitalWrite(ledPins[ledIndex], HIGH); + tone(SPEAKER_PIN, gameTones[ledIndex]); + delay(300); + digitalWrite(ledPins[ledIndex], LOW); + noTone(SPEAKER_PIN); +} + +/** + Plays the current sequence of notes that the user has to repeat +*/ +void playSequence() { + for (int i = 0; i < gameIndex; i++) { + byte currentLed = gameSequence[i]; + lightLedAndPlayTone(currentLed); + delay(50); + } +} + +/** + Waits until the user pressed one of the buttons, + and returns the index of that button +*/ +byte readButtons() { + while (true) { + for (byte i = 0; i < 4; i++) { + byte buttonPin = buttonPins[i]; + if (digitalRead(buttonPin) == LOW) { + return i; + } + } + delay(1); + } +} + +/** + Play the game over sequence, and report the game score +*/ +void gameOver() { + Serial.print("Game over! your score: "); + Serial.println(gameIndex - 1); + gameIndex = 0; + delay(200); + + // Play a Wah-Wah-Wah-Wah sound + tone(SPEAKER_PIN, NOTE_DS5); + delay(300); + tone(SPEAKER_PIN, NOTE_D5); + delay(300); + tone(SPEAKER_PIN, NOTE_CS5); + delay(300); + for (byte i = 0; i < 10; i++) { + for (int pitch = -10; pitch <= 10; pitch++) { + tone(SPEAKER_PIN, NOTE_C5 + pitch); + delay(5); + } + } + noTone(SPEAKER_PIN); + + sendScore(DASH, DASH); + delay(500); +} + +/** + Get the user's input and compare it with the expected sequence. +*/ +bool checkUserSequence() { + for (int i = 0; i < gameIndex; i++) { + byte expectedButton = gameSequence[i]; + byte actualButton = readButtons(); + lightLedAndPlayTone(actualButton); + if (expectedButton != actualButton) { + return false; + } + } + + return true; +} + +/** + Plays a hooray sound whenever the user finishes a level +*/ +void playLevelUpSound() { + tone(SPEAKER_PIN, NOTE_E4); + delay(150); + tone(SPEAKER_PIN, NOTE_G4); + delay(150); + tone(SPEAKER_PIN, NOTE_E5); + delay(150); + tone(SPEAKER_PIN, NOTE_C5); + delay(150); + tone(SPEAKER_PIN, NOTE_D5); + delay(150); + tone(SPEAKER_PIN, NOTE_G5); + delay(150); + noTone(SPEAKER_PIN); +} + +/** + The main game loop +*/ +void loop() { + displayScore(); + + // Add a random color to the end of the sequence + gameSequence[gameIndex] = random(0, 4); + gameIndex++; + if (gameIndex >= MAX_GAME_LENGTH) { + gameIndex = MAX_GAME_LENGTH - 1; + } + + playSequence(); + if (!checkUserSequence()) { + gameOver(); + } + + delay(300); + + if (gameIndex > 0) { + playLevelUpSound(); + delay(300); + } +} diff --git a/example_zip/extracted/simon-with-score/wokwi-project.txt b/example_zip/extracted/simon-with-score/wokwi-project.txt new file mode 100644 index 0000000..c4394e3 --- /dev/null +++ b/example_zip/extracted/simon-with-score/wokwi-project.txt @@ -0,0 +1,3 @@ +Downloaded from https://wokwi.com/projects/328451800839488084 + +Simulate this project on https://wokwi.com diff --git a/example_zip/pong.zip b/example_zip/pong.zip new file mode 100644 index 0000000..3c2c53a Binary files /dev/null and b/example_zip/pong.zip differ diff --git a/example_zip/simon-with-score.zip b/example_zip/simon-with-score.zip new file mode 100644 index 0000000..486f62d Binary files /dev/null and b/example_zip/simon-with-score.zip differ