코드는 로봇에게 주는 행동 규칙이다
로봇은 스스로 생각하는 것처럼 보이지만, 실제로는 사람이 미리 정한 규칙에 따라 움직인다. 이 규칙을 적어 둔 것이 코드이다. 앞에서 살펴본 입력, 처리, 출력의 흐름을 코드로 바꾸면 다음과 같다.
입력: 센서값을 읽는다.
처리: 읽은 값을 조건에 따라 판단한다.
출력: LED를 켜거나, 모터를 돌리거나, 소리를 낸다.
예를 들어 장애물 회피 로봇은 이런 규칙을 가진다.
앞에 물체가 가까이 있으면 멈춘다.
앞이 비어 있으면 앞으로 간다.
이 문장을 아두이노 코드로 바꾸려면 몇 가지 기본 문법이 필요하다. 값을 저장하는 변수, 선택을 만드는 조건문, 같은 행동을 반복하는 반복문, 코드를 기능별로 나누는 함수가 그 도구이다.
아두이노 코드는 C/C++이라는 프로그래밍 언어를 바탕으로 한다. 이름은 어렵지만 처음부터 모든 것을 알 필요는 없다. 로봇을 움직이는 데 자주 쓰는 문법부터 익히면 된다.
AI 생성 이미지
변수: 값을 담아 두는 작은 상자
로봇은 센서값과 상태를 기억해야 한다. 앞에 장애물이 얼마나 가까운지, LED가 켜져 있는지, 모터가 움직이는 중인지 같은 정보가 필요하다. 이런 값을 담는 상자를 변수라고 한다.
int distance = 20;이 코드는 distance라는 이름의 상자에 20이라는 값을 넣는다는 뜻이다. 여기서 int는 정수를 저장하는 자료형이다. 자료형은 변수에 어떤 종류의 값을 넣을지 정하는 표시이다.
자주 쓰는 자료형은 다음과 같다.
| 자료형 | 담을 수 있는 값 | 예 |
|---|---|---|
int | 정수 | 10, 255, -3 |
float | 소수 | 3.14, 12.5 |
bool | 참 또는 거짓 | true, false |
char | 문자 하나 | 'A', 'L' |
로봇 코드에서는 int와 bool을 특히 많이 사용한다. 센서에서 읽은 숫자는 보통 int에 담고, 어떤 상태가 맞는지 아닌지는 bool에 담는다.
int sensorValue = 0;
bool obstacleDetected = false;= 기호는 수학에서처럼 "같다"라는 뜻이 아니다. 프로그래밍에서는 오른쪽 값을 왼쪽 변수에 넣는다는 뜻이다. 이것을 대입이라고 한다.
sensorValue = 450;
obstacleDetected = true;이렇게 변수에 값을 저장해 두면, 로봇은 그 값을 기준으로 다음 행동을 결정할 수 있다.
변수로 LED 밝기 바꾸기
아두이노의 디지털 핀은 HIGH 또는 LOW처럼 두 가지 상태를 다룬다. 하지만 일부 핀에서는 LED 밝기처럼 중간값이 있는 출력도 만들 수 있다. 이를 PWM이라고 한다. PWM은 전기를 아주 빠르게 켰다 껐다 하면서 평균 밝기를 조절하는 방식이다. 실제 전압을 연속적으로 부드럽게 바꾸는 것은 아니지만, LED나 모터 입장에서는 밝기나 속도가 달라지는 것처럼 보인다.
아두이노 우노에서는 보통 ~ 표시가 있는 디지털 핀에서 PWM을 사용할 수 있다. 예를 들어 9번 핀에 LED를 연결했다면 다음처럼 밝기를 바꿀 수 있다.
int ledPin = 9;
int brightness = 120;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
analogWrite(ledPin, brightness);
}analogWrite(ledPin, brightness);는 9번 핀에 brightness 값만큼의 PWM 출력을 보내라는 뜻이다. 밝기 값은 보통 0부터 255까지 사용한다.
0: 꺼짐255: 가장 밝음120: 중간 정도 밝기
여기서 중요한 점은 숫자를 코드 곳곳에 직접 쓰지 않고 변수에 담았다는 것이다. 나중에 밝기를 바꾸고 싶으면 brightness 값만 수정하면 된다.
if 조건문: 상황에 따라 다른 행동 선택하기
로봇은 항상 같은 행동만 하면 안 된다. 앞에 장애물이 있으면 멈추고, 없으면 앞으로 가야 한다. 어두우면 LED를 켜고, 밝으면 꺼야 한다. 이런 선택을 만들 때 if 조건문을 사용한다.
if (조건) {
조건이 맞을 때 실행할 코드
}예를 들어 장애물까지의 거리가 15cm보다 가까우면 LED를 켜는 코드는 다음과 같다.
int ledPin = 13;
int distance = 10;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
if (distance < 15) {
digitalWrite(ledPin, HIGH);
}
}distance < 15는 "distance 값이 15보다 작은가?"라는 질문이다. 이 질문의 답이 참이면 { } 안의 코드가 실행된다. 위 예제에서는 distance 값이 10으로 정해져 있으므로 LED가 켜진다.
하지만 거리가 멀 때 LED를 끄는 규칙도 필요하다. 이때는 else를 붙인다.
if (distance < 15) {
digitalWrite(ledPin, HIGH);
} else {
digitalWrite(ledPin, LOW);
}이제 규칙이 더 분명해졌다.
거리가 15보다 작으면 LED 켜기
그렇지 않으면 LED 끄기
이 구조가 바로 로봇 판단의 기본이다. 나중에 초음파 센서를 연결하면 distance 변수에는 실제 거리값이 들어간다. 모터 드라이버를 연결하면 LED 대신 모터 정지나 전진 명령을 넣을 수 있다.
비교 연산자: 조건을 만드는 기호
조건문에는 값을 비교하는 기호가 자주 나온다. 이런 기호를 비교 연산자라고 한다.
| 기호 | 뜻 | 예 |
|---|---|---|
> | 크다 | distance > 20 |
< | 작다 | distance < 15 |
>= | 크거나 같다 | speed >= 100 |
<= | 작거나 같다 | light <= 300 |
== | 같다 | buttonState == HIGH |
!= | 같지 않다 | mode != 0 |
주의할 점은 =와 ==의 차이이다.
speed = 100; // speed에 100을 넣는다
speed == 100; // speed가 100인지 비교한다초보자가 자주 하는 실수는 조건문 안에서 == 대신 =를 쓰는 것이다. if (speed = 100)처럼 쓰면 비교가 아니라 대입이 되어 의도와 다른 동작을 만들 수 있다.
for 반복문: 정해진 횟수만큼 반복하기
로봇은 같은 행동을 여러 번 반복해야 할 때가 많다. LED를 5번 깜빡이거나, 모터 속도를 조금씩 올리거나, 센서값을 여러 번 읽어 평균을 낼 수 있다.
정해진 횟수만큼 반복할 때는 for 반복문을 사용한다.
for (int i = 0; i < 5; i++) {
반복할 코드
}이 코드는 { } 안의 코드를 5번 실행한다. i는 반복 횟수를 세는 변수이다.
LED를 5번 깜빡이는 예를 보자.
int ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT);
for (int i = 0; i < 5; i++) {
digitalWrite(ledPin, HIGH);
delay(300);
digitalWrite(ledPin, LOW);
delay(300);
}
}
void loop() {
}이 코드는 아두이노가 시작될 때 LED를 5번 깜빡인 뒤 멈춘다. for문이 setup() 안에 있기 때문이다. setup()은 전원이 켜지거나 리셋될 때 한 번만 실행된다.
반대로 loop() 안에 넣으면 어떻게 될까?
void loop() {
for (int i = 0; i < 5; i++) {
digitalWrite(ledPin, HIGH);
delay(300);
digitalWrite(ledPin, LOW);
delay(300);
}
}loop() 자체가 계속 반복되므로, LED는 5번 깜빡이는 묶음을 계속 반복한다. 아두이노 코드에서는 "어디에 반복문을 넣는가"도 중요하다.
while 반복문: 조건이 맞는 동안 반복하기
for문이 횟수를 정해 놓고 반복한다면, while문은 조건이 참인 동안 계속 반복한다.
while (조건) {
반복할 코드
}예를 들어 버튼을 누르고 있는 동안 LED를 계속 켜는 구조는 다음과 비슷하다.
while (buttonState == HIGH) {
digitalWrite(ledPin, HIGH);
}다만 while문은 조심해서 사용해야 한다. 조건이 계속 참이면 아두이노가 그 안에 갇혀 다른 일을 하지 못할 수 있다. 위 예제도 실제 코드로 쓰려면 버튼 상태를 반복문 안에서 다시 읽어 주어야 한다. 그렇지 않으면 버튼에서 손을 떼도 buttonState 값이 바뀌지 않아 반복문이 끝나지 않을 수 있다.
센서값을 사용할 때도 마찬가지이다. 센서값을 새로 읽지 않는 상태에서 while문에 들어가면 조건이 바뀌지 않아 끝나지 않을 수 있다.
while (distance < 15) {
digitalWrite(ledPin, HIGH);
distance = readDistance();
}위 코드처럼 반복문 안에서 조건에 쓰는 값을 다시 읽어 주어야 한다. 그래야 장애물이 사라졌을 때 distance 값이 바뀌고, while문을 빠져나올 수 있다.
처음에는 while문보다 if문과 loop()를 함께 쓰는 방식이 더 안전하다. loop()가 이미 계속 반복되기 때문이다.
Serial Monitor: 로봇의 속마음 보기
코드를 작성하다 보면 이런 문제가 생긴다.
LED가 왜 안 켜지는지 모르겠다.
센서값이 제대로 들어오는지 모르겠다.
조건문이 실행되는지 아닌지 확인하고 싶다.
이때 사용하는 도구가 Serial Monitor이다. Serial Monitor는 아두이노가 컴퓨터로 보내는 글자와 숫자를 보여 주는 창이다. 로봇의 내부 상태를 화면에 출력해 확인할 수 있다.
다음 코드를 업로드해 보자.
int sensorValue = 0;
void setup() {
Serial.begin(9600);
}
void loop() {
sensorValue = analogRead(A0);
Serial.print("sensorValue: ");
Serial.println(sensorValue);
delay(500);
}Serial.begin(9600);은 아두이노와 컴퓨터가 데이터를 주고받을 속도를 정하는 코드이다. 여기서 9600은 초보 실습에서 가장 흔히 쓰는 기본 속도라고 이해하면 된다.
Serial.print()는 내용을 출력한 뒤 줄을 바꾸지 않는다. Serial.println()은 출력한 뒤 줄을 바꾼다.
Arduino IDE에서 Serial Monitor를 열면 sensorValue 값이 계속 표시된다. 아직 A0에 센서를 연결하지 않았다면 값이 흔들릴 수 있다. 핀이 공중에 떠 있는 상태라 주변 전기 신호의 영향을 받기 때문이다. 다음 장에서 센서를 연결하면 이 값이 빛, 거리, 선 감지 같은 실제 정보로 바뀐다.
Serial Monitor는 디버깅에 꼭 필요하다. 디버깅은 코드나 회로에서 문제가 생겼을 때 원인을 찾아 고치는 과정이다. 로봇이 예상과 다르게 움직이면 먼저 변수값을 출력해 보자. 로봇이 무엇을 보고, 어떤 판단을 하는지 확인할 수 있다.
함수: 코드를 기능별로 정리하기
코드가 길어지면 어디서 무엇을 하는지 찾기 어려워진다. LED를 켜는 코드, 센서를 읽는 코드, 모터를 움직이는 코드가 한곳에 섞이면 작은 수정도 힘들어진다. 이때 함수를 사용한다.
함수는 자주 쓰는 코드를 이름 붙여 묶어 둔 것이다. 예를 들어 LED를 한 번 깜빡이는 코드를 함수로 만들 수 있다.
int ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
blinkLed();
}
void blinkLed() {
digitalWrite(ledPin, HIGH);
delay(300);
digitalWrite(ledPin, LOW);
delay(300);
}blinkLed();라고 쓰면 아래에 만들어 둔 blinkLed() 함수가 실행된다. 함수 이름은 그 기능을 알 수 있게 짓는 것이 좋다.
나중에 로봇 코드에서는 다음처럼 기능을 나눌 수 있다.
int readDistance() {
int distance = 20;
return distance;
}
void moveForward() {
Serial.println("move forward");
}
void stopRobot() {
Serial.println("stop");
}readDistance() 함수는 거리값을 읽어 돌려주는 역할을 한다. 여기서는 아직 실제 센서를 연결하지 않았으므로 20이라는 임시 값을 돌려준다. 다음 장에서 센서를 배우면 이 안의 코드를 실제 센서 읽기 코드로 바꾸면 된다.
moveForward()와 stopRobot()은 지금은 Serial Monitor에 글자를 출력할 뿐이다. 하지만 모터 드라이버를 배운 뒤에는 이 함수 안에 모터 제어 코드를 넣을 수 있다. 이렇게 해 두면 전체 판단 구조는 크게 바꾸지 않아도 된다.
작은 로봇 판단 코드 만들기
이제 변수, 조건문, 반복문, Serial Monitor, 함수를 함께 사용해 보자. 아직 모터를 직접 연결하지 않고, Serial Monitor와 내장 LED로 로봇의 판단을 확인하는 코드이다.
int ledPin = 13;
int distance = 0;
int dangerDistance = 15;
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
for (int i = 0; i < 3; i++) {
blinkLed();
}
}
void loop() {
distance = readDistance();
Serial.print("distance: ");
Serial.println(distance);
if (distance < dangerDistance) {
stopRobot();
} else {
moveForward();
}
delay(500);
}
int readDistance() {
int value = analogRead(A0);
int fakeDistance = map(value, 0, 1023, 5, 50);
return fakeDistance;
}
void moveForward() {
digitalWrite(ledPin, LOW);
Serial.println("앞이 비어 있습니다. 앞으로 이동합니다.");
}
void stopRobot() {
digitalWrite(ledPin, HIGH);
Serial.println("장애물이 가깝습니다. 멈춥니다.");
}
void blinkLed() {
digitalWrite(ledPin, HIGH);
delay(200);
digitalWrite(ledPin, LOW);
delay(200);
}이 코드는 실제 바퀴를 움직이지는 않는다. 대신 로봇의 판단 구조를 미리 연습한다.
setup()에서 LED 핀과 Serial Monitor를 준비한다.시작 표시로 LED를 3번 깜빡인다.
loop()에서 거리값을 읽는다.거리값을 Serial Monitor에 출력한다.
가까우면 멈춤 메시지를 출력하고 LED를 켠다.
멀면 전진 메시지를 출력하고 LED를 끈다.
여기서 map() 함수는 한 범위의 숫자를 다른 범위로 바꾸는 함수이다. analogRead(A0)는 0부터 1023 사이의 값을 읽는다. 이 값을 5부터 50 사이의 거리처럼 보이도록 바꾸었다. 실제 초음파 센서의 정확한 거리 측정은 다음 장 이후에 다룬다.
A0에 아무것도 연결하지 않은 상태라면 거리값이 계속 흔들릴 수 있다. 지금 예제의 목적은 정확한 거리를 재는 것이 아니라, "값을 읽고, 판단하고, 결과를 출력하는 흐름"을 연습하는 데 있다.
오류가 날 때 확인할 것
프로그래밍에서 오류는 자연스러운 과정이다. 오류가 났다는 것은 아두이노가 "어디를 이해하지 못했는지" 알려 주는 것이다. 다음 순서로 확인해 보자.
문장 끝에 세미콜론
;을 빠뜨리지 않았는가?{와}의 개수가 서로 맞는가?변수 이름을 선언할 때와 사용할 때 똑같이 썼는가?
=와==를 구분했는가?Serial.begin(9600);을setup()안에 넣었는가?Serial Monitor의 속도가 코드의
9600과 같은가?핀 번호가 회로 연결과 일치하는가?
특히 변수 이름은 대문자와 소문자를 구분한다. sensorValue와 SensorValue는 서로 다른 이름이다.
이번 장에서 익힌 것
이번 장에서는 로봇의 행동 규칙을 코드로 표현하는 기본 방법을 배웠다. 변수는 센서값과 상태를 저장하는 상자이고, 조건문은 상황에 따라 다른 행동을 고르는 도구이다. 반복문은 같은 일을 여러 번 실행하게 해 주며, Serial Monitor는 로봇의 내부 상태를 확인하는 창이다. 함수는 코드를 기능별로 나누어 더 읽기 쉽고 고치기 쉽게 만든다.
앞으로 센서와 모터를 연결하면 코드의 의미가 더 분명해진다. 센서값은 변수에 저장되고, 조건문은 그 값을 판단하며, 함수는 모터 동작을 실행한다. 결국 로봇의 움직임은 복잡한 마법이 아니라 작은 규칙들이 차례로 이어진 결과이다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.