From 0cd36d280993896133aec4ebf6ca43bfdef254d8 Mon Sep 17 00:00:00 2001 From: "@dev.mako" Date: Wed, 29 Jul 2026 10:14:50 +0800 Subject: [PATCH 1/4] refactor: make all interactive sketches non-blocking --- .../exercise_a_arrow_controller.ino | 93 +++--- .../exercise_b_multi_led_pattern.ino | 168 +++++----- .../exercise_c_smart_fan.ino | 105 ++++--- .../exercise_d_wheel_simulation.ino | 165 +++++----- .../exercise_e_knight_rider_motor_sync.ino | 108 +++---- .../exercise_f_full_system_integration.ino | 296 +++++++++++------- .../basic_bluetooth_led.ino | 31 +- 7 files changed, 497 insertions(+), 469 deletions(-) diff --git a/exercises/exercise-a-arrow-controller/exercise_a_arrow_controller.ino b/exercises/exercise-a-arrow-controller/exercise_a_arrow_controller.ino index 319b333..0a17196 100644 --- a/exercises/exercise-a-arrow-controller/exercise_a_arrow_controller.ino +++ b/exercises/exercise-a-arrow-controller/exercise_a_arrow_controller.ino @@ -1,55 +1,62 @@ -const int ledUp = 2; -const int ledLeft = 3; -const int ledRight = 4; -const int ledDown = 5; +constexpr uint8_t kLedUp = 2; +constexpr uint8_t kLedLeft = 3; +constexpr uint8_t kLedRight = 4; +constexpr uint8_t kLedDown = 5; -char btCommand = 'X'; +char currentCommand = 'X'; + +void writeDirection(bool up, bool left, bool right, bool down) { + digitalWrite(kLedUp, up ? HIGH : LOW); + digitalWrite(kLedLeft, left ? HIGH : LOW); + digitalWrite(kLedRight, right ? HIGH : LOW); + digitalWrite(kLedDown, down ? HIGH : LOW); +} + +void applyCommand(char command) { + switch (command) { + case 'Q': + writeDirection(true, true, false, false); + Serial.println(F("Direction: UP-LEFT")); + break; + case 'E': + writeDirection(true, false, true, false); + Serial.println(F("Direction: UP-RIGHT")); + break; + case 'Z': + writeDirection(false, true, false, true); + Serial.println(F("Direction: DOWN-LEFT")); + break; + case 'C': + writeDirection(false, false, true, true); + Serial.println(F("Direction: DOWN-RIGHT")); + break; + default: + currentCommand = 'X'; + writeDirection(false, false, false, false); + Serial.println(F("Direction: OFF")); + break; + } +} void setup() { Serial.begin(9600); - - pinMode(ledUp, OUTPUT); - pinMode(ledLeft, OUTPUT); - pinMode(ledRight, OUTPUT); - pinMode(ledDown, OUTPUT); + pinMode(kLedUp, OUTPUT); + pinMode(kLedLeft, OUTPUT); + pinMode(kLedRight, OUTPUT); + pinMode(kLedDown, OUTPUT); + applyCommand(currentCommand); } void loop() { - if (Serial.available() > 0) { - btCommand = Serial.read(); + if (Serial.available() == 0) { + return; } - if (btCommand == 'Q') { - digitalWrite(ledUp, HIGH); - digitalWrite(ledLeft, HIGH); - digitalWrite(ledRight, LOW); - digitalWrite(ledDown, LOW); - Serial.println("Up-Left"); - } else if (btCommand == 'E') { - digitalWrite(ledUp, HIGH); - digitalWrite(ledLeft, LOW); - digitalWrite(ledRight, HIGH); - digitalWrite(ledDown, LOW); - Serial.println("Up-Right"); - } else if (btCommand == 'Z') { - digitalWrite(ledUp, LOW); - digitalWrite(ledLeft, HIGH); - digitalWrite(ledRight, LOW); - digitalWrite(ledDown, HIGH); - Serial.println("Down-Left"); - } else if (btCommand == 'C') { - digitalWrite(ledUp, LOW); - digitalWrite(ledLeft, LOW); - digitalWrite(ledRight, HIGH); - digitalWrite(ledDown, HIGH); - Serial.println("Down-Right"); - } else { - digitalWrite(ledUp, LOW); - digitalWrite(ledLeft, LOW); - digitalWrite(ledRight, LOW); - digitalWrite(ledDown, LOW); - Serial.println("OFF"); + const char received = Serial.read(); + if (received == '\r' || received == '\n' || received == currentCommand) { + return; } - delay(150); + currentCommand = received; + applyCommand(currentCommand); } diff --git a/exercises/exercise-b-multi-led-pattern/exercise_b_multi_led_pattern.ino b/exercises/exercise-b-multi-led-pattern/exercise_b_multi_led_pattern.ino index 9a692de..6e09d5c 100644 --- a/exercises/exercise-b-multi-led-pattern/exercise_b_multi_led_pattern.ino +++ b/exercises/exercise-b-multi-led-pattern/exercise_b_multi_led_pattern.ino @@ -1,106 +1,84 @@ -const int led1 = 2; -const int led2 = 3; -const int led3 = 4; -const int led4 = 5; +constexpr uint8_t kLedPins[] = {2, 3, 4, 5}; +constexpr uint8_t kLedCount = sizeof(kLedPins) / sizeof(kLedPins[0]); + +const uint8_t kBlinkAll[] = {0b1111, 0b0000}; +const uint8_t kKnightRider[] = {0b0001, 0b0010, 0b0100, 0b1000, + 0b0100, 0b0010}; +const uint8_t kAlternate[] = {0b0101, 0b1010}; +const uint8_t kWave[] = {0b0001, 0b0010, 0b0100, 0b1000}; + +const uint8_t* pattern = kBlinkAll; +uint8_t patternLength = sizeof(kBlinkAll); +uint8_t patternIndex = 0; +uint16_t intervalMs = 200; +uint32_t changedAt = 0; +char currentCommand = 'A'; + +void writeMask(uint8_t mask) { + for (uint8_t index = 0; index < kLedCount; ++index) { + digitalWrite(kLedPins[index], bitRead(mask, index) ? HIGH : LOW); + } +} -char btCommand = 'A'; +void selectPattern(char command) { + currentCommand = command; + patternIndex = 0; + changedAt = millis(); + + switch (command) { + case 'A': + pattern = kBlinkAll; + patternLength = sizeof(kBlinkAll); + intervalMs = 200; + Serial.println(F("Pattern: BLINK ALL")); + break; + case 'B': + pattern = kKnightRider; + patternLength = sizeof(kKnightRider); + intervalMs = 120; + Serial.println(F("Pattern: KNIGHT RIDER")); + break; + case 'C': + pattern = kAlternate; + patternLength = sizeof(kAlternate); + intervalMs = 200; + Serial.println(F("Pattern: ALTERNATE")); + break; + case 'D': + pattern = kWave; + patternLength = sizeof(kWave); + intervalMs = 100; + Serial.println(F("Pattern: WAVE")); + break; + default: + currentCommand = 'X'; + writeMask(0); + Serial.println(F("Pattern: OFF")); + return; + } + writeMask(pattern[patternIndex]); +} void setup() { Serial.begin(9600); - - pinMode(led1, OUTPUT); - pinMode(led2, OUTPUT); - pinMode(led3, OUTPUT); - pinMode(led4, OUTPUT); + for (uint8_t pin : kLedPins) { + pinMode(pin, OUTPUT); + } + selectPattern(currentCommand); } void loop() { - if (Serial.available() > 0) { - btCommand = Serial.read(); + while (Serial.available() > 0) { + const char received = Serial.read(); + if (received != '\r' && received != '\n' && received != currentCommand) { + selectPattern(received); + } } - if (btCommand == 'A') { - Serial.println("LED Pattern Name: Blink All"); - - digitalWrite(led1, HIGH); - digitalWrite(led2, HIGH); - digitalWrite(led3, HIGH); - digitalWrite(led4, HIGH); - delay(200); - - digitalWrite(led1, LOW); - digitalWrite(led2, LOW); - digitalWrite(led3, LOW); - digitalWrite(led4, LOW); - delay(200); - } else if (btCommand == 'B') { - Serial.println("LED Pattern Name: Knight Rider"); - - digitalWrite(led1, HIGH); - digitalWrite(led2, LOW); - digitalWrite(led3, LOW); - digitalWrite(led4, LOW); - delay(120); - - digitalWrite(led1, LOW); - digitalWrite(led2, HIGH); - delay(120); - - digitalWrite(led2, LOW); - digitalWrite(led3, HIGH); - delay(120); - - digitalWrite(led3, LOW); - digitalWrite(led4, HIGH); - delay(120); - - digitalWrite(led4, LOW); - digitalWrite(led3, HIGH); - delay(120); - - digitalWrite(led3, LOW); - digitalWrite(led2, HIGH); - delay(120); - } else if (btCommand == 'C') { - Serial.println("LED Pattern Name: Alternate Blink"); - - digitalWrite(led1, HIGH); - digitalWrite(led2, LOW); - digitalWrite(led3, HIGH); - digitalWrite(led4, LOW); - delay(200); - - digitalWrite(led1, LOW); - digitalWrite(led2, HIGH); - digitalWrite(led3, LOW); - digitalWrite(led4, HIGH); - delay(200); - } else if (btCommand == 'D') { - Serial.println("LED Pattern Name: Wave"); - - digitalWrite(led1, HIGH); - digitalWrite(led2, LOW); - digitalWrite(led3, LOW); - digitalWrite(led4, LOW); - delay(100); - - digitalWrite(led1, LOW); - digitalWrite(led2, HIGH); - delay(100); - - digitalWrite(led2, LOW); - digitalWrite(led3, HIGH); - delay(100); - - digitalWrite(led3, LOW); - digitalWrite(led4, HIGH); - delay(100); - - digitalWrite(led4, LOW); - } else { - digitalWrite(led1, LOW); - digitalWrite(led2, LOW); - digitalWrite(led3, LOW); - digitalWrite(led4, LOW); + if (currentCommand != 'X' && + static_cast(millis() - changedAt) >= intervalMs) { + changedAt += intervalMs; + patternIndex = (patternIndex + 1) % patternLength; + writeMask(pattern[patternIndex]); } } diff --git a/exercises/exercise-c-smart-fan/exercise_c_smart_fan.ino b/exercises/exercise-c-smart-fan/exercise_c_smart_fan.ino index e1dbff3..9525adf 100644 --- a/exercises/exercise-c-smart-fan/exercise_c_smart_fan.ino +++ b/exercises/exercise-c-smart-fan/exercise_c_smart_fan.ino @@ -1,60 +1,65 @@ -const int pinLow = 3; -const int pinMedium = 5; -const int pinHigh = 6; -const int motorPin1 = 10; -const int motorPin2 = 11; +// Use a rated motor driver and external motor supply. Never power a motor +// directly from an Arduino pin. +constexpr uint8_t kLedLow = 3; +constexpr uint8_t kLedMedium = 5; +constexpr uint8_t kLedHigh = 6; +constexpr uint8_t kMotorPwm = 10; +constexpr uint8_t kMotorDirection = 11; -char switchState = 'A'; +char currentCommand = 'A'; -void setup() { - pinMode(pinLow, OUTPUT); - pinMode(pinMedium, OUTPUT); - pinMode(pinHigh, OUTPUT); - pinMode(motorPin1, OUTPUT); - pinMode(motorPin2, OUTPUT); +void setLevel(uint8_t pwm, uint8_t ledCount, const __FlashStringHelper* name) { + digitalWrite(kLedLow, ledCount >= 1 ? HIGH : LOW); + digitalWrite(kLedMedium, ledCount >= 2 ? HIGH : LOW); + digitalWrite(kLedHigh, ledCount >= 3 ? HIGH : LOW); + digitalWrite(kMotorDirection, LOW); + analogWrite(kMotorPwm, pwm); + + Serial.print(F("Fan: ")); + Serial.print(name); + Serial.print(F(" | PWM: ")); + Serial.println(pwm); +} + +void applyCommand(char command) { + currentCommand = command; + switch (command) { + case 'B': + setLevel(120, 3, F("ON")); + break; + case 'C': + setLevel(170, 1, F("LOW")); + break; + case 'D': + setLevel(200, 2, F("MEDIUM")); + break; + case '1': + setLevel(255, 3, F("HIGH")); + break; + default: + currentCommand = 'A'; + setLevel(0, 0, F("OFF")); + break; + } +} +void setup() { Serial.begin(9600); + pinMode(kLedLow, OUTPUT); + pinMode(kLedMedium, OUTPUT); + pinMode(kLedHigh, OUTPUT); + pinMode(kMotorPwm, OUTPUT); + pinMode(kMotorDirection, OUTPUT); + applyCommand(currentCommand); } void loop() { - if (Serial.available() > 0) { - switchState = Serial.read(); + if (Serial.available() == 0) { + return; + } - if (switchState == 'A') { - digitalWrite(pinLow, LOW); - digitalWrite(pinMedium, LOW); - digitalWrite(pinHigh, LOW); - analogWrite(motorPin1, 0); - analogWrite(motorPin2, 0); - Serial.println("Bluetooth app character code: A | Speed Level: OFF"); - } else if (switchState == 'B') { - digitalWrite(pinLow, HIGH); - digitalWrite(pinMedium, HIGH); - digitalWrite(pinHigh, HIGH); - analogWrite(motorPin1, 120); - analogWrite(motorPin2, 0); - Serial.println("Bluetooth app character code: B | Speed Level: ON"); - } else if (switchState == 'C') { - digitalWrite(pinLow, HIGH); - digitalWrite(pinMedium, LOW); - digitalWrite(pinHigh, LOW); - analogWrite(motorPin1, 170); - analogWrite(motorPin2, 0); - Serial.println("Bluetooth app character code: C | Speed Level: Low"); - } else if (switchState == 'D') { - digitalWrite(pinLow, HIGH); - digitalWrite(pinMedium, HIGH); - digitalWrite(pinHigh, LOW); - analogWrite(motorPin1, 200); - analogWrite(motorPin2, 0); - Serial.println("Bluetooth app character code: D | Speed Level: Medium"); - } else if (switchState == '1') { - digitalWrite(pinLow, HIGH); - digitalWrite(pinMedium, HIGH); - digitalWrite(pinHigh, HIGH); - analogWrite(motorPin1, 255); - analogWrite(motorPin2, 0); - Serial.println("Bluetooth app character code: 1 | Speed Level: High"); - } + const char received = Serial.read(); + if (received != '\r' && received != '\n' && received != currentCommand) { + applyCommand(received); } } diff --git a/exercises/exercise-d-wheel-simulation/exercise_d_wheel_simulation.ino b/exercises/exercise-d-wheel-simulation/exercise_d_wheel_simulation.ino index 179ab06..9c57b70 100644 --- a/exercises/exercise-d-wheel-simulation/exercise_d_wheel_simulation.ino +++ b/exercises/exercise-d-wheel-simulation/exercise_d_wheel_simulation.ino @@ -1,108 +1,89 @@ -const int ledLeft = 2; -const int ledForward = 4; -const int ledRight = 7; -const int ledReverse = 8; - -const int motorPin1B = 10; -const int motorPin2B = 11; -const int motorPin1A = 5; -const int motorPin2A = 6; - -char switchState = 'X'; - -void setup() { - Serial.begin(9600); - - pinMode(ledLeft, OUTPUT); - pinMode(ledForward, OUTPUT); - pinMode(ledRight, OUTPUT); - pinMode(ledReverse, OUTPUT); +// kLeftA/B and kRightA/B are logic inputs to a dual H-bridge, not direct +// motor connections. +constexpr uint8_t kLedLeft = 2; +constexpr uint8_t kLedForward = 4; +constexpr uint8_t kLedRight = 7; +constexpr uint8_t kLedReverse = 8; +constexpr uint8_t kLeftA = 10; +constexpr uint8_t kLeftB = 11; +constexpr uint8_t kRightA = 5; +constexpr uint8_t kRightB = 6; + +char currentCommand = 'S'; +uint32_t blinkChangedAt = 0; +bool reverseLedsOn = false; + +void setLeds(bool left, bool forward, bool right, bool reverse) { + digitalWrite(kLedLeft, left ? HIGH : LOW); + digitalWrite(kLedForward, forward ? HIGH : LOW); + digitalWrite(kLedRight, right ? HIGH : LOW); + digitalWrite(kLedReverse, reverse ? HIGH : LOW); +} - pinMode(motorPin1B, OUTPUT); - pinMode(motorPin2B, OUTPUT); - pinMode(motorPin1A, OUTPUT); - pinMode(motorPin2A, OUTPUT); +void setMotors(bool leftA, bool leftB, bool rightA, bool rightB) { + digitalWrite(kLeftA, leftA ? HIGH : LOW); + digitalWrite(kLeftB, leftB ? HIGH : LOW); + digitalWrite(kRightA, rightA ? HIGH : LOW); + digitalWrite(kRightB, rightB ? HIGH : LOW); } -void loop() { - if (Serial.available() > 0) { - switchState = Serial.read(); - } +void applyCommand(char command) { + currentCommand = command; + reverseLedsOn = true; + blinkChangedAt = millis(); - switch (switchState) { + switch (command) { case 'F': - Serial.println("Bluetooth app character code: F | Direction: Forward"); - - digitalWrite(ledLeft, LOW); - digitalWrite(ledForward, HIGH); - digitalWrite(ledRight, LOW); - digitalWrite(ledReverse, LOW); - - digitalWrite(motorPin1B, HIGH); - digitalWrite(motorPin2B, LOW); - digitalWrite(motorPin1A, LOW); - digitalWrite(motorPin2A, HIGH); + setLeds(false, true, false, false); + setMotors(true, false, false, true); + Serial.println(F("Direction: FORWARD")); break; - case 'L': - Serial.println("Bluetooth app character code: L | Direction: Left"); - - digitalWrite(ledLeft, HIGH); - digitalWrite(ledForward, LOW); - digitalWrite(ledRight, LOW); - digitalWrite(ledReverse, LOW); - - digitalWrite(motorPin1B, LOW); - digitalWrite(motorPin2B, HIGH); - digitalWrite(motorPin1A, LOW); - digitalWrite(motorPin2A, HIGH); + setLeds(true, false, false, false); + setMotors(false, true, false, true); + Serial.println(F("Direction: LEFT")); break; - case 'R': - Serial.println("Bluetooth app character code: R | Direction: Right"); - - digitalWrite(ledLeft, LOW); - digitalWrite(ledForward, LOW); - digitalWrite(ledRight, HIGH); - digitalWrite(ledReverse, LOW); - - digitalWrite(motorPin1B, HIGH); - digitalWrite(motorPin2B, LOW); - digitalWrite(motorPin1A, HIGH); - digitalWrite(motorPin2A, LOW); + setLeds(false, false, true, false); + setMotors(true, false, true, false); + Serial.println(F("Direction: RIGHT")); break; - case 'B': - Serial.println("Bluetooth app character code: B | Direction: Reverse"); - - digitalWrite(ledLeft, HIGH); - digitalWrite(ledForward, HIGH); - digitalWrite(ledRight, HIGH); - digitalWrite(ledReverse, HIGH); - delay(300); - - digitalWrite(ledLeft, LOW); - digitalWrite(ledForward, LOW); - digitalWrite(ledRight, LOW); - digitalWrite(ledReverse, LOW); - delay(300); - - digitalWrite(motorPin1B, LOW); - digitalWrite(motorPin2B, HIGH); - digitalWrite(motorPin1A, HIGH); - digitalWrite(motorPin2A, LOW); + setLeds(true, true, true, true); + setMotors(false, true, true, false); + Serial.println(F("Direction: REVERSE")); break; - default: - digitalWrite(ledLeft, LOW); - digitalWrite(ledForward, LOW); - digitalWrite(ledRight, LOW); - digitalWrite(ledReverse, LOW); - - digitalWrite(motorPin1B, LOW); - digitalWrite(motorPin2B, LOW); - digitalWrite(motorPin1A, LOW); - digitalWrite(motorPin2A, LOW); + currentCommand = 'S'; + setLeds(false, false, false, false); + setMotors(false, false, false, false); + Serial.println(F("Direction: STOP")); break; } } + +void setup() { + Serial.begin(9600); + const uint8_t outputs[] = {kLedLeft, kLedForward, kLedRight, kLedReverse, + kLeftA, kLeftB, kRightA, kRightB}; + for (uint8_t pin : outputs) { + pinMode(pin, OUTPUT); + } + applyCommand(currentCommand); +} + +void loop() { + while (Serial.available() > 0) { + const char received = Serial.read(); + if (received != '\r' && received != '\n' && received != currentCommand) { + applyCommand(received); + } + } + + if (currentCommand == 'B' && + static_cast(millis() - blinkChangedAt) >= 300) { + blinkChangedAt += 300; + reverseLedsOn = !reverseLedsOn; + setLeds(reverseLedsOn, reverseLedsOn, reverseLedsOn, reverseLedsOn); + } +} diff --git a/exercises/exercise-e-knight-rider-motor-sync/exercise_e_knight_rider_motor_sync.ino b/exercises/exercise-e-knight-rider-motor-sync/exercise_e_knight_rider_motor_sync.ino index f73195f..60c6eaf 100644 --- a/exercises/exercise-e-knight-rider-motor-sync/exercise_e_knight_rider_motor_sync.ino +++ b/exercises/exercise-e-knight-rider-motor-sync/exercise_e_knight_rider_motor_sync.ino @@ -1,70 +1,60 @@ -const int led1 = 3; -const int led2 = 5; -const int led3 = 6; -const int led4 = 9; - -const int motorPin1 = 10; -const int motorPin2 = 11; +// Motor pins connect to a rated driver, never directly to the motor. +constexpr uint8_t kLedPins[] = {3, 5, 6, 9}; +constexpr uint8_t kMotorPwm = 10; +constexpr uint8_t kMotorDirection = 11; +constexpr uint8_t kSequence[] = {0, 1, 2, 3, 2, 1}; + +uint8_t level = 1; +uint8_t sequenceIndex = 0; +uint16_t intervalMs = 300; +uint8_t motorPwm = 120; +uint32_t changedAt = 0; + +void writeCurrentLed() { + for (uint8_t index = 0; index < 4; ++index) { + digitalWrite(kLedPins[index], index == kSequence[sequenceIndex] ? HIGH : LOW); + } +} -int btValue = 1; -int ledDelay = 300; -int motorSpeed1 = 120; +void applyLevel(uint8_t newLevel) { + level = newLevel; + intervalMs = map(level, 1, 10, 300, 50); + motorPwm = map(level, 1, 10, 120, 255); + digitalWrite(kMotorDirection, LOW); + analogWrite(kMotorPwm, motorPwm); + + Serial.print(F("Level: ")); + Serial.print(level); + Serial.print(F(" | Interval ms: ")); + Serial.print(intervalMs); + Serial.print(F(" | Motor PWM: ")); + Serial.println(motorPwm); +} void setup() { Serial.begin(9600); - - pinMode(led1, OUTPUT); - pinMode(led2, OUTPUT); - pinMode(led3, OUTPUT); - pinMode(led4, OUTPUT); - pinMode(motorPin1, OUTPUT); - pinMode(motorPin2, OUTPUT); + for (uint8_t pin : kLedPins) { + pinMode(pin, OUTPUT); + } + pinMode(kMotorPwm, OUTPUT); + pinMode(kMotorDirection, OUTPUT); + applyLevel(level); + writeCurrentLed(); } void loop() { - if (Serial.available() > 0) { - btValue = Serial.parseInt(); - - if (btValue >= 1 && btValue <= 10) { - ledDelay = map(btValue, 1, 10, 300, 50); - motorSpeed1 = map(btValue, 1, 10, 120, 255); - - analogWrite(motorPin1, motorSpeed1); - analogWrite(motorPin2, 0); - - Serial.print("Bluetooth app character code: "); - Serial.print(btValue); - Serial.print(" | LED Delay: "); - Serial.print(ledDelay); - Serial.print(" | Motor Speed: "); - Serial.println(motorSpeed1); + while (Serial.available() > 0) { + const char received = Serial.read(); + if (received >= '1' && received <= '9') { + applyLevel(received - '0'); + } else if (received == '0') { + applyLevel(10); } } - digitalWrite(led1, HIGH); - delay(ledDelay); - digitalWrite(led1, LOW); - - digitalWrite(led2, HIGH); - delay(ledDelay); - digitalWrite(led2, LOW); - - digitalWrite(led3, HIGH); - delay(ledDelay); - digitalWrite(led3, LOW); - - digitalWrite(led4, HIGH); - delay(ledDelay); - digitalWrite(led4, LOW); - - digitalWrite(led3, HIGH); - delay(ledDelay); - digitalWrite(led3, LOW); - - digitalWrite(led2, HIGH); - delay(ledDelay); - digitalWrite(led2, LOW); - - analogWrite(motorPin1, motorSpeed1); - digitalWrite(motorPin2, LOW); + if (static_cast(millis() - changedAt) >= intervalMs) { + changedAt += intervalMs; + sequenceIndex = (sequenceIndex + 1) % sizeof(kSequence); + writeCurrentLed(); + } } diff --git a/exercises/exercise-f-full-system-integration/exercise_f_full_system_integration.ino b/exercises/exercise-f-full-system-integration/exercise_f_full_system_integration.ino index e62d6a8..b21af51 100644 --- a/exercises/exercise-f-full-system-integration/exercise_f_full_system_integration.ino +++ b/exercises/exercise-f-full-system-integration/exercise_f_full_system_integration.ino @@ -1,143 +1,205 @@ -const int ledPinForward = 2; -const int ledPinLeft = 4; -const int ledPinRight = 7; -const int ledPinBackward = 8; -const int ledPinSpeed = 11; -const int ledPinStatus = 13; - -const int motorPin1B = 3; -const int motorPin2B = 5; -const int motorPin1A = 6; -const int motorPin2A = 9; - -const int buttonPin = 12; -const int potPin = A0; - -char btCommand = 'S'; -int speedValue = 0; +// Motor pins are logic/PWM inputs to a rated dual H-bridge. Use an external +// motor supply and common ground; never connect motors directly to GPIO. +constexpr uint8_t kLedForward = 2; +constexpr uint8_t kLedLeft = 4; +constexpr uint8_t kLedRight = 7; +constexpr uint8_t kLedBackward = 8; +constexpr uint8_t kLedSpeed = 11; +constexpr uint8_t kLedStatus = 13; +constexpr uint8_t kLeftA = 3; +constexpr uint8_t kLeftB = 5; +constexpr uint8_t kRightA = 6; +constexpr uint8_t kRightB = 9; +constexpr uint8_t kEmergencyButton = 12; +constexpr uint8_t kSpeedPot = A0; +constexpr uint16_t kDirectionDeadTimeMs = 50; + +enum class Direction : uint8_t { kStop, kForward, kBackward, kLeft, kRight }; + +Direction requestedDirection = Direction::kStop; +Direction activeDirection = Direction::kStop; +uint32_t directionRequestedAt = 0; +uint32_t emergencyBlinkAt = 0; +uint32_t telemetryAt = 0; +uint8_t speedPwm = 0; +bool emergencyActive = false; +bool emergencyLightsOn = false; void stopMotors() { - analogWrite(motorPin1A, 0); - analogWrite(motorPin2A, 0); - analogWrite(motorPin1B, 0); - analogWrite(motorPin2B, 0); -} - -void setDirectionLeds(bool forwardOn, bool leftOn, bool rightOn, bool backwardOn) { - digitalWrite(ledPinForward, forwardOn ? HIGH : LOW); - digitalWrite(ledPinLeft, leftOn ? HIGH : LOW); - digitalWrite(ledPinRight, rightOn ? HIGH : LOW); - digitalWrite(ledPinBackward, backwardOn ? HIGH : LOW); + analogWrite(kLeftA, 0); + analogWrite(kLeftB, 0); + analogWrite(kRightA, 0); + analogWrite(kRightB, 0); } -void setup() { - Serial.begin(9600); - - pinMode(ledPinForward, OUTPUT); - pinMode(ledPinLeft, OUTPUT); - pinMode(ledPinRight, OUTPUT); - pinMode(ledPinBackward, OUTPUT); - pinMode(ledPinSpeed, OUTPUT); - pinMode(ledPinStatus, OUTPUT); - - pinMode(motorPin1B, OUTPUT); - pinMode(motorPin2B, OUTPUT); - pinMode(motorPin1A, OUTPUT); - pinMode(motorPin2A, OUTPUT); - - pinMode(buttonPin, INPUT_PULLUP); +void writeDirectionLeds(Direction direction) { + digitalWrite(kLedForward, direction == Direction::kForward ? HIGH : LOW); + digitalWrite(kLedLeft, direction == Direction::kLeft ? HIGH : LOW); + digitalWrite(kLedRight, direction == Direction::kRight ? HIGH : LOW); + digitalWrite(kLedBackward, direction == Direction::kBackward ? HIGH : LOW); } -void loop() { - if (Serial.available() > 0) { - btCommand = Serial.read(); +void requestDirection(Direction direction) { + if (direction == requestedDirection) { + return; } + stopMotors(); + activeDirection = Direction::kStop; + requestedDirection = direction; + directionRequestedAt = millis(); + writeDirectionLeds(direction); +} - speedValue = map(analogRead(potPin), 0, 1023, 0, 255); - - // INPUT_PULLUP means the button reads LOW when pressed. - if (digitalRead(buttonPin) == LOW) { +void updateMotors() { + if (emergencyActive || requestedDirection == Direction::kStop) { stopMotors(); - - digitalWrite(ledPinForward, HIGH); - digitalWrite(ledPinLeft, HIGH); - digitalWrite(ledPinRight, HIGH); - digitalWrite(ledPinBackward, HIGH); - analogWrite(ledPinSpeed, 255); - digitalWrite(ledPinStatus, HIGH); - delay(300); - - digitalWrite(ledPinForward, LOW); - digitalWrite(ledPinLeft, LOW); - digitalWrite(ledPinRight, LOW); - digitalWrite(ledPinBackward, LOW); - analogWrite(ledPinSpeed, 0); - digitalWrite(ledPinStatus, LOW); - delay(300); - - Serial.println("Bluetooth app character code: --- | Mode: EMERGENCY | Direction: STOP | Speed: 0"); + activeDirection = Direction::kStop; return; } - String direction = "Stop"; - - if (btCommand == 'F') { - direction = "Forward"; - - analogWrite(motorPin1B, 0); - analogWrite(motorPin2B, speedValue); - analogWrite(motorPin1A, speedValue); - analogWrite(motorPin2A, 0); + if (activeDirection != requestedDirection) { + if (static_cast(millis() - directionRequestedAt) < + kDirectionDeadTimeMs) { + return; + } + activeDirection = requestedDirection; + } - setDirectionLeds(true, false, false, false); - } else if (btCommand == 'B') { - direction = "Backward"; + switch (activeDirection) { + case Direction::kForward: + analogWrite(kLeftA, 0); + analogWrite(kLeftB, speedPwm); + analogWrite(kRightA, speedPwm); + analogWrite(kRightB, 0); + break; + case Direction::kBackward: + analogWrite(kLeftA, speedPwm); + analogWrite(kLeftB, 0); + analogWrite(kRightA, 0); + analogWrite(kRightB, speedPwm); + break; + case Direction::kLeft: + analogWrite(kLeftA, 0); + analogWrite(kLeftB, speedPwm); + analogWrite(kRightA, 0); + analogWrite(kRightB, speedPwm); + break; + case Direction::kRight: + analogWrite(kLeftA, speedPwm); + analogWrite(kLeftB, 0); + analogWrite(kRightA, speedPwm); + analogWrite(kRightB, 0); + break; + case Direction::kStop: + stopMotors(); + break; + } +} - analogWrite(motorPin1B, speedValue); - analogWrite(motorPin2B, 0); - analogWrite(motorPin1A, 0); - analogWrite(motorPin2A, speedValue); +void handleCommand(char command) { + switch (command) { + case 'F': + requestDirection(Direction::kForward); + break; + case 'B': + requestDirection(Direction::kBackward); + break; + case 'L': + requestDirection(Direction::kLeft); + break; + case 'R': + requestDirection(Direction::kRight); + break; + default: + requestDirection(Direction::kStop); + break; + } +} - setDirectionLeds(false, false, false, true); - } else if (btCommand == 'L') { - direction = "Left"; +const __FlashStringHelper* directionName(Direction direction) { + switch (direction) { + case Direction::kForward: + return F("FORWARD"); + case Direction::kBackward: + return F("BACKWARD"); + case Direction::kLeft: + return F("LEFT"); + case Direction::kRight: + return F("RIGHT"); + default: + return F("STOP"); + } +} - analogWrite(motorPin1B, 0); - analogWrite(motorPin2B, speedValue); - analogWrite(motorPin1A, 0); - analogWrite(motorPin2A, speedValue); +void updateEmergencyStop() { + const bool pressed = digitalRead(kEmergencyButton) == LOW; + if (pressed && !emergencyActive) { + emergencyActive = true; + requestDirection(Direction::kStop); + stopMotors(); + emergencyBlinkAt = millis(); + emergencyLightsOn = true; + Serial.println(F("Mode: EMERGENCY | Direction: STOP | Send a new command after release")); + } else if (!pressed && emergencyActive) { + emergencyActive = false; + emergencyLightsOn = false; + writeDirectionLeds(Direction::kStop); + Serial.println(F("Mode: READY | Direction remains STOP")); + } - setDirectionLeds(false, true, false, false); - } else if (btCommand == 'R') { - direction = "Right"; + if (emergencyActive && + static_cast(millis() - emergencyBlinkAt) >= 200) { + emergencyBlinkAt += 200; + emergencyLightsOn = !emergencyLightsOn; + } - analogWrite(motorPin1B, speedValue); - analogWrite(motorPin2B, 0); - analogWrite(motorPin1A, speedValue); - analogWrite(motorPin2A, 0); + if (emergencyActive) { + digitalWrite(kLedForward, emergencyLightsOn ? HIGH : LOW); + digitalWrite(kLedLeft, emergencyLightsOn ? HIGH : LOW); + digitalWrite(kLedRight, emergencyLightsOn ? HIGH : LOW); + digitalWrite(kLedBackward, emergencyLightsOn ? HIGH : LOW); + } +} - setDirectionLeds(false, false, true, false); - } else { - stopMotors(); - setDirectionLeds(false, false, false, false); +void printTelemetry() { + if (static_cast(millis() - telemetryAt) < 500) { + return; } + telemetryAt += 500; + Serial.print(F("Mode: ")); + Serial.print(emergencyActive ? F("EMERGENCY") : F("NORMAL")); + Serial.print(F(" | Direction: ")); + Serial.print(directionName(requestedDirection)); + Serial.print(F(" | PWM: ")); + Serial.println(emergencyActive ? 0 : speedPwm); +} - if (speedValue < 85) { - analogWrite(ledPinSpeed, 0); - } else if (speedValue < 170) { - analogWrite(ledPinSpeed, 128); - } else { - analogWrite(ledPinSpeed, 255); +void setup() { + Serial.begin(9600); + const uint8_t outputs[] = {kLedForward, kLedLeft, kLedRight, kLedBackward, + kLedSpeed, kLedStatus, kLeftA, kLeftB, + kRightA, kRightB}; + for (uint8_t pin : outputs) { + pinMode(pin, OUTPUT); } + pinMode(kEmergencyButton, INPUT_PULLUP); + stopMotors(); + writeDirectionLeds(Direction::kStop); +} - digitalWrite(ledPinStatus, HIGH); +void loop() { + updateEmergencyStop(); - Serial.print("Bluetooth app character code: "); - Serial.print(btCommand); - Serial.print(" | Mode: Normal | Direction: "); - Serial.print(direction); - Serial.print(" | Speed: "); - Serial.println(speedValue); + while (!emergencyActive && Serial.available() > 0) { + const char received = Serial.read(); + if (received != '\r' && received != '\n') { + handleCommand(received); + } + } - delay(100); + speedPwm = map(analogRead(kSpeedPot), 0, 1023, 0, 255); + analogWrite(kLedSpeed, emergencyActive ? 0 : speedPwm); + digitalWrite(kLedStatus, emergencyActive ? LOW : HIGH); + updateMotors(); + printTelemetry(); } diff --git a/extras/basic-bluetooth-led/basic_bluetooth_led.ino b/extras/basic-bluetooth-led/basic_bluetooth_led.ino index 50e88af..2142667 100644 --- a/extras/basic-bluetooth-led/basic_bluetooth_led.ino +++ b/extras/basic-bluetooth-led/basic_bluetooth_led.ino @@ -1,22 +1,27 @@ -const int ledPin = 5; - -char switchState = '0'; +constexpr uint8_t kLedPin = 5; +bool ledOn = false; void setup() { Serial.begin(9600); - pinMode(ledPin, OUTPUT); + pinMode(kLedPin, OUTPUT); + digitalWrite(kLedPin, LOW); + Serial.println(F("Send 1 for ON or 0 for OFF.")); } void loop() { - while (Serial.available() > 0) { - switchState = Serial.read(); - Serial.println(switchState); - delay(100); + if (Serial.available() == 0) { + return; + } - if (switchState == '1') { - digitalWrite(ledPin, HIGH); - } else if (switchState == '0') { - digitalWrite(ledPin, LOW); - } + const char received = Serial.read(); + if (received == '1') { + ledOn = true; + } else if (received == '0') { + ledOn = false; + } else { + return; } + + digitalWrite(kLedPin, ledOn ? HIGH : LOW); + Serial.println(ledOn ? F("LED: ON") : F("LED: OFF")); } From c5133012e6a573cc5d8e97ee634c6af498f79ea4 Mon Sep 17 00:00:00 2001 From: "@dev.mako" Date: Wed, 29 Jul 2026 10:14:50 +0800 Subject: [PATCH 2/4] docs: turn sketches into a safety-first learning path --- CHANGELOG.md | 10 +++ CODE_OF_CONDUCT.md | 13 ++++ CONTRIBUTING.md | 30 ++++++++ LICENSE | 21 ++++++ README.md | 151 +++++++++++++------------------------ SECURITY.md | 8 ++ VERSION | 1 + docs/getting-started.md | 41 ++++++++++ docs/hardware-safety.md | 38 ++++++++++ docs/lessons/exercise-a.md | 35 +++++++++ docs/lessons/exercise-b.md | 32 ++++++++ docs/lessons/exercise-c.md | 35 +++++++++ docs/lessons/exercise-d.md | 33 ++++++++ docs/lessons/exercise-e.md | 31 ++++++++ docs/lessons/exercise-f.md | 41 ++++++++++ docs/serial-control.md | 29 +++++++ docs/troubleshooting.md | 35 +++++++++ 17 files changed, 486 insertions(+), 98 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 SECURITY.md create mode 100644 VERSION create mode 100644 docs/getting-started.md create mode 100644 docs/hardware-safety.md create mode 100644 docs/lessons/exercise-a.md create mode 100644 docs/lessons/exercise-b.md create mode 100644 docs/lessons/exercise-c.md create mode 100644 docs/lessons/exercise-d.md create mode 100644 docs/lessons/exercise-e.md create mode 100644 docs/lessons/exercise-f.md create mode 100644 docs/serial-control.md create mode 100644 docs/troubleshooting.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1fdf8e2 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +## [1.0.0] - 2026-07-29 + +- Turn the sketch collection into a six-lesson learning path. +- Add wiring, safety, serial-control, troubleshooting, and lesson documentation. +- Refactor every sketch to remain responsive without blocking pattern delays. +- Add official Arduino Uno compilation checks and contributor templates. + +[1.0.0]: https://github.com/devkyato/Arduino-Programs-Guide/releases/tag/v1.0.0 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..ed15846 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,13 @@ +# Code of Conduct + +This project is a learning space. Be patient, specific, and constructive. Welcome +beginner questions, explain corrections without ridicule, and respect different +backgrounds and experience levels. + +Harassment, discriminatory language, personal attacks, and publishing private +information are not acceptable. Report problems privately through the +maintainer's GitHub profile. Maintainers may remove content or restrict +participation to protect the community. + +This policy is adapted from the +[Contributor Covenant 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..bcde258 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,30 @@ +# Contributing + +## Choose a contribution + +Look for issues labeled `good first issue`, `documentation`, or `hardware`. If a +change alters a pin map, command, or learning objective, open an issue first. + +## Verify your work + +- Compile every changed sketch for Arduino Uno. +- Test hardware changes with the exact circuit described in the pull request. +- Keep `loop()` responsive; do not add long `delay()` calls. +- Update the matching lesson when behavior or wiring changes. +- Never recommend powering a motor directly from GPIO or the USB 5 V rail. + +Install Arduino CLI and run: + +```sh +arduino-cli core update-index +arduino-cli core install arduino:avr +arduino-cli compile --fqbn arduino:avr:uno exercises/exercise-a-arrow-controller +``` + +CI recursively compiles all exercise and extra sketches. + +## Pull requests + +Keep the change focused, explain what a student learns from it, and list the +board, core version, power source, and driver used for any hardware test. +Participation is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f24b1a7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 devkyato + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 99cf46f..76b92c9 100644 --- a/README.md +++ b/README.md @@ -1,115 +1,70 @@ -# Supplementaries Arduino Exercises +# Arduino Programs Guide -This repository contains cleaned, formatted, and organized Arduino sketches for Exercises A to F, plus one extra Bluetooth LED example. +[![Sketch compilation](https://github.com/devkyato/Arduino-Programs-Guide/actions/workflows/compile.yml/badge.svg)](https://github.com/devkyato/Arduino-Programs-Guide/actions/workflows/compile.yml) +[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) -## Repository Contents +A progressive, hands-on Arduino Uno course covering serial commands, LED patterns, +PWM motor control, state machines, and emergency-stop behavior. Each lesson +includes a compile-checked sketch, wiring contract, test procedure, and extension +ideas. -- `exercises/exercise-a-arrow-controller/exercise_a_arrow_controller.ino` -- `exercises/exercise-b-multi-led-pattern/exercise_b_multi_led_pattern.ino` -- `exercises/exercise-c-smart-fan/exercise_c_smart_fan.ino` -- `exercises/exercise-d-wheel-simulation/exercise_d_wheel_simulation.ino` -- `exercises/exercise-e-knight-rider-motor-sync/exercise_e_knight_rider_motor_sync.ino` -- `exercises/exercise-f-full-system-integration/exercise_f_full_system_integration.ino` -- `extras/basic-bluetooth-led/basic_bluetooth_led.ino` +> [!CAUTION] +> Never connect a DC motor directly to an Arduino pin. Motor lessons require a +> suitable transistor or H-bridge driver, flyback protection, an external motor +> supply, and a shared ground. Read [Hardware safety](docs/hardware-safety.md) +> before Exercises C–F. -Everything listed above is already included in the repository. +## Learning path -## How To Use +| Lesson | Project | Main concepts | Hardware | +| --- | --- | --- | --- | +| [A](docs/lessons/exercise-a.md) | Arrow controller | serial input, functions, digital output | 4 LEDs | +| [B](docs/lessons/exercise-b.md) | Pattern selector | arrays, bit masks, non-blocking timing | 4 LEDs | +| [C](docs/lessons/exercise-c.md) | Smart fan | PWM, command mapping, safe motor stop | 3 LEDs, driver, motor | +| [D](docs/lessons/exercise-d.md) | Wheel simulation | H-bridge direction, state machines | 4 LEDs, dual driver, 2 motors | +| [E](docs/lessons/exercise-e.md) | Synchronized scanner | numeric parsing, `map()`, animation | 4 LEDs, driver, motor | +| [F](docs/lessons/exercise-f.md) | Integrated vehicle | analog input, emergency override, telemetry | full circuit | -1. Clone or download this repository. -2. Open the Arduino IDE. -3. Open the exercise folder you want to test. -4. Load the `.ino` file for that exercise. -5. Connect your Arduino board. -6. Select the correct board and COM port in the Arduino IDE. -7. Upload the sketch. -8. Open the Serial Monitor and set the baud rate to `9600`. -9. Send commands from your Bluetooth app or serial connection based on the exercise instructions below. +There is also a small [Bluetooth LED warm-up](extras/basic-bluetooth-led/) +for testing a serial module before starting the main exercises. -## Exercise Summary +## Start here -### Exercise A +1. Read [Getting started](docs/getting-started.md). +2. Build Exercise A with the board disconnected from power. +3. Upload the sketch and test it in Serial Monitor before adding Bluetooth. +4. Complete the lesson's verification checklist. +5. Commit your observations and improvements in your own fork. -Arrow controller using 4 LEDs. +All sketches use `9600` baud and accept the same characters from USB Serial or a +TTL serial Bluetooth module. On an Uno, the hardware serial pins are shared with +USB; disconnect the module from pins 0/1 while uploading. See +[Serial control](docs/serial-control.md). -- `Q` = Up-Left -- `E` = Up-Right -- `Z` = Down-Left -- `C` = Down-Right -- `X` = OFF +## Repository layout -The Serial Monitor prints only the direction name. +```text +exercises/ Six progressively more involved Arduino sketches +extras/ Small setup and diagnostic sketches +docs/lessons/ Objectives, wiring tables, tests, and challenges +docs/ Setup, safety, serial, and troubleshooting guides +.github/ Contribution templates and automated compile checks +``` -### Exercise B +## Supported environment -Bluetooth-controlled multi-LED pattern selector. +The examples target the Arduino Uno and the Arduino AVR Boards core. CI compiles +every sketch with the official Arduino toolchain. Other boards may use different +PWM pins, voltage levels, serial ports, or ADC ranges; porting notes are welcome +only when verified on hardware. -- `A` = Blink All -- `B` = Knight Rider -- `C` = Alternate Blink -- `D` = Wave +## Contributing -The Serial Monitor prints the selected LED pattern name. +Student contributions are encouraged. Good changes include clearer diagrams, +hardware results, non-blocking improvements, and new tests that preserve each +lesson's learning objective. Read [CONTRIBUTING.md](CONTRIBUTING.md) before +opening a pull request. -### Exercise C +## License -Smart fan control using Bluetooth, 3 LEDs, and a DC motor. - -- `A` = OFF -- `B` = ON -- `C` = LOW -- `D` = MEDIUM -- `1` = HIGH - -The LEDs show the speed level, and the motor speed changes with the selected mode. - -### Exercise D - -Wheel of a car simulation using Bluetooth. - -- `F` = Forward -- `L` = Left -- `R` = Right -- `B` = Reverse - -The LEDs show direction, and the wheels or motor outputs move according to the command. - -### Exercise E - -Knight Rider LED pattern with motor speed sync. - -- Send a Bluetooth value from `1` to `10` - -The LED animation becomes faster as the value increases, and the motor speed also increases. - -### Exercise F - -Full system integration with direction control, speed control, and emergency override. - -- `F` = Forward -- `L` = Left -- `R` = Right -- `B` = Backward -- Potentiometer = Speed control -- Tact switch = Emergency stop override - -The system status and speed indicator LEDs are also used in this sketch. - -## Notes - -- The sketches were reformatted for readability and split into separate files. -- Exercise A and Exercise B were created from the assignment screenshots. -- Exercise E was adjusted so values `1` to `10` work more reliably. -- Exercise F was adjusted so the emergency stop works correctly with `INPUT_PULLUP`. -- Exercise F pin assignments were cleaned up for a more practical Arduino setup. - -## Important Reminder - -You may still need to change some pin numbers depending on: - -- your Arduino board -- your motor driver -- your Bluetooth module wiring -- your actual LED and button connections - -If your Bluetooth app uses different button letters, you can edit the command characters in each sketch. +[MIT](LICENSE) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..af0c0fe --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,8 @@ +# Security and safety reports + +Use GitHub private vulnerability reporting for software vulnerabilities. For an +electrical safety concern, stop using the circuit and open an issue without +energizing it again. Do not include personal information. + +Include the lesson, board, driver, supply voltage/current rating, wiring, and +observed behavior. The latest release is the supported version. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.0 diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..4f91ccc --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,41 @@ +# Getting started + +## What you need + +- Arduino Uno or compatible 5 V AVR board +- data-capable USB cable +- breadboard and jumper wires +- 220–330 Ω resistor for each LED +- components listed in the selected lesson +- Arduino IDE 2 or Arduino CLI + +Exercises C–F also require an appropriate motor driver, protected motor supply, +and a multimeter. Do not begin those exercises until you understand the +[hardware safety rules](hardware-safety.md). + +## First upload + +1. Install Arduino IDE from the official Arduino website. +2. Connect the Uno and select **Tools > Board > Arduino Uno**. +3. Select the board's port. +4. Open the `.ino` file inside its matching folder. +5. Click **Verify**, then **Upload**. +6. Open Serial Monitor, choose `9600 baud`, and use **No line ending**. + +Test the USB serial command path before connecting a Bluetooth module. This keeps +software, wiring, and wireless troubleshooting separate. + +## Breadboard habits + +- Disconnect USB and external power before changing wiring. +- Check LED polarity and always use a series resistor. +- Use one ground reference across the Arduino, driver, and external supply. +- Keep a written pin map beside the circuit. +- Measure the supply before connecting the board. +- Upload a motor-stop sketch before attaching motor power. + +## Evidence for each lesson + +Record the board/core version, a wiring photo or diagram, commands tested, +observed serial output, and any changes made. A successful compile is not proof +that a physical circuit is safe or correct. diff --git a/docs/hardware-safety.md b/docs/hardware-safety.md new file mode 100644 index 0000000..dce9c9c --- /dev/null +++ b/docs/hardware-safety.md @@ -0,0 +1,38 @@ +# Hardware safety + +## LEDs + +Use one 220–330 Ω series resistor per LED. Confirm polarity before power-up. +The pin maps describe logic connections, not permission to exceed the board's +electrical ratings. + +## DC motors + +An Arduino GPIO pin is a logic signal, not a motor power source. Motors create +startup current and inductive voltage spikes that can damage the microcontroller. +Exercises C–F assume: + +- a transistor/MOSFET driver or H-bridge rated above the motor's stall current; +- flyback protection, built into the driver or added as required; +- a separately rated motor supply; +- a common ground between Arduino, driver, and motor supply; +- no motor current flowing through an Arduino GPIO pin. + +For bidirectional exercises, use an H-bridge or dual motor-driver module. Verify +its truth table because input polarity differs between modules. + +## Power-up checklist + +1. Disconnect the motor supply. +2. Check continuity and polarity. +3. Confirm driver input and enable voltage compatibility. +4. Upload the sketch. +5. Verify stop-state output with LEDs or a multimeter. +6. Secure the motor so moving parts cannot cause injury. +7. Connect motor power and begin at the lowest PWM command. + +If the Arduino resets, the driver heats rapidly, or wiring smells hot, disconnect +power immediately. Do not solve power problems by bypassing protection. + +Official background: [Arduino transistor motor control](https://docs.arduino.cc/learn/electronics/transistor-motor-control) +and [Arduino Motor Shield Rev3](https://docs.arduino.cc/hardware/motor-shield-rev3). diff --git a/docs/lessons/exercise-a.md b/docs/lessons/exercise-a.md new file mode 100644 index 0000000..7fb4c7c --- /dev/null +++ b/docs/lessons/exercise-a.md @@ -0,0 +1,35 @@ +# Exercise A: arrow controller + +## Objective + +Translate one-character serial commands into four LED outputs. This introduces +`Serial.available()`, `Serial.read()`, functions, and a safe default state. + +## Wiring + +Connect each pin through its own 220–330 Ω resistor to an LED anode; connect each +LED cathode to ground. + +| Meaning | Uno pin | +| --- | --- | +| Up | 2 | +| Left | 3 | +| Right | 4 | +| Down | 5 | + +## Commands + +`Q` = up-left, `E` = up-right, `Z` = down-left, `C` = down-right, and `X` (or +any unknown character) = off. + +## Verify + +1. Send every documented command once. +2. Confirm the two expected LEDs and serial message. +3. Send an unknown character and confirm all LEDs turn off. +4. Leave the sketch idle and confirm it does not repeatedly print. + +## Challenge + +Add `W`, `A`, `S`, and `D` for single-direction output without duplicating the +pin-writing logic. diff --git a/docs/lessons/exercise-b.md b/docs/lessons/exercise-b.md new file mode 100644 index 0000000..74ac6c7 --- /dev/null +++ b/docs/lessons/exercise-b.md @@ -0,0 +1,32 @@ +# Exercise B: multi-LED patterns + +## Objective + +Use arrays and bit masks to animate four LEDs while continuing to read serial +input. The sketch replaces long sequences of `delay()` calls with elapsed-time +checks. + +## Wiring + +Pins 2, 3, 4, and 5 each drive one LED through a 220–330 Ω resistor. + +## Commands + +| Command | Pattern | +| --- | --- | +| `A` | all LEDs blink | +| `B` | scanner / Knight Rider | +| `C` | alternating pairs | +| `D` | one-way wave | +| other | off | + +## Verify + +Start each pattern, then switch commands mid-animation. The new pattern should +start immediately. Leave it running across the `millis()` rollover only for an +extended validation; unsigned elapsed-time arithmetic keeps it safe. + +## Challenge + +Add a pattern by defining a mask array and selecting it in `selectPattern()`. +Avoid adding new `digitalWrite()` sequences or blocking delays. diff --git a/docs/lessons/exercise-c.md b/docs/lessons/exercise-c.md new file mode 100644 index 0000000..92398ce --- /dev/null +++ b/docs/lessons/exercise-c.md @@ -0,0 +1,35 @@ +# Exercise C: smart fan + +## Objective + +Map serial commands to PWM levels and LED indicators while maintaining a +fail-safe off command. + +Read [Hardware safety](../hardware-safety.md) before wiring a motor. + +## Driver interface + +| Signal | Uno pin | +| --- | --- | +| Low / medium / high LEDs | 3 / 5 / 6 | +| Driver PWM or enable | 10 | +| Driver direction input | 11 | + +The exact motor, supply, driver outputs, and flyback protection depend on the +driver datasheet. Pins 10 and 11 connect only to logic inputs. + +## Commands + +`A` = off, `B` = on at PWM 120, `C` = low at 170, `D` = medium at 200, and +`1` = high at 255. Unknown input also stops the motor. + +## Verify + +Test first with the motor supply disconnected and measure the PWM/logic response. +Then secure the motor, connect its rated supply, and start with `A`. Record driver +temperature and supply behavior; stop immediately if either is abnormal. + +## Challenge + +Replace the fixed levels with a table containing command, PWM, LED mask, and +label, while preserving the off-on-unknown behavior. diff --git a/docs/lessons/exercise-d.md b/docs/lessons/exercise-d.md new file mode 100644 index 0000000..24e7ffb --- /dev/null +++ b/docs/lessons/exercise-d.md @@ -0,0 +1,33 @@ +# Exercise D: wheel simulation + +## Objective + +Control two H-bridge channels as a small differential-drive system and run a +reverse warning animation without blocking serial commands. + +## Pin map + +| Signal | Uno pin | +| --- | --- | +| Left / forward / right / reverse LEDs | 2 / 4 / 7 / 8 | +| Left driver inputs A / B | 10 / 11 | +| Right driver inputs A / B | 5 / 6 | + +Confirm the H-bridge truth table before connecting motors. Reversing polarity or +using a different driver may require changing the four logic values. + +## Commands + +`F` = forward, `L` = left, `R` = right, `B` = reverse, and any other character += stop. + +## Verify + +With motor power disconnected, check each driver input and direction LED. With +the vehicle lifted so wheels are clear, connect motor power at low voltage and +verify one direction at a time. Confirm a stop command is accepted while the +reverse LEDs are blinking. + +## Challenge + +Add PWM enable pins and three speed levels without changing direction input pins. diff --git a/docs/lessons/exercise-e.md b/docs/lessons/exercise-e.md new file mode 100644 index 0000000..5f58453 --- /dev/null +++ b/docs/lessons/exercise-e.md @@ -0,0 +1,31 @@ +# Exercise E: synchronized scanner + +## Objective + +Map a numeric level to both animation interval and motor PWM, demonstrating +bounded input, arrays, and synchronized outputs. + +## Pin map + +| Signal | Uno pin | +| --- | --- | +| Four LEDs | 3, 5, 6, 9 | +| Driver PWM / direction | 10 / 11 | + +Motor pins connect only to a rated driver. Follow +[Hardware safety](../hardware-safety.md). + +## Commands + +Send `1`–`9` for those levels and `0` for level 10. Level 1 uses a 300 ms step +and PWM 120; level 10 uses a 50 ms step and PWM 255. + +## Verify + +Confirm every input produces values within those bounds. Change level while the +scanner is moving and verify serial input remains responsive. + +## Challenge + +Add an `S` command that stops the motor and LEDs, then resumes the previous level +when a numeric command arrives. diff --git a/docs/lessons/exercise-f.md b/docs/lessons/exercise-f.md new file mode 100644 index 0000000..1ca78cf --- /dev/null +++ b/docs/lessons/exercise-f.md @@ -0,0 +1,41 @@ +# Exercise F: integrated vehicle + +## Objective + +Combine differential drive, potentiometer speed input, direction indicators, +periodic telemetry, and an emergency-stop override in one responsive loop. + +## Pin map + +| Signal | Uno pin | +| --- | --- | +| Forward / left / right / backward LEDs | 2 / 4 / 7 / 8 | +| Speed / status LEDs | 11 / 13 | +| Left driver A / B | 3 / 5 | +| Right driver A / B | 6 / 9 | +| Emergency button | 12 to GND (`INPUT_PULLUP`) | +| Speed potentiometer wiper | A0 | + +The potentiometer's outer legs connect to 5 V and GND. Motor connections require +a rated dual H-bridge, external supply, protection, and common ground. + +## Behavior + +`F`, `B`, `L`, and `R` request motion; any other command requests stop. A 50 ms +stop interval occurs before a new direction is energized. Pressing the emergency +button stops all motor PWM immediately, flashes the direction LEDs, and discards +the old direction. Releasing the button does not resume motion—a new command is +required. + +## Verify + +1. Keep motor power disconnected and verify every indicator and driver input. +2. Press emergency stop during every requested direction. +3. Hold the button, send commands, then release it; the motors must remain stopped. +4. Turn the potentiometer through its range and check reported PWM. +5. Secure the vehicle before cautious powered testing. + +## Challenge + +Debounce the release transition without delaying emergency activation, then +explain why stopping should not wait for debounce. diff --git a/docs/serial-control.md b/docs/serial-control.md new file mode 100644 index 0000000..bf6b0ec --- /dev/null +++ b/docs/serial-control.md @@ -0,0 +1,29 @@ +# Serial and Bluetooth control + +All sketches use `Serial` at 9600 baud. Commands can come from Arduino IDE Serial +Monitor or from a TTL serial Bluetooth module that presents the same byte stream. + +## Uno wiring + +| Module | Arduino Uno | +| --- | --- | +| TX | RX / pin 0 | +| RX | TX / pin 1 through level shifting when required | +| GND | GND | +| VCC | Module-specific rated supply | + +Check the module's datasheet: many receive pins are 3.3 V logic even when the +breakout board accepts 5 V power. Disconnect pins 0 and 1 during upload because +the Uno USB interface shares this hardware serial port. + +## Reliable test sequence + +1. Test commands over USB with no wireless module connected. +2. Configure Serial Monitor to **No line ending**. +3. Connect the unpowered module and verify voltage levels. +4. Pair the module and send the same characters. +5. If commands fail, echo bytes with the basic Bluetooth LED sketch. + +The sketches ignore carriage-return and newline bytes, so other line-ending +settings should not cause an unsafe command. Unknown commands select a stopped or +off state in motor exercises. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..447f54a --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,35 @@ +# Troubleshooting + +## Upload fails + +- Disconnect anything from Uno pins 0 and 1. +- Confirm the board and port. +- Try a known data-capable USB cable. +- Close other serial-monitor applications. + +## LEDs behave backwards + +Check LED polarity, resistor placement, and the lesson pin table. Do not remove a +resistor to make an LED brighter. + +## Commands include unexpected values + +Select **No line ending** in Serial Monitor. The maintained sketches ignore CR/LF, +but a custom sketch may not. + +## Motor does not move + +Disconnect power first. Check the external supply, shared ground, driver enable +pin, driver truth table, and motor stall-current rating. Measure driver output +without holding the motor shaft. + +## Arduino resets when the motor starts + +This usually indicates supply noise, excessive current, missing protection, or +poor grounding—not a reason to draw motor power from GPIO. Recheck the complete +driver and power design. + +## Sketch compiles but hardware fails + +Compilation only verifies source compatibility. Compare the actual circuit to the +lesson pin map, then reduce the system to one LED or one unloaded driver channel. From 17b6a441e2d4da28f1f0a43e1ef9a69178e46bc5 Mon Sep 17 00:00:00 2001 From: "@dev.mako" Date: Wed, 29 Jul 2026 10:14:50 +0800 Subject: [PATCH 3/4] ci: compile every Uno sketch and package releases --- .github/ISSUE_TEMPLATE/bug.yml | 32 ++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 +++ .github/ISSUE_TEMPLATE/lesson.yml | 23 +++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 14 ++++++++ .github/dependabot.yml | 9 +++++ .github/workflows/compile.yml | 37 ++++++++++++++++++++ .github/workflows/release.yml | 25 ++++++++++++++ .gitignore | 7 ++++ tools/build_release.py | 56 +++++++++++++++++++++++++++++++ 9 files changed, 208 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/lesson.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/compile.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 tools/build_release.py diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..79b8756 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,32 @@ +name: Sketch bug +description: Report a reproducible sketch or documentation error. +title: "[Bug]: " +labels: ["bug"] +body: + - type: input + id: lesson + attributes: + label: Lesson and sketch + placeholder: Exercise B / exercise_b_multi_led_pattern.ino + validations: + required: true + - type: input + id: environment + attributes: + label: Board and core version + placeholder: Arduino Uno / Arduino AVR Boards 1.8.6 + validations: + required: true + - type: textarea + id: circuit + attributes: + label: Circuit and power details + description: For motor exercises, include the driver and supply ratings. + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Reproduction and observed behavior + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..7df5262 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Private security report + url: https://github.com/devkyato/Arduino-Programs-Guide/security/advisories/new + about: Report software vulnerabilities privately. diff --git a/.github/ISSUE_TEMPLATE/lesson.yml b/.github/ISSUE_TEMPLATE/lesson.yml new file mode 100644 index 0000000..21904ae --- /dev/null +++ b/.github/ISSUE_TEMPLATE/lesson.yml @@ -0,0 +1,23 @@ +name: Lesson improvement +description: Propose a clearer explanation, test, diagram, or challenge. +title: "[Lesson]: " +labels: ["documentation"] +body: + - type: input + id: lesson + attributes: + label: Lesson + validations: + required: true + - type: textarea + id: confusion + attributes: + label: What is unclear or missing? + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposed improvement + validations: + required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..8b3917b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,14 @@ +## Learning outcome + + + +## Verification + +- [ ] Changed sketches compile for Arduino Uno +- [ ] Matching lesson documentation is updated +- [ ] No blocking delay was added to interactive behavior +- [ ] Hardware/power details are included when applicable + +## Hardware tested + + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..478231b --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly + groups: + actions: + patterns: ["*"] diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml new file mode 100644 index 0000000..384c002 --- /dev/null +++ b/.github/workflows/compile.yml @@ -0,0 +1,37 @@ +name: Sketch compilation + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + compile-uno: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: arduino/compile-sketches@v1 + with: + fqbn: arduino:avr:uno + libraries: "[]" + sketch-paths: | + - exercises + - extras + enable-warnings-report: true + + offline-package: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-python@v7 + with: + python-version: "3.14" + - run: python tools/build_release.py + - uses: actions/upload-artifact@v6 + with: + name: Arduino-Programs-Guide + path: dist/*.zip diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..42e3071 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,25 @@ +name: Release + +on: + push: + tags: ["v*"] + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-python@v7 + with: + python-version: "3.14" + - name: Verify tag + shell: bash + run: test "$GITHUB_REF_NAME" = "v$(cat VERSION)" + - run: python tools/build_release.py + - name: Publish release + env: + GH_TOKEN: ${{ github.token }} + run: gh release create "$GITHUB_REF_NAME" dist/*.zip --generate-notes --verify-tag diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5deb25e --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.vscode/ +build/ +dist/ +*.hex +*.elf +*.bin +*.map diff --git a/tools/build_release.py b/tools/build_release.py new file mode 100644 index 0000000..b6c7be6 --- /dev/null +++ b/tools/build_release.py @@ -0,0 +1,56 @@ +"""Build a deterministic offline course archive.""" + +from __future__ import annotations + +import zipfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +DIST = ROOT / "dist" +INCLUDED = ( + "exercises", + "extras", + "docs", + "README.md", + "CHANGELOG.md", + "CONTRIBUTING.md", + "CODE_OF_CONDUCT.md", + "SECURITY.md", + "LICENSE", + "VERSION", +) +FIXED_TIME = (2026, 1, 1, 0, 0, 0) + + +def main() -> None: + version = (ROOT / "VERSION").read_text(encoding="utf-8").strip() + output = DIST / f"Arduino-Programs-Guide-{version}.zip" + DIST.mkdir(exist_ok=True) + + files: list[Path] = [] + for name in INCLUDED: + path = ROOT / name + files.extend( + (candidate for candidate in path.rglob("*") if candidate.is_file()) + if path.is_dir() + else (path,) + ) + + with zipfile.ZipFile(output, "w", zipfile.ZIP_DEFLATED) as archive: + for path in sorted(files, key=lambda item: item.as_posix()): + relative = path.relative_to(ROOT).as_posix() + info = zipfile.ZipInfo(f"Arduino-Programs-Guide/{relative}", FIXED_TIME) + info.compress_type = zipfile.ZIP_DEFLATED + info.external_attr = 0o100644 << 16 + archive.writestr(info, path.read_bytes()) + + with zipfile.ZipFile(output) as archive: + sketches = [name for name in archive.namelist() if name.endswith(".ino")] + if len(sketches) != 7: + raise RuntimeError(f"expected 7 sketches, found {len(sketches)}") + + print(output) + + +if __name__ == "__main__": + main() From 41d615af00f04728654fb40ef5c6731666c35fba Mon Sep 17 00:00:00 2001 From: "@dev.mako" Date: Wed, 29 Jul 2026 10:17:34 +0800 Subject: [PATCH 4/4] fix: match Arduino sketch names to their folders --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- ...e_a_arrow_controller.ino => exercise-a-arrow-controller.ino} | 0 ...b_multi_led_pattern.ino => exercise-b-multi-led-pattern.ino} | 0 .../{exercise_c_smart_fan.ino => exercise-c-smart-fan.ino} | 0 ...e_d_wheel_simulation.ino => exercise-d-wheel-simulation.ino} | 0 ...er_motor_sync.ino => exercise-e-knight-rider-motor-sync.ino} | 0 ...m_integration.ino => exercise-f-full-system-integration.ino} | 0 .../{basic_bluetooth_led.ino => basic-bluetooth-led.ino} | 0 8 files changed, 1 insertion(+), 1 deletion(-) rename exercises/exercise-a-arrow-controller/{exercise_a_arrow_controller.ino => exercise-a-arrow-controller.ino} (100%) rename exercises/exercise-b-multi-led-pattern/{exercise_b_multi_led_pattern.ino => exercise-b-multi-led-pattern.ino} (100%) rename exercises/exercise-c-smart-fan/{exercise_c_smart_fan.ino => exercise-c-smart-fan.ino} (100%) rename exercises/exercise-d-wheel-simulation/{exercise_d_wheel_simulation.ino => exercise-d-wheel-simulation.ino} (100%) rename exercises/exercise-e-knight-rider-motor-sync/{exercise_e_knight_rider_motor_sync.ino => exercise-e-knight-rider-motor-sync.ino} (100%) rename exercises/exercise-f-full-system-integration/{exercise_f_full_system_integration.ino => exercise-f-full-system-integration.ino} (100%) rename extras/basic-bluetooth-led/{basic_bluetooth_led.ino => basic-bluetooth-led.ino} (100%) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 79b8756..ed166ab 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -7,7 +7,7 @@ body: id: lesson attributes: label: Lesson and sketch - placeholder: Exercise B / exercise_b_multi_led_pattern.ino + placeholder: Exercise B / exercise-b-multi-led-pattern.ino validations: required: true - type: input diff --git a/exercises/exercise-a-arrow-controller/exercise_a_arrow_controller.ino b/exercises/exercise-a-arrow-controller/exercise-a-arrow-controller.ino similarity index 100% rename from exercises/exercise-a-arrow-controller/exercise_a_arrow_controller.ino rename to exercises/exercise-a-arrow-controller/exercise-a-arrow-controller.ino diff --git a/exercises/exercise-b-multi-led-pattern/exercise_b_multi_led_pattern.ino b/exercises/exercise-b-multi-led-pattern/exercise-b-multi-led-pattern.ino similarity index 100% rename from exercises/exercise-b-multi-led-pattern/exercise_b_multi_led_pattern.ino rename to exercises/exercise-b-multi-led-pattern/exercise-b-multi-led-pattern.ino diff --git a/exercises/exercise-c-smart-fan/exercise_c_smart_fan.ino b/exercises/exercise-c-smart-fan/exercise-c-smart-fan.ino similarity index 100% rename from exercises/exercise-c-smart-fan/exercise_c_smart_fan.ino rename to exercises/exercise-c-smart-fan/exercise-c-smart-fan.ino diff --git a/exercises/exercise-d-wheel-simulation/exercise_d_wheel_simulation.ino b/exercises/exercise-d-wheel-simulation/exercise-d-wheel-simulation.ino similarity index 100% rename from exercises/exercise-d-wheel-simulation/exercise_d_wheel_simulation.ino rename to exercises/exercise-d-wheel-simulation/exercise-d-wheel-simulation.ino diff --git a/exercises/exercise-e-knight-rider-motor-sync/exercise_e_knight_rider_motor_sync.ino b/exercises/exercise-e-knight-rider-motor-sync/exercise-e-knight-rider-motor-sync.ino similarity index 100% rename from exercises/exercise-e-knight-rider-motor-sync/exercise_e_knight_rider_motor_sync.ino rename to exercises/exercise-e-knight-rider-motor-sync/exercise-e-knight-rider-motor-sync.ino diff --git a/exercises/exercise-f-full-system-integration/exercise_f_full_system_integration.ino b/exercises/exercise-f-full-system-integration/exercise-f-full-system-integration.ino similarity index 100% rename from exercises/exercise-f-full-system-integration/exercise_f_full_system_integration.ino rename to exercises/exercise-f-full-system-integration/exercise-f-full-system-integration.ino diff --git a/extras/basic-bluetooth-led/basic_bluetooth_led.ino b/extras/basic-bluetooth-led/basic-bluetooth-led.ino similarity index 100% rename from extras/basic-bluetooth-led/basic_bluetooth_led.ino rename to extras/basic-bluetooth-led/basic-bluetooth-led.ino