{ "cells": [ { "cell_type": "markdown", "id": "1205c27c", "metadata": {}, "source": [ "\n", "*This notebook contains material from [cbe61622](https://jckantor.github.io/cbe61622);\n", "content is available [on Github](https://github.com/jckantor/cbe61622.git).*\n" ] }, { "cell_type": "markdown", "id": "26bab603", "metadata": {}, "source": [ "\n", "< [6.2 Coding for Device Control](https://jckantor.github.io/cbe61622/06.02-Coding-Paradigms.html) | [Contents](toc.html) | [6.4 Event Driven Programming](https://jckantor.github.io/cbe61622/06.04-Event-Driven-Programming.html) >

\"Open

\"Download\"" ] }, { "cell_type": "markdown", "id": "45d210eb-7479-4ee3-b5a5-3ea631bbcd37", "metadata": { "nbpages": { "level": 1, "link": "[6.3 Coding Paradigms for Device Control](https://jckantor.github.io/cbe61622/06.03-Coding-Paradigms.html#6.3-Coding-Paradigms-for-Device-Control)", "section": "6.3 Coding Paradigms for Device Control" } }, "source": [ "# 6.3 Coding Paradigms for Device Control" ] }, { "cell_type": "code", "execution_count": 34, "id": "594db2ba-61c9-4e32-b571-8a7fcb680a40", "metadata": { "nbpages": { "level": 1, "link": "[6.3 Coding Paradigms for Device Control](https://jckantor.github.io/cbe61622/06.03-Coding-Paradigms.html#6.3-Coding-Paradigms-for-Device-Control)", "section": "6.3 Coding Paradigms for Device Control" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found serial ports: /dev/cu.usbmodem14101, /dev/cu.Bluetooth-Incoming-Port \n", "\u001b[34mConnecting to --port=/dev/cu.usbmodem14101 --baud=115200 \u001b[0m\n", "\u001b[34mReady.\n", "\u001b[0m" ] } ], "source": [ "%serialconnect" ] }, { "cell_type": "markdown", "id": "459543b9-c24e-4ae7-91fe-0cf723b5f00f", "metadata": { "nbpages": { "level": 2, "link": "[6.3.1 The Coding Challenge](https://jckantor.github.io/cbe61622/06.03-Coding-Paradigms.html#6.3.1-The-Coding-Challenge)", "section": "6.3.1 The Coding Challenge" } }, "source": [ "## 6.3.1 The Coding Challenge\n", "\n", "PD control for a Ball on beam device. The device is to sense the position of a ball on a 50cm beam, compare to a setpoint, and adjust beam position with servo motor. The setpoint and control constant is to be given by the device user. Display all relevant data. Use a button to start and stop operation.\n", "\n", "Devices:\n", "\n", "* Distance sensor - sense ball position\n", "* Analog actuator - change beam angle\n", "* Analog sensor - proportional gain\n", "* Analog sensor - derivative time\n", "* Analog sensor - setpoint\n", "* Display - display position, angle\n", "* Display - display control parameters\n", "* Button - Start/Stop\n", "\n", "Create code to:\n", "\n", "* Measure the ball position\n", "* Perform an action in response to the analog signal\n", "* Display state on LCD\n", "* Use on-board LED to show operational status" ] }, { "cell_type": "markdown", "id": "63c19406-7f7a-46d3-9122-e279fac19490", "metadata": { "nbpages": { "level": 2, "link": "[6.3.2 Coding Paradigms](https://jckantor.github.io/cbe61622/06.03-Coding-Paradigms.html#6.3.2-Coding-Paradigms)", "section": "6.3.2 Coding Paradigms" } }, "source": [ "## 6.3.2 Coding Paradigms\n", "\n", "* Single threaded, imperative coding\n", "\n", "* Python classes\n", " * further modularizes coding\n", " * data logging classes* Python classes\n", " * further modularizes coding\n", " * data logging classes\n", " \n", "* Python generators\n", " * separates event loop from device details\n", " * modularizes the device coding\n", " * each device can maintain a separate state\n", "\n", "* Asynchronous coding\n", " * further abstraction the event loop\n", " * non-blocking\n", "* multi-threading" ] }, { "cell_type": "markdown", "id": "4dee46d6-1e28-4d3f-b6f6-eff59accf5a1", "metadata": { "nbpages": { "level": 2, "link": "[6.3.3 Single threaded, imperative coding](https://jckantor.github.io/cbe61622/06.03-Coding-Paradigms.html#6.3.3-Single-threaded,-imperative-coding)", "section": "6.3.3 Single threaded, imperative coding" } }, "source": [ "## 6.3.3 Single threaded, imperative coding" ] }, { "cell_type": "code", "execution_count": 67, "id": "c807402f-f343-4634-b315-a2674998e756", "metadata": { "nbpages": { "level": 2, "link": "[6.3.3 Single threaded, imperative coding](https://jckantor.github.io/cbe61622/06.03-Coding-Paradigms.html#6.3.3-Single-threaded,-imperative-coding)", "section": "6.3.3 Single threaded, imperative coding" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "." ] } ], "source": [ "from machine import Pin, PWM\n", "import time\n", "\n", "class Servo(object):\n", " def __init__(self, gpio, freq=50):\n", " self.gpio = gpio\n", " self.pwm = PWM(Pin(gpio, Pin.IN))\n", " self.pwm.freq(freq)\n", " self.pwm.duty_ns(0)\n", " \n", " def set_value(self, value):\n", " self.pulse_us = 500 + 20*max(0, min(100, value))\n", " self.pwm.duty_ns(int(1000*self.pulse_us))\n", " \n", " def off(self):\n", " self.pwm.duty_ns(0)\n", " \n", "\n", "servo = Servo(16)\n", "\n", "servo.set_value(0)\n", "time.sleep(2)\n", "servo.set_value(100)\n", "time.sleep(2)\n", " \n", "servo.off()" ] }, { "cell_type": "code", "execution_count": 80, "id": "d0bd65f2-1d41-483a-8bfa-f7f5de0ff8d6", "metadata": { "nbpages": { "level": 2, "link": "[6.3.3 Single threaded, imperative coding](https://jckantor.github.io/cbe61622/06.03-Coding-Paradigms.html#6.3.3-Single-threaded,-imperative-coding)", "section": "6.3.3 Single threaded, imperative coding" } }, "outputs": [], "source": [ "from machine import Pin, I2C\n", "from lcd1602 import LCD1602 as LCD\n", "\n", "\n", "class Screen(object):\n", " def __init__(self, id, sda, scl):\n", " self.sda = Pin(sda, Pin.OUT)\n", " self.scl = Pin(scl, Pin.OUT)\n", " self.i2c = I2C(id, sda=self.sda, scl=self.scl)\n", " self.lcd = LCD(self.i2c, 2, 16)\n", " self.lcd.clear()\n", " \n", " def print(self, lines):\n", " for k, line in enumerate(lines):\n", " if line is not None:\n", " self.lcd.setCursor(0, k)\n", " self.lcd.print(line)\n", " \n", " \n", "screen0 = Screen(0, sda=8, scl=9)\n", "screen1 = Screen(1, sda=6, scl=7)\n", " \n", "screen0.print((\"Hello World\", \"Go Irish!\"))\n", "screen1.print([\"\", \"Hello\"])\n" ] }, { "cell_type": "code", "execution_count": 107, "id": "0707702e-4571-4c2c-b738-eee6d9ecbb7f", "metadata": { "nbpages": { "level": 2, "link": "[6.3.3 Single threaded, imperative coding](https://jckantor.github.io/cbe61622/06.03-Coding-Paradigms.html#6.3.3-Single-threaded,-imperative-coding)", "section": "6.3.3 Single threaded, imperative coding" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]O\n", "[missing-OK]\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Traceback (most recent call last):\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ " File \"" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\", line 16, in \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ " File \"" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\", line 9, in __init__\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "NameError: name 'Y' isn't defined\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ ">" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\n", "**[ys] " ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\n", "**[ys] " ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "read failed: [Errno 6] Device not configured\n", "\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\n", "**[ys] " ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\n", "**[ys] " ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "read failed: [Errno 6] Device not configured\n", "\n" ] } ], "source": [ "from machine import Pin\n", "\n", "class PWM_motor(object):\n", " def __init__(self, gpio):\n", " self.pwm = PWM(Pin(gpio))\n", " \n", "class Servo(PWM_motor):\n", " def __init__(self, gpio, freq=50):\n", " super(Y, self).__init__(gpio)\n", " self.pwm.freq(freq)\n", " \n", " def set_value(self, value):\n", " self.pulse_us = 500 + 20*max(0, min(100, value))\n", " self.pwm.duty_ns(int(1000*self.pulse_us))\n", " \n", "servo = Servo(16)\n", "servo.set_value(50)" ] }, { "cell_type": "code", "execution_count": 99, "id": "313b80b6-e5de-493e-94ab-3f2c6ef168f8", "metadata": { "nbpages": { "level": 2, "link": "[6.3.3 Single threaded, imperative coding](https://jckantor.github.io/cbe61622/06.03-Coding-Paradigms.html#6.3.3-Single-threaded,-imperative-coding)", "section": "6.3.3 Single threaded, imperative coding" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "12.7069\n" ] } ], "source": [ "import machine\n", "import time\n", "\n", "class UltrasonicSensor(object):\n", " def __init__(self, gpio):\n", " self.pin = Pin(gpio)\n", " \n", " def get_distance_cm(self):\n", " # send pulse\n", " self.pin.init(Pin.OUT)\n", " self.pin.value(0)\n", " time.sleep_us(2)\n", " self.pin.value(1)\n", " time.sleep_us(10)\n", " self.pin.value(0)\n", "\n", " # listen for response\n", " self.pin.init(Pin.IN)\n", "\n", " # wait for on\n", " t0 = time.ticks_us()\n", " count = 0\n", " while count < 10000:\n", " if self.pin.value():\n", " break\n", " count += 1\n", "\n", " # wait for off\n", " t1 = time.ticks_us()\n", " count = 0\n", " while count < 10000:\n", " if not self.pin.value():\n", " break\n", " count += 1\n", "\n", " t2 = time.ticks_us()\n", "\n", " if t1 - t2 < 530:\n", " return (t2 - t1) / 29 / 2\n", " else:\n", " return 0\n", " \n", "sensor = UltrasonicSensor(20)\n", "print(sensor.get_distance_cm())" ] }, { "cell_type": "code", "execution_count": 105, "id": "262e7cd1-fe2e-495c-b12e-be3b8ac0a0c4", "metadata": { "nbpages": { "level": 2, "link": "[6.3.3 Single threaded, imperative coding](https://jckantor.github.io/cbe61622/06.03-Coding-Paradigms.html#6.3.3-Single-threaded,-imperative-coding)", "section": "6.3.3 Single threaded, imperative coding" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]O....\n", "[missing-OK]\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ ">" ] }, { "name": "stdout", "output_type": "stream", "text": [ "..............................................................................................................................................................................\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "aw " ] }, { "name": "stdout", "output_type": "stream", "text": [ ".\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "a" ] }, { "name": "stdout", "output_type": "stream", "text": [ "..\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\n", "**[ys] " ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\n", "**[ys] " ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "device reports readiness to read but returned no data (device disconnected or multiple access on port?)\n", "\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ ".....\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "ra REPL;" ] }, { "name": "stdout", "output_type": "stream", "text": [ ".\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "aw" ] }, { "name": "stdout", "output_type": "stream", "text": [ "...\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "raw REPL; CTRL-B to exit\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "aw>" ] }, { "name": "stdout", "output_type": "stream", "text": [ "...\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\n", "**[ys] " ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\n", "**[ys] " ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[missing-OK]" ] }, { "name": "stderr", "output_type": "stream", "text": [ "device reports readiness to read but returned no data (device disconnected or multiple access on port?)\n", "\n" ] } ], "source": [ "from machine import Pin, I2C, ADC, PWM\n", "import time\n", "from lcd1602 import LCD1602 as LCD\n", "from knob import Knob\n", "\n", "\n", "class Servo(object):\n", " def __init__(self, gpio, freq=50):\n", " self.gpio = gpio\n", " self.pwm = PWM(Pin(gpio))\n", " self.pwm.freq(freq)\n", " self.pwm.duty_ns(0)\n", " \n", " def set_value(self, value):\n", " self.pulse_us = 500 + 20*max(0, min(100, value))\n", " self.pwm.duty_ns(int(1000*self.pulse_us))\n", " \n", " def off(self):\n", " self.pwm.duty_ns(0)\n", "\n", "\n", "class Screen(object):\n", " def __init__(self, id, sda, scl):\n", " self.sda = Pin(sda, Pin.OUT)\n", " self.scl = Pin(scl, Pin.OUT)\n", " self.i2c = I2C(id, sda=self.sda, scl=self.scl)\n", " self.lcd = LCD(self.i2c, 2, 16)\n", " self.lcd.clear()\n", " \n", " def print(self, lines):\n", " for k, line in enumerate(lines):\n", " if line is not None:\n", " self.lcd.setCursor(0, k)\n", " self.lcd.print(line)\n", "\n", "\n", "## set up led\n", "led = Pin(25, Pin.OUT)\n", "\n", "## set up lcd display 0\n", "display0 = Screen(0, sda=8, scl=9)\n", "display1 = Screen(1, sda=6, scl=7)\n", "\n", "## setup rotary angle sensors\n", "knob0 = Knob(26)\n", "knob1 = Knob(27)\n", "\n", "## setup ultra-sonic distance sensor on Pin 20\n", "sensor = UltrasonicSensor(20)\n", "\n", "## set up servo motor\n", "servo = Servo(16)\n", "\n", "start = time.time()\n", "ball_position = 0\n", "\n", "while time.time() - start < 20:\n", "\n", " ball_position = sensor.get_distance_cm()\n", " ball_setpoint = 50*knob0.get_value()/100\n", " display0.print([f\"SP = {ball_setpoint:0.2f} cm\", \n", " f\"PV = {ball_position:0.2f} cm\"])\n", " \n", " Kp = knob1.get_value()\n", " u = Kp*(ball_setpoint - ball_position)\n", " servo.set_value(u)\n", " \n", " display1.print([f\"Kp = {Kp}\", f\"MV = {dt_us}\"])\n", " time.sleep(0.1)\n", "\n", "servo.off()" ] }, { "cell_type": "markdown", "id": "68914ed1-89f1-430d-9cdb-564bc1808849", "metadata": { "nbpages": { "level": 2, "link": "[6.3.4 Discuss](https://jckantor.github.io/cbe61622/06.03-Coding-Paradigms.html#6.3.4-Discuss)", "section": "6.3.4 Discuss" } }, "source": [ "## 6.3.4 Discuss\n", "\n", "* Does this code provide a working prototype?\n", "* Is this code maintaina" ] }, { "cell_type": "markdown", "id": "5908e0bf", "metadata": {}, "source": [ "\n", "< [6.2 Coding for Device Control](https://jckantor.github.io/cbe61622/06.02-Coding-Paradigms.html) | [Contents](toc.html) | [6.4 Event Driven Programming](https://jckantor.github.io/cbe61622/06.04-Event-Driven-Programming.html) >

\"Open

\"Download\"" ] } ], "metadata": { "kernelspec": { "display_name": "MicroPython - USB", "language": "micropython", "name": "micropython" }, "language_info": { "codemirror_mode": "python", "file_extension": ".py", "mimetype": "text/python", "name": "micropython" } }, "nbformat": 4, "nbformat_minor": 5 }