Metadata-Version: 2.4
Name: pykamilab
Version: 3.0.5
Summary: A Python Library For Kamibot-Pi (JupyterLab Desktop ready)
Home-page: http://www.moyalab.com
Author: devdio
Author-email: kei.devdio@gmail.com
License: GNU GPLv3
Keywords: Kamibot Pi,KamibotPi,Robot,JupyterLab
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Education
Classifier: Framework :: Jupyter
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: termcolor
Requires-Dist: pyserial<4,>=3.5
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: license
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

## pykamilab — Package for KamibotPi

KamibotPi 교육용 로봇을 USB 시리얼 동글로 제어하는 Python 라이브러리.
JupyterLab Desktop / Jupyter Notebook 환경에서 바로 사용할 수 있도록 설계됨.
(일반적인 파이썬 환경에서는 기존 `pyKamipi` 패키지를 사용해야함)

### 설치

```bash
pip install pykamilab
```

JupyterLab Desktop 노트북에서는 셀에서 다음을 실행:

```python
%pip install pykamilab
```

> `!pip` 대신 `%pip`을 사용해야 현재 커널 환경에 설치됩니다.

### JupyterLab Desktop 빠른 시작

```python
import pykamilab

# 1) 포트 검색
pykamilab.list_ports()

# 2) 연결 — response_timeout 권장 (펌웨어 무응답 시 셀 자동 종료)
from pykamilab import KamibotPi
bot = KamibotPi('COM3', response_timeout=3.0)
bot.init()

# 3) 5cm 전진
bot.move_forward_unit(5, "-l")
bot.delay(1)
bot.stop()

# 4) 종료 (커널 유지)
bot.disconnect()
```

### `with` 문 사용 (자동 disconnect)

`KamibotPi`는 컨텍스트 매니저를 지원합니다. `with` 블록을 벗어나면
예외가 발생하더라도 `disconnect()`가 자동으로 호출되므로,
포트가 열린 채로 남아 다음 셀 실행을 막는 사고를 방지할 수 있습니다.

```python
import pykamilab

with pykamilab.KamibotPi('COM69') as bot:
    bot.init()
    bot.draw_rect(10)
    bot.delay(1)
# 여기서 블록을 벗어나면 자동으로 disconnect() 호출됨
```

- `bot.init()`은 블록 안에서 한 번 호출해 펌웨어를 준비 상태로 둡니다.
- 블록 내부의 예외도 `__exit__`에서 처리되어 포트는 정상적으로 닫힙니다.
- 노트북에서 사용하는 경우 생성자에 `response_timeout=3.0` 을 같이 넘겨
  펌웨어 무응답으로 셀이 멈추는 상황을 방지하는 것이 좋습니다.

```python
with pykamilab.KamibotPi('COM3', response_timeout=3.0) as bot:
    bot.init()
    bot.move_forward_unit(5, "-l")
    bot.delay(1)
    bot.stop()
```

### 환경별 포트 이름

- Windows: `COM3`, `COM8` 등
- macOS: `/dev/cu.usbserial-*` (`/dev/tty.*` 는 행 가능성)
- Linux: `/dev/ttyUSB*` 또는 `/dev/ttyACM*` (`dialout` 그룹 필요)

### 변경 사항 (v3.0.x)

- `sys.exit()` 호출 제거 — 노트북 커널이 살아 있는 상태로 예외 raise
- `KamibotPi(..., response_timeout=...)` 옵션 추가 (펌웨어 무응답 시 `TimeoutError`)
- `pykamilab.list_ports()` 헬퍼 추가
- `close()`는 `disconnect()`의 alias (커널 안전) — 이후 코드는 `disconnect()` 권장
- 컨텍스트 매니저(`with` 문) 지원 — 블록을 벗어나면 자동 `disconnect()`

### 주요 메서드 한눈에 보기

| 분류 | 메서드 | 설명 |
|------|--------|------|
| 연결 | `init()`, `disconnect()`, `stop()` | 펌웨어 초기화 / 종료 / 두 바퀴 정지 |
| 콘트롤 (속도) | `go_dir_speed()`, `go_forward_speed()`, `go_backward_speed()`, `go_left_speed()`, `go_right_speed()` | 연속 동작 — `stop()` 필요 |
| 정밀 제어 (거리·시간·스텝) | `move_forward_unit()`, `move_backward_unit()`, `move_left_unit()`, `move_right_unit()`, `move_step()`, `move_time()`, `turn_left_speed()`, `turn_right_speed()` | 동작 완료까지 블로킹 |
| 탑모터 | `top_motor_degree()`, `top_motor_abspos()`, `top_motor_time()`, `top_motor_round()`, `top_motor_stop()` | `stop()` 은 바퀴만 — 탑모터는 별도로 정지 |
| 센서 | `get_object_detect()`, `get_line_sensor()`, `get_color_sensor()`, `get_color_elements()`, `get_battery()`, `get_version()` | `False` 인자로 OFF (해당 센서) |
| LED / 사운드 | `turn_led(r,g,b)`, `turn_led_idx(idx)`, `melody(scale, sec)`, `beep()` | |
| 그리기 | `draw_tri()`, `draw_rect()`, `draw_penta()`, `draw_hexa()`, `draw_star()`, `draw_circle()`, `draw_semicircle()`, `draw_arc()` | |
| 맵보드 | `toggle_linetracer()`, `move_forward()`, `move_backward()`, `turn_left()`, `turn_right()`, `turn_back()` | 라인/블록 모드 — 옵션 `"-b"` 로 블록 |
| 대기 | `delay(sec)`, `delayms(ms)`, `wait(ms)` | 블로킹 슬립 |

