1. 개요
가정용 솜사탕 기계에 로봇 타워를 장착하여 자동으로 솜사탕을 만들어 주는 솜사탕 기계입니다.
아이들의 재미을 위하여 리모콘으로 로봇타워를 제어할 수 있고, 청소가 편해 유지보수도 쉽습니다.
코딩으로 직접 제어 학습도 가능하여 중,고등학교의 동아리 활동으로도 매우 좋습니다.

가정용 솜사탕 기계에 로봇 타워를 장착하여 자동으로 솜사탕을 만들어 주는 솜사탕 기계입니다.
아이들의 재미을 위하여 리모콘으로 로봇타워를 제어할 수 있고, 청소가 편해 유지보수도 쉽습니다.
코딩으로 직접 제어 학습도 가능하여 중,고등학교의 동아리 활동으로도 매우 좋습니다.
1. 영상 보기
2. 기본 사용법 간단 설명
1) 쿠션에 솜사탕로봇을 올려놓은 뒤 안전보호막을 씌웁니다.







1. 영상 보기
원형트레이는 쉽게 분리되어 청소할 수 있습니다.
1. 솜사탕 제작 후 원형트레이에 붙은 것들은 빨대 등으로 톡톡 치면 떨어집니다.
2. 원형트레이 분리
: 시계반대 방향으로 돌리면 빠집니다. 사진처럼 자세히 보시면 고정하는 요철이 보입니다.^^
3. 따뜻한 물로 세척
: 물에 쉽게 녹습니다.^^
4. 재조립
롱노우즈 등 장비를 이용하여 원형 회전판을 분리할 수 있습니다.
오랫동안 청소를 안하시면 설탕 녹은것이 붙어 있을 수 있으니 살짝 힘을 주셔야 될 수도 있습니다.
솜사탕 만들 때 설탕 잔여물 없이 전부 설탕실로 날아갈 때까지 기다리면 원형 회전판이 시커멓게 변하는 것을 줄일 수 있습니다.
1. 꼭 충분한 시간을 두어 식힌 뒤에 작업하세요.
2. 원형회전판을 손으로 잡고, 가운데 너트를 롱노우즈 등 공구를 이용하여 반시계방향으로 돌려 빼냅니다.
3. 따뜻한 물로 청소합니다.
4. 물기를 제거한 뒤 재 조립합니다.
조립 전 확인사항
조립을 시작하기 전에 모든 서보모터를 반드시 90도 위치로 맞춰야합니다.
서보모터의 시작 각도가 서로 다르면,
같은 명령을 주더라도 움직임이 달라질 수 있습니다.
따라서 모든 서보모터를 동일한 90도 위치로 맞춘 뒤 조립을 진행해야 합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <Servo.h> Servo servo1; Servo servo2; void setup() { servo1.attach(3); // 서보모터 1 servo2.attach(4); // 서보모터 2 servo1.write(90); servo2.write(90); } void loop() { } | cs |
*알아두기*
핑크캔디 봇에 사용하는 서보모터는 아두이노 우노에 바로 연결하기에 적합하지 않은 서보모터입니다.
그 이유는 사용되는 서보모터가 동작할 때 순간적으로 많은 전류를 사용하기 때문입니다. (90도로 맞추는건 괜찮습니다.)
아두이노 우노의 전원으로 서보모터를 계속 구동할 경우 다음과 같은 문제가 발생할 수 있습니다.
· 아두이노가 갑자기 리셋됨
· USB 연결이 끊어짐
· 보드 및 전원부 발열 위험
해결 방법
이러한 문제를 해결하기 위해 PCA9685 서보 드라이버 모듈을 사용합니다.
PCA9685는?

서보 제어 신호를 안정적으로 출력하고 서보 전원을 외부 어댑터로 분리하여 공급할 수 있어,
아두이노를 보호하면서도 서보모터를 힘 있고 안정적으로 제어할 수 있습니다.
프로그램에 대한 자세한 설명은 5.5.서보모터 페이지에서 확인하실 수 있습니다.
1. 조립 매뉴얼
크게 1, 2의 2단계로 나누고 세부로 각각 4개와 6개로 나누어져 있습니다. 각각의 단계별로 조립영상을 보실 수 있습니다.
2. 조립 영상
2.1. DC모터 회전부 조립 영상
2.2. DC모터와 서보모터의 조립
2.3. 로봇암 서보혼 조립
2.4. 중심 서보모터의 조립
2.5. 아날로그 스위치, LED, DC모터 드라이버 조립
2.8. 몸체와 로봇암의 조립
2.9. 몸체 조립
| 장치 | 연결 위치 |
|---|---|
| 아날로그 키패드 | A0 |
| LED 빨강 | D9 |
| LED 노랑 | D10 |
| LED 녹색 | D11 |
| DC 모터 A1A | D5 |
| DC 모터 A1B | D6 |
| PCA9685 SDA | SDA |
| PCA9685 SCL | SCL |
| 서보모터 1 | PCA9685 채널 0 |
| 서보모터 2 | PCA9685 채널 1 |
1. 목표
-. 아두이노 우노에 연결된 장치들의 핀을 설정하여 초기 상태를 정의
-. 신호등 LED, DC모터 드라이버, 서보모터 2개의 핀을 설정
2. 핀 연결
| 장치 | 연결 위치 |
|---|---|
| 아날로그 키패드 | A0 |
| LED 빨강 | D9 |
| LED 노랑 | D10 |
| LED 녹색 | D11 |
| DC 모터 A1A | D5 |
| DC 모터 A1B | D6 |
| PCA9685 SDA | SDA |
| PCA9685 SCL | SCL |
| 서보모터 1 | PCA9685 채널 0 |
| 서보모터 2 | PCA9685 채널 1 |
1. 목표
-. 신호등 LED를 활용하여 상태를 표시합니다.
-. 실습 : 1초 단위로 색상이 바뀌는 LED 동작 구현
2. 핀연결
-. 빨강 9번, 노랑 10번, 녹색 11번에 연결
3. 코드 작성하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | // LED 핀 설정 #define LED_R 9 // 빨강 LED 9번핀 연결 #define LED_Y 10 // 노랑 LED 10번핀 연결 #define LED_G 11 // 녹색 LED 11번핀 연결 void setup() { // LED 핀을 출력 모드로 설정 pinMode(LED_R, OUTPUT); pinMode(LED_Y, OUTPUT); pinMode(LED_G, OUTPUT); } void loop() { // 빨강 LED 켜기 (1초) digitalWrite(LED_R, HIGH); delay(1000); digitalWrite(LED_R, LOW); // 노랑 LED 켜기 (1초) digitalWrite(LED_Y, HIGH); delay(1000); digitalWrite(LED_Y, LOW); // 녹색 LED 켜기 (1초) digitalWrite(LED_G, HIGH); delay(1000); digitalWrite(LED_G, LOW); } | cs |
4. 실습결과
5. 핑크캔디봇에 응용
-. 전원켜짐 : 빨강
-. 작동 중 : 노랑
-. 작업 완료 : 녹색
1. 목표
-. 아날로그 키패드를 사용하여 각각의 버튼 입력 값을 확인
-. 5개의 버튼을 감지하여 각각의 값을 측정하여, 특정 범위를 기반으로 이름을 정의
2. 핀 연결
-. A0 핀에 연결
3. 아날로그 입력값 확인하기
-. 시리얼 통신(Serial.begin(9600);)을 시작하고, 각각의 키를 눌렀을 때 입력값을 시리얼 모니터로 출력해봅니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //아날로그 키패드 핀 설정 #define KEYPAD_PIN A0 void setup() { Serial.begin(9600); // 시리얼 통신 시작 } void loop() { int keyValue = analogRead(KEYPAD_PIN); // 키패드 값 읽기 Serial.println(keyValue); // 시리얼 모니터에 출력 delay(100); // 0.1초 간격으로 측정 } | cs |
| 키 번호 | 입력값 |
|---|---|
| 누르지 않음 | 1023 |
| SW_1 | 0 |
| SW_2 | 144 ~ 145 |
| SW_3 | 330 ~ 331 |
| SW_4 | 507 ~ 508 |
| SW_5 | 741 ~ 742 |
5. 키 실습
-. 2번키(빨강색키)와 5번키(흰색키)로 빨강 LED ON/OFF 해보기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | //아날로그 키패드 핀 설정 #define KEYPAD_PIN A0 // 아날로그 키패드별 최대/최소 입력값 (5를 여유값으로) #define SW_1_MIN 0 #define SW_1_MAX 10 #define SW_2_MIN 140 #define SW_2_MAX 150 #define SW_3_MIN 325 #define SW_3_MAX 336 #define SW_4_MIN 502 #define SW_4_MAX 513 #define SW_5_MIN 736 #define SW_5_MAX 747 // 각 버튼을 눌렀을 때 해당 버튼이 감지되면 true 값을 반환 bool SW_1() { int value = analogRead(KEYPAD_PIN); return (value >= SW_1_MIN && value <= SW_1_MAX); } bool SW_2() { int value = analogRead(KEYPAD_PIN); return (value >= SW_2_MIN && value <= SW_2_MAX); } bool SW_3() { int value = analogRead(KEYPAD_PIN); return (value >= SW_3_MIN && value <= SW_3_MAX); } bool SW_4() { int value = analogRead(KEYPAD_PIN); return (value >= SW_4_MIN && value <= SW_4_MAX); } bool SW_5() { int value = analogRead(KEYPAD_PIN); return (value >= SW_5_MIN && value <= SW_5_MAX); } // LED 핀 설정 #define LED_R 9 // 빨강 LED 9번핀 연결 #define LED_Y 10 // 노랑 LED 10번핀 연결 #define LED_G 11 // 녹색 LED 11번핀 연결 void setup() { Serial.begin(9600); // 시리얼 통신 시작 // LED 핀을 출력 모드로 설정 pinMode(LED_R, OUTPUT); pinMode(LED_Y, OUTPUT); pinMode(LED_G, OUTPUT); } void loop() { int keyValue = analogRead(KEYPAD_PIN); // 키패드 값 읽기 Serial.println(keyValue); // 시리얼 모니터에 출력 delay(100); // 0.1초 간격으로 측정 if (SW_2()) { digitalWrite(LED_R, HIGH); // SW_2을 누르면 빨강 LED 켜기 } else if (SW_5()) { digitalWrite(LED_R, LOW); // SW_5를 누르면 빨강 LED 끄기 } } | cs |
6. 실습 결과
-. 노란색과 녹색도 해볼까요?^^
1. 목표
-. L9110S DC모터 드라이버를 이용하여 DC 모터의 방향과 속도를 제어할 수 있다.
2. 핀 연결
-. A1A - D5번핀, A1B - D6번 핀
-. DC 모터 1개를 사용하여 나머지는 연결하지 않습니다.
3. 핀 설정
-. DC모터가 핀 설정을 하지 않으면 회전할 수 있습니다.
- 아두이노가 부팅될 때, 디지털 핀은 기본적으로 입력 모드(INPUT) 상태가 됨. (이때, 잠시 DC모터가 오동작으로 회전할 수 있습니다.)
- 이 경우 핀 상태가 불확정(FLOAT 상태)가 되어, DC 모터 드라이버가 오작동할 가능성이 있음.
- 따라서, 핀을 반드시 OUTPUT 모드로 설정하고 초기 값을 LOW로 설정해야 불필요한 모터 회전을 방지할 수 있음.
4. 모터 제어 방법
-. L9110S는 H-브릿지 방식(4개의 트랜지스터로 구성되어 DC모터의 방향을 제어)으로 두 개의 입력 핀을 HIGH 또는 LOW로 설정하여 모터의 회전방향을 결정합니다.
| A1A | A1B | 결과 |
|---|---|---|
| HIGH | LOW | 정방향 회전 |
| LOW | HIGH | 역방향 회전 |
| LOW | LOW | 정지 |
| HIGH | HIGH | 급제동 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | // DC 모터 드라이버 핀 설정 #define MOTOR_A1A 5 // 모터 방향 제어 핀 1 5번핀 연결 #define MOTOR_A1B 6 // 모터 방향 제어 핀 2 6번핀 연결 void setup() { // DC 모터 핀을 출력 모드로 설정 pinMode(MOTOR_A1A, OUTPUT); pinMode(MOTOR_A1B, OUTPUT); // DC 모터 초기 상태 설정 (멈춤) digitalWrite(MOTOR_A1A, LOW); digitalWrite(MOTOR_A1B, LOW); } void loop() { // 정방향 회전 (1초) digitalWrite(MOTOR_A1A, HIGH); digitalWrite(MOTOR_A1B, LOW); delay(1000); // 정지 (1초) digitalWrite(MOTOR_A1A, LOW); digitalWrite(MOTOR_A1B, LOW); delay(1000); // 역방향 회전 (1초) digitalWrite(MOTOR_A1A, LOW); digitalWrite(MOTOR_A1B, HIGH); delay(1000); // 정지 (1초) digitalWrite(MOTOR_A1A, LOW); digitalWrite(MOTOR_A1B, LOW); delay(1000); } | cs |
2) 정/역회전 실습 결과
3) DC모터 속도 조절
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | // DC 모터 드라이버 핀 설정 #define MOTOR_A1A 5 // 모터 방향 제어 핀 1 5번핀 연결 #define MOTOR_A1B 6 // 모터 방향 제어 핀 2 6번핀 연결 void setup() { // DC 모터 핀을 출력 모드로 설정 pinMode(MOTOR_A1A, OUTPUT); pinMode(MOTOR_A1B, OUTPUT); // DC 모터 초기 상태 설정 (멈춤) digitalWrite(MOTOR_A1A, LOW); digitalWrite(MOTOR_A1B, LOW); } void loop() { //1초마다 50씩 속도 증가 for (int speed = 100; speed <= 250; speed += 50) { analogWrite(MOTOR_A1A, speed); digitalWrite(MOTOR_A1B, LOW); delay(1000); } // 정지 (1초) digitalWrite(MOTOR_A1A, LOW); digitalWrite(MOTOR_A1B, LOW); delay(1000); } | cs |
4) DC모터 속도 조절 실습 결과
5) 스위치로 DC모터 제어해보기
-. 1번스위치(노랑)으로 정회전, 4번스위치(녹색)으로 역회전, 5번스위치(흰색)으로 정지를 해봅니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | //아날로그 키패드 핀 설정 #define KEYPAD_PIN A0 // 아날로그 키패드별 최대/최소 입력값 (5를 여유값으로) #define SW_1_MIN 0 #define SW_1_MAX 10 #define SW_2_MIN 140 #define SW_2_MAX 150 #define SW_3_MIN 325 #define SW_3_MAX 336 #define SW_4_MIN 502 #define SW_4_MAX 513 #define SW_5_MIN 736 #define SW_5_MAX 747 // 각 버튼을 눌렀을 때 해당 버튼이 감지되면 true 값을 반환 bool SW_1() { int value = analogRead(KEYPAD_PIN); return (value >= SW_1_MIN && value <= SW_1_MAX); } bool SW_2() { int value = analogRead(KEYPAD_PIN); return (value >= SW_2_MIN && value <= SW_2_MAX); } bool SW_3() { int value = analogRead(KEYPAD_PIN); return (value >= SW_3_MIN && value <= SW_3_MAX); } bool SW_4() { int value = analogRead(KEYPAD_PIN); return (value >= SW_4_MIN && value <= SW_4_MAX); } bool SW_5() { int value = analogRead(KEYPAD_PIN); return (value >= SW_5_MIN && value <= SW_5_MAX); } // DC 모터 드라이버 핀 설정 #define MOTOR_A1A 5 // 모터 방향 제어 핀 1 5번핀 연결 #define MOTOR_A1B 6 // 모터 방향 제어 핀 2 6번핀 연결 void setup() { // DC 모터 핀을 출력 모드로 설정 pinMode(MOTOR_A1A, OUTPUT); pinMode(MOTOR_A1B, OUTPUT); // DC 모터 초기 상태 설정 (멈춤) digitalWrite(MOTOR_A1A, LOW); digitalWrite(MOTOR_A1B, LOW); } void loop() { if (SW_1()) { // 정방향 회전 digitalWrite(MOTOR_A1A, HIGH); digitalWrite(MOTOR_A1B, LOW); } else if (SW_4()) { // 역방향 회전 digitalWrite(MOTOR_A1A, LOW); digitalWrite(MOTOR_A1B, HIGH); } else if (SW_5()) { // 모터 정지 digitalWrite(MOTOR_A1A, LOW); digitalWrite(MOTOR_A1B, LOW); } } | cs |
PCA9685를 사용하기 위해서는
아두이노 IDE에 Adafruit PWM Servo Driver 라이브러리를 설치해야 합니다.
PCA9685 라이브러리 설치 방법
1. 아두이노 IDE를 실행합니다.
2. 왼쪽 메뉴에서 라이브러리 매니저를 클릭합니다.

