DynamiKontrol Python API

A DynamiKontrol module

DynamiKontrol API는 DynamiKontrol 모듈의 모터, LED와 같은 하드웨어를 제어할 수 있는 사용하기 쉬운 파이썬 API입니다. 구매를 원하시면 https://smartstore.naver.com/dynamikontrol 또는 https://dk.m47rix.com 으로 문의주세요.

설치

파이썬 설치

먼저 아래 링크에서 파이썬을 다운로드 받고 설치합니다

https://www.python.org/downloads

또는 아나콘다를 사용하여 설치합니다

https://www.anaconda.com/products/individual

pip을 이용한 DynamiKontrol 패키지 설치

pip을 이용해 DynamiKontrol 패키지를 설치하려면 터미널 창에서 아래 명령어를 입력하세요:

pip install -U DynamiKontrol

pypi에서 가장 최신 버전의 라이브러리를 설치합니다.

dynamikontrol 패키지

Submodules

dynamikontrol.LED module

class dynamikontrol.LED.LED(module)[소스]

기반 클래스: object

LED 서브 모듈 클래스.

from dynamikontrol import Module
import time

module = Module()

module.led.blink(color='r') # blink red
module.led.toggle(color='g') # toggle green

while True:
    module.led.on(color='y') # turn on yellow
    time.sleep(0.1)

    module.led.off(color='y') # turn off yellow
    time.sleep(0.1)

module.disconnect()
매개변수

module (object) – Module 객체

LED를 주기적으로 깜빡인다.

매개변수
  • color (str, optional) – LED 색상. r, y, g. 기본값 all.

  • on_delay (float, optional) – LED가 켜진 상태로 유지되는 시간, 단위 초. on_delay 는 반드시 0.0 에서 65.0 사이의 값을 갖는다. 기본값 0.256.

  • off_delay (float, optional) – LED가 꺼진 상태로 유지되는 시간, 단위 초. on_delay 는 반드시 0.0 에서 65.0 사이의 값을 갖는다. 기본값 0.256.

off(color='all')[소스]

LED를 끈다.

매개변수

color (str, optional) – LED 색상. r, y, g. 기본값 all.

on(color='all')[소스]

LED를 켠다.

매개변수

color (str, optional) – LED 색상. r, y, g. 기본값 all.

toggle(color='all')[소스]

LED 켜짐/꺼짐 상태를 토글한다. 만약 꺼져있다면 켜고, 켜져 있다면 끈다.

매개변수

color (str, optional) – LED 색상. r, y, g. 기본값 all.

dynamikontrol.Module module

class dynamikontrol.Module.Module(serial_no=None, debug=False)[소스]

기반 클래스: object

Module class.

from dynamikontrol import Module

module = Module(serial_no) # specify the module by serial number

