BLE 연결
G큐브는 Nordic UART Service(NUS)를 광고합니다. 클라이언트는 이 서비스에 연결한 뒤 TX 특성으로 명령을 쓰고, RX 특성의 Notify로 응답을 받습니다.
GATT UUID
| 구분 | UUID | 속성 | 방향 |
|---|---|---|---|
| Service (NUS) | 6e400001-b5a3-f393-e0a9-e50e24dcca9e | — | — |
| TX Characteristic | 6e400002-b5a3-f393-e0a9-e50e24dcca9e | Write / WriteWithoutResponse | 클라이언트→큐브 |
| RX Characteristic | 6e400003-b5a3-f393-e0a9-e50e24dcca9e | Notify | 큐브→클라이언트 |
주의: NUS의 명명 관습(TX=큐브가 데이터를 보내는 채널)과 반대입니다. 여기서 TX(...0002)는 클라이언트가 명령을 쓰는 특성, RX(...0003)는 클라이언트가 알림을 받는 특성입니다. 소스 코드 기준으로 표기했습니다.
기기 탐색(스캔)
광고 이름은 PINGPONG. 접두사로 시작합니다. 그룹ID를 사용하면 PINGPONG.<groupID> 형태가 됩니다.
// 스캔 필터 예 (Web Bluetooth / Scratch)
filters: [{ namePrefix: "PINGPONG." }]
// 서비스 UUID로도 필터 가능
optionalServices: ["6e400001-b5a3-f393-e0a9-e50e24dcca9e"]
연결 후 표준 시퀀스
펌웨어/클라이언트가 실제로 사용하는 초기화 흐름입니다.
- NUS 서비스 연결 → RX 특성 Notify 구독 시작
- 연결 안정화 대기(약 500ms)
- 단일 큐브면
getOrangeRedForSoundData(연결표시 LED), 다중 큐브면SetMultiroleInAction전송 - 연결 완료 알림 수신: 단일 큐브 0xCE(14B), 다중 큐브 0xAE(18B)
- 펌웨어 버전 조회
getProductVersion→ 필요 시 센서 구독getSensorsData(연결 후 약 3초 뒤 전송 권장)
fullConnect() 기준)펌웨어의 수신 패킷 검증 조건
ble_sync_multirole.c의 NUS 수신 핸들러는 아래 조건을 만족해야 유효 패킷으로 처리합니다.
- 누적 수신 길이 > 5바이트
Data[0] == 0xFF && Data[1] == 0xFFData[6](OpCode)가 등록된 Command Reference 목록에 존재Data[7:8]의 패킷 크기만큼 수신되면 처리 시작
패킷 구조
모든 명령은 공통 헤더로 시작합니다. 명령 종류에 따라 Generic 헤더(스텝모터·집계 계열, 13바이트 헤더)와 Task 헤더(LED·서보·음악·조회 계열)로 나뉩니다. 두 헤더 모두 [0:8] 구조는 동일합니다.
공통 앞부분 [0:8] (9바이트)
| 바이트 | 이름 | 설명 |
|---|---|---|
[0:2] | 가상 큐브 ID 접두 | 대부분 FF FF FF. 컬러LED 등 일부 태스크는 FF FF 00. 동글은 DD DD DD. |
[3] | Cube ID | FF=전체(All), 0x00~0x07=빌딩 순서(연결 순번), AA=집계기(Star/Aggregator) |
[4:5] | Assigned ID | Generic: [4]=큐브수<<4. Task: Task ID (예 00 A1, 00 C8). |
[6] | OpCode | 명령 코드. 「OPCODE 목록」 탭 참조. |
[7:8] | Packet Size | 전체 패킷 바이트 수(빅엔디언). = 13 + 값바이트수(Generic) 또는 명령별 고정. |
Generic 헤더 [9:12]
스텝모터·집계 계열(C1 CC CA CB CD).
[9] Mode | 1=Peripheral, 2=Multirole, 3=CRCCheck |
[10] Method | 0=Continuous, 1=RelativeSingle, 2=AbsoluteSingle, 3=ScheduledSteps, 4=ScheduledPoints |
[11] StepType | 0=FullSteps, 4=SetServo |
[12] PauseState | 1=Pause, 2=Resume |
값(속도·스텝 등)은 [13]부터 이어집니다.
Task 헤더 [9:10]
서보·LED·음악·조회 계열(E1 CE CF C7 E8 A2 B8 BA B1).
[9](및 [10])에 명령별 Property가 들어가고, 값은 그 뒤에 이어집니다. 명령별 정확한 오프셋은 각 탭의 바이트맵을 참조하세요.
예) 서보 E1: [9]=Mode(1), [11]=각도, [12]=타임아웃
예시로 이해하기 — 컬러LED 켜기
1번 큐브(연결 순번 0)를 빨강(R=255,G=0,B=0), 밝기 80으로 설정:
FF FF 00 00 00 00 CE 00 0E 01 FF 00 00 50 // 14 bytes
OPCODE 전체 목록
G큐브 펌웨어에서 실제 처리되는 명령을 정리한 표입니다. 활성은 현재 펌웨어에서 정상 처리되는 명령입니다.
동작 명령 (Set)
| OpCode | 이름 | 기능 | 상태 |
|---|---|---|---|
| 0xC1 | SET_SINGLESTEPS | 단일 스텝 동작(속도+스텝수, 1회) | 활성 |
| 0xCC | SET_CONTINUOUSSTEPS | 연속 회전(속도 기반 시작/정지) | 활성 |
| 0xCA | SET_SCHEDULEDSTEPS | 스케줄 스텝 데이터셋 전송/재생 | 활성 |
| 0xCB | SET_SCHEDULEDPOINTS | 스케줄 포인트 재생 | 활성 |
| 0xCD | SET_AGGREGATESTEPS | 여러 큐브 스텝 묶음 전송(집계) | 활성 |
| 0xC0 | SET_PAUSESTEPS | 일시정지/재개 | 활성 |
| 0xC6 | SET_INSTANTTORQUE | 토크 값 설정 | 활성 |
| 0xC2 | SET_MULTITASK_TIMER | RTC 타이머 기반 멀티태스크 | 활성 |
| 0xE1 | SET_SINGLESERVO | 서보 각도 설정 | 활성 |
| 0xCE | SET_COLORLED | 내장 컬러LED (HSV/RGB/인덱스) | 활성 |
| 0xCF | SET_DIGITALPORT | 확장포트 Dout HIGH/LOW | 활성 |
| 0xC7 | SET_WS2812B | WS2812(네오픽셀) LED | 활성 |
| 0xC9 | SET_POWERSTATE | 전원상태(리부트/전원오프) | 활성 |
| 0xE8 | SET_MUSICNOTE_INACTION | 음표 재생/집계 음악 | 활성 |
| 0xEF | SET_GENERATETONE | 버저 톤 생성 | 활성 |
| 0xE2 | SET_MULTIROLE_UNPLUGGED | 언플러그드(자율) 동작 | 활성 |
조회 명령 (Get) · 응답은 RX Notify
| OpCode | 이름 | 기능 |
|---|---|---|
| 0xB1 | GET_UPDATE / PRODUCTINFO | 제품 정보·펌웨어 버전 (Property B1=상세, B2=버전) |
| 0xBA | GET_PERIPHERALS | 버튼·컬러LED·근접·IMU·배터리 조회 (Property로 세부 선택) |
| 0xB8 | GET_SENSORS | 센서 통합 스트림(버튼/자이로/가속도/근접/확장) |
집계기(Aggregator/멀티롤)
| OpCode | 이름 | 기능 |
|---|---|---|
| 0xAA | SET_MULTIROLE_AGGREGATOR | 집계기에 연결 큐브 수·가상색 설정 |
| 0xAD | SET_MULTIROLE_INACTION | 멀티롤 시작(연결/그룹/모드 지정) |
| 0xAE | SET_MULTIROLE_VIRTUALCUBECOLOR | 가상 색 순서 지정 / 연결완료 알림 |
| 0xA0 | SET_MULTIROLE_CLEARVIRTUALCUBECOLOR | 가상 색/연결상태 초기화 |
| 0xAB | GET_MULTIROLE_VIRTUALCUBEID | 가상 큐브 ID 조회 |
| 0xAC | GET_MULTIROLE_VIRTUALCUBECOLOR | 가상 색 배열 조회 |
| 0xA8 | SET_MULTIROLE_REBOOTAGGREGATOR | 집계기 리부트 |
| 0xAF | CLASSIC_MULTIROLE_SETTINGS | 클래식 멀티롤 설정 |
아두이노·확장·플래시·기타
| OpCode | 이름 | 기능 |
|---|---|---|
| 0xA2 | ARDUINO_I2C | I2C 장치·LED매트릭스(주소 0x70) 제어 |
| 0xA3 | SET_CUBEINO_COMMAND | 큐브이노/아두이노/마이크로비트 제어 |
| 0xA4 | SET_LINEBOARD_COMMAND | 라인봇(트레이서보드) 제어 |
| 0xF9 | FLASH_DISCOVERYGROUPID | 그룹ID 탐색(임시 플래시 기록) |
| 0xFA | FLASH_DISCOVERYFINISHGROUPID | 그룹ID 확정(SetGroupID) |
| 0xFD | TEST / FACTORY | 공장/고객서비스 페이지 |
| 0xDA | DONGLE_INACTION | 전용 동글 제어(연결/해제/버전) |
0xC8(SetOrigin), 0xB0(GetOrigin), 0xB4(GetBattery 단독), 0xBB(GetProximity), 0xBE(GetColorLED), 리모컨 계열 0x0A~0x10 등. 필요 기능은 위 활성 명령으로 대체됩니다(예: 배터리는 0xBA+Property B4).스테퍼 모터 · 서보
G큐브의 핵심 구동부. 속도는 SPS(Steps Per Second) 또는 RPM으로 지정합니다. 속도 부호가 방향입니다.
±1~100 → SPS ±100~1000. 단일스텝: SPS = 속도×9 + 100. 연속회전: SPS = (1100×속도 − 10000) / 속도. 스텝수 최대 32768. 1도 ≈ 5.5스텝.SET SetSingleSteps 0xC1 — 19바이트
지정한 스텝수만큼 1회 이동 후 정지.
| 필드 | 값 |
|---|---|
[13:14] StepRate | SPS(빅엔디언, 음수는 65536+n). 정지=0 |
[15:16] StartPhase | 초기 위상(스텝), 보통 0 |
[17:18] StepValue | 이동 스텝수 0~65535(Relative) / 0~1000(Absolute) |
// 예시) 0번 큐브를 속도 50(정회전)으로 100스텝(약 18°) 이동
// SPS = 50×9 + 100 = 550 = 0x0226, 스텝 100 = 0x0064
FF FF FF 00 10 00 C1 00 13 02 01 00 01 02 26 00 00 00 64
SET SetContinuousSteps 0xCC — 15바이트
속도 기반 연속 회전. 속도 0이면 정지(PauseState=Pause), 그 외에는 Resume.
// 예시) 0번 큐브 속도 50으로 계속 회전 → SPS = (1100×50 − 10000)/50 = 900 = 0x0384
FF FF FF 00 10 00 CC 00 0F 02 00 00 02 03 84
// 정지 (속도 0, PauseState=Pause)
FF FF FF 00 10 00 CC 00 0F 02 00 00 01 00 00
SET SetScheduledSteps 0xCA — (15 + 값)바이트, CRC16
여러 (속도,스텝) 데이터셋을 미리 전송해두고 재생. Generic 헤더의 Mode=2, Method=3(ScheduledSteps), StepType=0(Full) 또는 4(SetServo).
| 바이트 | 필드 |
|---|---|
[13:14] | CRC16 (값 구간 [15:끝]에 대해 계산, CCITT 0x1021) |
[15:16] | StepRate #1 (SPS) |
[17:18] | Steps #1 |
[19],[20] | (SetServo일 때) ServoAngle, ServoTimeout |
| … | 데이터셋 반복 |
SET SetScheduledPoints 0xCB — (15 + 값)바이트
이미 전송된 스케줄 데이터셋에서 특정 포인트 구간을 반복 재생. 값: StartPoint[17:18]:StopPoint[19:20]:Repeats[21] 반복.
SET SetAggregateSteps 0xCD
여러 큐브의 SingleSteps/ContinuousSteps/Scheduled 데이터를 하나로 묶어 집계기(Star, ID=AA)에 전송. 헤더 크기 = 13 + Σ(각 큐브 패킷), Assigned ID 상위 = 큐브수<<4, Method는 묶는 명령 종류(0/1/2/3/4).
FF FF FF AA [N<<4] [00] CD [size_hi] [size_lo] 02 [method] 00 00
{ 큐브0 패킷 } { 큐브1 패킷 } ...
20바이트를 넘는 명령 (패킷 조각)
BLE 1패킷은 20바이트(MTU 23 − 3)이므로 0xCA·0xCB·0xCD 처럼 긴 명령은 여러 조각으로 나뉘어 전달됩니다. 예를 들어 35바이트 SetScheduledPoints는 20 + 15 두 조각입니다. 호스트는 조각을 나누어 보내기만 하면 되고, 재조립은 펌웨어가 합니다 — 조각 사이에 지연을 넣거나 응답을 기다릴 필요는 없습니다.
- 큐브가 앞 조각을 처리하는 동안 뒤 조각이 폐기됨 → 수신 스테이징 버퍼로 해결 (2026-07-22)
- 집계기 → 하위 큐브 전송 큐(GATTC)가 깊이 1이라 두 번째 조각을 실을 수 없어, 집계기만 움직이고 하위 큐브는 스케줄 명령을 통째로 놓침 → 큐 깊이 6으로 해결 (2026-08-11)
SET SetPauseSteps 0xC0 — 12바이트
[9] PauseState: 1=Pause, 2=Resume, 3=Radio. [10:11] SyncClock(StartTime). Radio 설정 시 집계기 ID 0xFFFFFFAA 사용.
SET SetInstantTorque 0xC6 — 10바이트
[9]: 0=기본(최소) 토크, 1=최대 토크.
서보 (Servo)
SET SetSingleServo 0xE1 — 13바이트
확장 서보 모터의 각도를 설정합니다.
| 필드 | 값 |
|---|---|
[9] Mode | 1=Peripheral, 2=Multirole |
[11] ServoAngle | 0~180도 |
[12] ServoTimeout | 전원유지 시간(초) 1~255. 0=각도만 걸고 유지 안 함, 0xFF=약 21.8분 |
// 예시) 0번 큐브의 서보를 90°로 (타임아웃 0 = 각도만 지정) → 90 = 0x5A
FF FF FF 00 00 00 E1 00 0D 01 00 5A 00
LED · 디스플레이 · 음악
SET SetColorLED 0xCE — 14바이트
큐브 내장 RGB LED. 색공간 3종 지원.
[9] 색공간 | [10:12] 값 | [13] |
|---|---|---|
| 0 = HSV | Hue(0~360, 2바이트) 등 | Brightness 0~100 |
| 1 = RGB | R[10], G[11], B[12] (각 0~255) | |
| 2 = ColorIndex | [12]에 인덱스: 0=RED,1=GREEN,2=BLUE,3=CYAN,4=MAGENTA,5=YELLOW,6=VIOLET,7=ORANGE,E=WHITE,F=BLACK |
// 2번 큐브 초록, 밝기 100 (RGB)
FF FF 00 01 00 00 CE 00 0E 01 00 FF 00 64
SET SetDigitalPort 0xCF — 10바이트
확장포트 Dout 핀 HIGH/LOW. [9]: 0=LOW, 1=HIGH.
FF FF FF [cube] 00 00 CF 00 0A [onoff]
SET SetWs2812bLED 0xC7 — 13바이트
외장 네오픽셀(WS2812) 제어. [9]=LED 번호(1,2,3…), [10:12]=R,G,B.
// 예시) 0번 큐브의 1번 네오픽셀을 파랑(R=0,G=0,B=255)으로
FF FF FF 00 00 00 C7 00 0D 01 00 00 FF
SET LED 매트릭스 (I2C, 0xA2 주소 0x70)
HT16K33 기반 8×8 LED 매트릭스. Task ID로 동작을 구분합니다.
| Task ID | 동작 | 값 |
|---|---|---|
| 00E1 | 픽셀 켜기 | x, y, on/off |
| 00E2 | 그림(8바이트) | 행 8바이트 비트맵 |
| 00E3 | 문자열 스크롤 | 스크롤주기[10] + ASCII HEX (최대 20자) |
| 00E4 | 디스플레이 제어 | 1=on, 0=off, 2=clear |
| 00E5 | 밝기 | 0~15 |
| 00E6 | 깜빡임 | 0=off,1=0.5Hz,2=1Hz,3=2Hz |
공통: [9]=I2C 주소 0x70. 스크래치 클라이언트는 픽셀 명령을 FF FF FF [cube] 00 [task] A2 00 0D 70 [y] [x] [plot] 형태로 전송합니다.
// 예시) 0번 큐브 매트릭스에서 (x=3, y=4) 픽셀 켜기 (Task 00E1)
FF FF FF 00 00 E1 A2 00 0D 70 04 03 01
음악 · 버저 (Music)
SET SetMusicNotes 0xE8 (Task 00A1) — (11 + 음표)바이트
단일 큐브 음표 재생. 음표 하나당 3바이트: PianoKey : Duration : Rest.
[10] PlayState | 0=Play, 1=Pause/Stop, 2=Resume/Play |
[11] PianoKey | 피아노 키 번호 (아래 음계표). 특수: A1=StartRepeat, A2=StopRepeat |
[12] Duration | 음길이 = 초×50 (예: 온음표 60BPM=0x64) |
[13] Rest | 쉼표 길이 = 초×50 |
// 예시) 0번 큐브에서 도(C5) 재생 → 키 0x34, 길이 0x32(=50→1초), 쉼표 0
FF FF FF 00 00 A1 E8 00 0E 00 00 34 32 00
0x70)은 영향이 없으므로,
멜로디에 맞춘 표시가 필요하면 앱에서 매트릭스 명령을 함께 보내세요.피아노 키 · 음계표 (발췌)
| 키(hex) | 음 | 음계 | 주파수(Hz) |
|---|---|---|---|
| 40 | Do (높은도) | C6 | 1046.4 |
| 34 | Do | C5 (Tenor C) | 523.2 |
| 33 | Ti | B4 | 493.9 |
| 31 | La | A4 | 440.0 |
| 28 | Do | C4 (Middle C) | 261.6 |
| 25 | La | A3 | 220.0 |
평균율 배율 1.05946. 스크래치 클라이언트는 PianoKey = 음번호 − 8 보정 후 전송합니다.
SET SetAggregateMusicNotes 0xE8 (Task 00A2)
여러 큐브 화음. 집계기 ID AA, Assigned ID 상위 = 큐브수<<4. 각 큐브의 음표 패킷을 이어붙입니다.
SET GenerateBuzzerTone 0xEF — 13바이트
[9] SoundFormat | E=PianoKey, C=WholeNote |
[10:12] | NoteIndex(WholeNote 0~16 또는 PianoKey 37~64) : Amplitude(1~10=10~100%) : Duration(1~40) |
// 예시) 0번 큐브 버저로 WholeNote C4(index 2)를 세기 5, 길이 10으로
FF FF FF 00 00 00 EF 00 0D 0C 02 05 0A
큐브 연결 · 센서 읽기
멀티롤 · 집계 (여러 큐브 연결)
여러 큐브를 하나의 Star(집계기)에 연결해 로봇을 구성하는 방식입니다. Star의 큐브 ID는 0xAA, 브로드캐스트는 0xFF입니다.
SET SetMultiroleInAction 0xAD — 11바이트
멀티롤 연결/모드를 시작합니다. 스크래치 클라이언트가 다중 큐브 연결 시 사용.
FF FF FF AA [N<<4] 00 AD 00 0B 0A 00 // 기본
FF FF FF AA [N<<4] 00 AD 00 0B 1A [groupId] // 그룹ID 사용 시
[9] InAction | 0A=그룹ID 비활성, 1A=그룹ID 활성, 0B=예약 |
[10] Property | 그룹번호 1바이트(0x00~0x99, 각 자리 0~9). 아래 그룹번호 절 참조. Web Bluetooth 모드는 03. |
SET SetMultiroleAggregator 0xAA
집계기에 연결할 큐브 수(ConnectionLinkCount, 최대 7 또는 0=전체)와 자동 가상색을 설정. [9]=큐브수, [10]=가상색 자동(0A/1A/00), 이후 각 큐브 ID.
RX 연결 상태 알림
| OpCode | 의미 | 길이 |
|---|---|---|
| 0xCE | 단일 큐브 연결 완료 | 14B |
| 0xAD | n개 연결 중 / 하위 큐브 끊김 | 18B, [9]=현재 연결 인덱스 |
| 0xAE | n개 모두 연결 완료 | 18B, [9]=큐브수−1 |
가상색 인덱스
연결 순서를 색으로 표현: 0=RED, 1=GREEN, 2=BLUE, 3=CYAN, 4=MAGENTA, 5=YELLOW, 6=SPRINGGREEN/VIOLET, 7=ORANGERED, E=WHITE(미정), F=BLACK(끊김).
SET RebootMultiroleAggregator 0xA8 — 10바이트
FF FF FF FF 00 00 A8 00 0A 01
연결 해제 시 클라이언트는 이 리부트 명령을 보낸 뒤 소켓을 닫습니다.
그룹번호 (Discovery Group ID)
여러 대의 G큐브가 같은 공간에 있을 때 서로 다른 로봇끼리 섞여 연결되는 것을 막는 식별자입니다. 집계기는 자신과 같은 그룹번호를 가진 큐브만 멤버로 받아들입니다. 값은 플래시에 저장되어 전원을 꺼도 유지됩니다.
범위와 표기
그룹번호는 0x00 ~ 0x99 범위의 16진수 1바이트입니다.
상위 4비트가 앞자리, 하위 4비트가 뒷자리이며, 각 자리는 0~9만 사용하고 A~F는 쓰지 않습니다.
groupId = (앞자리 << 4) | 뒷자리 // 각 자리 0~9
0x00 0x07 0x12 0x25 0x42 0x99
16진수로 읽은 두 글자가 곧 사용자가 보는 그룹번호입니다 — 0x12는 "일이", 0x25는 "이오"로 읽습니다.
십진수를 그대로 넣으면 안 됩니다. 예를 들어 그룹번호 12를 십진수 12(0x0C)로 보내면
앞자리 0, 뒷자리 0xC가 되어 뒷자리 색이 팔레트의 예약(소등) 영역으로 빠집니다.
0x00은 "미사용"을 뜻해, 이 경우 큐브는 그룹 제한 없이 동작하고 컬러 LED도 무지개(Hue) 표시로 돌아갑니다.
0x00~0x99 중 유효값)입니다.
0x0A~0x0F, 0xA0 이상처럼 A~F가 섞인 값은 펌웨어가 막지는 않지만
대응하는 음·색이 정의되어 있지 않으므로 사용하지 마세요.큐브가 그룹번호를 알려주는 방법
그룹번호가 설정되면 큐브는 소리와 색 두 가지로 자기 번호를 표현합니다. 두 방식 모두 자릿수를 앞자리 → 뒷자리 순으로 나타냅니다.
① 음계 — 자리 숫자마다 음 하나
숫자를 도레미에 그대로 대응시켰습니다. 0이 도(C4)이고 숫자가 하나 커질 때마다 음이 하나씩 올라갑니다. 앞자리를 0.2초 짧게, 뒷자리를 0.6초 길게 울려 둘을 구분합니다.
| 숫자 | 계이름 | 음이름 | 주파수 | 내부 WholeNote 인덱스 |
|---|---|---|---|---|
| 0 | 도 | C4 | 262 Hz | 2 |
| 1 | 레 | D4 | 294 Hz | 3 |
| 2 | 미 | E4 | 330 Hz | 4 |
| 3 | 파 | F4 | 349 Hz | 5 |
| 4 | 솔 | G4 | 392 Hz | 6 |
| 5 | 라 | A4 | 440 Hz | 7 |
| 6 | 시 | B4 | 494 Hz | 8 |
| 7 | 도(높은) | C5 | 523 Hz | 9 |
| 8 | 레(높은) | D5 | 587 Hz | 10 |
| 9 | 미(높은) | E5 | 659 Hz | 11 |
내부 인덱스 = 숫자 + 2 입니다. WholeNote 0·1은 A3(라)·B3(시)라서 도(C4)가 인덱스 2부터 시작합니다.
0x25 → 미(짧게) → 라(길게) ·
그룹번호 0x07 → 도(짧게) → 높은 도(길게)② 색 — 자리 숫자마다 색 하나
컬러 LED가 0.5초 간격 5단계를 반복하며 두 자리를 보여 줍니다. 뒷자리를 두 틱(1.0초) 켜서 앞자리와 구분합니다.
[앞자리 색] → [소등] → [뒷자리 색] → [뒷자리 색] → [소등] (총 2.5초 주기)
| 숫자 | 색 | RGB 비율 (R, G, B) |
|---|---|---|
| 0 | 빨강 RED | 0.4, 0, 0 |
| 1 | 초록 GREEN | 0, 0.4, 0 |
| 2 | 파랑 BLUE | 0, 0, 0.5 |
| 3 | 청록 CYAN | 0, 0.4, 0.4 |
| 4 | 자홍 MAGENTA | 0.4, 0, 0.4 |
| 5 | 노랑 YELLOW | 0.4, 0.4, 0 |
| 6 | 보라 VIOLET | 0.25, 0, 0.85 |
| 7 | 주황 ORANGE | 0.85, 0.15, 0 |
| 8 | 어두운 빨강 DARK RED | 0.1, 0, 0 |
| 9 | 어두운 초록 DARK GREEN | 0, 0.1, 0 |
표시가 나오는 시점
전원을 켤 때, 그룹번호를 새로 설정할 때, 그리고 연결이 끊어져 대기 상태로 돌아갈 때 소리가 납니다.
색 표시는 그룹번호가 설정되어 있는 동안 계속 반복됩니다(SET_DISCOVERYID 모드).
확장포트에 I2C 장치가 꽂혀 있으면 컬러 LED 자동표시가 멈추므로 그룹번호 색도 보이지 않습니다.
BLE 기기 이름
큐브는 광고 이름 뒤에 그룹번호를 16진수 두 자리로 붙입니다 — 0x25 → PINGPONG.25.
펌웨어가 %01X%01X 로 찍기 때문에 저장된 바이트를 16진수로 읽은 값이 그대로 이름이 됩니다.
앱이 스캔 목록에서 그룹을 구분하는 데 쓸 수 있습니다. 0x00이면 PINGPONG.00이 됩니다.
SET SetGroupID 0xFA (FlashDiscoveryFinishGroupId) — 11바이트
FF FF FF FF 00 00 FA 00 0B FE [groupId]
// 예) 그룹번호 0x25 로 설정
FF FF FF FF 00 00 FA 00 0B FE 25
[9] InAction | FE=쓰기, FA=읽기, FC=지움(그룹번호 해제) |
[10] groupId | 0x00~0x99 (각 자리 0~9). 읽기 요청 시 응답 [10]에 현재 값이 실림 |
0x90~0x99는 특수 동작이 있습니다. 앞자리가 9이면
5V 충전기를 연결해도 큐브가 자동으로 꺼지지 않습니다(FW 1.0.4.85, 2025-12-18).
전시·시연처럼 상시 급전 상태로 운용할 때 쓰라고 만든 예외입니다. 일반 사용에는 0x00~0x89를 쓰세요.예시 : 큐브 2개 연결 (집계기 Star + 멤버)
호스트(PC·앱)는 Star(집계기) 큐브 1개에만 BLE로 연결합니다. 그 뒤 0xAD 멀티롤 시작 명령을 보내면 Star가 자신의 무선으로 멤버 큐브를 탐색·연결합니다. 이때 멤버 큐브의 전원 버튼을 짧게 눌러 Star가 발견하도록 합니다.
연결 시퀀스 (2개 큐브 = Star 0번 + 멤버 1번)
| 단계 | 동작 / 바이트 |
|---|---|
| ① | 이름 PINGPONG.으로 스캔 → Star에 GATT 연결 → RX 특성 Notify 구독 |
| ② | 약 500ms 후 멀티롤 시작(N=2) 전송 → FF FF FF AA 20 00 AD 00 0B 0A 00 (20 = 2<<4) |
| ③ | 멤버 큐브 전원 버튼을 짧게 눌러 연결 유도 |
| ④ | 진행 알림 수신 0xAD ([9]=현재 인덱스) → 완료 알림 0xAE, [9]==1(=N−1)이면 2개 모두 연결 |
| ⑤ | 각 큐브 개별 제어([3]=0 Star, 1 멤버) 또는 집계기([3]=AA)로 일괄 제어. 센서는 연결 3초 뒤 0xB8 구독 |
Web Bluetooth 예제 (Chrome)
const SERVICE = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
const TX = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
const RX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
const CUBE_COUNT = 2; // Star(0) + 멤버(1)
let txChar;
const cmd = {
// 멀티롤 시작: N개 큐브 연결 (Star 포함). [4] = N<<4
multiroleStart: (n) => new Uint8Array([0xFF,0xFF,0xFF,0xAA, n<<4, 0x00, 0xAD, 0x00,0x0B, 0x0A, 0x00]),
// 컬러 LED (cube=0 Star, 1 멤버)
colorLED: (cube,r,g,b,br=80) => new Uint8Array([0xFF,0xFF,0x00,cube,0x00,0x00,0xCE,0x00,0x0E,0x01,r,g,b,br]),
// 전체 큐브 센서 구독 (pos=0xFF)
getSensors: (pos=0xFF,intv=10) => new Uint8Array([0xFF,0xFF,0xFF,pos,0x00,0xC8,0xB8,0x00,0x0B,intv,0x01]),
};
const send = (b) => txChar.writeValueWithoutResponse(b);
async function connectTwoCubes() {
// ① Star 한 대에만 BLE 연결
const device = await navigator.bluetooth.requestDevice({
filters: [{ namePrefix: "PINGPONG." }], optionalServices: [SERVICE]
});
const server = await device.gatt.connect();
const svc = await server.getPrimaryService(SERVICE);
txChar = await svc.getCharacteristic(TX);
const rx = await svc.getCharacteristic(RX);
await rx.startNotifications();
// ④ 연결 상태 알림 수신
rx.addEventListener('characteristicvaluechanged', e => {
const v = new Uint8Array(e.target.value.buffer);
if (v[6] === 0xAD) console.log(`연결 진행 중 · 인덱스 ${v[9]}`);
if (v[6] === 0xAE && v[9] === CUBE_COUNT - 1) {
console.log("2개 큐브 연결 완료!");
onAllConnected();
}
});
// ② 500ms 후 멀티롤 시작 → Star가 멤버를 탐색
setTimeout(() => send(cmd.multiroleStart(CUBE_COUNT)), 500);
// ③ 이제 멤버 큐브의 전원 버튼을 짧게 눌러 주세요.
}
async function onAllConnected() {
// ⑤ 개별 제어: 0번(Star) 빨강, 1번(멤버) 초록
await send(cmd.colorLED(0, 255, 0, 0));
await send(cmd.colorLED(1, 0, 255, 0));
// 두 큐브 센서 구독 (연결 3초 뒤 권장)
setTimeout(() => send(cmd.getSensors(0xFF, 10)), 3000);
}
전송 바이트 확인 — 멀티롤 시작(N=2): FF FF FF AA 20 00 AD 00 0B 0A 00 · 0번 빨강: FF FF 00 00 00 00 CE 00 0E 01 FF 00 00 50 · 1번 초록: FF FF 00 01 00 00 CE 00 0E 01 00 FF 00 50
CUBE_COUNT만 바꾸면 [4]=N<<4와 완료 조건 [9]==N−1이 자동으로 맞춰집니다. Python·Node.js·Unity도 같은 순서로, 같은 바이트를 보내면 됩니다.센서 읽기
SET GetSensors 0xB8 — 11바이트 (구독 시작)
버튼·자이로·가속도(기울기)·근접·확장포트 값을 스트림으로 받습니다.
[3] Position | 0xFF=전체, 0x00~=특정 큐브 |
[9] InAction | 0=단발, 10~100=연속. FW 1.0.4.85부터 실효 주기가 절반(InAction × 0.005초 = 0.05~0.5초). 84 이하는 × 0.01초 |
[10] Method | 1=실수 8bit(20B), 2=ADC 12bit, 3/4=실수 12bit, 7=+큐브이노 아날로그 |
getSensorsData(0xFF, 10)(전체·주기 10)을 사용합니다.
85 이상에서 84와 같은 주기를 원하면 InAction 값을 2배로 보내세요.여러 큐브를 연결했을 때의 실효 출력 주기
집계기는 하위 큐브들의 센서 패킷을 모아 한 프레임으로 올려 보냅니다. 하위 큐브는 각자의 타이머로 보내고 집계기도 자기 타이머로 깨어나므로, 둘의 위상이 어긋나면 그 주기에 아직 도착하지 않은 큐브가 생깁니다. FW 1.0.4.85는 한 주기만 놓쳐도 프레임을 강제로 내보냅니다(2026-08-08, 이전 임계 3주기).
| InAction | 설정 주기 | 이론 출력 | 큐브 3대 실측 85 |
|---|---|---|---|
| 10 | 0.05초 | 20.35 Hz | 18.5 ~ 20.4 Hz (명령 부하 4~64회/초) |
RX 센서 응답 (Method 1, 20바이트)
OpCode [6]=0xB8. 실제 클라이언트가 파싱하는 오프셋:
| 바이트 | 의미 | 변환 |
|---|---|---|
[0] | CubeId 최상위 바이트 (큐브 식별) | 0xA0 | 가상색인덱스. 상위 니블은 항상 0xA 고정,
하위 니블 [0] & 0x0F 가 큐브 번호(연결 순번 0~7). 집계기(Star)는 0 |
[1] | 배터리 잔량 85+ | 0~100 (%) |
[2] | 펌웨어 빌드 85+ | 예: 85 |
[3] | 큐브 번호 | 84와 동일. 단일 연결 시 0x07 → 0으로 매핑. [0] & 0x0F 와 같은 값 |
[5] | 구분자 85+ | 고정 0x77. 84 이하에서는 TaskId(연속 스트림에서 항상 0xC8) |
[11] | Button | 0=해제,1=눌림,2=Latched(눌렀다 뗌, 2초 후 해제) |
[12:14] | 이동 X/Y/Z (자이로) | 부호 있는 8bit. I2C 센서 연결 시 센서값으로 대체됨 |
[15:17] | 기울기 X/Y/Z (가속도) | −90~90도로 변환 (Y축은 부호 반전) |
[18] | 근접(Proximity) | 부호 없는 값 |
[19] | 확장포트 AD값 | 부호 없는 8bit. I2C 센서 연결 시 센서값으로 대체됨 |
[0] 으로 큐브를 구분합니다. 큐브를 여러 대 연결하면 집계기는
20바이트 블록을 큐브 수만큼 이어붙인 한 프레임(20 × N 바이트)으로 올려 보냅니다.
각 블록의 첫 바이트 하위 니블이 그 블록의 주인입니다 — 큐브번호 = Data[i×20] & 0x0F.
블록 순서는 보장되지 않으므로 순서가 아니라 이 값으로 찾아야 합니다.
같은 값이 [3] 에도 실리지만, 84·85 양쪽에서 안전한 쪽은 [0] 입니다.
// 3대 연결 시 60바이트 프레임 파싱
for (let i = 0; i < frame.length / 20; i++) {
const b = frame.subarray(i * 20, i * 20 + 20);
const cube = b[0] & 0x0F; // 0 = 집계기(Star)
if (cube >= 8) continue; // 미배정/끊김 블록은 건너뛴다
sensors[cube] = parse(b);
}
가상색인덱스와 같은 값이라 [0] 은 곧 「멀티롤 · 집계」 절의 가상색(0=RED … 7=ORANGERED)을 가리킵니다.
아직 집계기에 배정되지 않았거나 단독 연결 상태면 0x0E(WHITE·미배정), 끊기면 0x0F(BLACK)가 들어가
[0] 이 0xAE·0xAF 로 보일 수 있습니다. 펌웨어도 이 경우를 유효 큐브에서 제외합니다.[1]배터리 · [2]펌웨어 빌드 · [5]구분자 0x77 이 새로 실립니다.
세 자리 모두 84에서 앱이 쓰지 않던 값(가상큐브ID 일부 · 그룹ID · TaskId)이라 기존 파싱을 깨지 않습니다.
큐브 번호 자리인 [3]도 84와 동일하게 유지됩니다.
이 값들은 큐브이노·마이크로비트가 연결되지 않은 경우에만 채워지며, 연결된 경우 84와 완전히 동일하게 동작합니다.
[5]==0x77 인지로 85 이상 여부를 판별할 수 있습니다.I2C 센서별 바이트 매핑 (Method 1)
확장포트에 I2C 장치가 인식되면 [10]·[12]~[14]·[19] 가 해당 센서 값으로 대체됩니다.
장치는 부팅 시 자동 검출되며, 앱에서 0xA2 Task 0000·[9]=0x80 으로 현재 인식된 주소를 조회할 수 있습니다.
| 센서 | I2C 주소 | [10] | [12] | [13] | [14] | [19] | 갱신 |
|---|---|---|---|---|---|---|---|
| 초음파 거리 | 0x15 | — | — | — | — | 거리 2~255 cm | 매 주기 |
| 온도 (MCP9805) | 0x18 | 소수부 (÷4) | — | — | — | 정수부 ℃ | 매 주기 |
| 컬러 (VEML6040) | 0x10 | — | R | G | B | 컬러키 0~7 | 매 주기 |
| 온습도 DHT20 | 0x38 | 습도 % | — | — | — | 온도 ℃ | 매 주기 |
| CO₂ SCD40 | 0x62 | — | 온도 | 습도 | CO₂ LSB | CO₂ MSB | 5초 |
| 미세먼지 SPS30 | 0x69 | — | PM1.0 | PM2.5 | PM4.0 | PM10 | 1초 |
| 미세먼지 PM2008 | 0x28 | — | PM1.0 | PM2.5 | — | PM10 | 1초 |
| 인터랙션 모듈 TCA9534 | 0x20 | — | — | — | — | 버튼 1=눌림, 0=뗌 | 매 주기 |
| AI 카메라 (허스키렌즈·오즈아이) | 0x32 | X 상위 | X 하위 | Y 상위 | Y 하위 | 학습 ID | 매 주기 |
| (I2C 장치 없음) | — | AIN 하위니블 | 자이로 X/Y/Z | 확장포트 AIN 8bit | 매 주기 | ||
SCD40·SPS30·PM2008은 워밍업·측정 미완료 시 직전값을 유지합니다(0으로 떨어지지 않음).
TCA9534는 하드웨어가 active-low(눌림=0)이지만 펌웨어가 반전해 1=눌림으로 올려 보냅니다.
AI 카메라는 블록(물체) 인식 시 블록 중심 좌표와 ID를, 화살표(라인) 인식 시 목표점 좌표와 ID를 실어 보냅니다. 인식된 대상이 없으면 값이 갱신되지 않습니다.
(hi<<8|lo)×(1/512)+23 ℃.SET GetPeripherals 0xBA — 11바이트
버튼/컬러LED/근접/IMU/배터리를 개별 조회. [9]=InActionId, [10]=Property.
| [9] InActionId | 대상 | [10] Property 예 |
|---|---|---|
| C3 | 컬러LED | 2=인덱스 |
| B0 | 버튼 | 0=상태 |
| B1 | 근접 | 0=Mavg, 4=전압 |
| B3 | IMU | 2=Acc+Gyro, 4/5=온도 |
| B4 | 배터리 | 1=전압+퍼센트 |
| A1 | IMU 과열 경보 | — |
배터리 조회 예(스크래치 checkBattery): FF FF FF [cube] 00 00 BA 00 0B B4 01. 응답 [6]=BA, [9]=B4, [13]=배터리 %.
아두이노 · 큐브이노 · 라인봇
SET Cubino/Arduino/Microbit 0xA3
확장 보드(큐브이노 등) 제어. Task ID로 기능 구분.
| Task ID | 기능 | 값 [10],[11] |
|---|---|---|
| 00EA | 아날로그 읽기 시작 | 샘플링주기, analogFlag |
| 00EB | 디지털 포트 | dPortH(D8~D13), dPortL(D4~D7) |
| 00EC | 수중 온도센서(DS18B20) 시작 | dummy |
| 00ED | 미세먼지(GP2Y1010) 시작 | dummy |
| 00EE | SPS30 시작 | dummy |
| 00EF | SCD40(CO2) 시작 | dummy |
| 00E0 | PM2008 시작 | dummy |
| 00E3 | 인터랙션 모듈 RGB LED | [10]=rgb 비트마스크 |
응답: Mandatory 10바이트 + A5~A0 아날로그 6바이트(8bit/ch) = 16바이트.
SET 인터랙션 모듈 RGB LED 0xA3 · Task 00E3 — 11바이트
TCA9534 확장보드(I2C 0x20)의 RGB LED를 켭니다. 모듈이 인식된 상태에서만 동작합니다.
| [10] 값 | bit0 R | bit1 G | bit2 B | 색 |
|---|---|---|---|---|
0x00 | · | · | · | 소등 |
0x01 | ON | · | · | 빨강 |
0x02 | · | ON | · | 초록 |
0x03 | ON | ON | · | 노랑 |
0x04 | · | · | ON | 파랑 |
0x05 | ON | · | ON | 자홍 |
0x06 | · | ON | ON | 청록 |
0x07 | ON | ON | ON | 백색 |
// 예시) 0번 큐브의 인터랙션 모듈을 파랑(bit2)으로
FF FF FF 00 00 E3 A3 00 0B 00 04
[19] 로 올라옵니다.SET AI 카메라 제어 모드 0xAD · Task 00BC
허스키렌즈·오즈아이(I2C 0x32)가 연결된 큐브를 AI 카메라 제어 모드로 전환합니다.
부팅 시 라인(화살표)이 학습되어 있으면 자동으로 이 모드에 진입하며, 같은 그룹번호의 G큐브 2대만 연결됩니다.
0xBA GetPeripherals 응답의 종류 코드가 0xBC 로 회신되면 AI 카메라 모드입니다.
SET ArduinoI2C 0xA2
I2C 장치 질의/제어. [9]=I2C PortStatus(80=연결주소 질의, 8D=DIO 설정). LED매트릭스(주소 0x70)는 「LED·디스플레이」 탭 참조.
| Task ID | 기능 |
|---|---|
| 0000 | 연결 주소 질의 |
| 00D1~00D3 | SetDIN / SetDOUT / SetDIO |
| 00D4 | 리모컨 실드 |
| 00D5 | 초음파 거리 |
연결 주소 질의는 FF FF FF [cube] 00 00 A2 00 0B 80 로 보내며, 응답 [9] 에 현재 인식된 I2C 주소가 실립니다(미인식 시 0).
| 응답 [9] | 장치 | 비고 |
|---|---|---|
0x00 | 없음 | 서보·확장포트 AIN·리모컨이 대신 활성화됨 |
0x07 | 큐브이노 (Cubeino) | UART 보드 |
0x10 | 컬러 센서 VEML6040 | |
0x15 | 초음파 거리 | |
0x18 | 온도 MCP9805 | |
0x20 | 인터랙션 모듈 TCA9534 | 버튼 + RGB LED. 확장보드 |
0x28 | 미세먼지 PM2008 | |
0x32 | AI 카메라 (허스키렌즈·오즈아이) | |
0x38 | 온습도 DHT20 | |
0x62 | CO₂ SCD40 | Task 00EF 로 시작 |
0x69 | 미세먼지 SPS30 | Task 00EE 로 시작 |
0x70 | 도트매트릭스 HT16K33 | 「LED·디스플레이」 탭 참조 |
SET Control LineBot 0xA4 — 11바이트
라인봇(트레이서보드) 전용. Task ID로 동작, [9][10]에 파라미터.
00A6, PortStatus[9]=1)로 설정하면 이후 아래 명령들로 라인봇을 직접 제어할 수 있습니다.| Task ID | 동작 | 파라미터 |
|---|---|---|
| 00A6 | 자동/제어 모드 | [9]=1(HIGH)/0(LOW) |
| 00A7 | 속도 제어 | [9]=G0속도, [10]=G1속도 |
| 00B7 | 이동 | [9]=속도(−100~100), [10]=cm |
| 00C7 | 회전 | [9]=속도, [10]=각도 |
| 00D7 | 색 만날 때까지 라인추종 | [9]=색키 |
| 00D8 | 격자 통과까지 라인추종 | [9]=격자수 |
| 00E7 | 라인 감지까지 회전 | [9]=0(좌)/1(우)/2(U턴) |
| 00F7 | 그리퍼 | [9]=1(열기)/0(닫기) |
스크래치 orderLineBot(taskID, p9, p10): FF FF FF 00 00 [taskID] A4 00 0B [p9] [p10]
큐브이노 (Cubeino) — 시리얼 프로토콜
큐브이노는 G큐브의 확장포트에 꽂히는 아두이노 호환 보드입니다. 여기서의 통신은 BLE가 아니라 G큐브 MCU와의 시리얼(UART) 연결이며, 모든 명령이 10바이트 고정 패킷으로 오갑니다.
두 프로토콜 계층 관계
| 계층 | 구간 | 전송 | 패킷 |
|---|---|---|---|
| BLE 프로토콜 | 호스트(PC/앱) ↔ G큐브(Star) | BLE NUS | 가변, 최대 20B, FF FF ... |
| 큐브이노 시리얼 | 아두이노 보드 ↔ 꽂힌 G큐브 | UART Serial | 고정 10바이트 |
즉, 큐브이노는 자신이 꽂힌 G큐브를 시리얼로 직접 제어하고, 그 G큐브가 다시 BLE 멀티롤로 다른 큐브들과 연동됩니다. 큐브이노 보드가 센서 허브(0x71)나 라인보드(0x72)로도 동작할 수 있습니다.
패킷 형식 (10바이트 고정)
[0] Command | 명령 코드(아래 표) |
[1] IV (검증바이트) | get_iv(cmd) = 상·하위 니블 스왑. (cmd>>4) | ((cmd&0x0F)<<4) |
[2:9] Parameters | 명령별 인자(미사용 시 0) |
// 검증바이트 계산 (Cubeino.cpp)
byte get_iv(byte a){ return (a/16) + (a & 0x0F)*16; } // 니블 스왑
// 예: 0x30 → 0x03, 0xA0 → 0x0A, 0x51 → 0x15
// 전송: send_gcube()는 10바이트를 쓰고 50ms 지연
// direct_send_gcube()는 10바이트를 쓰고 1ms 지연(라인보드 등 저지연)
0xA0 + 큐브번호, 매트릭스 이미지 0x51 + 인덱스). IV 바이트도 더해진 최종 코드로 계산합니다.연결·핸드셰이크
| 명령 | 코드 | 동작 |
|---|---|---|
| GET_BOARD_ID | 0x10 | G큐브가 보드 종류를 물음 → 보드가 자기 ID로 응답 |
| GET_CUBE_NUMBER | 0x21 | Star에 연결된 큐브 개수 요청 |
| SET_ALL_CUBES_CONNECTED | 0x2A | 모든 큐브 연결 완료 통지(→ G큐브가 센서 수집 시작) |
| CONTROL_COMMAND | 0x1F | 리부트 등 제어(켜져 있던 큐브 초기화) |
보드 ID: Cubeino 0x70, Sensor Hub 0x71, LineBoard 0x72, 점자보드(마이크로비트) 0x73 85+.
G큐브는 0x10 응답으로 받은 보드 ID에 따라 동작 모드를 정합니다.
0x72·0x73 은 TracingMode(라인 주행)로 진입하고, 0x70·0x71 은 센서 허브 모드로 동작합니다.
G큐브 센서 읽기 (아두이노 → G큐브)
| 기능 | 코드 | 비고 |
|---|---|---|
| 기울기/자이로 | 0xA0 + 큐브# | 3바이트 응답(부호 8bit ×3) |
| 근접센서 | 0xAA | 파라미터에 큐브번호 |
| 버튼 상태 | 0xAB | 파라미터에 큐브번호 |
| 확장포트 2바이트 아날로그 | 0xB0 + 큐브# | 16bit 값([1]×16 + [2]) |
| 확장 컬러센서 색키 | 0xE0 + 큐브# | color key |
| 확장 컬러센서 RGB | 0xE8 + 큐브# | color value |
| Echo(연결확인) | 0xFF | 단순 에코 |
확장 센서 종류 코드 (s_name)
빛 0xC1 | 소리 0xC2 | 볼륨 0xC3 | 자석 0xC4 |
초음파 SEN-0307(520cm) 0xC5 | 온도 0xC6 | 컬러키 0xC7 | 컬러값 0xC8 |
초음파 US-016(100cm) 0xC9 | 아날로그 0xF7 | 초음파는 값×범위/4095로 cm 환산 | |
G큐브 구동 제어 (아두이노 → G큐브)
| 기능 | 코드 | 파라미터 |
|---|---|---|
| 단일 모터 속도 | 0x30 | 큐브번호, 속도(−100~100) |
| 다중 모터 속도 | 0x31 + (N−2) | s7…s0 (역순) |
| 단일 모터 각도 | 0x38 | 큐브번호, 각도(×100/18, 2바이트) |
| 다중 모터 각도 | 0x39 + (N−2) | s3…s0 각 2바이트 |
| 단일 서보 각도 | 0x40 | 큐브번호, 각도(0번은 보드 자체 서보) |
| 다중 서보 각도 | 0x41 + (N−2) | s7…s0 |
| 매트릭스 이미지(8B) | 0x51 + 인덱스 | 8바이트 비트맵 |
| 매트릭스 깜빡임 | 0x5A | 인덱스, blinkrate |
| 매트릭스 픽셀 | 0x60 | 인덱스, x, y, on/off |
| 매트릭스 문자열 | 0x61 + 인덱스 | 8바이트 |
모터 속도는 char(−100~100). 다중 명령의 큐브 수 N은 Star 포함 총 개수, 코드에 N−2를 더합니다.
큐브이노 자체 입출력 / 센서 허브
| 기능 | 코드 | 설명 |
|---|---|---|
| 아날로그 보고 | 0x11 | 보드의 A0~A3를 G큐브로 보고(A3·A2 8bit, A1·A0 10bit) |
| 디지털 포트 설정 | 0x1A | D4~D7(vl b4~b7), D8~D13(vh b2~b7) |
| 큐브-아두이노 아날로그 값 | 0xD0 + 큐브# | 포트 지정, 16bit 응답 |
| 큐브-아두이노 아날로그 시작 | 0xD8 | 보고 시작 |
| 큐브-아두이노 디지털 제어 | 0xD9 | port_hi, port_lo |
라인봇(LineBoard) 전용 · 로봇 모델
| 기능 | 코드 |
|---|---|
| 센서 데이터 / 속도 / 이동 / 회전 | 0x12 / 0x13 / 0x14 / 0x15 |
| 색까지 이동 / 라인까지 회전 / 그리퍼 / 격자 이동 | 0x16 / 0x17 / 0x18 / 0x19 |
| 멜로디 / 넘어짐 / 리부트 | 0x1B / 0x1C / 0x1E |
핑퐁로봇 모델(기어 유형)
DrawingBot 0(기어 없음), AutoCar 1(기어), AntBot·BattleBot 2(기어). move_pingpong_robot·rotate_pingpong_robot가 모델별 보정계수로 다중 모터 각도를 계산합니다. 펜/레버/그리퍼 업다운은 0번 큐브 각도(±90/±180)로 구현됩니다.
Cubeino 라이브러리 주요 API
모든 함수는 마지막 인자로 G큐브와 연결된 시리얼 포트 Stream &cubeSerial을 받습니다. 아래 표의 시그니처에서는 지면상 이 인자를 생략했습니다. 공통 규약: cube_number는 연결 순번(0부터), GCUBE_TOTAL은 Star를 포함한 전체 큐브 수, 속도는 -100~100입니다.
연결 · 초기화
| 함수 | 설명 |
|---|---|
wait_for_first_cube_connected() | 첫 G큐브의 켜짐(보드ID 요청 0x10)을 대기하고 큐브이노 보드ID 0x70으로 응답합니다. 이미 켜져 있던 큐브는 리부트시켜 초기 상태로 만듭니다. |
wait_for_hub_cube_connected() | 동일하게 대기하되 센서 허브 보드ID 0x71로 응답합니다. |
wait_for_lineboard_cube_connected(s1, s2, mode) | 두 시리얼 포트에 연결된 라인보드용 큐브를 대기하고 라인보드 보드ID 0x72로 응답합니다. mode는 연결을 기다릴 큐브 수. |
wait_for_all_cube_connected(GCUBE_TOTAL) | Star에 연결된 큐브 수(0x21)를 주기적으로 물어 GCUBE_TOTAL개가 될 때까지 대기한 뒤, 전체 연결 완료(0x2A)를 통지합니다 → G큐브가 센서 수집을 시작. |
check_main_cube_reconnected(GCUBE_TOTAL) → bool | 메인 큐브의 하드웨어 연결을 확인합니다. 미연결이면 false(시리얼 전송 중단 안내용), 연결·재부팅 감지 시 보드ID 재전송 후 전체 연결을 다시 대기합니다. |
센서 읽기 (G큐브 → 아두이노)
| 함수 | 설명 |
|---|---|
get_cube_acc_value(int8_t mdata[], cube_number) | 큐브의 기울기/자이로 3축을 읽어 mdata[0..2]에 부호 있는 8bit로 저장합니다. |
get_cube_proxi_value(byte mdata[], cube_number) | 근접센서 값을 3바이트 응답으로 읽습니다. |
get_cube_button_value(byte mdata[], cube_number) | 버튼 상태를 읽습니다(mdata[2]가 눌림 여부). |
get_cube_ext_sensor_value(byte mdata[], cube_number, s_name) → uint16_t | 확장포트 센서를 읽고 종류(s_name)에 맞게 환산한 값을 반환합니다. 예: 초음파는 cm(SEN-0307 0~520, US-016 0~100), 온도·빛·소리·볼륨·자석·아날로그는 정수. s_name: 빛 0xC1·소리 0xC2·볼륨 0xC3·자석 0xC4·초음파(흑) 0xC5·온도 0xC6·컬러키 0xC7·컬러값 0xC8·초음파(녹) 0xC9·아날로그 0xF7. |
get_cube_echo_value(byte mdata[]) | 연결 확인용 에코(0xFF)를 보내고 3바이트 응답을 받습니다. |
get_cube_command(byte mdata[]) | 큐브가 보낸 3바이트 명령을 그대로 읽습니다(저수준). |
모터 · 서보 제어 (아두이노 → G큐브)
| 함수 | 설명 |
|---|---|
send_a_cube_speed(cube_number, speed) | 지정 큐브 모터를 속도 -100~100으로 연속 회전(부호=방향). |
send_multi_cube_speed(GCUBE_TOTAL, s0…s7) | 여러 큐브 모터 속도를 한 번에 지정. |
stop_all_cube_motor(GCUBE_TOTAL) | 모든 큐브 모터를 정지(속도 0 일괄 전송). |
send_a_cube_angle(cube_number, angle) | 지정 큐브를 상대 각도만큼 회전(내부적으로 100×angle/18 스텝 환산). |
send_multi_cube_angle(cube_range, s0…s3) | 여러 큐브 각도를 한 번에 지정. |
send_a_servo_angle(cube_number, angle, svo) | 서보 각도 설정. cube_number==0이면 보드에 직접 연결된 서보(svo)를, 그 외에는 해당 큐브의 서보를 제어. |
send_multi_servo_angle(GCUBE_TOTAL, s0…s7, svo) | 여러 서보 각도를 한 번에 지정(0번은 보드 서보). |
도트매트릭스
| 함수 | 설명 |
|---|---|
send_matrix_still_image(cube_index, m[8]) | 8바이트 비트맵 정지 이미지를 표시. |
start_matrix_roll_image(GCUBE_TOTAL, m[], duration) | 여러 매트릭스 큐브에 걸쳐 이미지를 흐르게 표시(duration초). |
start_matrix_shift_image(cube_index, GCUBE_TOTAL, img[], xp, yp) | 기울기 값(xp, yp: −90~90)에 따라 이미지를 상하좌우로 이동시켜 표시. |
set_matrix_pixel_data(cube_index, x, y, onoff) | 특정 픽셀(x, y) 하나를 켜고/끔. |
set_matrix_string_data(cube_index, s0…s7) | 문자열/패턴 데이터를 매트릭스에 표시. |
set_matrix_blink_rate(cube_index, blinkrate) | 깜빡임 속도 설정. |
핑퐁로봇 모델 · 액추에이터
| 함수 | 설명 |
|---|---|
move_pingpong_robot(gear_flag, distance) | 로봇을 distance(cm)만큼 직진/후진. gear_flag(모델)에 따라 보정계수가 달라집니다: DrawingBot 0(기어 없음), AutoCar 1, AntBot·BattleBot 2. |
rotate_pingpong_robot(gear_flag, angle) | 로봇을 angle(도)만큼 제자리 회전(모델별 보정). |
pen_up() / pen_down() | 드로잉봇 펜을 올리고/내림(0번 큐브 ±90°). |
lever_up() / lever_down() | 레버를 올리고/내림(0번 큐브 ±90°). |
gripper_open() / gripper_close() | 그리퍼를 열고/닫음(0번 큐브 ±180°). |
아두이노 자체 입출력 · 저수준
| 함수 | 설명 |
|---|---|
report_analog_data() | 보드의 A0~A3 아날로그 값을 읽어 G큐브로 보고(0x11). A3·A2는 8bit, A1·A0는 10bit. |
set_digital_port(vh, vl) | 보드의 디지털 출력 설정: D4~D7은 vl의 b4~b7, D8~D13은 vh의 b2~b7. |
set_cube_arduino_analog_start(cube_number) | 지정 큐브에 꽂힌 아두이노의 아날로그 보고를 시작. |
get_cube_arduino_analog_value(cube_number, a_port) → uint16_t | 지정 큐브-아두이노의 특정 포트 아날로그 값(16bit)을 읽음. |
set_cube_arduino_digital_value(cube_number, port_hi, port_lo) | 지정 큐브-아두이노의 디지털 포트를 제어. |
send_gcube(x0…x9) / direct_send_gcube(x0…x9) | 10바이트 패킷을 직접 전송하는 저수준 함수. 앞은 50ms, 뒤는 1ms 지연(라인보드 등 저지연용). |
get_iv(cmd) → byte | 명령의 검증바이트(상·하위 니블 스왑)를 계산. |
라인보드(라인봇) 전용
| 함수 | 설명 |
|---|---|
direct_cube_speed_control(speed, colorkey, distance) | 라인 추종 속도/색/거리 제어(저지연 전송). |
direct_cube_move_control(speed, distance) | 지정 거리(최대 200cm) 이동. 거리 부호로 방향 결정. |
direct_cube_rotate_control(speed, angle) | 지정 각도(최대 180°) 회전. |
direct_cube_melody_control(melody_number, m1, m2, m3) | 라인봇 멜로디 재생. |
report_sensor_data(colorkey, distance) | 라인보드 센서 데이터를 보고. |