3. 검색창에 Adafruit PWM Servo Driver를 입력한 후 설치(Install) 버튼을 클릭합니다.

4. 아래와 같은 창이 나타나면 모두 설치(Install All) 를 눌러 주세요.

5. 라이브러리 목록에 설치됨으로 표시되면 정상적으로 설치가 완료된 것입니다.

전용 서보 라이브러리 만들기
라이브러리 설치가 완료되면, 이제 핑크캔디봇 서보모터만을 위한 전용 라이브러리를 만듭니다.
1. 아두이노 IDE 오른쪽 상단의 점 3개(⋮) 를 클릭합니다.

2. 새 탭(New Tab) 을 선택합니다.
(단축키 Ctrl + Shift + N 을 사용해도 됩니다.)

3. 새로 만든 파일의 이름을 CottonServo.h 로 입력하고 확인합니다.

4. 이 탭에서는 핑크캔디봇 서보모터를 제어하기 위한 코드만 작성합니다.

이렇게 전용 라이브러리를 분리하면 코드가 깔끔해지고, 프로젝트에서 재사용하기 편리합니다.
솜사탕 로봇 서보 제어 라이브러리(CottonServo.h)
아래는 솜사탕 로봇의 팔과 회전부 모터를 정밀하게 제어하기 위한 전용 라이브러리 코드와 상세 설명입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | /* CottonServo.h - 솜사탕 로봇 전용 라이브러리 */ #ifndef COTTON_SERVO_H //정의되어 있지 않다면 #define COTTON_SERVO_H // 정의해서 파일 읽기 #include <Wire.h> #include <Adafruit_PWMServoDriver.h> static Adafruit_PWMServoDriver _pwm = Adafruit_PWMServoDriver(); // 아두이노 표준 서보 설정값 (50Hz) #define _USMIN 544 // 0도 #define _USMAX 2400 //180도 #define _SERVO_FREQ 50 // 서보 주파수(50Hz) // 서보 모터 제어 void initServo() { _pwm.begin(); _pwm.setOscillatorFrequency(27000000); // 모터가 정확한 각도에서 딱 멈추게 기준 설정 _pwm.setPWMFreq(_SERVO_FREQ); // } // 기본 이동 (즉시 이동) void setAngle(int channel, int angle) { if (angle < 0) angle = 0; // 최소값 제한 if (angle > 180) angle = 180; // 최대값 제한 //각도를 신호 길이로 변환 후 전송 int microseconds = map(angle, 0, 180, _USMIN, _USMAX); _pwm.writeMicroseconds(channel, microseconds); } #endif | cs |
서보모터 테스트 안내