# Print module serial number
print('Serial number: %s' % (module.get_serial_no(),)

module.disconnect()
매개변수
  • serial_no (str) – 특정 시리얼 번호의 모듈을 제어한다.

  • debug (bool) – 디버그 메시지를 출력한다.

connect()[소스]

모듈에 연결한다.

disconnect()[소스]

모듈과 연결을 끊는다. 코드의 마지막에 반드시 module.disconnect() 를 호출해야 정상적으로 연결이 종료된다.

get_fw_version()[소스]

연결된 모듈의 기기 펌웨어 버전을 가져온다.

반환값

모듈의 펌웨어 버전.

반환 형식

str

get_id()[소스]

연결된 모듈의 ID를 가져온다.

반환값

모듈의 ID.

반환 형식

int

get_serial_no()[소스]

연결된 모듈의 시리얼 번호를 가져온다.

반환값

시리얼 번호

반환 형식

str

get_time()[소스]

연결된 모듈의 기기 시간을 가져온다.

반환값

기기 시간

반환 형식

datetime

send(data)[소스]

모듈에 데이터를 보낸다.

매개변수

data (bytearray of int) – 전송할 데이터

set_default_switch_operation(on)[소스]

스위치 기본 동작을 켜거나 끈다. 기본적으로 스위치를 누르면 모터의 기본 동작을 확인할 수 있다.

매개변수

on (bool) – 기본 동작을 켜거나 끈다.

dynamikontrol.Motor module

class dynamikontrol.Motor.BLDC(module)[소스]

기반 클래스: object

BLDC 모터 서브 모듈 클래스

from dynamikontrol import Module
import time

module = Module()

module.motor.speed(1000)
time.sleep(5)
module.motor.stop()

module.disconnect()
매개변수

module (object) – Module 객체

get_speed(func, unit='rpm')[소스]

모터의 현재 속도를 비동기로 요청한다.

from dynamikontrol import Module
import time

module = Module()

module.motor.speed(4000, period=10)

def get_speed_cb(speed):
    print('Current Speed', speed)

for i in range(60):
    time.sleep(0.5)
    module.motor.get_speed(func=get_speed_cb)

module.disconnect()
매개변수
  • func (function) – 모터의 속도를 전달받았을 때 실행할 콜백 함수.

  • unit (str, optional) – 속도 단위는 rpm, deg/s, rad/s 중에 하나의 값을 가진다. 기본값 'rpm'.

speed(speed, period=None, unit='rpm', func=None, args=(), kwargs={})[소스]

모터의 속도를 조절한다.

매개변수
  • speed (int) – 만약 speed > 0 이면 시계방향으로 회전하고, speed < 0 이면 반시계방향으로 회전한다.

  • period (int, optional) – 모터가 움직이는 시간을 정의한다, 단위 초. period 는 반드시 0.0 에서 65.0 사이의 값을 갖는다. 기본값 None.

  • unit (str, optional) – 속도 단위는 rpm, deg/s, rad/s 중에 하나의 값을 가진다. 기본값 'rpm'.

  • func (function, optional) – 모터가 멈추었을 때 실행할 콜백 함수. 기본값 None.

  • args (tuple, optional) – 콜백 함수의 args 인자. 기본값 ().

  • kwargs (dict, optional) – 콜백 함수의 kwargs 인자. 기본값 {}.

stop()[소스]

모터를 즉시 정지한다.

class dynamikontrol.Motor.Motor(module)[소스]

기반 클래스: object

angle(*args, **kwargs)[소스]
get_offset(*args, **kwargs)[소스]
get_speed(*args, **kwargs)[소스]
set_offset(*args, **kwargs)[소스]
speed(*args, **kwargs)[소스]
stop(*args, **kwargs)[소스]
class dynamikontrol.Motor.Servo(module)[소스]

기반 클래스: object

서보 모터 서브 모듈 클래스

from dynamikontrol import Module
import time

module = Module()

module.motor.angle(0)
time.sleep(2)

while True:
    module.motor.angle(45)
    time.sleep(2)

    def cb(string):
        print(string)

    module.motor.angle(-45, func=cb, args=('hello',)) # print 'hello' when motor stopped at -45 degree.
    time.sleep(2)

module.disconnect()
매개변수

module (object) – Module 객체

angle(angle, period=None, func=None, args=(), kwargs={})[소스]

모터의 각도를 조절한다.

매개변수
  • angle (int) – 만약 angle > 0 이면 시계방향으로 움직이고, angle < 0 이면 반시계방향으로 움직인다, 단위 도. angle 값은 반드시 -85 에서 85 사이의 값을 갖는다.

  • period (float, optional) – 모터가 움직이는 시간을 정의한다, 단위 초. period 는 반드시 0.0 에서 65.0 사이의 값을 갖는다. 기본값 None.

  • func (function, optional) – 모터가 멈추었을 때 실행할 콜백 함수. 기본값 None.

  • args (tuple, optional) – 콜백 함수의 args 인자. 기본값 ().

  • kwargs (dict, optional) – 콜백 함수의 kwargs 인자. 기본값 {}.

get_offset()[소스]

모터의 오프셋 각도를 가져온다.

반환값

모터의 오프셋, 단위 도.

반환 형식

float

set_offset(angle)[소스]

모터의 오프셋 각도를 조절한다. 만약 모터의 각도를 0으로 설정했는데도 불구하고 모터의 각도가 어느 한 쪽으로 기울어져 있으면 수동으로 오프셋 각도를 조절할 수 있다.

매개변수

angle (float) – 모터의 오프셋, 단위 도. 예) 17.5

dynamikontrol.Switch module

class dynamikontrol.Switch.Switch(module)[소스]

기반 클래스: object

Switch 서브 모듈 클래스.

from dynamikontrol import Module
import time

module = Module()

def callback(string, angle):
    print(string)
    module.motor.angle(angle)

module.switch.press(callback, ('Switched to on', 85,))
module.switch.release(callback, ('Switched to off', 0,))

while True:
    time.sleep(1)

module.disconnect()
매개변수

module (object) – Module 객체

off(func, args=(), kwargs={}, ch=0)[소스]

스위치를 off 로 설정했을 때 실행할 콜백 함수를 설정한다.

매개변수
  • func (function) – 콜백 함수.

  • args (tuple, optional) – args 인자. 기본값 ().

  • kwargs (dict, optional) – kwargs 인자. 기본값 {}.

  • ch (int, optional) – 스위치의 채널 번호. 반드시 0 또는 1 값을 가져야 한다. 기본값 0.

on(func, args=(), kwargs={}, ch=0)[소스]