### example

#### 콘트롤 모드 (속도 기반 연속 동작)

`go_*_speed` / `go_dir_speed` 계열은 "정지 명령이 올 때까지 계속" 형태입니다.
반드시 `kamibot.delay()` 로 동작 시간을 두고, `kamibot.stop()` 으로 정지하세요.

```python
from pykamilab import KamibotPi

kamibot = KamibotPi('COM8')
kamibot.init()

# 좌·우 바퀴 방향과 속도를 따로 지정 (좌:f/100, 우:f/100 → 전진)
kamibot.go_dir_speed("f", 100, "f", 100); kamibot.delay(1)
kamibot.go_dir_speed("b", 100, "f", 100); kamibot.delay(1)  # 제자리 왼쪽 회전
kamibot.go_dir_speed("f", 100, "b", 100); kamibot.delay(1)  # 제자리 오른쪽 회전
kamibot.go_dir_speed("b", 100, "b", 100); kamibot.delay(1)  # 후진
kamibot.stop()

# 좌·우 속도만 지정 (방향은 함수 이름이 결정)
kamibot.go_forward_speed(50, 50);  kamibot.delay(1)
kamibot.go_backward_speed(50, 50); kamibot.delay(1)
kamibot.stop()

# 한쪽 바퀴만 돌려 제자리에서 회전
kamibot.go_left_speed(100);  kamibot.delay(1)
kamibot.go_right_speed(100); kamibot.delay(1)
kamibot.stop()

kamibot.disconnect()
```

#### LED

```python
import random
from pykamilab import KamibotPi

kamibot = KamibotPi('COM3')
kamibot.init()

# RGB 값 직접 지정 (각 0~255)
kamibot.turn_led(255, 0, 0); kamibot.delay(1)   # 빨강
kamibot.turn_led(0, 255, 0); kamibot.delay(1)   # 초록
kamibot.turn_led(0, 0, 255); kamibot.delay(1)   # 파랑

# 미리 정의된 색상표(0~7) 사용 — RGB 계산이 어려운 학습자용 단축 함수
for i in range(8):
    kamibot.turn_led_idx(i)
    kamibot.delay(0.5)

# 랜덤 색상 깜빡임
for _ in range(10):
    kamibot.turn_led(
        random.randrange(0, 255),
        random.randrange(0, 255),
        random.randrange(0, 255),
    )
    kamibot.delay(0.5)

kamibot.disconnect()
```

#### 거리·시간·스텝으로 정밀하게 움직이기

`move_*_unit` 은 단위 옵션으로 같은 함수가 3가지 의미로 동작합니다.

| opt | 의미 | 예시 |
|-----|------|------|
| `"-l"` | 길이(cm) | `move_forward_unit(10, "-l")` → 10cm 전진 |
| `"-t"` | 시간(sec) | `move_forward_unit(3, "-t")` → 3초 전진 |
| `"-s"` | 스텝(step) | `move_forward_unit(50, "-s")` → 50스텝 전진 |

정밀 제어 명령은 **동작 완료까지 블로킹**됩니다. 그래서 끝나기를 기다리는 `delay()`나 추가 `stop()`을 굳이 넣지 않아도 됩니다.

```python
from pykamilab import KamibotPi

kamibot = KamibotPi('COM3')
kamibot.init()

# 단위만 바꿔서 3가지 방식으로 전진
kamibot.move_forward_unit(10, "-l")   # 10cm
kamibot.move_forward_unit(3,  "-t")   # 3초
kamibot.move_forward_unit(50, "-s")   # 50 step

# 좌·우·후진 (cm 기준)
kamibot.move_right_unit(160, "-l")
kamibot.move_left_unit(160,  "-l")
kamibot.move_backward_unit(10, "-l")

# 좌·우 바퀴를 따로 — 스텝 단위 / 시간 단위
kamibot.move_step("f", 100, "f", 100)
kamibot.move_time("f", 10, "f", 10)

# 제자리에서 각도(degree)만큼 회전
kamibot.turn_right_speed(90, speed=100)
kamibot.turn_left_speed(90, speed=100)

kamibot.stop()
kamibot.disconnect()
```

> 📝 참고: 옛 버전에 있던 `turn_continous()` 는 현재 펌웨어와의 호환 문제로 비활성화되어 있습니다. 같은 효과는 `go_left_speed()` / `go_right_speed()` + `delay()` + `stop()` 조합으로 만들 수 있습니다.