우리가 만든 CottonServo.h 라이브러리를 사용하기 위해 아래와 같이 라이브러리를 불러옵니다.
서보모터 번호 지정 방법
서보모터 번호는 두 가지 방법으로 지정할 수 있습니다.
1. int로 지정하는 방법
코드에서 값을 바꿀 수 있고 디버깅이나 계산이 필요한 경우에 유용합니다.
2. #define으로 지정하는 방법
고정된 값으로 사용되며 코드가 짧고 빠르게 읽히는 장점이 있습니다.
핑크캔디봇 서보모터의 채널 번호는 고정되어 있으므로 #define 방식을 사용하는 것을 권장합니다.
1 2 3 4 5 6 7 8 9 10 11 12 | #include "CottonServo.h" #define SERVO_BODY 0 // 몸체에 연결된 서보 #define SERVO_ARM 1 // DC모터와 연결된 서보 void setup() { } void loop() { } | cs |
CottonServo.h에는 다음과 같은 함수가 있습니다.
· channel : 어떤 서보모터를 움직일지 정하는 번호
· angle : 서보모터가 움직일 각도
서보모터 동작 범위 안내
알맞게 조립된 상태라면 서보모터의 동작 범위는 다음과 같습니다.
· SERVO_BODY : 60° ~ 150°
· SERVO_ARM : 0° ~ 90°
* 조립 상태에 따라 동작 범위는 달라질 수 있습니다.
시작 위치 설정
프로그램이 시작되면 두 서보모터를 기본 위치인 90도로 맞춰줍니다.
이를 통해 서보모터가 올바른 기준 위치에서 시작하도록 합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include "CottonServo.h" #define SERVO_BODY 0 // 몸체에 연결된 서보 #define SERVO_ARM 1 // DC모터와 연결된 서보 void setup() { initServo(); // 시작 위치 90도 setAngle(SERVO_BODY, 90); delay(2000); setAngle(SERVO_ARM, 90); delay(2000); } void loop() { } | cs |
1. SERVO_BODY 동작 테스트
먼저 SERVO_BODY만 움직여 보며 각도 변화에 따른 동작을 확인합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include "CottonServo.h" #define SERVO_BODY 0 // 몸체에 연결된 서보 #define SERVO_ARM 1 // DC모터와 연결된 서보 void setup() { initServo(); // 시작 위치 90도 setAngle(SERVO_BODY, 90); delay(2000); setAngle(SERVO_ARM, 90); delay(2000); } void loop() { // 몸체에 연결된 서보 테스트 setAngle(SERVO_BODY, 60); delay(2000); setAngle(SERVO_BODY, 90); delay(2000); setAngle(SERVO_BODY, 120); delay(2000); setAngle(SERVO_BODY, 150); delay(2000); setAngle(SERVO_BODY, 120); delay(2000); setAngle(SERVO_BODY, 90); delay(2000); } | cs |
2. SERVO_ARM 동작 테스트
SERVO_BODY의 동작을 확인했다면, 이제 SERVO_ARM를 움직여 봅니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include "CottonServo.h" #define SERVO_BODY 0 // 몸체에 연결된 서보 #define SERVO_ARM 1 // DC모터와 연결된 서보 void setup() { initServo(); // 시작 위치 90도 setAngle(SERVO_BODY, 90); delay(2000); setAngle(SERVO_ARM, 90); delay(2000); } void loop() { // DC모터와 연결된 서보 테스트 setAngle(SERVO_ARM, 45); delay(2000); setAngle(SERVO_ARM, 0); delay(2000); setAngle(SERVO_ARM, 45); delay(2000); setAngle(SERVO_ARM, 90); delay(2000); } | cs |
1. 목표
-. 서보모터의 각도를 제어할 수 있다.
-. 부드럽게 움직이는 서보 명령 함수를 만들 수 있다.
2. 서보모터 각도 제어 실습
1) 기본 각도 제어 실습
-. SERVO_ARM의 각도를 90도와 0도로 2초 간격으로 움직여 봅니다.
-. 동작의 모습을 관찰합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #include "CottonServo.h" #define SERVO_BODY 0 // 몸체에 연결된 서보 #define SERVO_ARM 1 // DC모터와 연결된 서보 void setup() { initServo(); // 시작 위치 90도 setAngle(SERVO_BODY, 90); delay(2000); setAngle(SERVO_ARM, 90); delay(2000); } void loop() { // DC모터와 연결된 서보 테스트 setAngle(SERVO_ARM, 90); delay(2000); setAngle(SERVO_ARM, 00); delay(2000); } | cs |
2) 부드럽게 각도 제어 실습
서보모터를 부드럽게 움직이게 하려면 어떻게 해야 할까요?
서보모터는 기본 설정으로 1~2초 안에 목표 각도로 바로 이동하기 때문에 움직임이 갑작스럽게 느껴집니다.
그렇다면, 어디를 수정해야 서보모터가 천천히 움직일까요?
이를 해결하기 위해
CottonServo.h 파일에서 서보모터를 부드럽게 움직이는 함수인 void moveSlow() 를 아래와 같이 만들어 봅니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #ifndef COTTON_SERVO_H #define COTTON_SERVO_H #include <Wire.h> #include <Adafruit_PWMServoDriver.h> static Adafruit_PWMServoDriver _pwm = Adafruit_PWMServoDriver(); #define _USMIN 544 #define _USMAX 2400 #define _SERVO_FREQ 50 void initServo() { _pwm.begin(); _pwm.setOscillatorFrequency(27000000); _pwm.setPWMFreq(_SERVO_FREQ); } // 기본 이동 (즉시 이동) void setAngle(int channel, int angle) { if (angle < 0) angle = 0; if (angle > 180) angle = 180; int microseconds = map(angle, 0, 180, _USMIN, _USMAX); _pwm.writeMicroseconds(channel, microseconds); } // 서보모터를 부드럽게 움직이는 함수 // 사용법: moveSlow(채널, 시작각도, 목표각도, 속도); // 속도: 숫자가 클수록 느림 (보통 10~20 추천) void moveSlow(int channel, int startAngle, int targetAngle, int speedDelay) { if (startAngle < targetAngle) { // 목표 각도가 더 클 때 (0° → 180° 방향) for (int i = startAngle; i <= targetAngle; i++) { setAngle(channel, i); // 1도씩 증가 delay(speedDelay); // 지연시간만큼 대기 } } else { // 목표 각도가 더 작을 때 (180° → 0° 방향) for (int i = startAngle; i >= targetAngle; i--) { setAngle(channel, i); // 1도씩 감소 delay(speedDelay); // 지연시간만큼 대기 } } } #endif | cs |
기본 코드 작성 화면으로 돌아와 아래 두 가지를 추가합니다.
SPEED 값으로 서보모터의 이동 속도를 조절합니다.
값이 작을수록 더 빠르게 움직입니다.
부드럽지 않으면 10 → 5~8로 조절해 보세요.
currentAngle은 서보모터의 현재 위치를 기억하기 위한 변수입니다.
부드러운 이동을 위해 기준 각도로 사용됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include "CottonServo.h" #define SERVO_BODY 0 // 몸체에 연결된 서보 #define SERVO_ARM 1 // DC모터와 연결된 서보 #define SPEED 10 // 모터 스피드 // 현재 각도 저장 변수 int currentAngle = 90; void setup() { initServo(); // 시작 위치 90도 setAngle(SERVO_BODY, 90); delay(2000); setAngle(SERVO_ARM, 90); delay(2000); } void loop() { // 90도 → 45도 → 0도 → 45도 → 90도 반복 moveSlow(SERVO_ARM, currentAngle, 45, SPEED); currentAngle = 45; delay(1000); moveSlow(SERVO_ARM, currentAngle, 0, SPEED); currentAngle = 0; delay(1000); moveSlow(SERVO_ARM, currentAngle, 45, SPEED); currentAngle = 45; delay(1000); moveSlow(SERVO_ARM, currentAngle, 90, SPEED); currentAngle = 90; delay(1000); } | cs |
3) 부드럽게 각도 제어 실습 결과
3. 아날로그 키패드로 서보모터 각도 조절
이번 실습에서는 아날로그 키패드 버튼을 눌러 서보모터의 각도를 조절해 봅니다.
1) 버튼 동작 설정
· SW1 버튼 → 90도
· SW2 버튼 → 45도
· SW4 버튼 → 0도
먼저 암(ARM) 서보모터를 제어하고,
같은 방식으로 바디(BODY) 서보모터로 확장할 수 있습니다.
SPEED 값에 서보모터의 알맞은 이동 속도를 설정해 주세요.
움직임이 부드럽지 않다면 10 → 5~8 범위로 값을 조절해 보세요.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | #include "CottonServo.h" #define SERVO_BODY 0 // 몸체에 연결된 서보 #define SERVO_ARM 1 // DC모터와 연결된 서보 #define SPEED 10 // 모터 스피드 // 아날로그 키패드 핀 설정 #define KEYPAD_PIN A0 // 아날로그 키패드별 최대/최소 입력값 #define SW_1_MIN 0 #define SW_1_MAX 10 #define SW_2_MIN 140 #define SW_2_MAX 150 #define SW_3_MIN 325 #define SW_3_MAX 336 #define SW_4_MIN 502 #define SW_4_MAX 513 #define SW_5_MIN 736 #define SW_5_MAX 747 // 현재 각도 저장 변수 int currentAngle = 90; // 키패드 감지 함수 bool SW_1() { int value = analogRead(KEYPAD_PIN); return (value >= SW_1_MIN && value <= SW_1_MAX); } bool SW_2() { int value = analogRead(KEYPAD_PIN); return (value >= SW_2_MIN && value <= SW_2_MAX); } bool SW_3() { int value = analogRead(KEYPAD_PIN); return (value >= SW_3_MIN && value <= SW_3_MAX); } bool SW_4() { int value = analogRead(KEYPAD_PIN); return (value >= SW_4_MIN && value <= SW_4_MAX); } bool SW_5() { int value = analogRead(KEYPAD_PIN); return (value >= SW_5_MIN && value <= SW_5_MAX); } void setup() { initServo(); // 시작 위치 90도 setAngle(SERVO_BODY, 90); delay(2000); setAngle(SERVO_ARM, 90); delay(2000); } void loop() { // 1번 버튼: 90도로 이동 if (SW_1()) { moveSlow(SERVO_ARM, currentAngle, 90, SPEED); currentAngle = 90; // 현재 각도 업데이트 delay(1000); } // 2번 버튼: 45도로 이동 else if (SW_2()) { moveSlow(SERVO_ARM, currentAngle, 45, SPEED); currentAngle = 45; // 현재 각도 업데이트 delay(1000); } // 4번 버튼: 0도로 이동 else if (SW_4()) { moveSlow(SERVO_ARM, currentAngle, 0, SPEED); currentAngle = 0; // 현재 각도 업데이트 delay(1000); } } | cs |
2) 아날로그 키패드로 서보모터 각도 조절 실습 결과
모든 입출력 장치를 제어하는 방법에 대하여 배웠습니다.
이제는 솜사탕을 멋지게 제작하기 위해 어떻게 동작 시킬 것인지 기획을 해볼까요?
1. 솜사탕 기계 동작 순서는?
1) 솜사탕 기계 예열
2) 로봇타워 전원 켜기
3) 종이 빨때 끼우고 예열 대기
4) 예열되면 설탕 넣고 솜사탕 기계 동작
5) 설탕실이 나오기 시작하면 설탕실을 잘 감는 위치로 이동하여 DC모터 회전하여 설탕실 감기 시작
6) 솜사탕의 크기가 핫도그 정도 되면 솜사탕 사이즈가 점점 커지는 시간에 맞추어 각도를 점점 올리기
7) 적당한 사이즈의 솜사탕이 되는 각도에 도달하기
8) 설탕실이 안나올 때까지 회전
9) 설탕실이 다 나오면 동작 완료 후 솜사탕 빼기
2. 기본 사항
1) CottonServo.h
-. freeServo() 기능을 사용해 모터의 힘을 풀어줍니다.
힘을 풀어주면 빨대를 끼우는 작업을 더 쉽게 할 수 있고, 억지로 눌러 끼울 때 서보모터에 강한 힘이 가해지는 것을 방지할 수 있습니다.