스위치를 on 으로 설정했을 때 실행할 콜백 함수를 설정한다.

매개변수
  • func (function) – 콜백 함수.

  • args (tuple, optional) – args 인자. 기본값 ().

  • kwargs (dict, optional) – kwargs 인자. 기본값 {}.

  • ch (int, optional) – 스위치의 채널 번호. 반드시 0 또는 1 값을 가져야 한다. 기본값 0.

press(*args, **kwargs)[소스]

스위치를 눌렀을 때 실행할 콜백 함수를 설정한다. on 메소드와 같다.

release(*args, **kwargs)[소스]

스위치를 눌렀다가 떼었을 때 실행할 콜백 함수를 설정한다. off 메소드와 같다.

dynamikontrol.Timer module

class dynamikontrol.Timer.Timer[소스]

기반 클래스: object

기본 타이머 클래스.

from dynamikontrol import Module, Timer
import time

t1 = Timer()
t2 = Timer()

module = Module()

t1.callback_at(func=module.led.toggle, args=('r',), at='2021-03-02 19:46:30', interval=0.1)

t2.callback_after(func=module.led.toggle, args=('g',), after=1, interval=0.1)

time.sleep(5)

t1.stop()
t2.stop()

module.disconnect()
callback_after(func, args=(), kwargs={}, after=0, interval=None)[소스]

일정 시간 후에 콜백 함수를 호출한다.

매개변수
  • func (function) – 콜백 함수.

  • args (tuple, optional) – args 인자. 기본값 ().

  • kwargs (dict, optional) – kwargs 인자. 기본값 {}.

  • after (int, optional) – 콜백 대기 시간 (초). 기본값 0.

  • interval (int, optional) – 콜백 주기 (초). 기본값 None.

callback_at(func, args=(), kwargs={}, at=None, interval=None)[소스]

특정 시간에 콜백 함수를 호출한다.

매개변수
  • func (function) – 콜백 함수.

  • args (tuple, optional) – args 인자. 기본값 ().

  • kwargs (dict, optional) – kwargs 인자. 기본값 {}.

  • at (datetime str, optional) – 콜백 시간 datetime 문자열. 예) 2021-03-04 21:57:30. 기본값 None.

  • interval ([type], optional) – 콜백 주기 (초). 기본값 None.

stop()[소스]

타이머를 중지한다.

Module contents

얼굴 추적 카메라

DynamiKontrol에는 얼굴 추적 알고리즘이 포함되어 있지 않습니다. 따라서 Mediapipe의 Face tracking solution을 사용하였고, OpenCV를 사용하여 웹캠을 제어합니다.

설명

_images/face_tracking_cam.jpg

x1, x2 를 각각 얼굴 바운딩 박스의 왼쪽 위 좌표, 오른쪽 아래 좌표라고 가정하고, cx 를 얼굴의 중심 좌표라고 가정합시다. Mediapipe는 좌표를 입력 이미지의 상대 좌표로 반환하므로 cx 의 값 범위는 0.0 에서 1.0 사이가 될 것입니다. 예를 들어 이미지 안에서 얼굴이 정중앙에 위치한다면 cx 의 값은 0.5 가 됩니다.

cx = (x1 + x2) / 2 # center of the face

if cx < 0.4: # left -> clockwise
    angle += ANGLE_STEP
    module.motor.angle(angle)
elif cx > 0.6: # right -> counter clockwise
    angle -= ANGLE_STEP
    module.motor.angle(angle)

만약 cx < 0.4 이면 이미지의 왼쪽에 얼굴이 위치하는 것이고, 카메라는 왼쪽으로 회전해야 합니다. 이것은 모터가 시계방향으로 움직이는 것을 뜻합니다. cx > 0.6 이면 반대로 움직입니다.

소스코드

import cv2
import mediapipe as mp
from dynamikontrol import Module

ANGLE_STEP = 1

module = Module()

mp_drawing = mp.solutions.drawing_utils
mp_face_detection = mp.solutions.face_detection

face_detection = mp_face_detection.FaceDetection(
    min_detection_confidence=0.7)

cap = cv2.VideoCapture(0)
angle = 0 # motor current angle