#### 센서값 / 배터리 / 소리

센서 함수는 첫 번째 인자에 `True` (기본) 를 넘기면 켜고 값을 읽으며,
`False` 를 넘기면 해당 센서를 끕니다 (전력 절감 / 다른 센서와 충돌 방지).

```python
from pykamilab import KamibotPi

kamibot = KamibotPi('COM3')
kamibot.init()

# 물체감지(IR) — 좌·우 두 채널
left, right = kamibot.get_object_detect()
print(f"object: left={left}, right={right}")
kamibot.get_object_detect(False)   # 다 썼으면 끄기

# 라인센서 — 좌·중·우 (반사값)
left, center, right = kamibot.get_line_sensor()
print(f"line: L={left}, C={center}, R={right}")

# 컬러센서 — 인덱스 또는 R/G/B 원소
idx = kamibot.get_color_sensor()
print(f"color index = {idx}")
r, g, b = kamibot.get_color_elements()
print(f"color rgb = {r},{g},{b}")
kamibot.get_color_elements(False)

# 배터리 / 펌웨어 버전
print(f"battery = {kamibot.get_battery()}")
kamibot.get_version()

# 사운드
kamibot.melody(45, 1)   # scale=45, 1초
kamibot.beep()

kamibot.disconnect()
```

#### Top모터 (위쪽 보조 모터)

> ⚠️ `stop()` 은 좌·우 바퀴만 멈춥니다. 탑모터를 멈추려면 별도로 `top_motor_stop()` 을 호출하세요.

```python
from pykamilab import KamibotPi

kamibot = KamibotPi('COM3')
kamibot.init()

# 1) 각도(degree) 만큼 회전 — 방향: 'l'(왼쪽) / 'r'(오른쪽)
kamibot.top_motor_degree("l", 180); kamibot.delay(1)
kamibot.top_motor_degree("r", 180); kamibot.delay(1)

# 2) 절대 위치(0~180°) 로 이동 — 펜이나 액세서리 위치 잡을 때 편함
kamibot.top_motor_abspos(0);   kamibot.delay(1)
kamibot.top_motor_abspos(90);  kamibot.delay(1)
kamibot.top_motor_abspos(180); kamibot.delay(1)

# 3) 시간(초) 동안 회전
kamibot.top_motor_time("l", 10); kamibot.delay(1)
kamibot.top_motor_time("r", 10); kamibot.delay(1)

# 4) "한 바퀴" 단위로 회전 (round)
kamibot.top_motor_round("r", 1); kamibot.delay(1)

# 5) 회전 중 즉시 정지
kamibot.top_motor_stop()

kamibot.disconnect()
```

#### 그리기 (도형 모드)

각 `draw_*` 호출은 도형 한 개를 완성할 때까지 블로킹입니다.
탑모터에 펜을 꽂아 두면 바닥에 직접 그리고, 없으면 경로만 따라 움직입니다.

```python
from pykamilab import KamibotPi

kamibot = KamibotPi('COM3')
kamibot.init()

kamibot.draw_tri(10);          kamibot.delay(1)   # 정삼각형 (한 변 10)
kamibot.draw_rect(10);         kamibot.delay(1)   # 사각형
kamibot.draw_penta(10);        kamibot.delay(1)   # 오각형
kamibot.draw_hexa(10);         kamibot.delay(1)   # 육각형
kamibot.draw_star(10);         kamibot.delay(1)   # 별
kamibot.draw_circle(10);       kamibot.delay(1)   # 원 (반지름)
kamibot.draw_semicircle(10, "l"); kamibot.delay(1)  # 반원 — 'l'/'r'
kamibot.draw_arc(10, 3);       kamibot.delay(1)   # 원호 (반지름, 비율)

kamibot.disconnect()
```

#### 맵보드 / 라인 트레이서

맵보드 모드에서는 이동 단위가 "칸(블록)" 또는 "라인 한 구간" 입니다.

- 옵션 `"-b"` → **블록 맵보드** (격자 한 칸 단위로 이동/회전)
- 옵션 없음(기본) → **라인 맵보드** (다음 교차로까지 라인을 따라 이동)

```python
from pykamilab import KamibotPi

kamibot = KamibotPi('COM3')
kamibot.init()

# 라인 트레이서 — 켠 뒤 일정 시간 주행 후 끔
kamibot.toggle_linetracer(True, 100)
kamibot.delay(10)
kamibot.toggle_linetracer(False)

# 블록 맵보드 (격자 한 칸씩)
kamibot.move_forward(1, "-b")
kamibot.move_backward(1)        # backward 는 옵션 없이 호출
kamibot.turn_left(1,  "-b")
kamibot.turn_right(1, "-b")
kamibot.turn_back(1,  "-b")

# 라인 맵보드 (라인을 따라 다음 교차로까지)
kamibot.move_forward(1)
kamibot.turn_left()
kamibot.turn_right()
kamibot.turn_back()

kamibot.disconnect()
```