2) 단계 설정
-. 각 단계에서는 마지막 단계에 가기전 이전 단계로 갈 수 없도록 코딩하기
: 단계 값 설정합니다, 저는 아래에서 전원 켜기를 1단계로 하였으니 실제 동작이 되는 것은 2단계로 2부터 시작합니다.

-. 전원켜짐 : 녹색
-. 솜사탕 만드는 중 : 처음 솜사탕 감는 동작 : 빨강, 각도 올리기 동작 : 노랑
-. 솜사탕 만드는 동작 완료 : 녹색

3. 세부 동작
1) 1단계
-. 전원 켜기
→ LED 녹색 켜지기
→ 종이 빨대 끼우기 좋은 위치로 서보각도 초기 설정

2) 2단계
-. 솜사탕에서 나오는 설탕실을 잘 감을 수 있는 위치로 이동
-. DC모터 회전하여 설탕실 감기
-. 적당한 굵기로 설탕실이 감길 때까지 대기 (다음 단계 버튼 눌릴 때 까지)
→ 노랑 버튼(SW_1) 그리고 3단계 일때
→ 각도는 실제로 움직여 보면서 찾아보기
→ LED는 빨강색


3) 3단계
-. 솜설탕이 핫도그 정도 크기가 되면, 각도(서보모터1/ 몸체에 연결된)를 60초 동안 조금씩 올려 더 크게 만들기
-. 적당한 위치(80도)까지 도달하고 동작 중 노랑 LED 켜짐
-. 설탕실이 나오지 않을 때까지 계속 DC모터 회전
→ 녹색 버튼(SW_4)으로 동작

