MQTT for Micropython #1

Dalam tutorial ini, kami akan menunjukkan cara menggunakan MQTT untuk bertukar data antara hape android dan board mikrokontroler ESP8266 menggunakan firmware MicroPython. 

Diagram Progam

  • hape android sebagai publisher akan mengirim pesan text dengan topic “sooko/lampu” dan dengan message “on” atau “off”
  • board esp 8266 sebagai subscriber mensubsribe topic “sooko/lampu” sehingga akan menerima pesan “on” atau “off” dan mengubahnya menjadi logic hight atau low
  • setelah pesan diterima si esp8266 akan ganti mempublish dengan topic “loger” dengan message “saya hidup”
  • sehinnga hape telah mensubsribe “loger” akan menerima pesan “saya hidup”

Catatan : tutorial ini kompatibel dengan board ESP32 dan ESP8266 .

Requirements

Persyaratan yang kita butuhkan meliputi:

  • Laptop terinstal python 3
  • text editor untuk menulis kode (vscode,kate,nano,pycharm,etc)
  • Install esptool untuk flash frimaware micropython ke board
    pip install esptool
  • Install adafruit-ampy untuk mendownload progam ke board
    pip install adafruit-ampy

Flash micropython firmware

  • download micropython firmware dan jalankan pada terminal
esptool.py --port /dev/ttyUSB0 erase_flash
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=detect 0 esp8266-20191220-v1.12.bin

Mulai Menulis Progam

1. buat file bernama wifi.py


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import network
class Wifi(object):
    station = network.WLAN(network.STA_IF)
    state="not_connect"
    def __init__(self, *args, **kwargs):
        super(Wifi,self).__init__(*args, **kwargs)
    def connect(self,ssid,password):
        print("connecting. . . .  .")
        if self.station.isconnected() == True:
            self.state="connected"
            return
        self.station.active(True)
        self.station.disconnect()
        self.station.connect(ssid, password)
        print("connecting")
        while self.station.isconnected() == False:
            self.state="connecting"
            pass
        self.state="connected"
        print(self.station.ifconfig())
    def disconnect(self):
        self.station.disconnect()
        print("forget")
    def isconnected(self):
        return self.state

2. buat file bernama mqtt.py


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
try:
    import usocket as socket
except:
    import 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
    def check_msg(self):
        self.sock.setblocking(False)
        return self.wait_msg()

3. buat file bernama main.py


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import machine
from wifi import Wifi
Wifi().connect("Suko","sooko9999")
from mqtt import MQTTClient
import ubinascii
from machine import Pin
class Iot(object):
    client_id = ubinascii.hexlify(machine.unique_id())
    client=None
    def __init__(self,*args,**kwargs):
        super(Iot,self).__init__(*args,**kwargs)
        try:
            print('mqtt running')
            self.client=MQTTClient(self.client_id,"mqtt.eclipse.org",keepalive=0)
            self.client.connect()
            self.client.set_callback(self.on_message)
            self.client.subscribe(b"sooko/lampu")
           
            while True:
                self.client.wait_msg()
        except:
            print('mqtt stoped')
            Wifi().disconnect()
            machine.reset()
    def on_message(self,topic,msg):
        print(topic,msg)
        if msg==b"on":
            self.grenn_on()
            self.client.publish(b"loger",b"saya hidup")
        elif msg==b"off":
            self.grenn_off()
            self.client.publish(b"loger", b"saya mati")
    def grenn_on(self):
        Pin(2,Pin.OUT).value(0)
    def grenn_off(self):
        Pin(2,Pin.OUT).value(1)
Iot()

Download progam ke board

Setelelah ketiga file diatas berhasil dibuat.saat nya kita medownload progam tadi ke dalam board esp8266 menggunakan adafruit-ampy

Menggunakan adafruit-ampy tulis perintah berikut pada terminal untuk mendownload file satu persatu

  • ampy –port <port com anda> put wifi.py
  • ampy –port <port com anda> put mqtt.py
  • ampy –port <port com anda> put main.py

Leave a Reply

Your email address will not be published. Required fields are marked *