while cap.isOpened():
    ret, img = cap.read()
    if not ret:
        break

    img = cv2.flip(img, 1) # mirror image

    results = face_detection.process(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

    if results.detections:
        for detection in results.detections:
            mp_drawing.draw_detection(img, detection)

            x1 = detection.location_data.relative_bounding_box.xmin # left side of face bounding box
            x2 = x1 + detection.location_data.relative_bounding_box.width # right side of face bounding box

            cx = (x1 + x2) / 2 # center of the face

            if cx < 0.4: # left -> clockwise
                angle += ANGLE_STEP
                module.motor.angle(angle)
            elif cx > 0.6: # right -> counter clockwise
                angle -= ANGLE_STEP
                module.motor.angle(angle)

            cv2.putText(img, '%d deg' % (angle), org=(10, 30), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=255, thickness=2)

            break

    cv2.imshow('Face Cam', img)
    if cv2.waitKey(1) == ord('q'):
        break

cap.release()
face_detection.close()
module.disconnect()

점심 골라주는 기계

오늘 점심 뭐 먹지? 이제 걱정은 그만!

_images/lunch_roulette.gif

소스코드

from dynamikontrol import Module
import time, random

module = Module()

direction = 1 # initial direction, clockwise
start_time = time.time()
stop_time = 5 # stop after 5 seconds
stop_angle = random.randint(-80, 80) # random angle

while True:
    if time.time() - start_time > stop_time:
        module.motor.angle(angle=stop_angle)
        print(f'Motor stopped at {stop_angle} degree')
        break

    direction = direction * -1 # change direction
    module.motor.angle(angle=direction * 80)
    time.sleep(0.6)

module.led.blink()
module.disconnect()

다이얼 GUI

PyQt5를 이용한 간단한 다이얼 GUI

_images/dial_gui.gif

소스코드

PyQt5 설치

pip install PyQt5
from dynamikontrol import Module
from PyQt5.QtWidgets import *
import sys

class Dial(QWidget):
    def __init__(self, module):
        QWidget.__init__(self)
        self.module = module

        self.dial = QDial()
        self.dial.setRange(-150, 150)
        self.dial.setNotchesVisible(True)
        self.dial.valueChanged.connect(self.dialMoved)

        layout = QGridLayout()
        layout.addWidget(self.dial)
        self.setLayout(layout)

        self.setGeometry(500, 500, 500, 500)

    def dialMoved(self):
        self.module.motor.angle(self.dial.value())
        print(f'Dial value {self.dial.value()}')

if __name__ == '__main__':
    app = QApplication(sys.argv)

    screen = Dial(Module())
    screen.show()

    sys.exit(app.exec_())

온도계

날씨 API를 사용하여 간단한 IoT 아날로그 온도계를 만들 수 있습니다. 아래 그림은 섭씨 13도 정도를 나타냅니다.

_images/thermometer.jpg

눈금자를 아래 링크에서 다운로드 받으세요.

https://github.com/TheMatrixGroup/DynamiKontrol/raw/master/examples/thermometer.pdf

소스코드

from dynamikontrol import Module
import requests, time
from datetime import datetime

module = Module()

# weather of Seoul, Korea
lat = 37.566536  # latitude
lon = 126.977966 # longitude
url = f'https://fcc-weather-api.glitch.me/api/current?lat={lat}&lon={lon}'

while True:
    res = requests.get(url).json()
    temp = res['main']['temp']

    print(f'{datetime.now()} temperature {temp} degree')

    angle = int(temp * 10 / 3)
    module.motor.angle(angle)

    time.sleep(60)

module.disconnect()

IoT 도어락

모바일 앱과 웹에서 동작하는 스마트 IoT 도어락 시스템을 쉽게 만들 수 있습니다.

이 예제에서는 Flask 프레임워크를 사용했습니다. Node.js 또는 다른 언어, 프레임워크를 사용할 수 있습니다.

_images/iot_door_lock.gif

소스코드

서버 door_lock.py

from dynamikontrol import Module
from flask import Flask, request, render_template

module = Module()

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/change', methods=['POST'])
def change():
    is_locked = request.form['is_locked']

    if is_locked == 'true':
        module.motor.angle(85)
        module.led.off(color='r')
        module.led.on(color='g')
    else:
        module.motor.angle(-45)
        module.led.off(color='g')
        module.led.on(color='r')

    return {'result': True}

if __name__ == '__main__':
    app.run()
    module.disconnect()

클라이언트 templates/index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <title>IoT Door Lock System</title>
  </head>

  <body>
    <div class="d-flex justify-content-center mt-5">
      <h5>IoT Door Lock System</h5>
    </div>
    <div class="d-flex justify-content-center">
      <div class="custom-control custom-switch">
        <input type="checkbox" class="custom-control-input" id="lockSwitch">
        <label class="custom-control-label" for="lockSwitch">Lock the Door</label>
      </div>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

    <script>
      $(function() {
        $('#lockSwitch').change(function(e) {
          $.ajax({
            method: 'POST',
            url: '/change',
            data: { is_locked: this.checked }
          })
          .done(function(msg) {
            console.log(msg);
          });
        });
      })
    </script>
  </body>
</html>