4) 4단계
-. 설탕실이 계속 많이 나올 때 수동으로 로봇암의 각도를 올리고 내려서 조절하기
→ 빨강 버튼(SW_2) => 각도 올리기
→ 파랑버튼(SW_3) => 각도 내리기
→ 이 단계는 선택사항으로 건너 뛸 수 있음


5) 5단계
-. 종이빨대를 끼우는 위치로 이동하여 종료하기
-. 다음 명령 대기하기
→ 흰색버튼(SW_5)

3. 전체 프로그램 보기
1) 솜사탕 메인 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | /* [솜사탕 로봇 메인 컨트롤러] */ #include "CottonServo.h" // --- [1] 핀 번호 설정 --- #define LED_R 9 // 빨간 불 (작업 중) #define LED_Y 10 // 노란 불 (자동 키우기) #define LED_G 11 // 초록 불 (대기/완료) #define MOTOR_A1A 5 // DC 모터 (솜사탕 기계 회전) #define MOTOR_A1B 6 #define KEYPAD_PIN A0 // 5버튼 키패드 // 서보 모터 채널 (PCA9685) #define SERVO_BODY 0 // 몸체에 연결된 서보 #define SERVO_ARM 1 // DC모터와 연결된 서보 // 버튼 아날로그 값 범위 #define SW_1_MIN 0 // [1단계] 작업 시작 #define SW_1_MAX 10 #define SW_2_MIN 140 // [수동] 각도 올리기 #define SW_2_MAX 150 #define SW_3_MIN 325 // [수동] 각도 내리기 #define SW_3_MAX 336 #define SW_4_MIN 502 // [2단계] 자동 키우기 #define SW_4_MAX 513 #define SW_5_MIN 736 // [종료] 초기화 #define SW_5_MAX 747 // --- [2] 전역 변수 --- int currentStep = 2; // 현재 단계 (2: 대기, 3: 작업중, 4: 수동/완료) int currentAngle = 140; // 팔의 현재 각도 // --- [3] 편의 함수 --- bool isKey(int val, int min, int max) { return (val >= min && val <= max); } // --- [4] 초기 설정 (setup) --- void setup() { Serial.begin(9600); initServo(); pinMode(LED_R, OUTPUT); pinMode(LED_Y, OUTPUT); pinMode(LED_G, OUTPUT); pinMode(MOTOR_A1A, OUTPUT); pinMode(MOTOR_A1B, OUTPUT); // 초기 상태: 초록불, 모터 끔 digitalWrite(LED_G, HIGH); digitalWrite(MOTOR_A1A, LOW); digitalWrite(MOTOR_A1B, LOW); Serial.println("--- 솜사탕 로봇 부팅 완료 ---"); // 초기 위치 복귀 및 힘 풀기 moveSlow(SERVO_BODY, 90, 140, 25); delay(500); moveSlow(SERVO_ARM, 45, 0, 20); delay(500); currentAngle = 140; delay(1000); freeServo(SERVO_BODY); freeServo(SERVO_ARM); Serial.println(">> 대기 모드 (버튼 1을 눌러 시작하세요)"); } // --- [5] 메인 루프 (loop) --- void loop() { int keyVal = analogRead(KEYPAD_PIN); // ============================================================ // [1번 버튼] 작업 시작 (Step 2 -> 3) // ============================================================ if (currentStep == 2 && isKey(keyVal, SW_1_MIN, SW_1_MAX)) { Serial.println(">> 작업 시작! (5번 버튼 잠금됨)"); digitalWrite(LED_G, LOW); digitalWrite(LED_R, HIGH); moveSlow(SERVO_ARM, 0, 85, 20); delay(500); moveSlow(SERVO_BODY, 140, 60, 10); delay(1000); currentAngle = 60; digitalWrite(MOTOR_A1A, HIGH); // 기계 회전 시작 currentStep = 3; delay(500); } // ============================================================ // [4번 버튼] 자동 키우기 (Step 3 -> 4) // ============================================================ if (currentStep == 3 && isKey(keyVal, SW_4_MIN, SW_4_MAX)) { Serial.println(">> 자동 키우기 시작..."); digitalWrite(LED_R, LOW); digitalWrite(LED_Y, HIGH); while (currentAngle < 80) { currentAngle += 2; if (currentAngle > 180) currentAngle = 180; setAngle(SERVO_BODY, currentAngle); Serial.print("자동 진행 중.. 각도: "); Serial.println(currentAngle); // 솜사탕이 감길 시간 (작업 중엔 5번 버튼 무시) delay(6000); } Serial.println(">> 자동 작업 완료! 이제 종료가 가능합니다."); digitalWrite(LED_Y, LOW); digitalWrite(LED_G, HIGH); currentStep = 4; // 드디어 완료 단계로 진입 } // ============================================================ // [2, 3번 버튼] 수동 조절 (Step 4에서만 가능) // ============================================================ if (currentStep == 4) { if (isKey(keyVal, SW_2_MIN, SW_2_MAX)) { if (currentAngle < 180) { currentAngle++; setAngle(SERVO_BODY, currentAngle); delay(100); } } if (isKey(keyVal, SW_3_MIN, SW_3_MAX)) { if (currentAngle > 0) { currentAngle--; setAngle(SERVO_BODY, currentAngle); delay(100); } } } // ============================================================ // [5번 버튼] 종료 (Step 3이 아닐 때만 작동) // ============================================================ // 작업 중(Step 3)에는 이 조건문이 실행되지 않습니다. if (currentStep != 3 && isKey(keyVal, SW_5_MIN, SW_5_MAX)) { Serial.println(">> 종료 및 원위치 복귀"); digitalWrite(MOTOR_A1A, LOW); digitalWrite(MOTOR_A1B, LOW); digitalWrite(LED_R, LOW); digitalWrite(LED_Y, LOW); digitalWrite(LED_G, HIGH); moveSlow(SERVO_BODY, currentAngle, 140, 10); delay(500); moveSlow(SERVO_ARM, 85, 0, 20); delay(500); freeServo(SERVO_BODY); freeServo(SERVO_ARM); currentAngle = 140; currentStep = 2; // 다시 처음 대기 상태로 Serial.println(">> 초기화 완료 (대기 모드)"); delay(1000); } } | cs |
2) 솜사탕 서보 제어 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | /* [솜사탕 로봇 서보 제어 라이브러리] */ #ifndef COTTON_SERVO_H #define COTTON_SERVO_H #include <Wire.h> #include <Adafruit_PWMServoDriver.h> static Adafruit_PWMServoDriver _pwm = Adafruit_PWMServoDriver(); #define _USMIN 544 #define _USMAX 2400 #define _SERVO_FREQ 50 void initServo() { _pwm.begin(); _pwm.setOscillatorFrequency(27000000); _pwm.setPWMFreq(_SERVO_FREQ); } // 기본 이동 (즉시 이동) void setAngle(int channel, int angle) { if (angle < 0) angle = 0; if (angle > 180) angle = 180; int microseconds = map(angle, 0, 180, _USMIN, _USMAX); _pwm.writeMicroseconds(channel, microseconds); } // 서보모터를 부드럽게 움직이는 함수 // 사용법: moveSlow(채널, 시작각도, 목표각도, 속도); // 속도: 숫자가 클수록 느림 (보통 10~20 추천) void moveSlow(int channel, int startAngle, int targetAngle, int speedDelay) { if (startAngle < targetAngle) { // 목표 각도가 더 클 때 (0° → 180° 방향) for (int i = startAngle; i <= targetAngle; i++) { setAngle(channel, i); // 1도씩 증가 delay(speedDelay); // 지연시간만큼 대기 } } else { // 목표 각도가 더 작을 때 (180° → 0° 방향) for (int i = startAngle; i >= targetAngle; i--) { setAngle(channel, i); // 1도씩 감소 delay(speedDelay); // 지연시간만큼 대기 } } } // 힘 풀기 void freeServo(int channel) { _pwm.setPWM(channel, 0, 4096); } #endif | cs |
4. 동작 영상
우리가 작성한 코드를 상상해보며 보세요^^