/*
* Step 2: 순차적 폭포 애니메이션
*
* 목적:
* - 단일 LED가 위에서 아래로 떨어지는 애니메이션
* - 한 LED가 끝까지 떨어진 후 다음 LED 시작
* - (0,0) → (1,0) → (2,0) → ... → (7,0) 순서로 순차 실행
*
* 하드웨어:
* - Arduino 보드 (Uno, Nano 등)
* - MAX7219 8x8 LED Matrix 모듈
*
* 핀 연결:
* - VCC → 5V
* - GND → GND
* - DIN → 디지털 핀 11 (MOSI)
* - CLK → 디지털 핀 13 (SCK)
* - CS → 디지털 핀 10 (SS)
*
* 라이브러리:
* - LedControl
*/
#include <LedControl.h>
// 상수 정의
constexpr uint8_t MATRIX_WIDTH = 8;
constexpr uint8_t MATRIX_HEIGHT = 8;
constexpr uint8_t PIN_DIN = 11;
constexpr uint8_t PIN_CLK = 13;
constexpr uint8_t PIN_CS = 10;
constexpr uint8_t NUM_DEVICES = 1;
constexpr uint32_t DROP_INTERVAL = 100; // LED 떨어지는 속도 (ms)
// 사용자 설정 변수
constexpr uint8_t TAIL_LENGTH = 2; // 떨어지는 LED 꼬리 길이 (1~8)
constexpr uint8_t DROP_WIDTH = 1; // 한 번에 떨어지는 열 개수 (1~8)
constexpr uint8_t DROP_HEIGHT = 1; // 한 번에 이동하는 행 개수 (1~8)
// LED 매트릭스 객체
LedControl matrix = LedControl(PIN_DIN, PIN_CLK, PIN_CS, NUM_DEVICES);
// 물방울 상태 변수
uint32_t previous_millis = 0;
uint8_t current_column = 0; // 현재 시작 열
int8_t column_rows[8] = {-1, -1, -1, -1, -1, -1, -1, -1}; // 각 열의 현재 행 위치 (-1: 비활성)
void setup()
{
Serial.begin(115200);
while (!Serial && millis() < 3000)
{
// 시리얼 대기 (최대 3초)
}
Serial.println("=== Step 2: Waterfall Sequence Animation ===");
Serial.println("Matrix Size: 8x8");
Serial.println("Sequential drop pattern");
Serial.print("Tail Length: ");
Serial.println(TAIL_LENGTH);
Serial.print("Drop Width (Columns): ");
Serial.println(DROP_WIDTH);
Serial.print("Drop Height (Rows per step): ");
Serial.println(DROP_HEIGHT);
Serial.println();
// LED 매트릭스 초기화
matrix.shutdown(0, false);
matrix.setIntensity(0, 8);
matrix.clearDisplay(0);
Serial.println("Matrix initialized successfully!");
Serial.println("Starting waterfall animation...");
Serial.println();
}
void loop()
{
uint32_t current_millis = millis();
// 주기적으로 LED 한 칸씩 아래로 이동
if (current_millis - previous_millis >= DROP_INTERVAL)
{
previous_millis = current_millis;
// LED 위치 업데이트
update_waterdrop();
}
}
// 물방울 위치 업데이트 및 렌더링
void update_waterdrop()
{
// 화면 지우기
matrix.clearDisplay(0);
// 전체 매트릭스의 모든 열을 확인하여 렌더링
for (uint8_t col = 0; col < MATRIX_WIDTH; col++)
{
// 이 열이 시작할 시점인지 확인
if (col == 0)
{
// 첫 번째 열: 아직 시작 안했으면 시작
if (column_rows[col] == -1)
{
column_rows[col] = 0;
}
}
else
{
// 이후 열: 이전 열이 TAIL_LENGTH만큼 진행했는지 확인
if (column_rows[col - 1] >= (TAIL_LENGTH - 1) && column_rows[col] == -1)
{
column_rows[col] = 0;
}
}
// 이 열이 활성화되어 있고 아직 완료되지 않았으면 렌더링
if (column_rows[col] >= 0 && column_rows[col] < MATRIX_HEIGHT + TAIL_LENGTH)
{
// 꼬리 효과: 현재 위치부터 위로 TAIL_LENGTH 개의 LED 켜기
for (uint8_t i = 0; i < TAIL_LENGTH; i++)
{
int8_t tail_row = column_rows[col] - i;
// 유효한 행 범위인지 확인
if (tail_row >= 0 && tail_row < MATRIX_HEIGHT)
{
matrix.setLed(0, tail_row, col, true);
}
}
}
}
// 디버그 출력 (활성화된 열만)
Serial.print("Active columns: ");
bool has_active = false;
for (uint8_t col = 0; col < MATRIX_WIDTH; col++)
{
if (column_rows[col] >= 0 && column_rows[col] < MATRIX_HEIGHT + TAIL_LENGTH)
{
Serial.print("C");
Serial.print(col);
Serial.print(":");
Serial.print(column_rows[col]);
Serial.print(" ");
has_active = true;
}
}
if (!has_active)
{
Serial.print("None");
}
Serial.println();
// 각 활성화된 열의 행 위치를 DROP_HEIGHT만큼 이동
bool all_completed = true;
for (uint8_t col = 0; col < MATRIX_WIDTH; col++)
{
if (column_rows[col] >= 0 && column_rows[col] < MATRIX_HEIGHT + TAIL_LENGTH)
{
// 활성화되어 있고 아직 완전히 사라지지 않은 경우 이동
column_rows[col] += DROP_HEIGHT;
all_completed = false;
}
}
// 모든 열이 완료되었으면 다시 시작
if (all_completed)
{
// 모든 열 상태 초기화
for (uint8_t col = 0; col < MATRIX_WIDTH; col++)
{
column_rows[col] = -1;
}
Serial.println();
Serial.println("=== All columns completed! Restarting... ===");
Serial.println();
}
}