commit fc2d84dcd8b3df31650ae21bfb5d9691c0e3ce6c Author: androiddrew Date: Mon May 28 15:50:29 2018 -0400 Initial commit. Includes working photoresistor sensor sending data to MQTT broker diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62c8935 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..bfe44bc --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# MicroPython ESP8266 + +## Installation +You will want to make sure you are installing the latest version of [Micropython](hhttps://micropython.org/). Download the appropriate [.bin for your chip](https://micropython.org/download) and flash it to your board using the [esptool](https://github.com/espressif/esptool). You will most likely need the UART driver + +``` +pip install esptool +``` + +### Flashing your board + +First be sure to erase the flash: + +``` +esptool.py -p /dev/tty.SLAB_USBtoUART erase_flash +``` + +Then you will need to write the flash to your board. Be sure to connect with an appropriate baud rate: + +``` +esptool.py --port /dev/tty.SLAB_USBtoUART --baud 115200 write_flash --flash_size=detect 0 esp8266-20170108-v1.8.7.bin + +``` + +### Connecting to your board + +If you are using a mac just leverage the `screen` program: + +``` +screen /dev/tty.SLAB_USBtoUART 115200 +``` + +This should connect you to the boards REPL + +## Connecting to the network + +PLACEHOLDER + +## Code files + +Micropython provides a "Virtual" filesystem for you code and collateral (config files etc.). + +There are two files that you should take note of `boot.py` and `main.py`. The `boot.py` file will be executed immediately as the interpreter is brought online. It is here that we can place code to connect to a network for example. The `main.py` file should contain the entry point for your Micropython code. This will typically follow the same "Initialize" and enter "While Loop" pattern of code that you see if Arduinos + +## Extras + +An excellent source for additional "Standard Library" like code can be found at [Micropython-lib](https://github.com/micropython/micropython-lib). + +For shipping up code to you board I highly suggest using either the [Pycharm Pluggin](https://blog.jetbrains.com/pycharm/2018/01/micropython-plugin-for-pycharm/) or the [ampy](https://github.com/adafruit/ampy) modul +e tool from adafruit. + +``` +ampy --help + +ampy -p /dev/tty.SLAB_USBtoUART -b 115200 ls +``` + +## MQTT on Hassio + +PLACEHOLDER \ No newline at end of file diff --git a/boot.py b/boot.py new file mode 100644 index 0000000..d7aca5c --- /dev/null +++ b/boot.py @@ -0,0 +1,16 @@ +def connect(): + import network + sta_if = network.WLAN(network.STA_IF) + if not sta_if.isconnected(): + print("connecting to wireless network....") + sta_if.active(True) + sta_if.connect(b'YOUR_WIRELESS_SSID', b'YOUR_WIRELESS_PASSWORD') + while not sta_if.isconnected(): + pass + print('network config:', sta_if.ifconfig()) + + #def no_debug(): + # import esp + # esp.osdebug(None) + +connect() \ No newline at end of file diff --git a/light_sensor/main.py b/light_sensor/main.py new file mode 100644 index 0000000..9e68b96 --- /dev/null +++ b/light_sensor/main.py @@ -0,0 +1,76 @@ +import ujson as json +import machine +import ubinascii +import utime +from umqtt.simple import MQTTClient + + +class LightSensor: + """A class for interacting with a photoresistor on a ESP8266. Suggested using 1K Ohm Resistor and 3.3V power + source""" + + def __init__(self, pin=0, threshold=200): + self.adc = machine.ADC(pin) + self.threshold = threshold + + def read_light(self): + if self.adc.read() > self.threshold: + return True + else: + False + + +CONFIG = { + "broker": "10.0.1.146", + "sensor_pin": 0, + "client_id": b"esp8266_" + ubinascii.hexlify(machine.unique_id()), + "topic": b"light", +} + +client = None +sensor_pin = None + + +def setup_pins(): + global sensor_pin + sensor_pin = machine.ADC(CONFIG.get("sensor_pin")) + + +def load_config(): + try: + with open("/config.json") as f: + config = json.loads(f.read()) + except (OSError, ValueError): + print("Couldn't load /config.json") + save_config() + else: + CONFIG.update(config) + print("Loaded config from /config.json") + + +def save_config(): + try: + with open("/config.json", "w") as f: + f.write(json.dumps(CONFIG)) + except OSError: + print("Couldn't save /config.json") + + +def main(): + client = MQTTClient(CONFIG['client_id'], CONFIG['broker']) + client.connect() + print("Connected to {} using id {}".format(CONFIG['broker'], CONFIG['client_id'])) + while True: + data = sensor_pin.read() + payload = json.dumps({"payload": data}).encode('utf-8') + client.publish('{}/{}'.format(CONFIG['topic'], + CONFIG['client_id']), + payload) + print('Sensor state: {}'.format(data)) + utime.sleep(5) + + +if __name__ == '__main__': + load_config() + setup_pins() + main() diff --git a/main.py b/main.py new file mode 100644 index 0000000..9e68b96 --- /dev/null +++ b/main.py @@ -0,0 +1,76 @@ +import ujson as json +import machine +import ubinascii +import utime +from umqtt.simple import MQTTClient + + +class LightSensor: + """A class for interacting with a photoresistor on a ESP8266. Suggested using 1K Ohm Resistor and 3.3V power + source""" + + def __init__(self, pin=0, threshold=200): + self.adc = machine.ADC(pin) + self.threshold = threshold + + def read_light(self): + if self.adc.read() > self.threshold: + return True + else: + False + + +CONFIG = { + "broker": "10.0.1.146", + "sensor_pin": 0, + "client_id": b"esp8266_" + ubinascii.hexlify(machine.unique_id()), + "topic": b"light", +} + +client = None +sensor_pin = None + + +def setup_pins(): + global sensor_pin + sensor_pin = machine.ADC(CONFIG.get("sensor_pin")) + + +def load_config(): + try: + with open("/config.json") as f: + config = json.loads(f.read()) + except (OSError, ValueError): + print("Couldn't load /config.json") + save_config() + else: + CONFIG.update(config) + print("Loaded config from /config.json") + + +def save_config(): + try: + with open("/config.json", "w") as f: + f.write(json.dumps(CONFIG)) + except OSError: + print("Couldn't save /config.json") + + +def main(): + client = MQTTClient(CONFIG['client_id'], CONFIG['broker']) + client.connect() + print("Connected to {} using id {}".format(CONFIG['broker'], CONFIG['client_id'])) + while True: + data = sensor_pin.read() + payload = json.dumps({"payload": data}).encode('utf-8') + client.publish('{}/{}'.format(CONFIG['topic'], + CONFIG['client_id']), + payload) + print('Sensor state: {}'.format(data)) + utime.sleep(5) + + +if __name__ == '__main__': + load_config() + setup_pins() + main() diff --git a/umqtt/simple.py b/umqtt/simple.py new file mode 100644 index 0000000..88f0d07 --- /dev/null +++ b/umqtt/simple.py @@ -0,0 +1,204 @@ +import usocket as socket +import ustruct as struct +from ubinascii import hexlify + +class MQTTException(Exception): + pass + +class MQTTClient: + + def __init__(self, client_id, server, port=0, user=None, password=None, keepalive=0, + ssl=False, ssl_params={}): + if port == 0: + port = 8883 if ssl else 1883 + self.client_id = client_id + self.sock = None + self.server = server + self.port = port + self.ssl = ssl + self.ssl_params = ssl_params + self.pid = 0 + self.cb = None + self.user = user + self.pswd = password + self.keepalive = keepalive + self.lw_topic = None + self.lw_msg = None + self.lw_qos = 0 + self.lw_retain = False + + def _send_str(self, s): + self.sock.write(struct.pack("!H", len(s))) + self.sock.write(s) + + def _recv_len(self): + n = 0 + sh = 0 + while 1: + b = self.sock.read(1)[0] + n |= (b & 0x7f) << sh + if not b & 0x80: + return n + sh += 7 + + def set_callback(self, f): + self.cb = f + + def set_last_will(self, topic, msg, retain=False, qos=0): + assert 0 <= qos <= 2 + assert topic + self.lw_topic = topic + self.lw_msg = msg + self.lw_qos = qos + self.lw_retain = retain + + def connect(self, clean_session=True): + self.sock = socket.socket() + addr = socket.getaddrinfo(self.server, self.port)[0][-1] + self.sock.connect(addr) + if self.ssl: + import ussl + self.sock = ussl.wrap_socket(self.sock, **self.ssl_params) + premsg = bytearray(b"\x10\0\0\0\0\0") + msg = bytearray(b"\x04MQTT\x04\x02\0\0") + + sz = 10 + 2 + len(self.client_id) + msg[6] = clean_session << 1 + if self.user is not None: + sz += 2 + len(self.user) + 2 + len(self.pswd) + msg[6] |= 0xC0 + if self.keepalive: + assert self.keepalive < 65536 + msg[7] |= self.keepalive >> 8 + msg[8] |= self.keepalive & 0x00FF + if self.lw_topic: + sz += 2 + len(self.lw_topic) + 2 + len(self.lw_msg) + msg[6] |= 0x4 | (self.lw_qos & 0x1) << 3 | (self.lw_qos & 0x2) << 3 + msg[6] |= self.lw_retain << 5 + + i = 1 + while sz > 0x7f: + premsg[i] = (sz & 0x7f) | 0x80 + sz >>= 7 + i += 1 + premsg[i] = sz + + self.sock.write(premsg, i + 2) + self.sock.write(msg) + #print(hex(len(msg)), hexlify(msg, ":")) + self._send_str(self.client_id) + if self.lw_topic: + self._send_str(self.lw_topic) + self._send_str(self.lw_msg) + if self.user is not None: + self._send_str(self.user) + self._send_str(self.pswd) + resp = self.sock.read(4) + assert resp[0] == 0x20 and resp[1] == 0x02 + if resp[3] != 0: + raise MQTTException(resp[3]) + return resp[2] & 1 + + def disconnect(self): + self.sock.write(b"\xe0\0") + self.sock.close() + + def ping(self): + self.sock.write(b"\xc0\0") + + def publish(self, topic, msg, retain=False, qos=0): + pkt = bytearray(b"\x30\0\0\0") + pkt[0] |= qos << 1 | retain + sz = 2 + len(topic) + len(msg) + if qos > 0: + sz += 2 + assert sz < 2097152 + i = 1 + while sz > 0x7f: + pkt[i] = (sz & 0x7f) | 0x80 + sz >>= 7 + i += 1 + pkt[i] = sz + #print(hex(len(pkt)), hexlify(pkt, ":")) + self.sock.write(pkt, i + 1) + self._send_str(topic) + if qos > 0: + self.pid += 1 + pid = self.pid + struct.pack_into("!H", pkt, 0, pid) + self.sock.write(pkt, 2) + self.sock.write(msg) + if qos == 1: + while 1: + op = self.wait_msg() + if op == 0x40: + sz = self.sock.read(1) + assert sz == b"\x02" + rcv_pid = self.sock.read(2) + rcv_pid = rcv_pid[0] << 8 | rcv_pid[1] + if pid == rcv_pid: + return + elif qos == 2: + assert 0 + + def subscribe(self, topic, qos=0): + assert self.cb is not None, "Subscribe callback is not set" + pkt = bytearray(b"\x82\0\0\0") + self.pid += 1 + struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid) + #print(hex(len(pkt)), hexlify(pkt, ":")) + self.sock.write(pkt) + self._send_str(topic) + self.sock.write(qos.to_bytes(1, "little")) + while 1: + op = self.wait_msg() + if op == 0x90: + resp = self.sock.read(4) + #print(resp) + assert resp[1] == pkt[2] and resp[2] == pkt[3] + if resp[3] == 0x80: + raise MQTTException(resp[3]) + return + + # Wait for a single incoming MQTT message and process it. + # Subscribed messages are delivered to a callback previously + # set by .set_callback() method. Other (internal) MQTT + # messages processed internally. + def wait_msg(self): + res = self.sock.read(1) + self.sock.setblocking(True) + if res is None: + return None + if res == b"": + raise OSError(-1) + if res == b"\xd0": # PINGRESP + sz = self.sock.read(1)[0] + assert sz == 0 + return None + op = res[0] + if op & 0xf0 != 0x30: + return op + sz = self._recv_len() + topic_len = self.sock.read(2) + topic_len = (topic_len[0] << 8) | topic_len[1] + topic = self.sock.read(topic_len) + sz -= topic_len + 2 + if op & 6: + pid = self.sock.read(2) + pid = pid[0] << 8 | pid[1] + sz -= 2 + msg = self.sock.read(sz) + self.cb(topic, msg) + if op & 6 == 2: + pkt = bytearray(b"\x40\x02\0\0") + struct.pack_into("!H", pkt, 2, pid) + self.sock.write(pkt) + elif op & 6 == 4: + assert 0 + + # Checks whether a pending message from server is available. + # If not, returns immediately with None. Otherwise, does + # the same processing as wait_msg. + def check_msg(self): + self.sock.setblocking(False) + return self.wait_msg() \ No newline at end of file