{ "info": { "author": "xlfe", "author_email": "", "bugtrack_url": null, "classifiers": [ "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7" ], "description": "## reticul8\n\n**Remotely articulated MCU endpoints for Python**\n\nreticul8 allows you to use Python to remotely control a compatible microcontroller \nsuch as an Arduino or ESP32.\n\nOn the Python side, it uses Python 3.5+ and asyncio\n\nOn the microcontroller side it uses [PJON](https://github.com/gioblu/PJON) \nand [PJON-cython](https://github.com/xlfe/PJON-cython) to communicate with \nthe micro controller - anything uC that can run PJON should be compatible.\n\nIt also uses [protocol buffers](reticul8.proto) to encapsulate the RPC messages.\n\nFor example, you could use the following setup to wirelessly control an\nESP32 using ESPNOW\n\n```\n \n\"HUB\" (Running Python) <--Serial/UART--> NODE 1 (ESP32)\n ^--ESPNOW --> NODE 2 (ESP32)\n ^--ESPNOW --> NODE n (ESP32)\n```\n\n### Rationale\n\nreticul8 is designed to meet the following requirements :-\n\n* The system should be able to run \"complex application logic\" and be \"internet connected\"\n* Nodes in the system should be able to connect to the hub using a variety of mediums (wired and wireless)\n* Nodes should be able to run on common MCU hardware (Arduino and ESP32 targeted initially)\n* Nodes should be fast and reliable, but don't need to be \"smart\" - application logic can live elsewhere\n* Communication between nodes and controller should be fast and reliable (ie not over the internet!)\n\nNotice that one key requirement is the **absence of internet connectivity**. What happens to your home\nautomation system when the internet goes down? \n\nreticul8 is designed for a home automation system where the nodes are not (necessarily) directly connected\nto the internet. This also has the benefit of making communication between the controller/hub and the nodes much \nfaster than something like pub/sub (<70ms rtt for a two node setup with ESPNOW and ThroughSerial).\n\nBuilding on PJON as the communication medium between the nodes allows for plenty of options.\n\nreticul8 is designed to be part of a home automation system - specifically it allows nodes (eg an ESP32 or Arduino) to \noperate as dumb remote endpoints controlled by a smart controller (eg Python running on RaspberryPi).\n\nCompeting projects include :-\n\n* Mongoose OS - An open source Operating System for the Internet of Things\n* MicroPython - Python for microcontrollers\n* Zerynth - The Middleware for IoT using Python on Microcontrollers\n\nBut when I looked at the features I required, none of these seemed like a good fit. MicroPython and Zerynth seemed to \nbe too \"resource heavy\" to run a simple dumb endpoint. Mongoose OS was a pretty close fit but it still assumes your \nnodes are on the internet.\n\n### Arduino-like API:\n\nThe nodes (endpoints) are controlled using Remote Procedure Calls (RPC) defined with [protocolbuf](reticul8.proto).\n\nAn Arduino-like API is provided :-\n\n```python\nimport asyncio\nimport uvloop\nfrom reticul8 import rpc, pjon_strategies\nfrom reticul8.arduino import *\n\nclass Node(rpc.Node):\n\n async def notify_startup(self):\n print(\"Received startup message from {}\".format(self.device_id))\n\n with self:\n\n # schedule the inbuilt LED to blink 10 times\n with Schedule(count=10, after_ms=100, every_ms=500):\n await digitalWrite(22, LOW)\n\n with Schedule(count=10, after_ms=600, every_ms=500):\n await digitalWrite(22, HIGH)\n\n await asyncio.sleep(10)\n\n #manually blink the LED \n\n await pinMode(22, OUTPUT)\n for i in range(5):\n await digitalWrite(22, HIGH)\n await sleep(.1)\n await digitalWrite(22, LOW)\n await sleep(.1)\n \n #read the value of the pin\n await pinMode(19, INPUT_PULLUP)\n value = await digitalRead(19)\n print(\"HIGH\" if value == HIGH else \"LOW\")\n\n #ping the remote node\n for i in range(10):\n await ping()\n\n #an ESP32 feature - built in PWM\n await PWM_config(22)\n while True:\n await PWM_fade(pin=22, duty=0, fade_ms=500)\n await sleep(1)\n await PWM_fade(pin=22, duty=8192, fade_ms=500)\n await sleep(1)\n\n\nclass PJON(pjon_strategies.SerialAsyncio):\n\n def notify_connection_made(self):\n print(\"ESP32 connected\")\n\n def notify_connection_lost(self):\n asyncio.get_event_loop().stop()\n \n\nasyncio.set_event_loop_policy(uvloop.EventLoopPolicy())\nloop = asyncio.get_event_loop()\ntransport = PJON(device_id=10, url=\"/dev/ttyUSB0\", baudrate=115200)\nNode(remote_device_id=11, transport=PJON)\nloop.run_forever()\nloop.close()\n```\n\n## Supported RPCs\n\nGPIO \n* pinMode()\n* digitalRead()\n* digitalWrite()\n* INPUT -> Watch pin for changes with callback on change, debounce\n\nI2C\n* i2c_read\n* i2c_write\n\nESP32 specific features:\n* PWM (ledc)\n* OTA Update \n\nreticul8 helpers\n* schedule commands to run repeatedly\n* run multiple commands \n\n\n## Planned features\n\n* Analog output\n* Analog input\n* Touch sensor (ESP32)\n* Pulse counter (ESP32)\n\n\n## Building an ESP-IDF component node\n\n[Create a new ESP-IDF project](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html), \nand [add the Arduino component](https://github.com/espressif/arduino-esp32/blob/master/docs/esp-idf_component.md).\n\nAdd reticul8 as a component :-\n\n```bash\ncd components\ngit clone https://github.com/xlfe/reticul8\n```\n\nYour `main.cpp` just needs to setup your PJON buses, and pass these to the reticul8 class. Call setup and loop as per\nthe arduino functions.\n\n```cpp\n\n// Define Wifi config for ESPNOW \n\n#include \"esp_wifi_types.h\"\nstatic wifi_country_t wifi_country = {\n cc: \"AU\",\n schan: 1,\n nchan: 14,\n max_tx_power: 80, // Level 10\n policy: WIFI_COUNTRY_POLICY_MANUAL\n};\n\n#include \"Arduino.h\"\n\n# PJON defines\n\n#define PJON_INCLUDE_ANY\n#define PJON_INCLUDE_TSA\n#define PJON_INCLUDE_EN\n#define TSA_RESPONSE_TIME_OUT 100000\n\n#include \n\nPJON *bus = NULL;\nRETICUL8 *r8 = NULL;\n\nvoid loop() {\n r8->loop();\n}\n\nvoid setup() {\n\n Serial.begin(115200);\n Serial.flush();\n\n //EPSNOW\n StrategyLink *link_esp = new StrategyLink;\n PJON *bus_esp = new PJON();\n\n bus_esp->set_asynchronous_acknowledge(false);\n bus_esp->set_synchronous_acknowledge(true);\n bus_esp->set_packet_id(true);\n bus_esp->set_crc_32(true);\n bus_esp->strategy.set_link(link_esp);\n\n //Uncomment the line below to make a single bus device (eg leaf)\n // otherwise the device is initialised as a bridge between esp-now and serial\n\n // r8 = new RETICUL8(bus_esp, 10); /*\n\n\n //Serial\n StrategyLink *link_tsa = new StrategyLink;\n link_tsa->strategy.set_serial(&Serial);\n\n bus = new PJON(11);\n bus->strategy.set_link(link_tsa);\n bus->set_asynchronous_acknowledge(false);\n bus->set_synchronous_acknowledge(false);\n bus->set_packet_id(false);\n bus->set_crc_32(false);\n\n PJON *secondary[1] = {bus_esp};\n r8 = new RETICUL8(bus, 10, secondary, 1);\n //*/\n\n r8->begin();\n}\n```\n\nFinally, make sure your `component.mk` (in same directory as main.cpp) includes the following :-\n\n```cmake\nCOMPONENT_DEPENDS += reticul8\n\n#Used for build timestamp\nCPPFLAGS += -D\"__COMPILE_TIME__ =`date '+%s'`\"\n```", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/xlfe/reticul8", "keywords": "", "license": "Apache 2.0", "maintainer": "", "maintainer_email": "", "name": "reticul8", "package_url": "https://pypi.org/project/reticul8/", "platform": "", "project_url": "https://pypi.org/project/reticul8/", "project_urls": { "Homepage": "https://github.com/xlfe/reticul8" }, "release_url": "https://pypi.org/project/reticul8/0.1/", "requires_dist": null, "requires_python": "", "summary": "What do you get if you combine Python and and MCUs?", "version": "0.1" }, "last_serial": 4396395, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "bfea16933b517e0661dd7c76f74ddc36", "sha256": "3fc7aae8c662c3053d7592ca1375dc594f80f919fc328a4e4b7f5152c41b0968" }, "downloads": -1, "filename": "reticul8-0.1.tar.gz", "has_sig": false, "md5_digest": "bfea16933b517e0661dd7c76f74ddc36", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19901, "upload_time": "2018-10-20T03:34:50", "url": "https://files.pythonhosted.org/packages/fe/72/ef604987e94e3a0737b1138bed194baffef763adf92d4911102aed055242/reticul8-0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "bfea16933b517e0661dd7c76f74ddc36", "sha256": "3fc7aae8c662c3053d7592ca1375dc594f80f919fc328a4e4b7f5152c41b0968" }, "downloads": -1, "filename": "reticul8-0.1.tar.gz", "has_sig": false, "md5_digest": "bfea16933b517e0661dd7c76f74ddc36", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19901, "upload_time": "2018-10-20T03:34:50", "url": "https://files.pythonhosted.org/packages/fe/72/ef604987e94e3a0737b1138bed194baffef763adf92d4911102aed055242/reticul8-0.1.tar.gz" } ] }