├── README.md ├── arduino_server.py ├── extension_arduino_nano.py └── index.js /README.md: -------------------------------------------------------------------------------- 1 | 此插件使用[pymata-io](https://github.com/bilikyar/pymata-aio)库作为Arduino_nano的固件。 2 | 3 | 如果有什么问题,尽管开issue,欢迎来交流。 4 | 5 | ref: [创建你的第一个Scratch3.0 Extension](https://blog.just4fun.site/create-first-Scratch3-Extension.html) 6 | -------------------------------------------------------------------------------- /arduino_server.py: -------------------------------------------------------------------------------- 1 | ''' 2 | arduino nano 3 | requirement: 4 | pip3 install pymata-aio --user 5 | ''' 6 | import zmq 7 | from time import sleep 8 | 9 | from pymata_aio.pymata3 import PyMata3 10 | from pymata_aio.constants import Constants 11 | 12 | # zmq socket 13 | port = 38782 14 | context = zmq.Context() 15 | socket = context.socket(zmq.REP) 16 | socket.bind("tcp://*:%s" % port) 17 | 18 | 19 | def main(): 20 | ConnectedToArduino = False 21 | while True: 22 | if not ConnectedToArduino: 23 | try: 24 | board = PyMata3() 25 | except: 26 | pass 27 | else: 28 | ConnectedToArduino = True 29 | board.set_pin_mode(13, Constants.OUTPUT) 30 | board.digital_write(13, 1) 31 | 32 | arduino_code = socket.recv_json().get("arduino_code") 33 | 34 | if not arduino_code: 35 | socket.send_json({"result": { 36 | "pin_2_state": board.get_pin_state(2), 37 | "pin_3_state": board.get_pin_state(3), 38 | "pin_4_state": board.get_pin_state(4), 39 | "pin_5_state": board.get_pin_state(5), 40 | "pin_6_state": board.get_pin_state(6), 41 | "pin_7_state": board.get_pin_state(7), 42 | "pin_8_state": board.get_pin_state(8), 43 | "pin_9_state": board.get_pin_state(9), 44 | "pin_10_state": board.get_pin_state(10), 45 | "pin_11_state": board.get_pin_state(11), 46 | "pin_12_state": board.get_pin_state(12), 47 | "pin_13_state": board.get_pin_state(13), 48 | "digital_pin_2": board.digital_read(2), 49 | "digital_pin_3": board.digital_read(3), 50 | "digital_pin_4": board.digital_read(4), 51 | "digital_pin_5": board.digital_read(5), 52 | "digital_pin_6": board.digital_read(6), 53 | "digital_pin_7": board.digital_read(7), 54 | "digital_pin_8": board.digital_read(8), 55 | "digital_pin_9": board.digital_read(9), 56 | "digital_pin_10": board.digital_read(10), 57 | "digital_pin_11": board.digital_read(11), 58 | "digital_pin_12": board.digital_read(12), 59 | "digital_pin_13": board.digital_read(13), 60 | "analog_pin_0": board.analog_read(0), 61 | "analog_pin_1": board.analog_read(1), 62 | "analog_pin_2": board.analog_read(2), 63 | "analog_pin_3": board.analog_read(3), 64 | "analog_pin_4": board.analog_read(4), 65 | "analog_pin_5": board.analog_read(5), 66 | "analog_pin_6": board.analog_read(6), 67 | "analog_pin_7": board.analog_read(7), 68 | }}) 69 | sleep(0.05) 70 | continue 71 | 72 | if arduino_code == "quit!": 73 | output = eval("board.shutdown()", {}, { 74 | "board": board, "Constants": Constants}) 75 | socket.send_json({"result": "quit!"}) 76 | break 77 | else: 78 | try: 79 | output = eval(arduino_code, {}, { 80 | "board": board, "Constants": Constants}) 81 | # output = exec(arduino_code) # 安全性问题 82 | except Exception as e: 83 | output = e 84 | socket.send_json({ 85 | "result": { 86 | "output": str(output), 87 | "pin_2_state": board.get_pin_state(2), 88 | "pin_3_state": board.get_pin_state(3), 89 | "pin_4_state": board.get_pin_state(4), 90 | "pin_5_state": board.get_pin_state(5), 91 | "pin_6_state": board.get_pin_state(6), 92 | "pin_7_state": board.get_pin_state(7), 93 | "pin_8_state": board.get_pin_state(8), 94 | "pin_9_state": board.get_pin_state(9), 95 | "pin_10_state": board.get_pin_state(10), 96 | "pin_11_state": board.get_pin_state(11), 97 | "pin_12_state": board.get_pin_state(12), 98 | "pin_13_state": board.get_pin_state(13), 99 | "digital_pin_2": board.digital_read(2), 100 | "digital_pin_3": board.digital_read(3), 101 | "digital_pin_4": board.digital_read(4), 102 | "digital_pin_5": board.digital_read(5), 103 | "digital_pin_6": board.digital_read(6), 104 | "digital_pin_7": board.digital_read(7), 105 | "digital_pin_8": board.digital_read(8), 106 | "digital_pin_9": board.digital_read(9), 107 | "digital_pin_10": board.digital_read(10), 108 | "digital_pin_11": board.digital_read(11), 109 | "digital_pin_12": board.digital_read(12), 110 | "digital_pin_13": board.digital_read(13), 111 | "analog_pin_0": board.analog_read(0), 112 | "analog_pin_1": board.analog_read(1), 113 | "analog_pin_2": board.analog_read(2), 114 | "analog_pin_3": board.analog_read(3), 115 | "analog_pin_4": board.analog_read(4), 116 | "analog_pin_5": board.analog_read(5), 117 | "analog_pin_6": board.analog_read(6), 118 | "analog_pin_7": board.analog_read(7), 119 | }}) 120 | sleep(0.05) 121 | 122 | socket.close() 123 | context.term() 124 | 125 | 126 | if __name__ == '__main__': 127 | main() 128 | -------------------------------------------------------------------------------- /extension_arduino_nano.py: -------------------------------------------------------------------------------- 1 | 2 | ''' 3 | Arduino 4 | requirement: 5 | pip3 install pymata-aio --user 6 | ''' 7 | import zmq 8 | import subprocess 9 | import pathlib 10 | import platform 11 | import time 12 | import threading 13 | 14 | from codelab_adapter import settings 15 | from codelab_adapter.core_extension import Extension 16 | 17 | 18 | def get_python3_path(): 19 | # If it is not working, Please replace python3_path with your local python3 path. shell: which python3 20 | if (platform.system() == "Darwin"): 21 | # which python3 22 | # 不如用PATH python 23 | # 不确定 24 | path = "/usr/local/bin/python3" 25 | if platform.system() == "Windows": 26 | path = "python3" 27 | if platform.system() == "Linux": 28 | path = "/usr/bin/python3" 29 | return path 30 | 31 | 32 | python3_path = get_python3_path() 33 | 34 | 35 | class arduinoExtension(Extension): 36 | def __init__(self): 37 | name = type(self).__name__ # class name 38 | super().__init__(name) 39 | self.scratch3_message = {} 40 | self.TOPIC = "eim/arduino" 41 | self.first_start = 1 42 | 43 | 44 | def run(self): 45 | # 抽象掉这部分 Class 46 | port = 38782 # todo 随机分配 47 | context = zmq.Context.instance() 48 | socket = context.socket(zmq.REQ) 49 | socket.connect("tcp://localhost:%s" % port) 50 | 51 | codelab_adapter_server_dir = pathlib.Path.home( 52 | ) / "codelab_adapter" / "servers" 53 | script = "{}/arduino_server.py".format(codelab_adapter_server_dir) 54 | 55 | cmd = [python3_path, script] 56 | arduino_server = subprocess.Popen(cmd) 57 | settings.running_child_procs.append(arduino_server) 58 | 59 | lock = threading.Lock() 60 | 61 | def request(): 62 | while self._running: 63 | lock.acquire() 64 | self.scratch3_message = self.read() 65 | lock.release() 66 | 67 | bg_task = threading.Thread(target=request) 68 | self.logger.debug("thread start") 69 | bg_task.daemon = True 70 | bg_task.start() 71 | 72 | while self._running: 73 | scratch3_message = self.scratch3_message 74 | self.logger.debug("scratch3_message {}".format(scratch3_message)) 75 | self.scratch3_message = {} 76 | if scratch3_message == {}: 77 | scratch3_message = {"topic": self.TOPIC, "payload": ""} 78 | 79 | topic = scratch3_message.get('topic') 80 | arduino_code = scratch3_message.get("payload") 81 | 82 | if topic == self.TOPIC: 83 | socket.send_json({"arduino_code": arduino_code}) 84 | 85 | result = socket.recv_json().get("result") 86 | 87 | if self.first_start == 1: 88 | self.publish({"topic": "eim/arduino/init","payload": ""}) 89 | self.first_start = 0 90 | 91 | # 发往scratch3.0 92 | self.publish({"topic": self.TOPIC,"payload": result}) 93 | time.sleep(0.05) 94 | 95 | 96 | 97 | 98 | # release socket 99 | socket.send_json({"arduino_code": "quit!"}) 100 | result = socket.recv_json().get("result") 101 | arduino_server.terminate() 102 | arduino_server.wait() 103 | socket.close() 104 | context.term() 105 | 106 | 107 | export = arduinoExtension 108 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const ArgumentType = require("../../extension-support/argument-type"); 2 | const BlockType = require("../../extension-support/block-type"); 3 | const formatMessage = require("format-message"); 4 | const io = require("socket.io-client"); // yarn add socket.io-client socket.io-client@2.2.0 5 | const Cast = require('../../util/cast'); 6 | 7 | /** 8 | * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI. 9 | * @type {string} 10 | */ 11 | // eslint-disable-next-line max-len 12 | const blockIconURI = 13 | "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTkycHQiIGhlaWdodD0iMTczcHQiIHZpZXdCb3g9IjAgMCAxOTIgMTczIiB2ZXJzaW9uPSIxLjEiPgo8ZyBpZD0ic3VyZmFjZTEiPgo8cGF0aCBzdHlsZT0iIHN0cm9rZTpub25lO2ZpbGwtcnVsZTpldmVub2RkO2ZpbGw6cmdiKDEwMCUsNzIuMTU2ODYzJSwxMS43NjQ3MDYlKTtmaWxsLW9wYWNpdHk6MTsiIGQ9Ik0gMTEyLjgwNDY4OCAxMzYuMzE2NDA2IEMgODMuNTQ2ODc1IDExNi41MzEyNSA3Ni4yMTQ4NDQgOTQuMzYzMjgxIDkwLjgwODU5NCA2OS44MDg1OTQgQyAxMTIuNjk1MzEyIDMyLjk4MDQ2OSAxMjQuMTEzMjgxIDE5LjI0MjE4OCAxMjAuNDQ5MjE5IDAuMTg3NSBDIDEzNC44NzUgMTYuOTIxODc1IDE3Ni40MTc5NjkgNjIuNTAzOTA2IDEzMS43ODkwNjIgMTE1LjQwNjI1IEMgMTMxLjc4OTA2MiA5NC44NjMyODEgMTI3LjA5Mzc1IDgzLjI0NjA5NCAxMTcuNjk5MjE5IDgwLjU0Njg3NSBDIDExMS4yNDIxODggODguMjg5MDYyIDEwOC4wMTE3MTkgOTguMTg3NSAxMDguMDExNzE5IDExMC4yMzgyODEgQyAxMDguMDExNzE5IDEyMi4yOTI5NjkgMTA5LjYwOTM3NSAxMzAuOTg0Mzc1IDExMi44MDQ2ODggMTM2LjMxNjQwNiBaIE0gMTEyLjgwNDY4OCAxMzYuMzE2NDA2ICIvPgo8cGF0aCBzdHlsZT0iIHN0cm9rZTpub25lO2ZpbGwtcnVsZTpldmVub2RkO2ZpbGw6cmdiKDIzLjEzNzI1NSUsMjQuNzA1ODgyJSwyNi42NjY2NjclKTtmaWxsLW9wYWNpdHk6MTsiIGQ9Ik0gMC40MTAxNTYgMTU1LjEzNjcxOSBDIDEuMjIyNjU2IDE1MC4yNDIxODggMy4yOTY4NzUgMTQ2LjQ3NjU2MiA2LjU1ODU5NCAxNDMuNTM5MDYyIEMgMTAuNjMyODEyIDEzOS44NTE1NjIgMTUuODk0NTMxIDEzNC40MjU3ODEgMjEuNTI3MzQ0IDEzNi45ODgyODEgQyAyMi40MTc5NjkgMTM3LjM2MzI4MSAyMy4wMDc4MTIgMTM4LjM0Mzc1IDIyLjQ5MjE4OCAxMzkuOTI1NzgxIEMgMjAuOTMzNTk0IDE0NC41MTk1MzEgMTYuNzg1MTU2IDE0NC4yMTg3NSAxMy4yMjY1NjIgMTQ2Ljg1MTU2MiBDIDEwLjI2NTYyNSAxNDkuMTg3NSA3LjY3MTg3NSAxNTIuMTk5MjE5IDcuNDQ5MjE5IDE1NS44OTA2MjUgQyA3LjMwMDc4MSAxNTggNy42NzE4NzUgMTYwLjg1OTM3NSA5LjAwMzkwNiAxNjIuODk0NTMxIEMgMTAuNTU4NTk0IDE2NS4zNzg5MDYgMTMuMjI2NTYyIDE2NS42Nzk2ODggMTUuODIwMzEyIDE2Ni42NjAxNTYgQyAxOS43NSAxNjguMTY0MDYyIDIyLjM0Mzc1IDE2Mi43NDIxODggMjUuNjAxNTYyIDE2Mi41OTM3NSBDIDI4Ljg2MzI4MSAxNjIuMzY3MTg4IDI2Ljc4OTA2MiAxNjcuMTA5Mzc1IDI2LjA0Njg3NSAxNjguMzE2NDA2IEMgMjIuMzQzNzUgMTc0LjExMzI4MSAxNS40NDkyMTkgMTczLjI4NTE1NiA5Ljc0NjA5NCAxNzEuNjI4OTA2IEMgMi44NTU0NjkgMTY5LjU5NzY1NiAtMC43NzczNDQgMTYyLjU5Mzc1IDAuNDEwMTU2IDE1NS4xMzY3MTkgWiBNIDM1LjA1ODU5NCAxNjguNjc5Njg4IEMgMzAuMDE5NTMxIDE2NS42Njc5NjkgMjcuODcxMDk0IDE1OS4zMzk4NDQgMjkuNSAxNTMuNjE3MTg4IEMgMzAuNjg3NSAxNDkuNDAyMzQ0IDMzLjM1NTQ2OSAxNDcuMjE0ODQ0IDM0LjI0MjE4OCAxNDUuMTA5Mzc1IEMgMzYuMDIzNDM4IDE0MS4wNDI5NjkgNDEuMjEwOTM4IDE0Mi4yNDYwOTQgNDQuMDIzNDM4IDE0NS40MTAxNTYgQyA0NS45NTMxMjUgMTQ3LjY2Nzk2OSA0Ni45ODgyODEgMTUwLjIzMDQ2OSA0Ny42NTYyNSAxNTIuNzg5MDYyIEMgNDkuMjEwOTM4IDE1OC43MzgyODEgNDYuNDcyNjU2IDE2My43ODUxNTYgNDIuNTQyOTY5IDE2OCBDIDQwLjkxNDA2MiAxNjkuNzM0Mzc1IDM3LjA1ODU5NCAxNjkuODA4NTk0IDM1LjA1ODU5NCAxNjguNjc5Njg4IFogTSAzNy40Mjk2ODggMTYxLjIyMjY1NiBDIDQyLjc2NTYyNSAxNjIuNDI5Njg4IDQzLjI4NTE1NiAxNTUuMTk5MjE5IDM5LjI4MTI1IDE1MC43NTc4MTIgQyAzNy42NTIzNDQgMTQ5LjAyMzQzOCAyOS42NDg0MzggMTU5LjQxNzk2OSAzNy40Mjk2ODggMTYxLjIyMjY1NiBaIE0gNTQuMzYzMjgxIDE1Ni4wMjczNDQgQyA1NC4zNjMyODEgMTQ2LjYxMzI4MSA2NS43NzM0MzggMTQ1LjE4MzU5NCA3MS40ODA0NjkgMTQwLjUxNTYyNSBDIDczLjI1NzgxMiAxMzkuMDA3ODEyIDcyLjUxNTYyNSAxMzUuMDkzNzUgNzMuNjI4OTA2IDEzMi41MzEyNSBDIDc0LjY2Nzk2OSAxMzAuNzIyNjU2IDc2LjgxNjQwNiAxMjkuMjE4NzUgNzguNTkzNzUgMTI5Ljk3MjY1NiBDIDc5LjMzNTkzOCAxMzAuMzQ3NjU2IDgxLjAzOTA2MiAxMzEuNDc2NTYyIDgwLjc0MjE4OCAxMzIuOTA2MjUgQyA3Ni40NDUzMTIgMTQ0LjgwODU5NCA3Ni44MTY0MDYgMTU3LjgzNTkzOCA3OC45NjQ4NDQgMTcwLjU2MjUgQyA3OC45NjQ4NDQgMTcxLjIzODI4MSA3Ny4xODc1IDE3Mi4zNzEwOTQgNzYuMDc0MjE5IDE3Mi4zNzEwOTQgQyA3Mi44ODY3MTkgMTcyLjc0NjA5NCA3Mi44ODY3MTkgMTY4IDcxLjEwOTM3NSAxNjcuNjI1IEMgNjguMjE4NzUgMTY3LjYyNSA2NS4zMjgxMjUgMTY4LjQ1MzEyNSA2Mi41ODU5MzggMTY2LjU3MDMxMiBDIDU4Ljk1NzAzMSAxNjQuMDg1OTM4IDU0LjM2MzI4MSAxNjEuNTIzNDM4IDU0LjM2MzI4MSAxNTYuMDI3MzQ0IFogTSA2MS44NDc2NTYgMTU0Ljk3MjY1NiBDIDYyLjM2NzE4OCAxNTguNTg5ODQ0IDY2LjQ0MTQwNiAxNjIuMjAzMTI1IDcwLjM2NzE4OCAxNjAuNzczNDM4IEMgNjkuNzAzMTI1IDE1Ni40MDIzNDQgNzEuMTA5Mzc1IDE1Mi40MTQwNjIgNzEuMTA5Mzc1IDE0OC4xMjEwOTQgQyA2Ny4xODM1OTQgMTQ4LjEyMTA5NCA2MS4xMDU0NjkgMTUwLjMwNDY4OCA2MS44NDc2NTYgMTU0Ljk3MjY1NiBaIE0gODUuNzQ2MDk0IDE1MS42NjAxNTYgQyA4NS41OTc2NTYgMTQ0LjIwMzEyNSA5MS4wMDM5MDYgMTM4Ljc4MTI1IDk4LjI2NTYyNSAxMzkuODM1OTM4IEMgMTA0LjA0Njg3NSAxNDAuNjY0MDYyIDEwOS4zMDg1OTQgMTQ2LjMxMjUgMTA5LjIzNDM3NSAxNTIuNjQwNjI1IEMgMTA4LjA1MDc4MSAxNTYuMjUzOTA2IDk5Ljc1IDE1OS4zMzk4NDQgOTYuOTMzNTk0IDE2Mi4wNTA3ODEgQyA5Ni42MzY3MTkgMTYyLjEyODkwNiA5NS4zMDQ2ODggMTYyLjg3ODkwNiA5NS43NSAxNjMuNjMyODEyIEMgOTguMzM5ODQ0IDE2OC4yMjY1NjIgMTAzLjA4NTkzOCAxNjYuMTk1MzEyIDEwNy4yMzQzNzUgMTY0LjAxMTcxOSBDIDEwOS45MDIzNDQgMTY1Ljk2ODc1IDEwOC40OTIxODggMTcwLjQ4ODI4MSAxMDUuNDU3MDMxIDE3MC45Mzc1IEMgMTAyLjEyMTA5NCAxNzEuNDY0ODQ0IDk4LjQ4ODI4MSAxNzIuMzcxMDk0IDk0LjkzMzU5NCAxNzAuNzg5MDYyIEMgODcuNTIzNDM4IDE2Ny4zOTg0MzggODUuODk0NTMxIDE1OC44OTA2MjUgODUuNzQ2MDk0IDE1MS42NjAxNTYgWiBNIDkzLjgyMDMxMiAxNTUuNSBDIDk3LjMwNDY4OCAxNTUuMzUxNTYyIDEwMC43MTQ4NDQgMTU0LjM3MTA5NCAxMDEuODI0MjE5IDE1MC43NTc4MTIgQyAxMDMuNDUzMTI1IDE0Ni45OTIxODggOTcuMzA0Njg4IDE0NS4wMzEyNSA5NC4zMzk4NDQgMTQ2LjYxMzI4MSBDIDkxLjA3ODEyNSAxNDguMzQ3NjU2IDkyLjA0Mjk2OSAxNTUuMzUxNTYyIDkzLjgyMDMxMiAxNTUuNSBaIE0gMTQ5LjgzMjAzMSAxNzEuOTQxNDA2IEMgMTQ4LjQ5MjE4OCAxNzEuMjUgMTQ4LjIyMjY1NiAxNjkuMzg2NzE5IDE0Ny4xOTE0MDYgMTY5LjQ5NjA5NCBDIDE0MS44NTU0NjkgMTY5LjA3MDMxMiAxMzUuOTkyMTg4IDE2OS45OTIxODggMTMyLjUxOTUzMSAxNjUuMjg1MTU2IEMgMTMwLjI0NjA5NCAxNjIuODAwNzgxIDEzMC41IDE1Ny4zOTQ1MzEgMTMyLjA4MjAzMSAxNTQuNzMwNDY5IEMgMTM3LjE5NTMxMiAxNDUuOTM3NSAxNDcuODU1NDY5IDE0Ny4zOTA2MjUgMTQ3Ljk4MDQ2OSAxNDcuMTQ4NDM4IEMgMTQ4LjUzMTI1IDE0Ni4wMzEyNSAxNDguNjIxMDk0IDE0Mi42MTMyODEgMTQ2Ljk2ODc1IDE0Mi40ODQzNzUgQyAxNDIuNTE5NTMxIDE0MS45Njg3NSAxNDAuMDAzOTA2IDE0NC4yNzczNDQgMTM2LjIzODI4MSAxNDYuMDM5MDYyIEMgMTMzLjQwMjM0NCAxNDcuMzk4NDM4IDEzMi4xMDE1NjIgMTQxLjQwMjM0NCAxMzIuNjk1MzEyIDE0MC42NTYyNSBDIDEzNy42MTcxODggMTM2LjQyOTY4OCAxNDguMzc1IDEzMy4xNzU3ODEgMTUyLjQyOTY4OCAxMzkuODYzMjgxIEMgMTU2LjA0Njg3NSAxNDUuOTE3OTY5IDE1My4xNzU3ODEgMTUyLjY2MDE1NiAxNTMuMzkwNjI1IDE1OS42Nzk2ODggQyAxNTMuNTc0MjE5IDE2NC4yNzczNDQgMTU0LjE3NTc4MSAxNzQuMjg1MTU2IDE0OS44MzIwMzEgMTcxLjk0MTQwNiBaIE0gMTQ2LjY2MDE1NiAxNjMuNDg0Mzc1IEMgMTQ3LjMyODEyNSAxNTkuNjQwNjI1IDE0OS4yNTM5MDYgMTU3LjA4MjAzMSAxNDcuNjI1IDE1My4wODk4NDQgQyAxNDMuMzI0MjE5IDE1NC43NDYwOTQgMTM1LjgzOTg0NCAxNTMuNDY4NzUgMTM1LjY5MTQwNiAxNTkuOTQ1MzEyIEMgMTM1LjYxNzE4OCAxNjMuMjU3ODEyIDE0Ni4wNjY0MDYgMTY0LjkxNDA2MiAxNDYuNjYwMTU2IDE2My40ODQzNzUgWiBNIDE2Ny43MzgyODEgMTcwLjc4OTA2MiBDIDE2Ni45OTYwOTQgMTcwLjYzNjcxOSAxNjYuMTc5Njg4IDE2OS4yMDcwMzEgMTY1LjgxMjUgMTY4LjM3ODkwNiBDIDE2NC45OTYwOTQgMTYxLjMwMDc4MSAxNjUuODEyNSAxNTQuNTk3NjU2IDE2NC4yNTM5MDYgMTQ3LjUxOTUzMSBDIDE2My44MDg1OTQgMTQ2LjMxMjUgMTYyLjY5OTIxOSAxNDQuNzMwNDY5IDE2My4wNzAzMTIgMTQzLjE0ODQzOCBDIDE2My40NDE0MDYgMTQyLjM5ODQzOCAxNjQuMjUzOTA2IDE0MS45NDUzMTIgMTY0LjYyNSAxNDEuMTkxNDA2IEMgMTY0Ljk5NjA5NCAxMzYuNDQ5MjE5IDE2My45NTcwMzEgMTMxLjc3NzM0NCAxNjQuOTk2MDk0IDEyNi45NTcwMzEgQyAxNjUuNTE1NjI1IDEyNC42MjUgMTY4LjkyMTg3NSAxMjEuMDg1OTM4IDE3MC44NTE1NjIgMTIzLjc5Njg3NSBDIDE3NS4wNzQyMTkgMTI4LjE2NDA2MiAxNjguOTIxODc1IDEzNC40ODgyODEgMTcxLjk2MDkzOCAxMzkuOTg4MjgxIEMgMTgwLjE4NzUgMTQwLjM2MzI4MSAxODguNzA3MDMxIDE0My41MjczNDQgMTkxLjM3NSAxNTEuNDMzNTk0IEMgMTkzLjAwNzgxMiAxNTYuMTc5Njg4IDE4OS44MjAzMTIgMTYxLjY3NTc4MSAxODUuOTY0ODQ0IDE2NC40NjA5MzggQyAxODAuNTU4NTk0IDE2OC4zNzg5MDYgMTc0LjMzMjAzMSAxNzEuOTkyMTg4IDE2Ny43MzgyODEgMTcwLjc4OTA2MiBaIE0gMTczLjE0ODQzOCAxNjMuMjU3ODEyIEMgMTcxLjIxODc1IDE1Ny43NTc4MTIgMTcxLjIxODc1IDE1MS44MDg1OTQgMTcxLjIxODc1IDE0Ni4zMTI1IEMgMTc2LjYyODkwNiAxNDUuNTU4NTk0IDE4NC42MzI4MTIgMTQ5LjA5NzY1NiAxODQuNzgxMjUgMTU0LjIxODc1IEMgMTg1LjAwMzkwNiAxNTkuNzE4NzUgMTc4LjE4NzUgMTYyLjg3ODkwNiAxNzMuMTQ4NDM4IDE2My4yNTc4MTIgWiBNIDEyMC42ODc1IDE3MS4yMzQzNzUgQyAxMTguMDM1MTU2IDE3MS4yMzQzNzUgMTE1LjIyNjU2MiAxNjguOTQ5MjE5IDExNS44MTY0MDYgMTYzLjk2MDkzOCBDIDExNi40MTAxNTYgMTU4Ljk3NjU2MiAxMTYuNjgzNTk0IDE2MS41IDExNy4yOTI5NjkgMTUyLjI3NzM0NCBDIDExNy44OTg0MzggMTQzLjA1NDY4OCAxMTcuMjkyOTY5IDEzNC44OTQ1MzEgMTE3LjI5Mjk2OSAxMjguNjI4OTA2IEMgMTE3LjI5Mjk2OSAxMjIuMzYzMjgxIDEwOC4yODkwNjIgMTA0Ljc4NTE1NiAxMjAuNzE0ODQ0IDEwNC43ODUxNTYgQyAxMjYuNDAyMzQ0IDEwNy4wNTg1OTQgMTIzLjU1MDc4MSAxMTMuMDQ2ODc1IDEyMy41NTA3ODEgMTE4LjUzMTI1IEMgMTIzLjU1MDc4MSAxMjQuODAwNzgxIDEyMy41NTA3ODEgMTI3LjQxMDE1NiAxMjMuNTUwNzgxIDEzMC43OTY4NzUgQyAxMjMuNTUwNzgxIDEzNC4xODc1IDEyMy41NTA3ODEgMTQ2LjYwNTQ2OSAxMjMuNTUwNzgxIDE1NS40ODA0NjkgQyAxMjMuNTUwNzgxIDE2NC4zNTU0NjkgMTIzLjM0Mzc1IDE3MS4yMzQzNzUgMTIwLjY4NzUgMTcxLjIzNDM3NSBaIE0gMTIwLjY4NzUgMTcxLjIzNDM3NSAiLz4KPC9nPgo8L3N2Zz4K"; 14 | const menuIconURI = blockIconURI; 15 | 16 | /** 17 | * Enum for icon parameter values. 18 | * @readonly 19 | * @enum {string} 20 | */ 21 | 22 | var board = { 23 | digital_pin_2: "None", 24 | digital_pin_3: "None", 25 | digital_pin_4: "None", 26 | digital_pin_5: "None", 27 | digital_pin_6: "None", 28 | digital_pin_7: "None", 29 | digital_pin_8: "None", 30 | digital_pin_9: "None", 31 | digital_pin_10: "None", 32 | digital_pin_11: "None", 33 | digital_pin_12: "None", 34 | digital_pin_13: "None", 35 | analog_pin_0: "None", 36 | analog_pin_1: "None", 37 | analog_pin_2: "None", 38 | analog_pin_3: "None", 39 | analog_pin_4: "None", 40 | analog_pin_5: "None", 41 | analog_pin_6: "None", 42 | analog_pin_7: "None" 43 | }; 44 | 45 | const USBSendInterval = 100; 46 | 47 | 48 | class arduinoBlocks { 49 | constructor(runtime) { 50 | /** 51 | * The runtime instantiating this block package. 52 | * @type {Runtime} 53 | */ 54 | this.runtime = runtime; 55 | 56 | const url = new URL(window.location.href); 57 | var adapterHost = url.searchParams.get("adapter_host"); // 支持树莓派(分布式使用) 58 | if (!adapterHost) { 59 | var adapterHost = "codelab-adapter.codelab.club"; 60 | } 61 | 62 | this.socket = io(`//${adapterHost}:12358` + "/test", { 63 | transports: ["websocket"] 64 | }); 65 | this.socket.on("sensor", msg => { 66 | this.message = msg.message; 67 | const topic = this.message.topic; 68 | const message = this.message.payload; 69 | const first_start = this.message.first_sart 70 | this.message = message; // 可能被清空 71 | this.topic = topic; 72 | this.origin_message = message; 73 | if (this.topic == "eim/arduino/init") { 74 | console.log("extention start"); 75 | board = { 76 | digital_pin_2: "None", 77 | digital_pin_3: "None", 78 | digital_pin_4: "None", 79 | digital_pin_5: "None", 80 | digital_pin_6: "None", 81 | digital_pin_7: "None", 82 | digital_pin_8: "None", 83 | digital_pin_9: "None", 84 | digital_pin_10: "None", 85 | digital_pin_11: "None", 86 | digital_pin_12: "None", 87 | digital_pin_13: "None", 88 | analog_pin_0: "None", 89 | analog_pin_1: "None", 90 | analog_pin_2: "None", 91 | analog_pin_3: "None", 92 | analog_pin_4: "None", 93 | analog_pin_5: "None", 94 | analog_pin_6: "None", 95 | analog_pin_7: "None" 96 | }; 97 | } 98 | }); 99 | } 100 | /** 101 | * The key to load & store a target's test-related state. 102 | * @type {string} 103 | */ 104 | static get STATE_KEY() { 105 | return "Scratch.arduino"; 106 | } 107 | 108 | /** 109 | * @returns {object} metadata for this extension and its blocks. 110 | */ 111 | getInfo() { 112 | return { 113 | id: "arduino", 114 | name: "arduino", 115 | menuIconURI: menuIconURI, 116 | blockIconURI: blockIconURI, 117 | blocks: [ 118 | { 119 | opcode: "read_analog_value", 120 | blockType: BlockType.REPORTER, // BOOLEAN, COMMAND 121 | text: formatMessage({ 122 | id: "arduino.read_analog_value", 123 | default: "read analog value from [analogPinNumber] ", 124 | description: "read_analog_value" 125 | }), 126 | arguments: { 127 | analogPinNumber: { 128 | type: ArgumentType.STRING, 129 | menu: "analogPinNumber", 130 | defaultValue: "0" 131 | } 132 | } 133 | }, 134 | { 135 | opcode: "changeLedState", 136 | blockType: BlockType.COMMAND, 137 | text: formatMessage({ 138 | id: "arduino.changeLedState", 139 | default: "led [digitalPinNumber] set [logicState]", 140 | description: "changeLedState" 141 | }), 142 | arguments: { 143 | digitalPinNumber: { 144 | type: ArgumentType.STRING, 145 | menu: "digitalPinNumber", 146 | defaultValue: "2" 147 | }, 148 | logicState: { 149 | type: ArgumentType.STRING, 150 | menu: "logicState", 151 | defaultValue: "1" 152 | } 153 | } 154 | }, 155 | { 156 | opcode: "changePwmLedValue", 157 | blockType: BlockType.COMMAND, 158 | text: formatMessage({ 159 | id: "arduino.changePwmLedValue", 160 | default: "Pwm_led [PwmPinNumber] set [pwmValue]", 161 | description: "changeLedState" 162 | }), 163 | arguments: { 164 | PwmPinNumber: { 165 | type: ArgumentType.STRING, 166 | menu: "PwmPinNumber", 167 | defaultValue: "9" 168 | }, 169 | pwmValue: { 170 | type: ArgumentType.STRING, 171 | defaultValue: "50" 172 | } 173 | } 174 | }, 175 | { 176 | opcode: "read_button_state", 177 | blockType: BlockType.REPORTER, // BOOLEAN, COMMAND 178 | text: formatMessage({ 179 | id: "arduino.read_button_state", 180 | default: "read button [digitalPinNumber] state", 181 | description: "read_button_state" 182 | }), 183 | arguments: { 184 | digitalPinNumber: { 185 | type: ArgumentType.STRING, 186 | menu: "digitalPinNumber", 187 | defaultValue: "12" 188 | } 189 | } 190 | }, 191 | { 192 | opcode: "changeServoDegree", 193 | blockType: BlockType.COMMAND, 194 | text: formatMessage({ 195 | id: "arduino.changeServoDegree", 196 | default: "servo [PwmPinNumber] set [degree]", 197 | description: "changeServoDegree" 198 | }), 199 | arguments: { 200 | PwmPinNumber: { 201 | type: ArgumentType.STRING, 202 | menu: "PwmPinNumber", 203 | defaultValue: "9" 204 | }, 205 | degree: { 206 | type: ArgumentType.STRING, 207 | defaultValue: "0" 208 | } 209 | } 210 | } 211 | ], 212 | menus: { 213 | digitalPinNumber: [ 214 | "2", 215 | "3", 216 | "4", 217 | "5", 218 | "6", 219 | "7", 220 | "8", 221 | "9", 222 | "10", 223 | "11", 224 | "12", 225 | "13" 226 | ], 227 | analogPinNumber: ["0", "1", "2", "3", "4", "5", "6", "7"], 228 | PwmPinNumber: ["3", "5", "6", "9", "10", "11"], 229 | logicState: ["1", "0"] 230 | } 231 | }; 232 | } 233 | 234 | /** 235 | * Retrieve the block primitives implemented by this package. 236 | * @return {object.} Mapping of opcode to Function. 237 | */ 238 | 239 | read_analog_value(args) { 240 | const topic = "eim/arduino"; 241 | let message = ""; 242 | if (board["analog_pin_" + args.analogPinNumber] == "ANALOG") { 243 | if (this.topic == topic) { 244 | return this.origin_message[ 245 | "analog_pin_" + args.analogPinNumber 246 | ]; 247 | } 248 | } else { 249 | message = 250 | "board.set_pin_mode(" + 251 | args.analogPinNumber + 252 | ", Constants.ANALOG)"; 253 | board["analog_pin_" + args.analogPinNumber] = "ANALOG"; 254 | console.log(message); 255 | this.socket.emit("actuator", { 256 | topic: topic, 257 | payload: message 258 | }); 259 | if (this.topic == topic) { 260 | return this.origin_message[ 261 | "analog_pin_" + args.analogPinNumber 262 | ]; 263 | } 264 | } 265 | } 266 | 267 | changeLedState(args, util) { 268 | const topic = "eim/arduino"; 269 | let message = ""; 270 | if (board["digital_pin_" + args.digitalPinNumber] == "OUTPUT") { 271 | message = 272 | "board.digital_write(" + 273 | args.digitalPinNumber + 274 | "," + 275 | args.logicState + 276 | ")"; 277 | console.log(message); 278 | } else { 279 | message = 280 | "board.set_pin_mode(" + 281 | args.digitalPinNumber + 282 | ", Constants.OUTPUT)"; 283 | board["digital_pin_" + args.digitalPinNumber] = "OUTPUT"; 284 | console.log(message); 285 | this.socket.emit("actuator", { 286 | topic: topic, 287 | payload: message 288 | }); 289 | message = 290 | "board.digital_write(" + 291 | args.digitalPinNumber + 292 | "," + 293 | args.logicState + 294 | ")"; 295 | console.log(message); 296 | } 297 | 298 | this.socket.emit("actuator", { 299 | topic: topic, 300 | payload: message 301 | }); 302 | 303 | 304 | return new Promise(resolve => { 305 | setTimeout(() => { 306 | resolve(); 307 | }, USBSendInterval); 308 | }); 309 | } 310 | 311 | read_button_state(args) { 312 | const topic = "eim/arduino"; 313 | let message = ""; 314 | if (board["digital_pin_" + args.digitalPinNumber] == "INPUT") { 315 | if (this.topic == topic) { 316 | return this.origin_message[ 317 | "digital_pin_" + args.digitalPinNumber 318 | ]; 319 | } 320 | } else { 321 | const message = 322 | "board.set_pin_mode(" + 323 | args.digitalPinNumber + 324 | ", Constants.INPUT)"; 325 | board["digital_pin_" + args.digitalPinNumber] = "INPUT"; 326 | console.log(message); 327 | this.socket.emit("actuator", { 328 | topic: topic, 329 | payload: message 330 | }); 331 | if (this.topic == topic) { 332 | return this.origin_message[ 333 | "digital_pin_" + args.digitalPinNumber 334 | ]; 335 | } 336 | } 337 | } 338 | 339 | changeServoDegree(args) { 340 | const topic = "eim/arduino"; 341 | let message = ""; 342 | if (board["digital_pin_" + args.digitalPinNumber] == "SERVO") { 343 | message = 344 | "board.analog_write(" + 345 | args.PwmPinNumber + 346 | "," + 347 | args.degree + 348 | ")"; 349 | console.log(message); 350 | } else { 351 | message = "board.servo_config(" + args.PwmPinNumber + ")"; 352 | 353 | board["digital_pin_" + args.digitalPinNumber] = "SERVO"; 354 | console.log(message); 355 | this.socket.emit("actuator", { 356 | topic: topic, 357 | payload: message 358 | }); 359 | message = 360 | "board.analog_write(" + 361 | args.PwmPinNumber + 362 | "," + 363 | args.degree + 364 | ")"; 365 | console.log(message); 366 | } 367 | 368 | this.socket.emit("actuator", { 369 | topic: topic, 370 | payload: message 371 | }); 372 | 373 | return new Promise(resolve => { 374 | setTimeout(() => { 375 | resolve(); 376 | }, USBSendInterval); 377 | }); 378 | } 379 | 380 | changePwmLedValue(args) { 381 | const topic = "eim/arduino"; 382 | let message = ""; 383 | if (board["digital_pin_" + args.PwmPinNumber] == "PWM") { 384 | message = 385 | "board.analog_write(" + 386 | args.PwmPinNumber + 387 | "," + 388 | args.pwmValue + 389 | ")"; 390 | console.log(message); 391 | } else { 392 | message = 393 | "board.set_pin_mode(" + args.PwmPinNumber + ", Constants.PWM)"; 394 | board["digital_pin_" + args.PwmPinNumber] = "PWM"; 395 | console.log(message); 396 | this.socket.emit("actuator", { 397 | topic: topic, 398 | payload: message 399 | }); 400 | message = 401 | "board.analog_write(" + 402 | args.PwmPinNumber + 403 | "," + 404 | args.pwmValue + 405 | ")"; 406 | console.log(message); 407 | } 408 | 409 | this.socket.emit("actuator", { 410 | topic: topic, 411 | payload: message 412 | }); 413 | return new Promise(resolve => { 414 | setTimeout(() => { 415 | resolve(); 416 | }, USBSendInterval); 417 | }); 418 | } 419 | } 420 | 421 | module.exports = arduinoBlocks; 422 | --------------------------------------------------------------------------------