DynamiKontrol Python API

A DynamiKontrol module

DynamiKontrol is a API for controlling DynamiKontrol module as motors, LEDs or so on. You can find our modules at https://dk.m47rix.com. Please contact us matrix.ai.solution@gmail.com

Installation

Install Python

First download and install Python from here:

https://www.python.org/downloads

or use Anaconda:

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

Install DynamiKontrol Package in pip

To install DynamiKontrol package via pip, simply execute in terminal:

pip install -U DynamiKontrol

This installs the latest version from pypi.

dynamikontrol package

Submodules

dynamikontrol.LED module

class dynamikontrol.LED.LED(module)[source]

Bases: object

LED submodule class.

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()
Parameters

module (object) – Module object.

Blink the LED light periodically.

Parameters
  • color (str, optional) – Color of the LED light. r, y or g. Defaults to all.

  • on_delay (float, optional) – Delay time for turned-on status. on_delay must be between 0.0 to 65.0 in second. Defaults to 0.256.

  • off_delay (float, optional) – Delay time for turned-off status. off_delay must be between 0.0 to 65.0 in second. Defaults to 0.256.

off(color='all')[source]

Turn off the LED light

Parameters

color (str, optional) – Color of the LED light. r, y or g. Defaults to all.

on(color='all')[source]

Turn on the LED light.

Parameters

color (str, optional) – Color of the LED light. r, y or g. Defaults to all.

toggle(color='all')[source]

Toggle the LED light. Turn off while the light on status and turn on while the light off.

Parameters

color (str, optional) – Color of the LED light. r, y or g. Defaults to all.

dynamikontrol.Module module

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

Bases: 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()
Parameters
  • serial_no (str) – Specify serial number of the module.

  • debug (bool) – print debug messages.

connect()[source]

Connect to the module.

disconnect()[source]

Close the connection of the module. Must include module.disconnect() at the end of the code so that module can close connection properly.

get_fw_version()[source]

Get firmware version of the connected module.

Returns

Device firmware version.

Return type

str

get_id()[source]

Get ID of the connected module.

Returns

Module ID.

Return type

int

get_serial_no()[source]

Get serial number of the connected module.

Returns

Serial number.

Return type

str

get_time()[source]

Get device time of the connected module.

Returns

Device time.

Return type

datetime

send(data)[source]

Send the data to the module

Parameters

data (bytearray of int) – Data to send.

set_default_switch_operation(on)[source]

Set either turn default switch operation on or not. Motor is spinning when switch is pressed by default.

Parameters

on (bool) – Set default operation on or off.

dynamikontrol.Motor module

class dynamikontrol.Motor.BLDC(module)[source]

Bases: object

BLDC(Speed) motor submodule class.

from dynamikontrol import Module
import time

module = Module()

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

module.disconnect()
Parameters

module (object) – Module object.

get_speed(func, unit='rpm')[source]

Get speed of the motor asynchronously.

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()
Parameters
  • func (function) – Callback function when getting speed from the motor.

  • unit (str, optional) – Speed unit must be one of rpm, deg/s and rad/s. Defaults to 'rpm'.

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

Control speed of the motor.

Parameters
  • speed (int) – If speed > 0 spins along clockwise, otherwise spins along counter clockwise.

  • period (int, optional) – Control period. period must be between 0.0 to 65.0 in second. Defaults to None.

  • unit (str, optional) – Speed unit must be one of rpm, deg/s and rad/s. Defaults to 'rpm'.

  • func (function, optional) – Callback function when motor has been stopped. Defaults to None.

  • args (tuple, optional) – args for callback function. Defaults to ().

  • kwargs (dict, optional) – kwargs for callback function. Defaults to {}.

stop()[source]

Stop the motor immediately.

class dynamikontrol.Motor.Motor(module)[source]

Bases: object

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

Bases: object

Servo(Angle) motor submodule class.

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()
Parameters

module (object) – Module object.

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

Control the angle of motor.

