Wiki source code of 2 Script
Last modified by Devin Chen on 2026/03/10 10:53
Show last authors
| author | version | line-number | content |
|---|---|---|---|
| 1 | = **1 General Script Demo** = | ||
| 2 | |||
| 3 | == **1.1 Address Operation** == | ||
| 4 | |||
| 5 | ((( | ||
| 6 | Write/Read data from address A to B**. **For example:transfer D2 to D0 | ||
| 7 | ))) | ||
| 8 | |||
| 9 | (% style="text-align:center" %) | ||
| 10 | [[image:1624245865976-320.png||height="182" width="1000" class="img-thumbnail"]] | ||
| 11 | |||
| 12 | Depend on diffferent format of data.V-Box use different script functions. | ||
| 13 | for example. addr_setshort(addr,num) Function: Write 16-bit signed decimal address | ||
| 14 | addr_getshort(addr) Function:Read 16-bit signed decimal address | ||
| 15 | addr_getword(string addr)Function: Read 16-bit unsigned decimal address | ||
| 16 | More script function are in the second section of [[“V-BOX Script Interface Manual”>>doc:V-BOX.V-Net.Manual.04 Lua Script.01 Lua Functions.WebHome]] | ||
| 17 | |||
| 18 | == **1.2 Arithmetic** == | ||
| 19 | |||
| 20 | (% style="text-align:center" %) | ||
| 21 | [[image:1624249623612-177.png||height="337" width="400" class="img-thumbnail"]] | ||
| 22 | |||
| 23 | == **1.3 Set 100 to D0~-~-D19** == | ||
| 24 | |||
| 25 | (% style="text-align:center" %) | ||
| 26 | [[image:1624249693457-742.png||height="135" width="400" class="img-thumbnail"]] | ||
| 27 | |||
| 28 | == **1.4 Short message** == | ||
| 29 | |||
| 30 | The following demo shows that when the alarm condition is reached: temp1 > 50 (lasts more than 5 seconds) , then send an "alarm trigger" sms. | ||
| 31 | |||
| 32 | When the alarm condition is released,then send an "alarm release" sms. | ||
| 33 | |||
| 34 | Real-time tags setting | ||
| 35 | [[image:1757401730077-284.png]] | ||
| 36 | |||
| 37 | Script is as below: | ||
| 38 | |||
| 39 | {{code language="lua"}} | ||
| 40 | function sms.main() | ||
| 41 | ------send condition------ | ||
| 42 | local temp1 = addr_getword("@Temperature1") | ||
| 43 | local timer = addr_getword("@Timer") | ||
| 44 | local tag = addr_getbit("@Tag") | ||
| 45 | local tag2 = addr_getbit("@Y0") | ||
| 46 | local sms_timer = addr_getword("@SMSTimer") | ||
| 47 | local sms_id = addr_getword("@id") -- SMS ID | ||
| 48 | |||
| 49 | ------lasting time------ | ||
| 50 | if temp1 > 50 then | ||
| 51 | timer = timer + 1 | ||
| 52 | addr_setword("@Timer", timer) | ||
| 53 | else | ||
| 54 | timer = 0 | ||
| 55 | addr_setword("@Timer", timer) | ||
| 56 | end | ||
| 57 | |||
| 58 | ------send sms & output Y0------ | ||
| 59 | if timer > 5 then | ||
| 60 | if tag == 0 then | ||
| 61 | --send SMS to 2 number | ||
| 62 | local id = send_sms_ira("198****4800", "alarm trigger") | ||
| 63 | local id = send_sms_ira("187****3130", "alarm trigger") | ||
| 64 | addr_setword("@id", id) -- Store SMS ID to dedicated variable | ||
| 65 | addr_setbit("@Tag", 1) | ||
| 66 | addr_setword("@SMSTimer", 0) -- Reset SMS status check timer | ||
| 67 | else | ||
| 68 | -- If SMS already sent, increment timer | ||
| 69 | sms_timer = sms_timer + 1 | ||
| 70 | addr_setword("@SMSTimer", sms_timer) | ||
| 71 | |||
| 72 | -- Check SMS status after 20 seconds | ||
| 73 | if sms_timer >= 20 then | ||
| 74 | local state = sms_get_state(sms_id) | ||
| 75 | addr_setword("@state", state) | ||
| 76 | addr_setword("@SMSTimer", 0) -- Reset timer | ||
| 77 | end | ||
| 78 | end | ||
| 79 | elseif tag == 1 then | ||
| 80 | -- Send alarm release SMS | ||
| 81 | send_sms_ira("198****4800","alarm release") | ||
| 82 | send_sms_ira("187****3130", "alarm release") | ||
| 83 | addr_setbit("@Tag", 0) | ||
| 84 | addr_setword("@SMSTimer", 0) -- Reset timer | ||
| 85 | end | ||
| 86 | end | ||
| 87 | {{/code}} | ||
| 88 | |||
| 89 | == **1.5 Telegram notification** == | ||
| 90 | |||
| 91 | This example shows how to use the Bot API to send message into Telegram group or channel. When monitoring bit "@HDX" changes, it will trigger and send the message. Please replace with your own Token and chat id. | ||
| 92 | |||
| 93 | As for How to get the botToken and chatID, please check the following videos: | ||
| 94 | |||
| 95 | [[https:~~/~~/www.youtube.com/watch?v=zh6yYlnjX7k>>https://www.youtube.com/watch?v=zh6yYlnjX7k]] | ||
| 96 | |||
| 97 | [[https:~~/~~/www.youtube.com/watch?v=Pj8mwuMZZvg>>https://www.youtube.com/watch?v=Pj8mwuMZZvg]] | ||
| 98 | |||
| 99 | |||
| 100 | If when you follow the second video to get chatID and there is a display like the following, you can change "getUpdates" to "deleteWebhook" to solve in website. | ||
| 101 | |||
| 102 | |||
| 103 | (% style="text-align:center" %) | ||
| 104 | [[image:6Z7mdnm13p.png]] | ||
| 105 | |||
| 106 | |||
| 107 | (% style="text-align:center" %) | ||
| 108 | [[image:WjE1P2yxIA.png]] | ||
| 109 | |||
| 110 | After deleting Webhook, you can change to "getUpdates" and continue to follow the second video. | ||
| 111 | |||
| 112 | |||
| 113 | (% style="text-align:center" %) | ||
| 114 | [[image:zl6CJlwhER.png]] | ||
| 115 | |||
| 116 | (% class="box infomessage" %) | ||
| 117 | ((( | ||
| 118 | Note: Bots added to Groups or Channels need to be provided with administrator rights. | ||
| 119 | ))) | ||
| 120 | |||
| 121 | === 1.Permissions === | ||
| 122 | |||
| 123 | Bots added to Groups or Channels need to be provided with administrator rights. For example "joe" is bot I created. | ||
| 124 | |||
| 125 | |||
| 126 | (% style="text-align:center" %) | ||
| 127 | [[image:3GS3dP01Wx.png]] | ||
| 128 | |||
| 129 | (% style="text-align:center" %) | ||
| 130 | [[image:eEyv361VZs.png]] | ||
| 131 | |||
| 132 | |||
| 133 | === 2.URL check === | ||
| 134 | |||
| 135 | You can use the URL to test the status of a group or channel. | ||
| 136 | |||
| 137 | url = "https:~/~/api.telegram.org/bot"..telegramBotToken.."/sendMessage?text="..message.."&chat_id="..telegramChatID | ||
| 138 | |||
| 139 | (% style="text-align:center" %) | ||
| 140 | [[image:telegram URL.png]] | ||
| 141 | |||
| 142 | === 3.Script === | ||
| 143 | |||
| 144 | {{code language="Lua"}} | ||
| 145 | local tempBit = 0 | ||
| 146 | local tempWord = 0 | ||
| 147 | |||
| 148 | local botToken = "5504549693:AAEy6a5G-sOF3CINONxMNABeYnoS4ABVlfg" | ||
| 149 | local chatID = "-641959124"--The chat id from Channel or Group | ||
| 150 | |||
| 151 | local https = require("https") | ||
| 152 | local json = require("json") | ||
| 153 | |||
| 154 | -- Send http.get request and return response result | ||
| 155 | function getHttpsUrl(url) | ||
| 156 | local body = {} | ||
| 157 | local bodyJson = json.encode(body) | ||
| 158 | local header = {} | ||
| 159 | header["content-type"] = "application/json" | ||
| 160 | local result_table, code, headers, status = https.request(url, bodyJson) | ||
| 161 | print("code:"..code) | ||
| 162 | if code~= 200 then | ||
| 163 | return | ||
| 164 | else | ||
| 165 | return body | ||
| 166 | end | ||
| 167 | end | ||
| 168 | |||
| 169 | function sendAlarm(telegramBotToken, message, telegramChatID) | ||
| 170 | local url = "https://api.telegram.org/bot"..telegramBotToken.."/sendMessage?text="..message.."&chat_id="..telegramChatID | ||
| 171 | --local url = 'http://v-box.net' | ||
| 172 | --local url = 'https://www.google.com/' | ||
| 173 | print("Get the link:"..url) | ||
| 174 | getHttpsUrl(url) | ||
| 175 | end | ||
| 176 | |||
| 177 | |||
| 178 | function AlarmNotificate.main() | ||
| 179 | local bitValue = addr_getbit("@HDX"); | ||
| 180 | local message = '' | ||
| 181 | print("b=="..bitValue) | ||
| 182 | if bitValue == 1 and bitValue ~= tempBit then | ||
| 183 | message = 'Alarm triggered, the monitoring point test value is '.. bitValue | ||
| 184 | sendAlarm(botToken, message, chatID) | ||
| 185 | print("Notification pushed of triggering alarm,"..bitValue) | ||
| 186 | elseif bitValue == 0 and bitValue ~= tempBit then | ||
| 187 | message = 'Alarm dismissed, the monitoring point test value is '.. bitValue | ||
| 188 | sendAlarm(botToken, message, chatID) | ||
| 189 | print("Notification pushed of dismissing alarm,"..bitValue) | ||
| 190 | end | ||
| 191 | tempBit = bitValue----Prevent monitoring values from continuous being sent to the platform | ||
| 192 | |||
| 193 | local wordValue = addr_getword("@HDW10") | ||
| 194 | print("w=="..wordValue) | ||
| 195 | --dosomething | ||
| 196 | if wordValue >= 100 and wordValue ~= tempWord and tempWord <= 100 then | ||
| 197 | message = 'Word alarm triggered, the word value is '.. wordValue | ||
| 198 | sendAlarm(botToken, message, chatID) | ||
| 199 | print("Notification pushed of triggering alarm,"..wordValue) | ||
| 200 | elseif wordValue < 100 and wordValue ~= tempWord and tempWord >= 100 then | ||
| 201 | message = 'Word alarm dismissed, the word value is '.. wordValue | ||
| 202 | sendAlarm(botToken, message, chatID) | ||
| 203 | print("Notification pushed of dismissing alarm,"..wordValue) | ||
| 204 | end | ||
| 205 | tempWord = wordValue----Prevent monitoring values from continuous being sent to the platform | ||
| 206 | end | ||
| 207 | {{/code}} | ||
| 208 | |||
| 209 | [[image:1715678008611-743.png]] | ||
| 210 | |||
| 211 | (% class="box infomessage" %) | ||
| 212 | ((( | ||
| 213 | Note: The name of script must be the same as the main function. | ||
| 214 | ))) | ||
| 215 | |||
| 216 | == **1.6 LINE Notify(Not available)** == | ||
| 217 | |||
| 218 | {{info}} | ||
| 219 | The following scripts are no longer available because the related API service has been discontinued. | ||
| 220 | {{/info}} | ||
| 221 | |||
| 222 | This example shows how to use the LINE Notify to send message into LINE group. When monitoring bit "@test" changes, it will trigger and send the message. Please replace with your own Token. | ||
| 223 | |||
| 224 | {{code language="lua"}} | ||
| 225 | local tempBit = 0 | ||
| 226 | local tempWord = 0 | ||
| 227 | |||
| 228 | local LineToken = "08XCpubkOdwGdGgRTXF0x8umiyrALtoM0v6lBFUV6PC" | ||
| 229 | |||
| 230 | local https = require("https") | ||
| 231 | local json = require("json") | ||
| 232 | local ltn12 = require("ltn12") | ||
| 233 | |||
| 234 | -- Send http.get request and return response result | ||
| 235 | function getHttpsUrl(url,header,reqbody) | ||
| 236 | local body = {} | ||
| 237 | local bodyJson = json.encode(body) | ||
| 238 | local result_table, code, headers, status = https.request{ | ||
| 239 | method = "POST", | ||
| 240 | url = url, | ||
| 241 | source = ltn12.source.string(reqbody), | ||
| 242 | headers = header, | ||
| 243 | sink = ltn12.sink.table(body) | ||
| 244 | } | ||
| 245 | print("code:"..code) | ||
| 246 | if code~= 200 then | ||
| 247 | return | ||
| 248 | else | ||
| 249 | return body | ||
| 250 | end | ||
| 251 | end | ||
| 252 | |||
| 253 | function getMessageUrl(lineMessage) | ||
| 254 | local url = "https://notify-api.line.me/api/notify" | ||
| 255 | local reqMess = "message="..lineMessage | ||
| 256 | local headers = | ||
| 257 | { | ||
| 258 | ["Authorization"] = "Bearer "..LineToken, | ||
| 259 | ["Content-Type"] = "application/x-www-form-urlencoded", | ||
| 260 | ["Content-Length"] = #reqMess | ||
| 261 | } | ||
| 262 | |||
| 263 | print("Get the link:"..url) | ||
| 264 | getHttpsUrl(url, headers, reqMess) | ||
| 265 | end | ||
| 266 | |||
| 267 | |||
| 268 | function linenotify.main() | ||
| 269 | local bitValue = addr_getbit("@test"); | ||
| 270 | local message = '' | ||
| 271 | print("b=="..bitValue) | ||
| 272 | if bitValue == 1 and bitValue ~= tempBit then | ||
| 273 | message = 'Alarm V-Box triggered, the output is '.. bitValue | ||
| 274 | getMessageUrl(message) | ||
| 275 | print("Notification pushed of triggering alarm,"..bitValue) | ||
| 276 | elseif bitValue == 0 and bitValue ~= tempBit then | ||
| 277 | message = 'Alarm V-Box dismissed, the output is '.. bitValue | ||
| 278 | getMessageUrl(message) | ||
| 279 | print("Notification pushed of dismissing alarm,"..bitValue) | ||
| 280 | end | ||
| 281 | tempBit = bitValue----Prevent monitoring values from continuous being sent to the platform | ||
| 282 | |||
| 283 | local wordValue = addr_getword("@t2") | ||
| 284 | print("w=="..wordValue) | ||
| 285 | --dosomething | ||
| 286 | if wordValue >= 100 and wordValue ~= tempWord and tempWord <= 100 then | ||
| 287 | message = 'Alarm V-Box triggered, the temperature is '.. wordValue | ||
| 288 | getMessageUrl(message) | ||
| 289 | print("Notification pushed of triggering alarm,"..wordValue) | ||
| 290 | elseif wordValue < 100 and wordValue ~= tempWord and tempWord >= 100 then | ||
| 291 | message = 'Alarm V-Box dismissed, the temperature is '.. wordValue | ||
| 292 | getMessageUrl(message) | ||
| 293 | print("Notification pushed of dismissing alarm,"..wordValue) | ||
| 294 | end | ||
| 295 | tempWord = wordValue----Prevent monitoring values from continuous being sent to the platform | ||
| 296 | end | ||
| 297 | {{/code}} | ||
| 298 | |||
| 299 | == **1.7 Twilio WhatsApp Messaging** == | ||
| 300 | |||
| 301 | This example shows how to use the Twilio API to send WhatsApp message to private number. When monitoring bit "@testBit" changes, it will trigger and send the message. Please replace with your own SID, Token, twilioPhoneNumber and receiverPhoneNumber. | ||
| 302 | |||
| 303 | About how to register the Twilio API, please check the following video: | ||
| 304 | |||
| 305 | [[https:~~/~~/www.youtube.com/watch?v=Id4lKichauU>>https://www.youtube.com/watch?v=Id4lKichauU]] | ||
| 306 | |||
| 307 | {{code language="Lua"}} | ||
| 308 | local tempBit = 0 | ||
| 309 | local tempWord = 0 | ||
| 310 | |||
| 311 | local https = require("https") | ||
| 312 | local json = require("json") | ||
| 313 | local ltn12 = require("ltn12") | ||
| 314 | |||
| 315 | local SID = 'AC1703bd710ffa98006d2bcc0b********' | ||
| 316 | local Token = 'd3c11897623c39e538b20263ec19****' | ||
| 317 | |||
| 318 | local twilioPhoneNumber = '+14155238886' | ||
| 319 | local receiverPhoneNumber = '+8615880018277' | ||
| 320 | |||
| 321 | local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' | ||
| 322 | function encodingBase64(data) | ||
| 323 | return ((data:gsub('.', function(x) | ||
| 324 | local r,b='',x:byte() | ||
| 325 | for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end | ||
| 326 | return r; | ||
| 327 | end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x) | ||
| 328 | if (#x < 6) then return '' end | ||
| 329 | local c=0 | ||
| 330 | for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end | ||
| 331 | return b:sub(c+1,c+1) | ||
| 332 | end)..({ '', '==', '=' })[#data%3+1]) | ||
| 333 | end | ||
| 334 | |||
| 335 | function encodeUrl(str) | ||
| 336 | str = string.gsub(str, "([^%w%.%- ])", function(c) | ||
| 337 | return string.format("%%%02X", string.byte(c)) end) | ||
| 338 | return string.gsub(str, " ", "+") | ||
| 339 | end | ||
| 340 | |||
| 341 | |||
| 342 | |||
| 343 | |||
| 344 | function requestBodySplice(message, sender, receiver) | ||
| 345 | local reqBody = '' | ||
| 346 | local encodeMess = encodeUrl(message) | ||
| 347 | local encodeSend = encodeUrl(sender) | ||
| 348 | local encodeRece = encodeUrl(receiver) | ||
| 349 | --reqBody = "Body=Hello%20Wecon2&From=whatsapp%3A%2B14155238886&To=whatsapp%3A%2B8615880018277" | ||
| 350 | reqBody = string.format("Body=%s&From=whatsapp:%s&To=whatsapp:%s", encodeMess, encodeSend, encodeRece) | ||
| 351 | print(reqBody) | ||
| 352 | return reqBody | ||
| 353 | end | ||
| 354 | |||
| 355 | |||
| 356 | -- Send http.get request and return response result | ||
| 357 | function getHttpsUrl(url,header,reqbody) | ||
| 358 | local body = {} | ||
| 359 | local bodyJson = json.encode(body) | ||
| 360 | local result_table, code, headers, status = https.request{ | ||
| 361 | method = "POST", | ||
| 362 | url = url, | ||
| 363 | source = ltn12.source.string(reqbody), | ||
| 364 | headers = header, | ||
| 365 | sink = ltn12.sink.table(body) | ||
| 366 | } | ||
| 367 | print("code:"..code) | ||
| 368 | if code~= 200 then | ||
| 369 | return | ||
| 370 | else | ||
| 371 | return body | ||
| 372 | end | ||
| 373 | end | ||
| 374 | |||
| 375 | function getMessageUrl(whatsAppMessage) | ||
| 376 | local auth = SID..':'..Token | ||
| 377 | local url = "https://api.twilio.com/2010-04-01/Accounts/"..SID.."/Messages" | ||
| 378 | --local reqMess = "message="..twilioMessage | ||
| 379 | local reqMess = requestBodySplice(whatsAppMessage, twilioPhoneNumber, receiverPhoneNumber) | ||
| 380 | local headers = | ||
| 381 | { | ||
| 382 | ["Authorization"] = "Basic "..encodingBase64(auth), | ||
| 383 | ["Content-Type"] = "application/x-www-form-urlencoded", | ||
| 384 | ["Content-Length"] = #reqMess | ||
| 385 | } | ||
| 386 | |||
| 387 | print("Get the link:"..url) | ||
| 388 | getHttpsUrl(url, headers, reqMess) | ||
| 389 | end | ||
| 390 | |||
| 391 | |||
| 392 | |||
| 393 | function Twilio.main() | ||
| 394 | --dosomething | ||
| 395 | --local auth = SID..':'..Token | ||
| 396 | --print(requestBodySplice("HelloWorld", twilioPhoneNumber, receiverPhoneNumber)) | ||
| 397 | --print(encodingBase64(auth)) | ||
| 398 | local bitValue = addr_getbit("@testBit"); | ||
| 399 | local message = '' | ||
| 400 | print("b=="..bitValue) | ||
| 401 | if bitValue == 1 and bitValue ~= tempBit then | ||
| 402 | message = 'Alarm V-Box triggered, the output is '.. bitValue | ||
| 403 | getMessageUrl(message) | ||
| 404 | print("Notification pushed of triggering alarm,"..bitValue) | ||
| 405 | elseif bitValue == 0 and bitValue ~= tempBit then | ||
| 406 | message = 'Alarm V-Box dismissed, the output is '.. bitValue | ||
| 407 | getMessageUrl(message) | ||
| 408 | print("Notification pushed of dismissing alarm,"..bitValue) | ||
| 409 | end | ||
| 410 | tempBit = bitValue----Prevent monitoring values from continuous being sent to the platform | ||
| 411 | |||
| 412 | local wordValue = addr_getword("@testWord") | ||
| 413 | print("w=="..wordValue) | ||
| 414 | --dosomething | ||
| 415 | if wordValue >= 100 and wordValue ~= tempWord and tempWord <= 100 then | ||
| 416 | message = 'Alarm V-Box triggered, the temperature is '.. wordValue | ||
| 417 | getMessageUrl(message) | ||
| 418 | print("Notification pushed of triggering alarm,"..wordValue) | ||
| 419 | elseif wordValue < 100 and wordValue ~= tempWord and tempWord >= 100 then | ||
| 420 | message = 'Alarm V-Box dismissed, the temperature is '.. wordValue | ||
| 421 | getMessageUrl(message) | ||
| 422 | print("Notification pushed of dismissing alarm,"..wordValue) | ||
| 423 | end | ||
| 424 | tempWord = wordValue----Prevent monitoring values from continuous being sent to the platform | ||
| 425 | end | ||
| 426 | {{/code}} | ||
| 427 | |||
| 428 | == **1.8 HTTP response body** == | ||
| 429 | |||
| 430 | This example use [[https:~~/~~/www.weatherapi.com/>>https://www.weatherapi.com/]] as example, to show how to parse value from HTTP response body. When we input the city name into address "@HDW5050": | ||
| 431 | |||
| 432 | (% style="text-align:center" %) | ||
| 433 | [[image:InputHTTPparameter.png]] | ||
| 434 | |||
| 435 | Then the response body would be like as following: | ||
| 436 | |||
| 437 | {{code language="json"}} | ||
| 438 | { | ||
| 439 | "location": { | ||
| 440 | "name": "Madrid", | ||
| 441 | "region": "Madrid", | ||
| 442 | "country": "Spain", | ||
| 443 | "lat": 40.4, | ||
| 444 | "lon": -3.68, | ||
| 445 | "tz_id": "Europe/Madrid", | ||
| 446 | "localtime_epoch": 1669022636, | ||
| 447 | "localtime": "2022-11-21 10:23" | ||
| 448 | }, | ||
| 449 | "current": { | ||
| 450 | "last_updated_epoch": 1669022100, | ||
| 451 | "last_updated": "2022-11-21 10:15", | ||
| 452 | "temp_c": 13.0, | ||
| 453 | "temp_f": 55.4, | ||
| 454 | "is_day": 1, | ||
| 455 | "condition": { | ||
| 456 | "text": "Partly cloudy", | ||
| 457 | "icon": "//cdn.weatherapi.com/weather/64x64/day/116.png", | ||
| 458 | "code": 1003 | ||
| 459 | }, | ||
| 460 | "wind_mph": 11.9, | ||
| 461 | "wind_kph": 19.1, | ||
| 462 | "wind_degree": 210, | ||
| 463 | "wind_dir": "SSW", | ||
| 464 | "pressure_mb": 1015.0, | ||
| 465 | "pressure_in": 29.97, | ||
| 466 | "precip_mm": 0.0, | ||
| 467 | "precip_in": 0.0, | ||
| 468 | "humidity": 88, | ||
| 469 | "cloud": 75, | ||
| 470 | "feelslike_c": 10.8, | ||
| 471 | "feelslike_f": 51.4, | ||
| 472 | "vis_km": 10.0, | ||
| 473 | "vis_miles": 6.0, | ||
| 474 | "uv": 3.0, | ||
| 475 | "gust_mph": 22.1, | ||
| 476 | "gust_kph": 35.6 | ||
| 477 | } | ||
| 478 | } | ||
| 479 | {{/code}} | ||
| 480 | |||
| 481 | (% class="wikigeneratedid" %) | ||
| 482 | So we decode json into lua object to assign the value into addresses HDW6060(temperature), HDW7070(humidity), the code example like follows: | ||
| 483 | |||
| 484 | {{code language="lua"}} | ||
| 485 | local APIkey = '70faaecf926b4341b1974006221711' | ||
| 486 | |||
| 487 | |||
| 488 | local http = require("socket.http") | ||
| 489 | local json = require("json") | ||
| 490 | |||
| 491 | -- Send http.get request and return response result | ||
| 492 | function getHttpsUrl(url) | ||
| 493 | local result_table, code, headers, status = http.request(url) | ||
| 494 | print("code:"..code) | ||
| 495 | if code~= 200 then | ||
| 496 | return | ||
| 497 | else | ||
| 498 | return result_table | ||
| 499 | end | ||
| 500 | end | ||
| 501 | |||
| 502 | function sendAPI(key, city) | ||
| 503 | local url = "http://api.weatherapi.com/v1/current.json?key="..key.."&q="..city.."&aqi=no" | ||
| 504 | --local url = 'http://v-box.net' | ||
| 505 | --local url = 'https://www.google.com/' | ||
| 506 | --http://api.weatherapi.com/v1/current.json?key=70faaecf926b4341b1974006221711&q=Barcelona&aqi=no | ||
| 507 | print("Get the link:"..url) | ||
| 508 | local body = getHttpsUrl(url) | ||
| 509 | --print(body) | ||
| 510 | local jsonBody = json.decode(body) | ||
| 511 | --print(jsonBody["current"]["temp_c"]) | ||
| 512 | --print(type(jsonBody["current"]["temp_c"])) | ||
| 513 | --print(type(jsonBody["current"]["humidity"])) | ||
| 514 | addr_setfloat("@HDW6060", jsonBody["current"]["temp_c"]) | ||
| 515 | addr_setword("@HDW7070", jsonBody["current"]["humidity"]) | ||
| 516 | end | ||
| 517 | |||
| 518 | |||
| 519 | function Weather.main() | ||
| 520 | local cityName = addr_getstring("@HDW5050",6) | ||
| 521 | print("cityName: "..cityName) | ||
| 522 | sendAPI(APIkey, cityName) | ||
| 523 | end | ||
| 524 | {{/code}} | ||
| 525 | |||
| 526 | == **1.9 High-Low Byte Switch** == | ||
| 527 | |||
| 528 | The following example is converting the floating number from order 1234 to order 3412, and formating output the number with 2 decimal point. About which high-low word order corresponding to which value, please refer to the [[Address Operation Table>>doc:V-BOX.V-Net.Manual.04 Lua Script.01 Lua Functions.WebHome||anchor="H2Addressoperation"]]. | ||
| 529 | |||
| 530 | {{code language="lua"}} | ||
| 531 | function highLowByteSwitch(floatNumber) | ||
| 532 | addr_setfloat("@W_0#HDW23036",floatNumber,0,2) | ||
| 533 | local newFloat = addr_getfloat("@W_0#HDW23036") | ||
| 534 | local formattedFloat = string.format("%.2f",newFloat) | ||
| 535 | print("The formatted float value is the : "..formattedFloat) | ||
| 536 | return formattedFloat | ||
| 537 | end | ||
| 538 | {{/code}} | ||
| 539 | |||
| 540 | == **1.10 Read 64bits Unsigned Value** == | ||
| 541 | |||
| 542 | In our built-in function library doesn't have the function for reading 64-bit unsigned format value, so the following function is for solve this. But if the number is greater 2^53, the precision will be lost. So the final result will be a little bit different from the original value | ||
| 543 | |||
| 544 | {{code language="lua"}} | ||
| 545 | function addr_getquatra(address) | ||
| 546 | local highAddress = addr_newnoaddr(address,2) | ||
| 547 | local low32 = addr_getdword(address) | ||
| 548 | local high32 = addr_getdword(highAddress) | ||
| 549 | --print("the low number is "..low32) | ||
| 550 | --print("the high number is "..high32) | ||
| 551 | local formatVal = string.format("%64.0f",2^32*high32+low32) | ||
| 552 | print("the format value is ".. formatVal) | ||
| 553 | return formatVal | ||
| 554 | end | ||
| 555 | {{/code}} | ||
| 556 | |||
| 557 | == **1.11 Bit-Activated Timer with Reset and Data Output** == | ||
| 558 | |||
| 559 | This script implements a timer that increments while enabled (by a control bit) until reaching a threshold (5s), then resets the timer, disables itself, and sets an output data value. | ||
| 560 | |||
| 561 | (% style="text-align:center" %) | ||
| 562 | [[image:1749188641322-409.png]] | ||
| 563 | |||
| 564 | (% style="text-align:center" %) | ||
| 565 | [[image:1749189179543-849.png||height="300" width="571"]] | ||
| 566 | |||
| 567 | {{code language="lua"}} | ||
| 568 | function timer.main() | ||
| 569 | -- Read current state of control bit and timer value | ||
| 570 | local a = addr_getbit("@Bit") -- Get status of control bit (0 or 1) | ||
| 571 | local b = addr_getword("@Timer") -- Get current timer value (word/integer) | ||
| 572 | |||
| 573 | -- Increment phase: When activated and timer < threshold | ||
| 574 | if a == 1 and b < 5 then | ||
| 575 | b = b + 1 -- Increment timer value | ||
| 576 | addr_setword("@Timer", b) -- Update timer memory | ||
| 577 | print(b) -- Output current timer value (debug) | ||
| 578 | end | ||
| 579 | |||
| 580 | -- Reset phase: When timer reaches threshold | ||
| 581 | if b >= 5 then | ||
| 582 | addr_setword("@Timer", 0) -- Reset timer to 0 | ||
| 583 | addr_setbit("@Bit", 0) -- Deactivate control bit | ||
| 584 | addr_setword("@Data", 10) -- Set output data to fixed value (10) | ||
| 585 | end | ||
| 586 | end | ||
| 587 | {{/code}} | ||
| 588 | |||
| 589 | == **1.12 Get UTC timestamp and local timezone timestamp** == | ||
| 590 | |||
| 591 | This demo shows how to obtain UTC standard time from the V-Box and get the local time based on the device's time zone. | ||
| 592 | |||
| 593 | **Real-time tags configuration** | ||
| 594 | |||
| 595 | [[image:1757043722849-910.png]] | ||
| 596 | |||
| 597 | **Script configuration** | ||
| 598 | |||
| 599 | (% style="text-align:center" %) | ||
| 600 | [[image:1757043900216-610.png||height="412" width="439"]] | ||
| 601 | |||
| 602 | **Script** | ||
| 603 | |||
| 604 | {{code language="lua"}} | ||
| 605 | function time.main() | ||
| 606 | |||
| 607 | --Get Coordinated Universal Time | ||
| 608 | local timestamp = os.time() | ||
| 609 | local utcTimeTable = os.date("!*t", timestamp) | ||
| 610 | local utcTimestamp = os.time(utcTimeTable) | ||
| 611 | local formattedTime = os.date("%Y-%m-%d %H:%M:%S", utcTimestamp) | ||
| 612 | |||
| 613 | print("UTC: " ..formattedTime) | ||
| 614 | |||
| 615 | --Calculate the local time at the device's location | ||
| 616 | local utc_7Timestamp = os.time(utcTimeTable) + 25200 -- Enter the corresponding number of seconds based on the time difference,used UTC+7 as an example.. | ||
| 617 | local formattedTime1 = os.date("%Y-%m-%d %H:%M:%S", utc_7Timestamp) | ||
| 618 | print("UTC+7: " .. formattedTime1) | ||
| 619 | |||
| 620 | |||
| 621 | --Assign the decomposed timestamp to the specified register | ||
| 622 | addr_setword("@UTC+7_Second",os.date("%S", utc_7Timestamp)) | ||
| 623 | addr_setword("@UTC+7_Minute",os.date("%M", utc_7Timestamp)) | ||
| 624 | addr_setword("@UTC+7_Hour",os.date("%H", utc_7Timestamp)) | ||
| 625 | addr_setword("@UTC+7_Day",os.date("%d", utc_7Timestamp)) | ||
| 626 | addr_setword("@UTC+7_Month",os.date("%m", utc_7Timestamp)) | ||
| 627 | addr_setword("@UTC+7_Year",os.date("%Y", utc_7Timestamp)) | ||
| 628 | print("--------------------------------------") | ||
| 629 | |||
| 630 | end | ||
| 631 | {{/code}} | ||
| 632 | |||
| 633 | **Result** | ||
| 634 | |||
| 635 | (% style="text-align:center" %) | ||
| 636 | [[image:1757044137457-543.png||height="468" width="708"]] | ||
| 637 | |||
| 638 | == **1.13 JSON encoding and decoding** == | ||
| 639 | |||
| 640 | This demo shows how to encode tag values into JSON format and decode the JSON to assign data to new tags. | ||
| 641 | |||
| 642 | **Real-time tags configuration** | ||
| 643 | |||
| 644 | (% style="text-align:center" %) | ||
| 645 | [[image:PixPin_2025-11-05_09-21-33.png]] | ||
| 646 | |||
| 647 | (% class="wikigeneratedid" %) | ||
| 648 | **Script configuration** | ||
| 649 | |||
| 650 | (% style="text-align:center" %) | ||
| 651 | [[image:PixPin_2025-11-05_09-23-03.png||height="376" width="400"]] | ||
| 652 | |||
| 653 | (% class="wikigeneratedid" %) | ||
| 654 | **Script** | ||
| 655 | |||
| 656 | {{code language="lua"}} | ||
| 657 | function json_test.main() | ||
| 658 | |||
| 659 | -- Load JSON module for encoding and decoding JSON data | ||
| 660 | local json = require("json") | ||
| 661 | |||
| 662 | -- Read values from different types of address tags | ||
| 663 | local a = addr_getbit("@bit") -- Read a bit (boolean) value | ||
| 664 | local b = addr_getword("@word") -- Read a word (integer) value | ||
| 665 | local c = addr_getfloat("@floating number") -- Read a floating point value | ||
| 666 | local d = addr_getstring("@string",10) -- Read a string value with max length 10 | ||
| 667 | local e = json.null -- JSON null value constant | ||
| 668 | |||
| 669 | -- Encode the data into JSON format | ||
| 670 | local jsondata = json.encode({ | ||
| 671 | bit = a or 0, -- Bit value with default 0 | ||
| 672 | word = b or 0, -- Word value with default 0 | ||
| 673 | float = c or 0, -- Float value with default 0 | ||
| 674 | str = d or 0, -- String value with default 0 | ||
| 675 | {none= e} -- Nested table with null value | ||
| 676 | }) | ||
| 677 | print("json encode:", jsondata) -- Print the encoded JSON data | ||
| 678 | print(".......................") | ||
| 679 | |||
| 680 | -- Decode the JSON string back to Lua table | ||
| 681 | local data = json.decode(jsondata) | ||
| 682 | |||
| 683 | -- Check if decoding was successful and print the results | ||
| 684 | if type(data) == 'table' then | ||
| 685 | print("json decode:") | ||
| 686 | -- Iterate through all key-value pairs in the decoded table | ||
| 687 | for k, v in pairs(data) do | ||
| 688 | print(k, v) | ||
| 689 | end | ||
| 690 | end | ||
| 691 | -- Write the decoded values back to corresponding address tags | ||
| 692 | addr_setbit("@bit_copy", data["bit"] or 0) -- Write bit value | ||
| 693 | addr_setword("@word_copy", data["word"] or 0) -- Write word value | ||
| 694 | addr_setfloat("@floating number_copy", data["float"] or 0) -- Write float value | ||
| 695 | addr_setstring("@string_copy", data["str"], 10) -- Write string value | ||
| 696 | |||
| 697 | print("-----------------------") | ||
| 698 | |||
| 699 | end | ||
| 700 | {{/code}} | ||
| 701 | |||
| 702 | (% class="wikigeneratedid" %) | ||
| 703 | **Result** | ||
| 704 | |||
| 705 | (% style="text-align:center" %) | ||
| 706 | [[image:1762306008662-362.png||height="485" width="1228"]] | ||
| 707 | |||
| 708 | |||
| 709 | = **2 Third part server** = | ||
| 710 | |||
| 711 | V-Box have two mode.One is for V-Net,User need to use WECON server to store data.We call this V-NET platform. | ||
| 712 | |||
| 713 | (% class="mark" %)1.Europe server:eu.v-box.net | ||
| 714 | |||
| 715 | (% class="mark" %)2.Asean server:asean.v-box.net | ||
| 716 | |||
| 717 | Second is for OpenCloud mode.It means V-Box data will transfer to third part server and do not want to store data to WECON server.We call OpenCloud paltform | ||
| 718 | |||
| 719 | OpenCloud platform is used in configuring the script.Then V-Box can connect with third part server. | ||
| 720 | |||
| 721 | Both mode can support script to connect with third part server.the difference: | ||
| 722 | |||
| 723 | (% class="mark" %)1.If you want to store in WECON server ,please use V-NET. | ||
| 724 | |||
| 725 | (% class="mark" %)2.If your server requires SSL certificate to log in,please use OpenCloud.Because only OpenCloud platform can support to upload certificate | ||
| 726 | |||
| 727 | {{info}} | ||
| 728 | **✎Note: **Before program the script of MQTT, please make sure the server(MQTT broker) can be connected through MQTT Client tool. | ||
| 729 | {{/info}} | ||
| 730 | |||
| 731 | (% class="wikigeneratedid" %) | ||
| 732 | Tool link: **[[MQTT.fx>>http://mqttfx.jensd.de/index.php/download]]** | ||
| 733 | |||
| 734 | == **2.1 Test server(General Example)** == | ||
| 735 | |||
| 736 | The following example is trying to publish to the topic "testtopic/test/no1/7890", and subscribe the topic "testtopic/test/no1/123456". | ||
| 737 | |||
| 738 | And the JSON message is like follows: | ||
| 739 | |||
| 740 | {{code language="JSON"}} | ||
| 741 | { | ||
| 742 | "timestamp": 1631152760, | ||
| 743 | "messageId": 1, | ||
| 744 | "event": "test_data", | ||
| 745 | "mfrs": "HMI/box", | ||
| 746 | "data": | ||
| 747 | { | ||
| 748 | "id" : 1436217747670454274, | ||
| 749 | "waterlevel" : 48, | ||
| 750 | "temperture" : 23 | ||
| 751 | } | ||
| 752 | } | ||
| 753 | {{/code}} | ||
| 754 | |||
| 755 | {{code language="lua"}} | ||
| 756 | --MQTT configuration table | ||
| 757 | local MQTT_CFG={} | ||
| 758 | --if there is no need the username and password, please put them as "" | ||
| 759 | MQTT_CFG.username = "weconsupport" | ||
| 760 | MQTT_CFG.password = "123456" | ||
| 761 | MQTT_CFG.netway = 0 | ||
| 762 | MQTT_CFG.keepalive = 60 | ||
| 763 | MQTT_CFG.cleansession = 1 | ||
| 764 | --TCP URL | ||
| 765 | MQTT_URL = "tcp://mq.tongxinmao.com:1883" | ||
| 766 | --Client ID | ||
| 767 | MQTT_CLIENT_ID = "V-BOXH-AG" | ||
| 768 | |||
| 769 | --Generate UUID | ||
| 770 | function uuid() | ||
| 771 | local seed = {'e','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'} | ||
| 772 | local tb = {} | ||
| 773 | for i=1, 32 do | ||
| 774 | table.insert(tb, seed[math.random(1,16)]) | ||
| 775 | end | ||
| 776 | local sid=table.concat(tb) | ||
| 777 | return string.format('%s', | ||
| 778 | string.sub(sid,1,32) | ||
| 779 | ) | ||
| 780 | end | ||
| 781 | |||
| 782 | |||
| 783 | --Topic name to subscribed | ||
| 784 | local SUBSCRIBE_TOPIC = 'testtopic/test/no1/123456' | ||
| 785 | |||
| 786 | --Topic name to be published | ||
| 787 | local PUBLISH_TOPIC = 'testtopic/test/no1/7890' | ||
| 788 | |||
| 789 | |||
| 790 | --real time | ||
| 791 | local LAST_TIME = 0 | ||
| 792 | |||
| 793 | |||
| 794 | --initialize mqtt | ||
| 795 | function mqtt_init() | ||
| 796 | print(string.format("mqtt init mqtt_url:%s mqtt_clientid:%s", MQTT_URL, MQTT_CLIENT_ID)) | ||
| 797 | if g_mq then | ||
| 798 | mqtt.close(g_mq) --Close mqtt object | ||
| 799 | end | ||
| 800 | g_mq, err = mqtt.create(MQTT_URL, MQTT_CLIENT_ID) -- create mqtt object,and declare it as a global variable | ||
| 801 | if g_mq then | ||
| 802 | g_mq:on("message", mqtt_msg_callback) -- Register a callback for receiving messages | ||
| 803 | g_mq:on("offline", mqtt_msg_offline) -- Register a callback for offline | ||
| 804 | print("mqtt init success") | ||
| 805 | else | ||
| 806 | print("mqtt init failed:", err) | ||
| 807 | end | ||
| 808 | end | ||
| 809 | |||
| 810 | -- connect to mqtt | ||
| 811 | function mqtt_connect() | ||
| 812 | print("mqtt connecting...") | ||
| 813 | local stat, err = g_mq:connect(MQTT_CFG) | ||
| 814 | if stat == nil then | ||
| 815 | print("mqtt connect failed:", err) | ||
| 816 | return | ||
| 817 | else | ||
| 818 | print("mqtt connected") | ||
| 819 | end | ||
| 820 | g_mq:subscribe(SUBSCRIBE_TOPIC, 0) | ||
| 821 | end | ||
| 822 | |||
| 823 | --Offline callback function | ||
| 824 | function mqtt_msg_offline(cause) | ||
| 825 | print("mqtt offline, cause:", cause) | ||
| 826 | end | ||
| 827 | |||
| 828 | -- Received message callback function | ||
| 829 | function mqtt_msg_callback(topic, msg) | ||
| 830 | print("topic:", topic) | ||
| 831 | print("msg:", msg) | ||
| 832 | local objMsg = json.decode(msg) | ||
| 833 | local water = objMsg.data.waterlevel | ||
| 834 | local temp = objMsg.data.temperature | ||
| 835 | addr_setword("@HDW20",water) | ||
| 836 | addr_setword("@HDW10",temp) | ||
| 837 | end | ||
| 838 | |||
| 839 | --Send data (data upload to platform and encapsulate it with custom functions) | ||
| 840 | function send_data() | ||
| 841 | local pub_data = { | ||
| 842 | timestamp = os.time(), | ||
| 843 | messageId = 1, | ||
| 844 | event = 'test_data', | ||
| 845 | mfrs = 'V-Box', | ||
| 846 | data = { | ||
| 847 | id = uuid(), | ||
| 848 | waterlevel = addr_getword("@HDW10"), | ||
| 849 | temperature = addr_getword("@HDW20") | ||
| 850 | } | ||
| 851 | } | ||
| 852 | return g_mq:publish(PUBLISH_TOPIC, json.encode(pub_data), 0, 0) | ||
| 853 | end | ||
| 854 | |||
| 855 | |||
| 856 | --main function fixed timed execution | ||
| 857 | function MQTT.main() | ||
| 858 | --dosomething | ||
| 859 | print(os.date("%Y-%m-%d %H:%M %S", os.time()) .. " main start") | ||
| 860 | --determine the mqtt object whether exist | ||
| 861 | if g_mq then | ||
| 862 | --determine the mqtt object whether has been connected or not | ||
| 863 | if g_mq:isconnected() then | ||
| 864 | send_data() | ||
| 865 | else | ||
| 866 | --if exceed 5 sec not connect, reconnect once | ||
| 867 | if os.time() - LAST_TIME > 5 then | ||
| 868 | LAST_TIME = os.time() | ||
| 869 | --reinitial the mqtt object | ||
| 870 | mqtt_init() | ||
| 871 | --connect to mqtt or reconnect | ||
| 872 | mqtt_connect() | ||
| 873 | end | ||
| 874 | end | ||
| 875 | else | ||
| 876 | --mqtt object does not exist so create new one | ||
| 877 | mqtt_init() | ||
| 878 | end | ||
| 879 | print(os.date("%Y-%m-%d %H:%M %S", os.time()) .. " main end") | ||
| 880 | end | ||
| 881 | {{/code}} | ||
| 882 | |||
| 883 | == **2.2 Customer server:grouprobotinfo.com** == | ||
| 884 | |||
| 885 | This demo does not use SSL certification. Script is as below | ||
| 886 | |||
| 887 | Demo1: | ||
| 888 | |||
| 889 | {{code language="lua"}} | ||
| 890 | -- Meta class | ||
| 891 | --main | ||
| 892 | function mq.main() | ||
| 893 | if not mq.m then | ||
| 894 | local err = "" | ||
| 895 | |||
| 896 | mq.m, err = mqtt.create("tcp://grouprobotinfo.com:1883", "ClienID") -- create connection | ||
| 897 | if mq.m then | ||
| 898 | mq.config = { | ||
| 899 | username = "",-- ID | ||
| 900 | password = "",-- password | ||
| 901 | netway = 1, -- Ethernet connection, WIFI=1 | ||
| 902 | -- keepalive = 100, -- Optional, set the connection heartbeat interval for 100 seconds. | ||
| 903 | -- cleansession = 0, -- Optional, keep session | ||
| 904 | } | ||
| 905 | mq.m:on("message", function(topic, msg) -- Register for receiving message callbacks | ||
| 906 | local str = string.format("%s:%s", topic, msg) | ||
| 907 | -- print("mqtt msg:", str) -- Print out the received topics and content | ||
| 908 | end) | ||
| 909 | mq.m:on("offline", function (cause) -- Register for lost connection callbacks | ||
| 910 | -- addr_setstring("@xxx", "cause"..(cause or " got nil")) | ||
| 911 | end) | ||
| 912 | mq.m:on("arrived", function() -- Registration for sending messages to callbacks | ||
| 913 | print("msg arrived") | ||
| 914 | end) | ||
| 915 | else | ||
| 916 | print("mqtt create failed:", err) -- Create object failed | ||
| 917 | end | ||
| 918 | else | ||
| 919 | if mq.m:isconnected() then -- If online, post a message | ||
| 920 | local phaseStatus ="unknow" | ||
| 921 | if addr_getbit("@Standby")== 1 then | ||
| 922 | phaseStatus = "Standby" | ||
| 923 | elseif addr_getbit("@Pre-Freeze")==1 then | ||
| 924 | phaseStatus= "Pre-Freeze" | ||
| 925 | elseif addr_getbit("@Prepare")==1 then | ||
| 926 | phaseStatus ="Prepare" | ||
| 927 | elseif addr_getbit("@Primary Dry")==1 then | ||
| 928 | phaseStatus = "Primary dry" | ||
| 929 | elseif addr_getbit("@Secondary Dry")==1 then | ||
| 930 | phaseStatus = "Secondary Dry" | ||
| 931 | end | ||
| 932 | --print(addr_getbit("@Primary Dry")) | ||
| 933 | ------------------------------------------------------------------------------------------------------------------------- | ||
| 934 | local activating ="unknow" | ||
| 935 | if addr_getbit("@Compressor")==1 then | ||
| 936 | activating = ",".."Compressor" | ||
| 937 | end | ||
| 938 | if addr_getbit("@Silicone Pump")==1 then | ||
| 939 | activating = activating..",".."Silicone Pump" | ||
| 940 | end | ||
| 941 | if addr_getbit("@Vacuum Pump")==1 then | ||
| 942 | activating = activating..",".."Vacuum Pump" | ||
| 943 | end | ||
| 944 | if addr_getbit("@Root Pump")==1 then | ||
| 945 | activating = activating..",".."Root Pump" | ||
| 946 | end | ||
| 947 | if addr_getbit("@Heater")==1 then | ||
| 948 | activating = activating..",".."Heater" | ||
| 949 | end | ||
| 950 | if addr_getbit("@Valve Silicone")==1 then | ||
| 951 | activating = activating..",".."Valve Silicone" | ||
| 952 | end | ||
| 953 | if addr_getbit("@Valve Ice Condenser")==1 then | ||
| 954 | activating = activating..",".."Valve Ice Condenser" | ||
| 955 | end | ||
| 956 | if addr_getbit("@Valve Vacuum Pump")==1 then | ||
| 957 | activating = activating..",".."Valve Vacuum Pump" | ||
| 958 | end | ||
| 959 | local pr_activating =string.sub(activating,2) | ||
| 960 | -- print(pr_activating) | ||
| 961 | local status_text ="unknow" | ||
| 962 | if addr_getbit("@Status Run")==1 then | ||
| 963 | status_text = "RUNNING" | ||
| 964 | else | ||
| 965 | status_text = "STOP" | ||
| 966 | end | ||
| 967 | ------------------------------------------------------------------------------------------------------------------------- | ||
| 968 | local js = {type="status", | ||
| 969 | mc_name ="FD300", | ||
| 970 | status=status_text, | ||
| 971 | elapsed_time={ | ||
| 972 | hour=addr_getword("@Elapsed Time (Hour)"), | ||
| 973 | min=addr_getword("@Elapsed Time (Minute)"), | ||
| 974 | sec=addr_getword("@Elapsed Time (Second)") | ||
| 975 | }, | ||
| 976 | phase = phaseStatus, | ||
| 977 | step = addr_getword("@Step"), | ||
| 978 | activating_output = pr_activating, | ||
| 979 | sv=addr_getshort("@SV Silicone")/10, | ||
| 980 | pv=addr_getshort("@PV Silicone")/10, | ||
| 981 | product1=addr_getshort("@Product 1")/10, | ||
| 982 | |||
| 983 | product2=addr_getshort("@Product 2")/10, | ||
| 984 | product3=addr_getshort("@Product 3")/10, | ||
| 985 | product4=addr_getshort("@Product 4")/10, | ||
| 986 | ice1=addr_getshort("@Ice condenser 1")/10, | ||
| 987 | ice2=addr_getshort("@Ice condenser 2")/10, | ||
| 988 | vacuum=addr_getfloat("@Vacuum") | ||
| 989 | } | ||
| 990 | local jsAlarm = { HPC = addr_getbit("@B_25395#W0.00"), | ||
| 991 | ODPC = addr_getbit("@B_25395#W0.01"), | ||
| 992 | MTPC=addr_getbit("@B_25395#W0.02"), | ||
| 993 | HTT = addr_getbit("@B_25395#W1.03"), | ||
| 994 | CPC = addr_getbit("@B_25395#W0.08"), | ||
| 995 | CPSP =addr_getbit("@B_25395#W1.00"), | ||
| 996 | CPVP =addr_getbit("@B_25395#W0.10"), | ||
| 997 | CPRP =addr_getbit("@B_25395#W0.11"), | ||
| 998 | HP =addr_getbit("@B_25395#W1.01"), | ||
| 999 | PP= addr_getbit("@B_25395#W1.02"), | ||
| 1000 | PO=addr_getbit("@B_25395#W0.07"), | ||
| 1001 | FSE=addr_getbit("@B_25395#W2.04"), | ||
| 1002 | AVVSVV=addr_getbit("@B_25395#W1.12"), | ||
| 1003 | ICHT=addr_getbit("@B_25395#W3.06") | ||
| 1004 | } | ||
| 1005 | -- ("@B_25395#CIO1.02") | ||
| 1006 | mq.m:publish("mqtt-v-box-epsilon-fd300", json.encode(js) , 0, 0) | ||
| 1007 | mq.m:publish("mqtt-v-box-epsilon-alarm-fd300", json.encode(jsAlarm) , 0, 0) | ||
| 1008 | else | ||
| 1009 | local stat, err = mq.m:connect(mq.config) -- connection | ||
| 1010 | if stat == nil then --Determine whether to connect | ||
| 1011 | print("mqtt connect failed:", err) | ||
| 1012 | return -- Connection failed, return directly | ||
| 1013 | end | ||
| 1014 | mq.m:subscribe("mqtt-v-box-epsilon", 0)-- Subscribe to topics | ||
| 1015 | |||
| 1016 | end | ||
| 1017 | -- mq.m:unsubscribe("stc/test") | ||
| 1018 | -- mq.m:disconnect() -- close matt | ||
| 1019 | -- mq.m:close() -- close clase | ||
| 1020 | end | ||
| 1021 | end | ||
| 1022 | {{/code}} | ||
| 1023 | |||
| 1024 | == **2.3 Azure platform** == | ||
| 1025 | |||
| 1026 | In this demo,V-Box connects with Azure by SSL certification. | ||
| 1027 | |||
| 1028 | Video link: [[https:~~/~~/youtu.be/cdI6rIQcpMY?list=PL_Bpnb2RgaksCic9HCcVAZhU9sYwCRKzW>>https://youtu.be/cdI6rIQcpMY?list=PL_Bpnb2RgaksCic9HCcVAZhU9sYwCRKzW]] | ||
| 1029 | |||
| 1030 | Tool Download link: [[https:~~/~~/wecon-disk.oss-ap-southeast-1.aliyuncs.com/Download/WIKI/V-BOX/Demo/Azure/Azure%20tool.zip>>https://wecon-disk.oss-ap-southeast-1.aliyuncs.com/Download/WIKI/V-BOX/Demo/Azure/Azure%20tool.zip]] | ||
| 1031 | |||
| 1032 | Script is as below | ||
| 1033 | |||
| 1034 | {{code language="lua"}} | ||
| 1035 | --https://support.huaweicloud.com/qs-IoT/iot_05_0005.html mqtt.fx monitor to connect azure iot | ||
| 1036 | sprint = print | ||
| 1037 | |||
| 1038 | --Get custom configuration parameters (vbox custom information) | ||
| 1039 | --local CUSTOM = bns_get_config("bind") | ||
| 1040 | --local DS_ID = CUSTOM.DSID or "60a71ccbbbe12002c08f3a1a_WECON" | ||
| 1041 | |||
| 1042 | |||
| 1043 | |||
| 1044 | --Cloud mode interface to obtain the MQTT information configured by the cloud platform: (5 returns, namely the server address, client ID, connection table, last word table, certificate table) | ||
| 1045 | local MQTT_URL, MQTT_CLIENTID, MQTT_CFG, MQTT_LWT, MQTT_CART = mqtt.setup_cfg() | ||
| 1046 | |||
| 1047 | --MQTT_CFG.username = '60a71ccbbbe12002c08f3a1a_WECON' | ||
| 1048 | --MQTT_CFG.password='wecon123' | ||
| 1049 | --MQTT_CLIENTID = '60a71ccbbbe12002c08f3a1a_WECON_0_0_2021052110usernxame:60a71ccbbbe12002c08f3a1a_WECONpassword:a0a951581855aa8e0262129da6cf1b43f2c0ecfac4fa56117fc5a20c90be169a' | ||
| 1050 | |||
| 1051 | --publish to topics | ||
| 1052 | local pub_RE_TOPIC = string.format('devices/wecon_02/messages/events/') | ||
| 1053 | --Subscribe topics | ||
| 1054 | local Subscribe_RE_TOPIC1 = string.format('devices/wecon_02/messages/devicebound/#') | ||
| 1055 | |||
| 1056 | --variable | ||
| 1057 | local last_time = 0 | ||
| 1058 | |||
| 1059 | |||
| 1060 | |||
| 1061 | --Timing main function | ||
| 1062 | function Azure.main() | ||
| 1063 | |||
| 1064 | sprint(os.date("%Y-%m-%d %H:%M %S", os.time()) .. " Azureiot.main start") | ||
| 1065 | if g_mq then | ||
| 1066 | if g_mq:isconnected() then | ||
| 1067 | send_Data() | ||
| 1068 | else | ||
| 1069 | if os.time() - last_time > 20 then | ||
| 1070 | last_time = os.time() | ||
| 1071 | mymqtt_connect() | ||
| 1072 | end | ||
| 1073 | end | ||
| 1074 | else | ||
| 1075 | mymqtt_init() | ||
| 1076 | end | ||
| 1077 | sprint(os.date("%Y-%m-%d %H:%M %S", os.time()) .. " Azureiot.main end") | ||
| 1078 | end | ||
| 1079 | |||
| 1080 | -- Initialize MQTT | ||
| 1081 | function mymqtt_init() | ||
| 1082 | sprint(string.format("mqtt init mqtt_url:%s mqtt_clientid:%s", MQTT_URL, MQTT_CLIENTID)) | ||
| 1083 | g_mq, err = mqtt.create(MQTT_URL, MQTT_CLIENTID) -- Create the object and declare it as a global variable | ||
| 1084 | if g_mq then | ||
| 1085 | g_mq:on("message", mymqtt_msg_callback) -- Register to receive message callbacks | ||
| 1086 | sprint("mqtt init success") | ||
| 1087 | else | ||
| 1088 | sprint("mqtt init failed:", err) | ||
| 1089 | end | ||
| 1090 | end | ||
| 1091 | |||
| 1092 | -- Connect to MQTT server | ||
| 1093 | function mymqtt_connect() | ||
| 1094 | sprint("mqtt connecting...") | ||
| 1095 | local stat, err = g_mq:connect(MQTT_CFG,MQTT_LWT, MQTT_CART) | ||
| 1096 | if stat == nil then | ||
| 1097 | sprint("mqtt connect failed:", err) | ||
| 1098 | return | ||
| 1099 | else | ||
| 1100 | sprint("mqtt connected") | ||
| 1101 | end | ||
| 1102 | g_mq:subscribe(Subscribe_RE_TOPIC1, 0) | ||
| 1103 | end | ||
| 1104 | |||
| 1105 | -- Receive MQTT message callback function | ||
| 1106 | function mymqtt_msg_callback(topic, msg) | ||
| 1107 | print("topic:",topic) | ||
| 1108 | print("revdata:",msg) | ||
| 1109 | -- local revData = json.decode(msg) | ||
| 1110 | -- if topic == Subscribe_RE_TOPIC1 then --Process topic information subscribed from the cloud | ||
| 1111 | -- if string.match(topic,Subscribe_RE_TOPIC1) then | ||
| 1112 | -- print("topi11:",topic) | ||
| 1113 | setValue(revData) | ||
| 1114 | -- end | ||
| 1115 | end | ||
| 1116 | |||
| 1117 | --Process the received data | ||
| 1118 | --function setValue(revData) | ||
| 1119 | -- if revData ~=nil then | ||
| 1120 | -- for i,v in pairs(revData) do | ||
| 1121 | -- print("Data received:",i,v) | ||
| 1122 | -- end | ||
| 1123 | -- end | ||
| 1124 | --end | ||
| 1125 | |||
| 1126 | --Get real-time data | ||
| 1127 | function getData() | ||
| 1128 | local jdata = {} | ||
| 1129 | local addr = bns_get_alldata() | ||
| 1130 | print(json.encode(addr)) | ||
| 1131 | for i,v in pairs(addr) do | ||
| 1132 | if v[2] == 1 then | ||
| 1133 | jdata[v[3]] = v[4] | ||
| 1134 | end | ||
| 1135 | end | ||
| 1136 | return jdata | ||
| 1137 | end | ||
| 1138 | |||
| 1139 | |||
| 1140 | |||
| 1141 | --send data | ||
| 1142 | function send_Data() | ||
| 1143 | local pub_data = {100 | ||
| 1144 | -- services={{ | ||
| 1145 | |||
| 1146 | --serviceId ='Temperature', | ||
| 1147 | -- properties={ | ||
| 1148 | -- value = 55 | ||
| 1149 | -- }, | ||
| 1150 | -- }} | ||
| 1151 | } | ||
| 1152 | sprint(json.encode(pub_data)) | ||
| 1153 | print("..........",pub_RE_TOPIC) | ||
| 1154 | return g_mq:publish(pub_RE_TOPIC, json.encode(pub_data), 0, 0) | ||
| 1155 | end | ||
| 1156 | {{/code}} | ||
| 1157 | |||
| 1158 | == **2.4 Huawei platform** == | ||
| 1159 | |||
| 1160 | {{info}} | ||
| 1161 | **✎Note**:**Huawei IOT DA function is only in China area.If you want this function,you need to use chinese mobile to register** | ||
| 1162 | {{/info}} | ||
| 1163 | |||
| 1164 | 1.Register a account: [[https:~~/~~/www.huaweicloud.com/intl/en-us/s/JUlPVERNJQ>>https://www.huaweicloud.com/intl/en-us/s/JUlPVERNJQ]] | ||
| 1165 | |||
| 1166 | 2.log in the Huawei IOTDA | ||
| 1167 | |||
| 1168 | [[https:~~/~~/console.huaweicloud.com/iotdm/?region=cn-north-4&locale=en-us#/dm-portal/home>>https://console.huaweicloud.com/iotdm/?region=cn-north-4&locale=en-us#/dm-portal/home]] | ||
| 1169 | |||
| 1170 | 3.Create product | ||
| 1171 | |||
| 1172 | (% style="text-align:center" %) | ||
| 1173 | [[image:1624433478954-859.png||height="497" width="1100" class="img-thumbnail"]] | ||
| 1174 | |||
| 1175 | 4.Product name,manufacturer,device type and industry,set according to your own needs. | ||
| 1176 | |||
| 1177 | Protocol: MQTT | ||
| 1178 | |||
| 1179 | Data Type: JSON | ||
| 1180 | |||
| 1181 | After finishing configuration,please click "OK" | ||
| 1182 | |||
| 1183 | (% style="text-align:center" %) | ||
| 1184 | [[image:1624433531968-337.png||height="568" width="700" class="img-thumbnail"]] | ||
| 1185 | |||
| 1186 | 5.Device | ||
| 1187 | |||
| 1188 | After product register,continue to configure "individual register".Click "Device"~-~->"individual register" | ||
| 1189 | |||
| 1190 | (% style="text-align:center" %) | ||
| 1191 | [[image:1624434757597-117.png||class="img-thumbnail"]] | ||
| 1192 | |||
| 1193 | **Notes for registering device:** | ||
| 1194 | |||
| 1195 | Product: Previous product registration. | ||
| 1196 | |||
| 1197 | Node ID, Device Name: set according to your own needs. | ||
| 1198 | |||
| 1199 | Secret: need to be configured, will be used when connecting later | ||
| 1200 | |||
| 1201 | After configuration, click OK to generate a device ID and password, which will be used for device access later. | ||
| 1202 | |||
| 1203 | (% style="text-align:center" %) | ||
| 1204 | [[image:1624436421499-613.png||height="499" width="700" class="img-thumbnail"]] | ||
| 1205 | |||
| 1206 | (% style="text-align:center" %) | ||
| 1207 | [[image:1624437798012-126.png||height="366" width="500" class="img-thumbnail"]] | ||
| 1208 | |||
| 1209 | 6. Connection authentication (use MQTT.fx tool to access the IoT platform) | ||
| 1210 | |||
| 1211 | (1)Open mqttClientIdGenerator tool Java(TM) Platform SE binary | ||
| 1212 | |||
| 1213 | **[[Download>>https://wecon-disk.oss-ap-southeast-1.aliyuncs.com/Download/WIKI/V-BOX/Demo/Huawei/mqttClientIdGenerator-19.2.0.zip]]** | ||
| 1214 | |||
| 1215 | (% style="text-align:center" %) | ||
| 1216 | [[image:1624437573798-815.png||height="351" width="700" class="img-thumbnail"]] | ||
| 1217 | |||
| 1218 | (2)Fill in the device ID and secret (deviceid and secret generated when registering the device) to generate connection message | ||
| 1219 | |||
| 1220 | Client ID, user name, password | ||
| 1221 | |||
| 1222 | (% style="text-align:center" %) | ||
| 1223 | [[image:1624437756866-251.png||height="405" width="700" class="img-thumbnail"]] | ||
| 1224 | |||
| 1225 | (3) Download certificate file"North-Beijing4" | ||
| 1226 | |||
| 1227 | [[https:~~/~~/support.huaweicloud.com/en-us/devg-iothub/iot_02_1004.html>>https://support.huaweicloud.com/en-us/devg-iothub/iot_02_1004.html]] | ||
| 1228 | |||
| 1229 | (% style="text-align:center" %) | ||
| 1230 | [[image:1624438225398-363.png||height="403" width="800" class="img-thumbnail"]] | ||
| 1231 | |||
| 1232 | (% style="text-align:center" %) | ||
| 1233 | [[image:1624438260025-610.png||height="408" width="700" class="img-thumbnail"]] | ||
| 1234 | |||
| 1235 | 7.Run MQTTfx tool to connect with Huawei | ||
| 1236 | |||
| 1237 | Download link: [[http:~~/~~/mqttfx.jensd.de/index.php/download>>url:http://mqttfx.jensd.de/index.php/download]] | ||
| 1238 | |||
| 1239 | (1)Click on the setting ICON | ||
| 1240 | |||
| 1241 | (% style="text-align:center" %) | ||
| 1242 | [[image:1624438821280-974.png||height="198" width="500" class="img-thumbnail"]] | ||
| 1243 | |||
| 1244 | (2)Fill in IIOT MQTT device access address, and configure authentication parameters. | ||
| 1245 | First: It is the server and port connected to Huawei IOT, which can be viewed through the overview of the interface. | ||
| 1246 | |||
| 1247 | (% style="text-align:center" %) | ||
| 1248 | [[image:1624439086268-985.png||class="img-thumbnail"]] | ||
| 1249 | |||
| 1250 | Domain name:iot-mqtts.cn-north-4.myhuaweicloud.com | ||
| 1251 | |||
| 1252 | Port: 8883 | ||
| 1253 | |||
| 1254 | Client ID: check step 6 | ||
| 1255 | |||
| 1256 | (% style="text-align:center" %) | ||
| 1257 | [[image:1624439672168-492.png||height="458" width="600" class="img-thumbnail"]] | ||
| 1258 | |||
| 1259 | (3)Upload SSL certificate file,check step 6 | ||
| 1260 | |||
| 1261 | Select folder java~-~->DigiCertGlobalRootCA.crt.pem and click OK or apply button | ||
| 1262 | |||
| 1263 | (% style="text-align:center" %) | ||
| 1264 | [[image:1624439912938-659.png||height="458" width="600" class="img-thumbnail"]] | ||
| 1265 | |||
| 1266 | (4)Connect and test publish and subscribe | ||
| 1267 | |||
| 1268 | (% style="text-align:center" %) | ||
| 1269 | [[image:1624440014872-688.png||height="232" width="700" class="img-thumbnail"]] | ||
| 1270 | |||
| 1271 | (% style="text-align:center" %) | ||
| 1272 | [[image:1624440026937-386.png||height="215" width="700" class="img-thumbnail"]] | ||
| 1273 | |||
| 1274 | Huawei publish topic format: $oc/devices/{device_id}/sys/properties/report | ||
| 1275 | |||
| 1276 | (% style="text-align:center" %) | ||
| 1277 | [[image:1624440404119-815.png||class="img-thumbnail"]] | ||
| 1278 | |||
| 1279 | Huawei subscribe topic format: **$oc/devices/{device_id}/sys/commands/#** | ||
| 1280 | |||
| 1281 | (% style="text-align:center" %) | ||
| 1282 | [[image:1624447157493-672.png||class="img-thumbnail"]] | ||
| 1283 | |||
| 1284 | (% style="text-align:center" %) | ||
| 1285 | [[image:1624447209982-715.png||class="img-thumbnail"]] | ||
| 1286 | |||
| 1287 | (5).How to configure to view the received data format intuitively, multiple products (Huawei) are required to configure the model. The specific steps are as follows: | ||
| 1288 | ①Select the corresponding product from Huawei products to view | ||
| 1289 | |||
| 1290 | (% style="text-align:center" %) | ||
| 1291 | [[image:1624440647663-632.png||class="img-thumbnail"]] | ||
| 1292 | |||
| 1293 | ②Custom model: used to display the service ID name of the configuration report. | ||
| 1294 | |||
| 1295 | (% style="text-align:center" %) | ||
| 1296 | [[image:1624440793982-974.png||height="410" width="700" class="img-thumbnail"]] | ||
| 1297 | |||
| 1298 | (% style="text-align:center" %) | ||
| 1299 | [[image:1624440883015-105.png||height="370" width="600" class="img-thumbnail"]] | ||
| 1300 | |||
| 1301 | ③Add property, ID of monitoring point, and data format: | ||
| 1302 | |||
| 1303 | (% style="text-align:center" %) | ||
| 1304 | [[image:1624441052296-108.png||height="477" width="600" class="img-thumbnail"]] | ||
| 1305 | |||
| 1306 | ④After the configuration is complete, check the received data on the device | ||
| 1307 | |||
| 1308 | (% style="text-align:center" %) | ||
| 1309 | [[image:1624441186851-536.png||height="434" width="700" class="img-thumbnail"]] | ||
| 1310 | |||
| 1311 | === Huawei by SSL certification. === | ||
| 1312 | |||
| 1313 | 1.Create a project access for Huawei IOT | ||
| 1314 | |||
| 1315 | 2.configure MQTT configuration | ||
| 1316 | |||
| 1317 | (% style="text-align:center" %) | ||
| 1318 | [[image:1624506363847-661.png||height="507" width="1000" class="img-thumbnail"]] | ||
| 1319 | |||
| 1320 | 3.Create a script with the demo as below. | ||
| 1321 | |||
| 1322 | Script is as below | ||
| 1323 | |||
| 1324 | (% class="box infomessage" %) | ||
| 1325 | ((( | ||
| 1326 | (% class="box infomessage" %) | ||
| 1327 | ((( | ||
| 1328 | ~-~- mqtt.fx simulated access to Huawei Cloud IoT platform refer to 2.4 | ||
| 1329 | sprint = print | ||
| 1330 | |||
| 1331 | ~-~-Get custom configuration parameters (gateway customization information) | ||
| 1332 | local CUSTOM = bns_get_config("bind") | ||
| 1333 | local DS_ID = CUSTOM.DSID or "5dfa0700df1ae506179afb9c_wecon" | ||
| 1334 | |||
| 1335 | |||
| 1336 | ~-~-OpenCloud mode interface, obtain MQTT information configured on cloud platform: (5 returned, respectively server address, client ID, connection table, last word table, certificate table) | ||
| 1337 | local MQTT_URL, MQTT_CLIENTID, MQTT_CFG, MQTT_LWT, MQTT_CART = mqtt.setup_cfg() | ||
| 1338 | |||
| 1339 | MQTT_CFG.username =DS_ID | ||
| 1340 | MQTT_CFG.password='d030d92338fcc18cd10fabb3003a4a0f6620fa6822cd3c23b1d9bc790200c6e7' | ||
| 1341 | MQTT_CLIENTID = '5dfa0700df1ae506179afb9c_wecon_0_0_2019121819' | ||
| 1342 | |||
| 1343 | ~-~-publish topic format:$oc/devices/{device id}/sys/properties/report | ||
| 1344 | |||
| 1345 | local pub_RE_TOPIC = string.format('$oc/devices/60a71ccbbbe12002c08f3a1a_WECON/sys/properties/report') | ||
| 1346 | |||
| 1347 | ~-~-variate | ||
| 1348 | local last_time = 0 | ||
| 1349 | |||
| 1350 | |||
| 1351 | ~-~-Timing principal function | ||
| 1352 | function hwyiot.main() | ||
| 1353 | |||
| 1354 | sprint(os.date("%Y-%m-%d %H:%M %S", os.time()) .. " hwyiot.main start") | ||
| 1355 | if g_mq then | ||
| 1356 | if g_mq:isconnected() then | ||
| 1357 | send_Data() | ||
| 1358 | else | ||
| 1359 | if os.time() - last_time > 20 then | ||
| 1360 | last_time = os.time() | ||
| 1361 | mymqtt_connect() | ||
| 1362 | end | ||
| 1363 | end | ||
| 1364 | else | ||
| 1365 | mymqtt_init() | ||
| 1366 | end | ||
| 1367 | sprint(os.date("%Y-%m-%d %H:%M %S", os.time()) .. " hwyiot.main end") | ||
| 1368 | end | ||
| 1369 | |||
| 1370 | ~-~- initializationMQTT | ||
| 1371 | function mymqtt_init() | ||
| 1372 | sprint(string.format("mqtt init mqtt_url:%s mqtt_clientid:%s", MQTT_URL, MQTT_CLIENTID)) | ||
| 1373 | g_mq, err = mqtt.create(MQTT_URL, MQTT_CLIENTID) ~-~- Create the object and declare it as a global variable | ||
| 1374 | if g_mq then | ||
| 1375 | g_mq:on("message", mymqtt_msg_callback) ~-~- Register to receive message callbacks | ||
| 1376 | sprint("mqtt init success") | ||
| 1377 | else | ||
| 1378 | sprint("mqtt init failed:", err) | ||
| 1379 | end | ||
| 1380 | end | ||
| 1381 | |||
| 1382 | ~-~- Connect to the MQTT server | ||
| 1383 | function mymqtt_connect() | ||
| 1384 | sprint("mqtt connecting...") | ||
| 1385 | local stat, err = g_mq:connect(MQTT_CFG,MQTT_LWT, MQTT_CART) | ||
| 1386 | if stat == nil then | ||
| 1387 | sprint("mqtt connect failed:", err) | ||
| 1388 | return | ||
| 1389 | else | ||
| 1390 | sprint("mqtt connected") | ||
| 1391 | end | ||
| 1392 | |||
| 1393 | ~-~-subscribe topic format:$oc/devices/{device_id}/sys/commands/# | ||
| 1394 | |||
| 1395 | g_mq:subscribe('$oc/devices/60a71ccbbbe12002c08f3a1a_WECON/sys/commands/#', 0) | ||
| 1396 | end | ||
| 1397 | |||
| 1398 | ~-~- Receive the message callback function | ||
| 1399 | function mymqtt_msg_callback(topic, msg) | ||
| 1400 | sprint("recv data!") | ||
| 1401 | sprint("topic:msg", topic,msg) | ||
| 1402 | print(msg) | ||
| 1403 | local revData = json.decode(msg) | ||
| 1404 | \\end | ||
| 1405 | |||
| 1406 | ~-~-Send data | ||
| 1407 | function send_Data() | ||
| 1408 | local pub_data = { | ||
| 1409 | msgType = 'deviceReq', | ||
| 1410 | data = ~{~{ | ||
| 1411 | serviceId ='Battery', | ||
| 1412 | serviceData={ | ||
| 1413 | batteryLevel = 55 | ||
| 1414 | } | ||
| 1415 | }} | ||
| 1416 | } | ||
| 1417 | return g_mq:publish(pub_RE_TOPIC, json.encode(pub_data), 0, 0) | ||
| 1418 | end | ||
| 1419 | ))) | ||
| 1420 | ))) | ||
| 1421 | |||
| 1422 | 4.Download project access into V-Box to test in debug page | ||
| 1423 | |||
| 1424 | (% style="text-align:center" %) | ||
| 1425 | [[image:1624506710354-406.png||height="658" width="1000" class="img-thumbnail"]] | ||
| 1426 | |||
| 1427 | (% style="text-align:center" %) | ||
| 1428 | [[image:1624506666650-161.png||height="547" width="1000" class="img-thumbnail"]] | ||
| 1429 | |||
| 1430 | == **2.6 AWS platform** == | ||
| 1431 | |||
| 1432 | === **Log in AWS** === | ||
| 1433 | |||
| 1434 | Login aws account and click“Connect an IoT device” | ||
| 1435 | |||
| 1436 | [[image:image-20220709165402-1.png]] | ||
| 1437 | |||
| 1438 | [[image:image-20220709165402-2.png]] | ||
| 1439 | |||
| 1440 | |||
| 1441 | === **Create policy** === | ||
| 1442 | |||
| 1443 | Click “Secure”~-~-->“Policies”~-~-->“Create policy”~-~-->Click “Create” | ||
| 1444 | |||
| 1445 | [[image:image-20220709165402-3.png]] | ||
| 1446 | |||
| 1447 | Name the policy~-~-->Click “JSON”~-~-->Copy the following content~-~-->Click “Create” | ||
| 1448 | |||
| 1449 | [[image:image-20220709165402-5.png]] | ||
| 1450 | |||
| 1451 | [[image:image-20220709165402-4.png]] | ||
| 1452 | |||
| 1453 | {{code language="java"}} | ||
| 1454 | { | ||
| 1455 | "Version": "2012-10-17", | ||
| 1456 | "Statement": [ | ||
| 1457 | { | ||
| 1458 | "Effect": "Allow", | ||
| 1459 | "Action": [ | ||
| 1460 | "iot:Connect", | ||
| 1461 | "iot:Publish", | ||
| 1462 | "iot:Subscribe", | ||
| 1463 | "iot:Receive", | ||
| 1464 | "greengrass:Discover" | ||
| 1465 | ], | ||
| 1466 | "Resource": "*" | ||
| 1467 | } | ||
| 1468 | ] | ||
| 1469 | } | ||
| 1470 | {{/code}} | ||
| 1471 | |||
| 1472 | === **Create things** === | ||
| 1473 | |||
| 1474 | Click “Manage”~-~-->“Things”~-~-->“Create things”~-~-->“Create single thing” | ||
| 1475 | |||
| 1476 | [[image:image-20220709165402-6.png]] | ||
| 1477 | |||
| 1478 | [[image:image-20220709165402-7.png]] | ||
| 1479 | |||
| 1480 | Name the thing~-~-->Click “Next” | ||
| 1481 | |||
| 1482 | [[image:image-20220709165402-8.png]] | ||
| 1483 | |||
| 1484 | Select the way to create certificate | ||
| 1485 | |||
| 1486 | [[image:image-20220709165402-9.png]] | ||
| 1487 | |||
| 1488 | Select policy | ||
| 1489 | |||
| 1490 | [[image:image-20220709165402-10.png]] | ||
| 1491 | |||
| 1492 | [[image:image-20220709165402-11.png]] | ||
| 1493 | |||
| 1494 | |||
| 1495 | === **MQTT.fx tool** === | ||
| 1496 | |||
| 1497 | Click “View Setting” to get the “Broker Adress” | ||
| 1498 | |||
| 1499 | [[image:image-20220709165402-13.png]] | ||
| 1500 | |||
| 1501 | [[image:image-20220709165402-12.png]] | ||
| 1502 | |||
| 1503 | Create one connection in MQTT.fx tool, set broker port as 8883. | ||
| 1504 | |||
| 1505 | [[image:image-20220709165402-14.png]] | ||
| 1506 | |||
| 1507 | Upload the CA File, Client Certificate File, Client Key File | ||
| 1508 | |||
| 1509 | [[image:image-20220709165402-15.png]] | ||
| 1510 | |||
| 1511 | Publish message to topic “TEST” | ||
| 1512 | |||
| 1513 | [[image:image-20220709165402-17.png]] | ||
| 1514 | |||
| 1515 | Click”Test”~-~-->”MQTT test client”~-~-->”Subscrible to a topic”, to get message publish from MQTT.fx tool. | ||
| 1516 | |||
| 1517 | [[image:image-20220709173500-1.png]] | ||
| 1518 | |||
| 1519 | And we can also send message form AWS platform to MQTT.fx tool. | ||
| 1520 | |||
| 1521 | [[image:image-20220709165402-18.png]] | ||
| 1522 | |||
| 1523 | === **CloudTool** === | ||
| 1524 | |||
| 1525 | Copy the same setting in MQTT.fx to MQTT configuration | ||
| 1526 | |||
| 1527 | [[image:image-20220709165402-19.png]] | ||
| 1528 | |||
| 1529 | Add a lua script and copy the lua demo into it. | ||
| 1530 | |||
| 1531 | [[image:image-20220709165402-20.png]] | ||
| 1532 | |||
| 1533 | {{info}} | ||
| 1534 | **✎Note:** Before using the following demo script, please make sure the V-Box firmware is newer than 22110701 | ||
| 1535 | {{/info}} | ||
| 1536 | |||
| 1537 | {{code language="lua"}} | ||
| 1538 | sprint = print | ||
| 1539 | |||
| 1540 | --Cloud mode interface to obtain the MQTT information configured by the cloud platform: (5 returns, namely the server address, client ID, connection table, last word table, certificate table) | ||
| 1541 | |||
| 1542 | local MQTT_URL, MQTT_CLIENTID, MQTT_CFG, MQTT_LWT, MQTT_CART = mqtt.setup_cfg() | ||
| 1543 | |||
| 1544 | --publish to topics | ||
| 1545 | |||
| 1546 | local pub_RE_TOPIC = string.format('TEST') | ||
| 1547 | |||
| 1548 | --Subscribe topics | ||
| 1549 | |||
| 1550 | local Subscribe_RE_TOPIC1 = string.format('TEST') | ||
| 1551 | |||
| 1552 | --variable | ||
| 1553 | |||
| 1554 | local last_time = 0 | ||
| 1555 | |||
| 1556 | --Timing main function | ||
| 1557 | |||
| 1558 | function aws.main() | ||
| 1559 | |||
| 1560 | sprint(os.date("%Y-%m-%d %H:%M %S", os.time()) .. " aws.main start") | ||
| 1561 | |||
| 1562 | if g_mq then | ||
| 1563 | |||
| 1564 | if g_mq:isconnected() then | ||
| 1565 | |||
| 1566 | send_Data() | ||
| 1567 | |||
| 1568 | else | ||
| 1569 | |||
| 1570 | if os.time() - last_time > 5 then | ||
| 1571 | |||
| 1572 | last_time = os.time() | ||
| 1573 | |||
| 1574 | mymqtt_connect() | ||
| 1575 | |||
| 1576 | end | ||
| 1577 | |||
| 1578 | end | ||
| 1579 | |||
| 1580 | else | ||
| 1581 | |||
| 1582 | mymqtt_init() | ||
| 1583 | |||
| 1584 | end | ||
| 1585 | |||
| 1586 | sprint(os.date("%Y-%m-%d %H:%M %S", os.time()) .. " aws.main end") | ||
| 1587 | |||
| 1588 | end | ||
| 1589 | |||
| 1590 | |||
| 1591 | |||
| 1592 | -- Initialize MQTT | ||
| 1593 | |||
| 1594 | function mymqtt_init() | ||
| 1595 | |||
| 1596 | sprint(string.format("mqtt init mqtt_url:%s mqtt_clientid:%s", MQTT_URL, MQTT_CLIENTID)) | ||
| 1597 | |||
| 1598 | g_mq, err = mqtt.create(MQTT_URL, MQTT_CLIENTID, 1) -- Create the object and declare it as a global variable, 1 means using the domain to connect | ||
| 1599 | |||
| 1600 | if g_mq then | ||
| 1601 | |||
| 1602 | g_mq:on("message", mymqtt_msg_callback) -- Register to receive message callbacks | ||
| 1603 | |||
| 1604 | sprint("mqtt init success") | ||
| 1605 | |||
| 1606 | else | ||
| 1607 | |||
| 1608 | sprint("mqtt init failed:", err) | ||
| 1609 | |||
| 1610 | end | ||
| 1611 | |||
| 1612 | end | ||
| 1613 | |||
| 1614 | -- Connect to MQTT server | ||
| 1615 | |||
| 1616 | function mymqtt_connect() | ||
| 1617 | |||
| 1618 | sprint("mqtt connecting...") | ||
| 1619 | |||
| 1620 | local stat, err = g_mq:connect(MQTT_CFG,MQTT_LWT, MQTT_CART) | ||
| 1621 | |||
| 1622 | if stat == nil then | ||
| 1623 | |||
| 1624 | sprint("mqtt connect failed:", err) | ||
| 1625 | |||
| 1626 | return | ||
| 1627 | |||
| 1628 | else | ||
| 1629 | |||
| 1630 | sprint("mqtt connected") | ||
| 1631 | |||
| 1632 | end | ||
| 1633 | |||
| 1634 | g_mq:subscribe(Subscribe_RE_TOPIC1, 0) | ||
| 1635 | |||
| 1636 | end | ||
| 1637 | |||
| 1638 | -- Receive MQTT message callback function | ||
| 1639 | |||
| 1640 | function mymqtt_msg_callback(topic, msg) | ||
| 1641 | |||
| 1642 | print("topic:",topic) | ||
| 1643 | |||
| 1644 | print("revdata:",msg) | ||
| 1645 | |||
| 1646 | local revData = json.decode(msg) | ||
| 1647 | |||
| 1648 | print (revData) | ||
| 1649 | |||
| 1650 | if topic == Subscribe_RE_TOPIC1 then --Process topic information subscribed from the cloud | ||
| 1651 | |||
| 1652 | if string.match(topic,Subscribe_RE_TOPIC1) then | ||
| 1653 | |||
| 1654 | --if revData ~= nil then | ||
| 1655 | |||
| 1656 | for k,v in pairs (revData) do | ||
| 1657 | |||
| 1658 | print("printing revdata after kv here") | ||
| 1659 | |||
| 1660 | print (k,v) | ||
| 1661 | |||
| 1662 | end | ||
| 1663 | |||
| 1664 | print ("current state is",fanstate) | ||
| 1665 | |||
| 1666 | --end | ||
| 1667 | |||
| 1668 | end | ||
| 1669 | |||
| 1670 | end | ||
| 1671 | |||
| 1672 | end | ||
| 1673 | |||
| 1674 | |||
| 1675 | |||
| 1676 | --Get real-time data | ||
| 1677 | |||
| 1678 | function getData() | ||
| 1679 | |||
| 1680 | local jdata = {} | ||
| 1681 | |||
| 1682 | local addr = bns_get_alldata() | ||
| 1683 | |||
| 1684 | print(json.encode(addr)) | ||
| 1685 | |||
| 1686 | for i,v in pairs(addr) do | ||
| 1687 | |||
| 1688 | if v[2] == 1 then | ||
| 1689 | |||
| 1690 | jdata[v[3]] = v[4] | ||
| 1691 | |||
| 1692 | end | ||
| 1693 | |||
| 1694 | end | ||
| 1695 | |||
| 1696 | return jdata | ||
| 1697 | |||
| 1698 | end | ||
| 1699 | |||
| 1700 | --send data | ||
| 1701 | |||
| 1702 | function send_Data() | ||
| 1703 | |||
| 1704 | local pub_data = | ||
| 1705 | { | ||
| 1706 | 123 | ||
| 1707 | } | ||
| 1708 | |||
| 1709 | sprint(json.encode(pub_data)) | ||
| 1710 | |||
| 1711 | print("..........",pub_RE_TOPIC) | ||
| 1712 | |||
| 1713 | return g_mq:publish(pub_RE_TOPIC, json.encode(pub_data), 0, 0) | ||
| 1714 | |||
| 1715 | end | ||
| 1716 | {{/code}} | ||
| 1717 | |||
| 1718 | Get message in AWS | ||
| 1719 | |||
| 1720 | [[image:image-20220709165402-21.png]] | ||
| 1721 | |||
| 1722 | == **2.7 Mysql** == | ||
| 1723 | |||
| 1724 | In this demo you can use Mysql import the data to the terminal device through the V-box or save the data to the any Mysql database by the V-box. | ||
| 1725 | |||
| 1726 | (% class="wikigeneratedid" id="H1.InstallMysql" %) | ||
| 1727 | **1.Install Mysql** | ||
| 1728 | |||
| 1729 | version | ||
| 1730 | |||
| 1731 | (% style="text-align:center" %) | ||
| 1732 | [[image:Mysql的软件版本.png]] | ||
| 1733 | |||
| 1734 | (% class="wikigeneratedid" id="H2.InstallNavicat" %) | ||
| 1735 | **2.Install Navicat** | ||
| 1736 | |||
| 1737 | (% class="wikigeneratedid" %) | ||
| 1738 | version | ||
| 1739 | |||
| 1740 | (% style="text-align:center" %) | ||
| 1741 | [[image:navicat 版本.png]] | ||
| 1742 | |||
| 1743 | |||
| 1744 | Connecting to Mysql with navicat. | ||
| 1745 | |||
| 1746 | (% style="text-align:center" %) | ||
| 1747 | [[image:连接到mysql.png]] | ||
| 1748 | |||
| 1749 | (% class="wikigeneratedid" id="H3.Createdatabase" %) | ||
| 1750 | **3.Create database** | ||
| 1751 | |||
| 1752 | Character set should be choose utf8mb3/utf8mb4 and Collation choose utf8mb3_danish_ci/utf8mb4_danish_ci. | ||
| 1753 | |||
| 1754 | (% style="text-align:center" %) | ||
| 1755 | [[image:创建数据库.png]] | ||
| 1756 | |||
| 1757 | (% class="wikigeneratedid" id="H4.Createdatatable" %) | ||
| 1758 | **4.Create data table** | ||
| 1759 | |||
| 1760 | Create a table, enter the fields you need. | ||
| 1761 | |||
| 1762 | (% style="text-align:center" %) | ||
| 1763 | [[image:创建表1.png]] | ||
| 1764 | |||
| 1765 | |||
| 1766 | Save, enter table name. | ||
| 1767 | |||
| 1768 | (% style="text-align:center" %) | ||
| 1769 | [[image:创建表2.png]] | ||
| 1770 | |||
| 1771 | (% class="wikigeneratedid" id="H5.InputTableData" %) | ||
| 1772 | **5.Input Table Data** | ||
| 1773 | |||
| 1774 | (% style="text-align:center" %) | ||
| 1775 | [[image:输入或者导入数据.png]] | ||
| 1776 | |||
| 1777 | (% class="wikigeneratedid" id="H6.Script" %) | ||
| 1778 | **6.Script** | ||
| 1779 | |||
| 1780 | **luaMySql.init(string sourcename, string username, string password, string host, number port, string character)** | ||
| 1781 | |||
| 1782 | **Function:** Configure database connection parameters | ||
| 1783 | |||
| 1784 | **Parameter:** | ||
| 1785 | |||
| 1786 | sourcename: the name of database | ||
| 1787 | |||
| 1788 | username: the username of the connection | ||
| 1789 | |||
| 1790 | password: the password of the connection | ||
| 1791 | |||
| 1792 | host: the host name of the connection | ||
| 1793 | |||
| 1794 | port: the host port of the connection | ||
| 1795 | |||
| 1796 | character: the character set of the connection | ||
| 1797 | |||
| 1798 | **Return:** | ||
| 1799 | |||
| 1800 | Succeed: string | ||
| 1801 | |||
| 1802 | Failed: multi | ||
| 1803 | |||
| 1804 | **luaMySql.exec(string statement)** | ||
| 1805 | |||
| 1806 | **Function:** Execute the given SQL statement without returning the result set (add, delete, change) | ||
| 1807 | |||
| 1808 | **Parameter:** | ||
| 1809 | |||
| 1810 | statement: the given SQL statement | ||
| 1811 | |||
| 1812 | **Return:** | ||
| 1813 | |||
| 1814 | Succeed: status: returns the number of rows affected by SQL statement execution. | ||
| 1815 | |||
| 1816 | Failed: nil, errorString | ||
| 1817 | |||
| 1818 | **luaMySql.execWithResult(string statement)** | ||
| 1819 | |||
| 1820 | **Function:** Execute the given SQL statement returning the result set (check) | ||
| 1821 | |||
| 1822 | **Parameter:** | ||
| 1823 | |||
| 1824 | statement: the given SQL statement | ||
| 1825 | |||
| 1826 | **Return:** | ||
| 1827 | |||
| 1828 | Succeed: table: returns the result set | ||
| 1829 | |||
| 1830 | Failed: nil, errorString | ||
| 1831 | |||
| 1832 | **For example:** | ||
| 1833 | |||
| 1834 | |||
| 1835 | {{code language="LUA"}} | ||
| 1836 | -- Assuming the "mysqlclient" library is properly installed and available | ||
| 1837 | mysql = require("mysqlclient") | ||
| 1838 | |||
| 1839 | function DataInitRight() | ||
| 1840 | local dbName = "excel" | ||
| 1841 | local user = "root" | ||
| 1842 | local pwd = "XXXXX" | ||
| 1843 | local host = "192.168.39.146" | ||
| 1844 | local port = 3306 | ||
| 1845 | local character = "utf8mb3" | ||
| 1846 | |||
| 1847 | mysql.init(dbName, user, pwd, host, port, character) | ||
| 1848 | end | ||
| 1849 | |||
| 1850 | function ExecFunc() | ||
| 1851 | status, errorString = mysql.exec("delete from student where Name = 'XXX';") --Delete statement, column name = table element | ||
| 1852 | if nil == status then | ||
| 1853 | print("ExecFunc() error:", errorString) | ||
| 1854 | return -1 | ||
| 1855 | else | ||
| 1856 | print("the number of rows affected by the command:", status) | ||
| 1857 | end | ||
| 1858 | return 0 | ||
| 1859 | end | ||
| 1860 | |||
| 1861 | |||
| 1862 | function ExecWithResultFunc() | ||
| 1863 | status, errorString = mysql.execWithResult("select * from student;") | ||
| 1864 | if nil == status then | ||
| 1865 | print("ExecWithResultFunc() error:", errorString) | ||
| 1866 | return -1 | ||
| 1867 | else | ||
| 1868 | print("ExecWithResultFunc() success : status type = ", type(status)) | ||
| 1869 | print("ExecWithResultFunc() success : status len = ", #status) | ||
| 1870 | local num = #status | ||
| 1871 | local i = 1 | ||
| 1872 | if num > 0 then | ||
| 1873 | for i = 1, num, 1 do | ||
| 1874 | local var = string.format("select result[%d] :Num = %d,Name = %s,Age = %d", i, status[i].Num, status[i].Name,status[i].Age) --Iterate through the data in the table, noting whether the elements are strings or numbers | ||
| 1875 | print(var) | ||
| 1876 | end | ||
| 1877 | end | ||
| 1878 | print("---------------") | ||
| 1879 | end | ||
| 1880 | return 0 | ||
| 1881 | end | ||
| 1882 | |||
| 1883 | function MySQL.main() | ||
| 1884 | print("script running ...") | ||
| 1885 | DataInitRight() | ||
| 1886 | |||
| 1887 | -- use exec demo | ||
| 1888 | if ExecFunc() < 0 then | ||
| 1889 | return | ||
| 1890 | end | ||
| 1891 | |||
| 1892 | -- use execWithResult demo | ||
| 1893 | if ExecWithResultFunc() < 0 then | ||
| 1894 | return | ||
| 1895 | end | ||
| 1896 | |||
| 1897 | print("script running success") | ||
| 1898 | end | ||
| 1899 | {{/code}} | ||
| 1900 | |||
| 1901 | (% class="wikigeneratedid" id="H7.Debug" %) | ||
| 1902 | **7.Debug** | ||
| 1903 | |||
| 1904 | (% style="text-align:center" %) | ||
| 1905 | [[image:调试结果.png]] | ||
| 1906 | |||
| 1907 | (% class="wikigeneratedid" id="H8.Problem" %) | ||
| 1908 | 8.Problem | ||
| 1909 | |||
| 1910 | During our debugging process it may there are some problems about Mysql, such as | ||
| 1911 | |||
| 1912 | * MySQL: Host 192.168.XXX.XXX is not allowed to connect. | ||
| 1913 | * MySQL: Host 192.168.XXX.XXX is blocked because of many connection errors. | ||
| 1914 | * MySQL: SSL connection error: unknown error number. | ||
| 1915 | * ((( | ||
| 1916 | 1449-The user specified as a definer(‘mysql.infoschema‘@localhost‘) does not exist. | ||
| 1917 | ))) | ||
| 1918 | |||
| 1919 | This type of problem has nothing to do with scripts or devices, it is a Mysql configuration issue. If you have any of these problems, please look for a solution online. | ||
| 1920 | |||
| 1921 | {{info}} | ||
| 1922 | ✎Note: If you want to use CMD to access Mysql, you need to add the bin file to the environment variable, please check online for details of the operation. | ||
| 1923 | {{/info}} | ||
| 1924 | |||
| 1925 | == **2.8 Google sheet** == | ||
| 1926 | |||
| 1927 | In this demo you can upload the real-time tags data to google sheet | ||
| 1928 | |||
| 1929 | **1.Google sheets setting** | ||
| 1930 | |||
| 1931 | 1.1 Create a table file based on actual needs and fill in the headers. | ||
| 1932 | |||
| 1933 | (% style="text-align:center" %) | ||
| 1934 | [[image:WeCom Screenshot_20260305114836.png||height="618" width="977"]] | ||
| 1935 | |||
| 1936 | 1.2 Modify the access permissions of the table to 'Anyone on the internet with the link can edit'. | ||
| 1937 | |||
| 1938 | (% style="text-align:center" %) | ||
| 1939 | [[image:z6rgXmeOMz1.png||height="621" width="982"]] | ||
| 1940 | |||
| 1941 | 1.3 Enable the Apps Script extension feature for the sheet. | ||
| 1942 | |||
| 1943 | (% style="text-align:center" %) | ||
| 1944 | [[image:u8QbgKcOgA.png||height="621" width="982"]] | ||
| 1945 | |||
| 1946 | 1.4 On the Apps Script editing page, program according to actual needs. | ||
| 1947 | |||
| 1948 | Google Apps Script Demo | ||
| 1949 | |||
| 1950 | {{code language="JavaScript"}} | ||
| 1951 | /** | ||
| 1952 | * Handles POST requests: parses JSON data, writes it to the sheet with timestamp | ||
| 1953 | * Expected JSON format: { | ||
| 1954 | * sheetName: "Sheet1" (optional, defaults to "Sheet1"), | ||
| 1955 | * integerNumber: 123, | ||
| 1956 | * floatingNumber: 45.67, | ||
| 1957 | * stringData: "example text" | ||
| 1958 | * } | ||
| 1959 | */ | ||
| 1960 | function doPost(e) { | ||
| 1961 | // 1. Parse the JSON data from the request body | ||
| 1962 | var data; | ||
| 1963 | try { | ||
| 1964 | data = JSON.parse(e.postData.contents); | ||
| 1965 | } catch (error) { | ||
| 1966 | return ContentService.createTextOutput('Error: Invalid JSON data'); | ||
| 1967 | } | ||
| 1968 | |||
| 1969 | // 2. Get the target sheet (defaults to "Sheet1") | ||
| 1970 | var sheetName = data.sheetName || 'Sheet1'; | ||
| 1971 | var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName); | ||
| 1972 | if (!sheet) { | ||
| 1973 | return ContentService.createTextOutput('Error: Specified sheet not found'); | ||
| 1974 | } | ||
| 1975 | |||
| 1976 | // 3. Verify required fields are present | ||
| 1977 | if (data.integerNumber === undefined || | ||
| 1978 | data.floatingNumber === undefined || | ||
| 1979 | data.stringData === undefined) { | ||
| 1980 | return ContentService.createTextOutput('Error: Missing required fields (integerNumber, floatingNumber, stringData)'); | ||
| 1981 | } | ||
| 1982 | |||
| 1983 | // 4. Generate formatted current timestamp (first column) | ||
| 1984 | var now = new Date(); | ||
| 1985 | var timeZone = SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone(); | ||
| 1986 | var formattedTimestamp = Utilities.formatDate(now, timeZone, "yyyy-MM-dd HH:mm:ss"); | ||
| 1987 | |||
| 1988 | // 5. Extract data from JSON | ||
| 1989 | var integerNumber = data.integerNumber; | ||
| 1990 | var floatingNumber = data.floatingNumber; | ||
| 1991 | var stringData = data.stringData; | ||
| 1992 | |||
| 1993 | // 6. Append the data to the sheet with proper column mapping: | ||
| 1994 | // Column A: Time (timestamp) | ||
| 1995 | // Column B: Integer number | ||
| 1996 | // Column C: Floating number | ||
| 1997 | // Column D: String data | ||
| 1998 | sheet.appendRow([formattedTimestamp, integerNumber, floatingNumber, stringData]); | ||
| 1999 | |||
| 2000 | // 7. Return success response | ||
| 2001 | return ContentService.createTextOutput('Success: Data written to Google Sheets'); | ||
| 2002 | } | ||
| 2003 | {{/code}} | ||
| 2004 | |||
| 2005 | (% style="text-align:center" %) | ||
| 2006 | [[image:PixPin_2026-03-05_14-40-27.png||height="623" width="985"]] | ||
| 2007 | |||
| 2008 | 1.5 Click "Deploy" to make a new deployment. Select the 'Web APP' type, set the user permissions, and complete the deployment of the script. | ||
| 2009 | |||
| 2010 | (% style="text-align:center" %) | ||
| 2011 | [[image:3W00uSwkVQ1.png||height="641" width="981"]] | ||
| 2012 | |||
| 2013 | 1.6 In the 'Manage deployments' interface, obtain the URL of the web app. | ||
| 2014 | |||
| 2015 | (% style="text-align:center" %) | ||
| 2016 | [[image:HoLYu2nzvq.png||height="631" width="966"]] | ||
| 2017 | |||
| 2018 | 2. Test the Web APP URL | ||
| 2019 | |||
| 2020 | With the Web APP URL, we can test it via API testing tool (Postman, Hoppscotch) | ||
| 2021 | |||
| 2022 | Web APP Handles POST requests: parses JSON data, writes it to the sheet with timestamp | ||
| 2023 | |||
| 2024 | Header: [Content-Type: application/json] | ||
| 2025 | |||
| 2026 | Expected JSON format: { | ||
| 2027 | |||
| 2028 | sheetName: "Sheet1" (optional, defaults to "Sheet1"), | ||
| 2029 | |||
| 2030 | integerNumber: 123, | ||
| 2031 | |||
| 2032 | floatingNumber: 45.67, | ||
| 2033 | |||
| 2034 | stringData: "example text" | ||
| 2035 | |||
| 2036 | } | ||
| 2037 | |||
| 2038 | (% style="text-align:center" %) | ||
| 2039 | [[image:8a8vG9Z7IT.png||height="597" width="913"]] | ||
| 2040 | |||
| 2041 | POST request execution effect | ||
| 2042 | |||
| 2043 | (% style="text-align:center" %) | ||
| 2044 | [[image:WeCom Screenshot_20260305153954.png||height="602" width="921"]] | ||
| 2045 | |||
| 2046 | {{info}} | ||
| 2047 | After successfully testing with the API tool, it is recommended to change the General access of the Google Sheet to 'Restricted.' This will help improve data security without affecting the use of the Web App. | ||
| 2048 | |||
| 2049 | (% style="text-align:center" %) | ||
| 2050 | [[image:RW7OUCVVp1.png||height="617" width="943"]] | ||
| 2051 | {{/info}} | ||
| 2052 | |||
| 2053 | **3. V-box settings** | ||
| 2054 | |||
| 2055 | 3.1 Real-time tags setting | ||
| 2056 | |||
| 2057 | (% style="text-align:center" %) | ||
| 2058 | [[image:PixPin_2026-03-05_16-00-15.png||height="291" width="982"]] | ||
| 2059 | |||
| 2060 | 3.2 Lua Script: | ||
| 2061 | |||
| 2062 | 3.2.1 Basic Configuration Information | ||
| 2063 | |||
| 2064 | (% style="text-align:center" %) | ||
| 2065 | [[image:PixPin_2026-03-05_16-02-35.png||height="451" width="480"]] | ||
| 2066 | |||
| 2067 | 3.2.2 Lua Script Demo | ||
| 2068 | |||
| 2069 | {{code language="Lua"}} | ||
| 2070 | -- Google Apps Script deployment URL (required) | ||
| 2071 | local req_url = "https://script.google.com/macros/s/AKfycbxbAxKSysisJKdXeL5k1IuH4mYhnN2qOuq9ZbTtYJRQMSBgYi67eaqZRrS4JmhsA2dL/exec" | ||
| 2072 | |||
| 2073 | function sheet.main() | ||
| 2074 | -- Entry point: trigger the data sending process | ||
| 2075 | prepareAndSendSheetData() | ||
| 2076 | end | ||
| 2077 | |||
| 2078 | -- Prepares data from PLC addresses and sends it to Google Sheets | ||
| 2079 | -- Fetches: name (string) and score (word) from specified memory addresses | ||
| 2080 | -- Constructs JSON payload and triggers HTTP POST request | ||
| 2081 | |||
| 2082 | function prepareAndSendSheetData() | ||
| 2083 | |||
| 2084 | float_num = addr_getfloat("@floating number") | ||
| 2085 | -- Build the data payload to be sent to Google Sheets | ||
| 2086 | local SheetData = { | ||
| 2087 | sheetName = "Sheet1", -- Target sheet name | ||
| 2088 | stringData = addr_getstring("@string", 20), -- Fetch string from address (max 20 chars) | ||
| 2089 | integerNumber = addr_getword("@word"), -- Fetch 16-bit integer from address | ||
| 2090 | floatingNumber = string.format("%0.4f", float_num) -- Fetch 32-bit floating number from address | ||
| 2091 | } | ||
| 2092 | |||
| 2093 | -- Directly use the single dataset as the message | ||
| 2094 | local message = SheetData | ||
| 2095 | |||
| 2096 | -- Debug output (optional, can be removed in production) | ||
| 2097 | print("URL: " .. tostring(req_url)) | ||
| 2098 | print("Request Body: " .. json.encode(message)) | ||
| 2099 | |||
| 2100 | -- Send the request via HTTP POST | ||
| 2101 | executeHttpPostRequest(req_url, message) | ||
| 2102 | end | ||
| 2103 | |||
| 2104 | |||
| 2105 | -- Executes HTTP POST request to Google Apps Script endpoint | ||
| 2106 | -- @param req_url: target URL for the POST request | ||
| 2107 | -- @param message: Lua table containing data to be sent (will be JSON-encoded) | ||
| 2108 | |||
| 2109 | |||
| 2110 | function executeHttpPostRequest(req_url, message) | ||
| 2111 | -- Required libraries | ||
| 2112 | local json = require("json") | ||
| 2113 | local ltn12 = require("ltn12") | ||
| 2114 | local https = require("https") | ||
| 2115 | |||
| 2116 | -- Encode the message table into a JSON string | ||
| 2117 | local request_body = json.encode(message) | ||
| 2118 | local max_redirects = 0 -- Number of redirects to follow (set to 0 for none) | ||
| 2119 | local current_url = req_url | ||
| 2120 | |||
| 2121 | local response_body = {} | ||
| 2122 | local res, code, response_headers = https.request{ | ||
| 2123 | url = current_url, | ||
| 2124 | method = "POST", | ||
| 2125 | headers = { | ||
| 2126 | ["Content-Type"] = "application/json", | ||
| 2127 | ["Content-Length"] = #request_body | ||
| 2128 | }, | ||
| 2129 | source = ltn12.source.string(request_body), | ||
| 2130 | sink = ltn12.sink.table(response_body) | ||
| 2131 | } | ||
| 2132 | |||
| 2133 | end | ||
| 2134 | {{/code}} |