-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.rb
284 lines (232 loc) · 7.51 KB
/
main.rb
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
#!mruby
#GR-CITRUS Version 2.50
# GR-CITRUS and WA-MIKAN MQTT Client Sample program
# http://gadget.renesas.com/ja/product/citrus.html
# http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html
WIFI_SSID = "enter_your_wifi_ssid"
WIFI_PASS = "enter_your_wifi_pass"
# MODE – Make IoT a reality for your business
# https://www.tinkermode.com/
MQTT_SERVER = "mqtt.tinkermode.com"
MQTT_PORT = 1883 # TCP=1883, SSL=8883
MQTT_KEEP_ALIVE = 60
USER_NAME = "enter_your_device_id"
PASSWORD = "enter_your_device_API_key"
PUB_TOPIC = "/devices/#{USER_NAME}/event"
SUB_TOPIC = "/devices/#{USER_NAME}/command"
ESP8266_EN = 5
LIGHT_LED = 17
BUTTON_PIN = 10
DEBUG = false
def wait_response(expected_value, timeout)
end_time = millis() + timeout
result = ""
while end_time > millis()
if @wifi.available() > 0
result = result + @wifi.read()
delay 10
end
if result.include?(expected_value)
puts result if DEBUG
return true
end
end
puts result if DEBUG
return false
end
def dump_packet_string(packet)
puts packet.length
debug = ""
packet.chars { |ch| debug += ch.ord.to_s(16) + " " }
puts debug
end
def build_mqtt_connect_packet(params)
keep_alive = params[:keep_alive] || 60
client_id = params[:client_id] || ""
will_topic = params[:will_topic] || ""
will_msg = params[:will_msg] || ""
user_name = params[:user_name] || ""
password = params[:password] || ""
# Calculate Remaining Length
remaining_length = 10 # header length
remaining_length += (2 + client_id.length)
remaining_length += (2 + will_topic.length + 2 + will_msg.length) if will_topic.length > 0 && will_msg.length > 0
remaining_length += (2 + user_name.length + 2 + password.length) if user_name.length > 0 && password.length > 0
# 3.1 CONNECT – Client requests a connection to a Server
# 3.1.1 Fixed header
# MQTT Control Packet type
result = 0x10.chr
# Remaining Length - see section 2.2.3.
len = remaining_length
loop do
encoded_byte = len % 128
len = len.div 128
encoded_byte = encoded_byte | 128 if len > 0
result += encoded_byte.chr
break unless len > 0
end
# 3.1.2 Variable header
# Protocol Name
result += 0x00.chr
result += 0x04.chr
result += "MQTT"
# Protocol Level - MQTT 3.1.1
result += 0x04.chr
# Connect Flags
connect_flag = 0x02
connect_flag |= 0x04 if (will_topic.length > 0 && will_msg.length > 0)
connect_flag |= 0xC0 if (user_name.length > 0 && password.length > 0)
result += connect_flag.chr
# Keep Alive
result += 0x00.chr
result += keep_alive.chr
# 3.1.3 Payload
# 3.1.3.1 Client Identifier
result += (client_id.length >> 8).chr
result += (client_id.length & 0xFF).chr
result += client_id
# 3.1.3.2 Will Topic
# 3.1.3.3 Will Message
if (will_topic.length > 0 && will_msg.length > 0)
result += (will_topic.length >> 8).chr
result += (will_topic.length & 0xFF).chr
result += will_topic
result += (will_msg.length >> 8).chr
result += (will_msg.length & 0xFF).chr
result += will_msg
end
# 3.1.3.4 User Name
# 3.1.3.5 Password
if (user_name.length > 0 && password.length > 0)
result += (user_name.length >> 8).chr
result += (user_name.length & 0xFF).chr
result += user_name
result += (password.length >> 8).chr
result += (password.length & 0xFF).chr
result += password
end
dump_packet_string(result) if DEBUG
return result
end
def build_mqtt_publish_packet(params)
topic = params[:topic] || ""
message = params[:message] || ""
# 3.3 PUBLISH – Publish message
# 3.3.1 Fixed header
result = 0x30.chr # DUP=0, QoS=0 (At most once delivery), RETAIN=0
result += (topic.length + message.length + 2).chr
# 3.3.2 Variable header
# 3.3.2.1 Topic Name
result += (topic.length >> 8).chr
result += (topic.length & 0xFF).chr
result += topic
# 3.3.3 Payload
result += message
dump_packet_string(result) if DEBUG
return result
end
def build_mqtt_subscribe_packet(params)
topic = params[:topic] || ""
# 3.8 SUBSCRIBE - Subscribe to topics
# 3.8.1 Fixed header
result = 0x82.chr
result += (topic.length + 5).chr
# 3.8.2 Variable header
# Packet Identifier set to 0x1
result += 0x00.chr
result += 0x01.chr
# 3.8.3 Payload
# Length
result += (topic.length >> 8).chr
result += (topic.length & 0xFF).chr
result += topic
# Requested QoS = 0
result += 0x00.chr
dump_packet_string(result) if DEBUG
return result
end
def mqtt_request(packet, expected_value)
@wifi.print("AT+CIPSEND=#{packet.length}\r\n")
System.exit "AT+CIPSEND failed" unless wait_response("OK\r\n>", 5000)
@wifi.print(packet)
wait_response(expected_value, 10000)
end
# setup
[ESP8266_EN, LIGHT_LED].each { |pin| pinMode(pin, OUTPUT) }
pinMode(BUTTON_PIN, INPUT_PULLUP)
puts "ESP8266 Reset..."
[LOW, HIGH].each do |value|
digitalWrite(ESP8266_EN, value)
delay 500
end
puts "WiFi Connecting..."
@wifi = Serial.new(3, 115200)
@wifi.print("AT+CWMODE=1\r\n")
System.exit "AT+CWMODE failed" unless wait_response("OK", 1000)
@wifi.print("AT+CWJAP=\"#{WIFI_SSID}\",\"#{WIFI_PASS}\"\r\n")
System.exit "AT+CWJAP failed" unless wait_response("OK", 10000)
if (MQTT_PORT == 1883)
puts "TCP Start..."
@wifi.print("AT+CIPSTART=\"TCP\",\"#{MQTT_SERVER}\",#{MQTT_PORT}\r\n")
System.exit "AT+CIPSTART failed" unless wait_response("OK", 5000)
else
puts "SSL Start..."
@wifi.print("AT+CIPSSLSIZE=4096\r\n")
System.exit "AT+CIPSSLSIZE failed" unless wait_response("OK", 1000)
@wifi.print("AT+CIPSTART=\"SSL\",\"#{MQTT_SERVER}\",#{MQTT_PORT}\r\n")
System.exit "AT+CIPSTART failed" unless wait_response("OK", 5000)
end
puts "MQTT Connect - #{MQTT_SERVER}"
packet = build_mqtt_connect_packet(keep_alive: MQTT_KEEP_ALIVE, user_name: USER_NAME, password: PASSWORD)
System.exit "CONNACK failed" unless mqtt_request(packet, "+IPD,4:" + 0x20.chr + 0x02.chr + 0x00.chr + 0x00.chr) # Connection Accepted
delay(100)
puts "MQTT Subscribe"
packet = build_mqtt_subscribe_packet(topic: SUB_TOPIC)
System.exit "SUBACK failed" unless mqtt_request(packet, "+IPD,5:" + 0x90.chr + 0x03.chr + 0x00.chr + 0x01.chr + 0x00.chr) # Success - Packet Identifier = 0x01, Maximum QoS = 0
delay(100)
puts "Topic - #{SUB_TOPIC}"
@lastmillis = millis()
one_sec_ticker = 0
led_state = 1
loop do
now = millis()
@lastmillis = now if (now - @lastmillis) < 0
if (now - @lastmillis) > 1000
@lastmillis = now
one_sec_ticker += 1
led(led_state)
led_state = (1 - led_state)
# PING
if one_sec_ticker == MQTT_KEEP_ALIVE
puts "MQTT PING"
System.exit "PINGRESP failed" unless mqtt_request((0xc0.chr + 0x00.chr), "+IPD,2:" + 0xD0.chr + 0x00.chr)
one_sec_ticker = 0
end
end
# PUBLISH
if digitalRead(BUTTON_PIN) == 0
puts "MQTT PUBLISH"
packet = build_mqtt_publish_packet(topic: PUB_TOPIC, message: "{ \"eventType\": \"test\", \"eventData\": { \"value\": \"#{now}\" } }")
System.exit "PUBLISH failed" unless mqtt_request(packet, "SEND OK")
end
# Check Subscribe
if @wifi.available() > 0
receive_message = ""
while @wifi.available() > 0
receive_message += @wifi.read()
delay 50 # a little hacky but this may be the best...
end
if receive_message.include?(SUB_TOPIC)
puts "MQTT PUBACK"
System.exit "PUBACK Fail" unless mqtt_request((0x40.chr + 0x02.chr), "SEND OK")
# do something
if receive_message.include?('switch":0')
puts "switch: off"
digitalWrite(LIGHT_LED, LOW)
elsif receive_message.include?('switch":1')
puts "switch: on"
digitalWrite(LIGHT_LED, HIGH)
end
end
end
end