Parameters
  • angle (int) – If angle > 0 moves along clockwise, otherwise moves along counter clockwise. angle must be between -85 to 85 in degrees.

  • period (float, optional) – Control period. period must be between 0.0 to 65.0 in second. Defaults to None.

  • func (function, optional) – Callback function when motor has been stopped. Defaults to None.

  • args (tuple, optional) – args for callback function. Defaults to ().

  • kwargs (dict, optional) – kwargs for callback function. Defaults to {}.

get_offset()[source]

Get offset of the motor.

Returns

Offset of the motor in degree.

Return type

float

set_offset(angle)[source]

Set offset of the motor. If the motor angle is inclined slightly even angle set to 0, you can adjust offset of the motor manually.

Parameters

angle (float) – Offset of the motor in degree. e.g) 17.5

dynamikontrol.Switch module

class dynamikontrol.Switch.Switch(module)[source]

Bases: object

Switch submodule class.

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()
Parameters

module (object) – Module object.

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

Define callback function when switch is set to off status.

Parameters
  • func (function) – Callback function.

  • args (tuple, optional) – args. Defaults to ().

  • kwargs (dict, optional) – kwargs. Defaults to {}.

  • ch (int, optional) – Switch channel number. Must be 0 or 1. Defaults to 0.

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

Define callback function when switch is set to on status.

Parameters
  • func (function) – Callback function.

  • args (tuple, optional) – args. Defaults to ().

  • kwargs (dict, optional) – kwargs. Defaults to {}.

  • ch (int, optional) – Switch channel number. Must be 0 or 1. Defaults to 0.

press(*args, **kwargs)[source]

Define callback function when switch is pressed. Exactly same as on method.

release(*args, **kwargs)[source]

Define callback function when switch is released. Exactly same as off method.

dynamikontrol.Timer module

class dynamikontrol.Timer.Timer[source]

Bases: object

General timer class.

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)[source]

Call the callback function after specific time.

Parameters
  • func (function) – Callback function.

  • args (tuple, optional) – args. Defaults to ().

  • kwargs (dict, optional) – kwargs. Defaults to {}.

  • after (int, optional) – Callback delay time in seconds. Defaults to 0.

  • interval (int, optional) – Callback interval time in seconds. Defaults to None.

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

Call the callback function at specific time.

Parameters
  • func (function) – Callback function.

  • args (tuple, optional) – args. Defaults to ().

  • kwargs (dict, optional) – kwargs. Defaults to {}.

  • at (datetime str, optional) – Callback time in datetime str. e.g) 2021-03-04 21:57:30. Defaults to None.

  • interval ([type], optional) – Callback interval time in seconds. Defaults to None.

stop()[source]

Stop the timer.

Module contents

Face Tracking Camera

Face tracking algorithm is not included in DynamiKontrol package. So we use Mediapipe face tracking solution for face tracking and OpenCV for streaming webcam.

Explanation

_images/face_tracking_cam.jpg

Let’s define x1, x2 are top left point and bottom right point of face bounding box, then cx would be center point of the face. Mediapipe face tracking solution returns relative coordinates of the input image, so range of cx would be 0.0 to 1.0. For instance, if your face is located in exact center of the image, cx is 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)

If cx < 0.4, face is located in the left of the image, camera should move to left, and it means the motor should move to clockwise(+). Otherwise vice versa.

Source Code

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()

Lunch Roulette

A Machine select your lunch menu with no worries.

_images/lunch_roulette.gif

Source Code

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()

Dial GUI

Simple dial GUI example using PyQt5.

_images/dial_gui.gif

Source Code

Install 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_())

Thermometer

Simple IoT analogue thermometer using weather API. It is around 13 degrees Celsius in the image below.

_images/thermometer.jpg

You can download a graduated ruler from this link.

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

Source Code

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 Door Lock

You can build IoT smart door lock system that can be integrated in the mobile app and the web app.

In this example, we use Flask web framework. You can also use Node.js and other languages and frameworks.

_images/iot_door_lock.gif

Source Code

Server 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()

Client 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>

Indices and tables