forked from LairdCP/RM1xx-Applications
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cayenne.mydevice.sb
597 lines (514 loc) · 22.2 KB
/
cayenne.mydevice.sb
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
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
//******************************************************************************
// Copyright (c) 2017, Laird
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
// IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// SPDX-License-Identifier:ISC
//
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// +++++ ++
// +++++ When UwTerminal downloads the app it will store it as a filename ++
// +++++ which consists of all characters up to the first . and excluding it ++
// +++++ ++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Description of this application:
// This app monitors temperature, Power supply level, Button1 & LED5 status on
// DVK and then send them to Cayenne server over LoRaWAN network.
//
// Please refer to the Cayenne MyDevices documentation on the Laird RM1xx website
// which guides you through using this application. Available under the
// documentation tab on https://www.lairdtech.com/products/rm1xx-lora-modules
//
// Button1 is connected to I/O Expander pin 4 on DVK.
// The I/O Expander is a Microchip MCP23S08 SPI device. The register map is
// below for reference. The datasheet for this device explains all registers
// in detail. This information is used in Initialise()
//
// Name Addr
// IODIR 0x00
// IPOL 0x01
// GPINTEN 0x02
// DEFVAL 0x03
// INTCON 0x04
// IOCON 0x05
// GPPU 0x06
// INTF 0x07
// INTCAP 0x08
// GPIO 0x09
// OLAT 0x0A
//
// Read op-code : 0x41
// Write op-code : 0x40
//
// Configure Inputs/Outputs by writing 0x10 to register 0x00
// IO7 IO6 IO5 IO4 IO3 IO2 IO1 IO0 // MCP23S08 pin
// n/a n/a n/a BUTN LED4 LED3 LED2 LED1 // RM1xx DVK pin
// 0 0 0 1 0 0 0 0 // 0=output 1=input
//
// This app provides for a command interface over the uart and the protocol is
// as follows:-
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Convention : (1) Case sensitive, and commands are presented in alphabetic order
// (2) If line ends with \ then it continues on next line. That does
// not mean that it should be sent as multiple lines
// (3) Replace anything between ##
// (4) #INTaaaa# means a number in decimal, hex, octal or binary
// format -> 23 == 0x17 == h'17 == o'23 == b'10111
// aaaa is just a description
// (5) #HEXaaaa# means a string without delimiters consisting of hex
// characters only aaaa is just a description
// (6) #STRaaaa# means a string without delimiters
// aaaa is just a description
// (7) "STRaaaa" means a string which must have the " delimiter
// aaaa is just a description
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//******************************************************************************
//******************************************************************************
// Definitions
//******************************************************************************
#include "RM1xx-defs.h"
//Set this to 0 to disable all debugging messages
#define ENABLE_DEBUG_PRINTS 1
//Size of i[]
#define NUM_OF_I_PARAMS (4)
//Size of s$[]
#define NUM_OF_S_PARAMS (4)
//Button1 : SIO4 is connected to the CS line via J18
#define SPI_CS_PIN 4
//SIO5 is connected to the temperature sensor
#define TEMP_SENS_PIN 5
//SIO28 is connected to Button2 with a jumper on DVK
#define BUTTON2 28
//SIO6 is connected to LED5
#define LED5 6
//Set to 1 to automatically join the LoRa network at startup. Set 0 to disable it
#define AUTO_JOIN 1
//OTAA. To use ABP, replace the value with LORAMAC_JOIN_BY_PERSONALIZATION or 0
#define JOIN_TYPE LORAMAC_JOIN_BY_REQUEST
//******************************************************************************
// Register Error Handler as early as possible
//******************************************************************************
sub HandlerOnErr()
print "\n OnErr - ";GetLastError();"\n"
endsub
onerror next HandlerOnErr
//******************************************************************************
// Debugging resource as early as possible
//******************************************************************************
//==============================================================================
sub AssertResCode(byval rc as integer,byval tag as integer)
if rc!=0 then
print "\nFailed with ";integer.h' rc;" at tag ";tag
endif
endsub
//==============================================================================
sub DbgMsgVal(byval msg$ as string, byval vl as integer)
if (ENABLE_DEBUG_PRINTS!=0) then
print "\n";msg$;" ";vl
endif
endsub
//==============================================================================
sub DbgMsg(byval msg$ as string)
if (ENABLE_DEBUG_PRINTS!=0) then
print "\n";msg$
endif
endsub
//******************************************************************************
// Library Import
//******************************************************************************
//******************************************************************************
// Debugging resource after libs
//******************************************************************************
//******************************************************************************
// Global Variable Declarations
//******************************************************************************
dim rc
dim stRsp$ as string //Uart rx data is stored here
dim str$ //Store data for string
dim ok$,er$,pr$ //String for OK, ERROR and carriage return
dim i[NUM_OF_I_PARAMS] //Index 0 used for return values
dim s$[NUM_OF_S_PARAMS] //Values stored in NVRAM (AppEUI and DevAddr) are stored here
dim urtcmd$ //Cmd line from uart
dim tkn$,tlen //Used by command parser
dim urts //Will be <0 if uart parser suspended
dim SPIHandle //Handle for Microchip MCP23S08 SPI peripheral
dim led : led = 0 //led status
dim okToTx : okToTx = 1 //Available duty cycle on one of sub-band (only matters in EU & AS)
//Change to 0 at data Tx and 1 at EVLORAMACNEXTTX
//******************************************************************************
// initialize Global Variable
//******************************************************************************
ok$ = "\nOK"
er$ = "\nERROR "
pr$ = "\r\n>"
urts=0 //not suspended
//******************************************************************************
// Function and Subroutine definitions
//******************************************************************************
//==============================================================================
SUB PrintMsg(str$)
rc = UartWrite(str$)
ENDSUB
//==============================================================================
sub UartRsp(rsp as integer)
if rsp == 0 then
print ok$;pr$
elseif rsp > 0 then
print er$;integer.h' rsp;pr$
endif
urts = rsp
endsub
//==============================================================================
function ExtractIntTokens(u$,stIdx,num)
while num>0
tlen = ExtractIntToken(u$,i[stIdx])
if tlen == 0 then
exitfunc 4
endif
num=num-1
stIdx = stIdx+1
endwhile
endfunc 0
//==============================================================================
function ReadTemp()
dim tempC : tempC = GpioRead(TEMP_SENS_PIN)
tempC = ((19028992-36000*tempC)/11971)
endfunc tempC
//==============================================================================
sub EndianSwap(byref atm)
atm = (atm >> 8 & 0xFF) | (atm << 8 & 0xFF00)
endsub
//==============================================================================
function ReadBtnState()
dim wr$, rd$
//Read (opcode 0x41) PORT/GPIO Register (0x09)
wr$="\41\09\00"
GpioWrite(SPI_CS_PIN,0)
rc=SpiReadWrite(wr$,rd$)
GpioWrite(SPI_CS_PIN,1)
//The GPIO pin status comes back in the 3rd byte
rc=StrGetChr(rd$,2)
//Check if IO4 is grounded
endfunc ((rc&0x10)==0)
//==============================================================================
// Read temperature, power supply level, Button1 & LED5 status on DVK
// and send them over LoRaWAN network in a format that Cayenne can understand
//==============================================================================
function LoraPost()
if (okToTx == 1) then
//There is available duty cycle
dim tempType$, tempVal$,txData$, powerVal$, bttn1State$, led5State$
dim atm, theoreticalMax, actualMax
//Data format for Cayenne - first byte is data channel and second byte is data type
tempType$="\01\67" // "\67" : Temperature
atm = ReadTemp()
EndianSwap(atm)
rc=bleencode16(tempVal$,atm,0)
powerVal$ = "\02\02" // "\02" : Analog input
atm = ReadPwrSupplyMv()/10
EndianSwap(atm)
rc=bleencode16(powerVal$,atm,2)
atm = ReadBtnState()
bttn1State$ = "\03\00" // "\00" : Digital input
rc=bleencode8(bttn1State$,atm,2)
led5State$ = "\04\01" // "\01" : Digital output
if (led == 0) then
rc=strsetchr(led5State$,0,2)
else
rc=strsetchr(led5State$,1,2)
endif
txData$ = tempType$ + tempVal$ + powerVal$ + bttn1State$ + led5State$
//Check the size of data is within maximum payload size
rc = LoramacQueryTxPossible(strlen(txData$), theoreticalMax, actualMax)
if (rc == 0) then
//Current data rate can handle the outbound data size
rc = LORAMACTxData(1, txData$, 1)
else
print "\nData payload is too large for current data rate"
print "\n Theoretical max payload at current data rate : ";theoreticalMax;
print "\n Actual max payload at current data rate: ";actualMax;
print "\n Size of data currently attempting to transmit: ";strlen(txData$)
//Increase data rate by one
rc = LORAMACGetOption(LORAMAC_OPT_DATA_RATE, str$)
dim dr : dr = StrValDec(str$)
if (dr <= 4) then
dr = dr + 1
sprint #str$, dr
print "\nRaising data rate to DR";dr;" for higher max payload\n"
endif
rc = LORAMACSetOption(LORAMAC_OPT_DATA_RATE, str$)
endif
else
str$ = "\nThere is no available duty cycle"
PrintMsg(str$)
okToTx = 0 //Will become 1 when EVLORAMACNEXTTX is triggered
endif
endfunc 1
//==============================================================================
//#CMD#// lora join
//#CMD#// lora activate
//#CMD#// lora send #STRdata# #INTport# #INTconfirm#
//#CMD#// lora post
//#CMD#// lora get #INToptID#
//#CMD#// lora set #INToptID# #INToptVal#
//#CMD#// lora debug #INTdebug# #INTtxSio# #INTrxSio#
//==============================================================================
function _Lora()
dim prAdr$
dim val
dim res
dim reg
dim stringVal$
tlen = ExtractStrToken(urtcmd$,tkn$)
if tlen == 0 then
exitfunc 5
elseif strcmp(tkn$,"join")==0 then
exitfunc LORAMACJoin(LORAMAC_JOIN_BY_REQUEST)
elseif strcmp(tkn$, "activate")==0 then
exitfunc LORAMACJoin(LORAMAC_JOIN_BY_PERSONALIZATION)
elseif strcmp(tkn$,"send")==0 then
// #>lora send <data> <port> <confirm>
rc = ExtractStrToken(urtcmd$, tkn$)
rc = ExtractIntTokens(urtcmd$,1,2)
exitfunc LORAMACTxData(i[1], tkn$, i[2])
elseif strcmp(tkn$,"post")==0 then
exitfunc LoraPost()
elseif strcmp(tkn$,"get")==0 then
rc = ExtractIntToken(urtcmd$,reg)
rc = LORAMACGetOption(reg, stringVal$)
print stringVal$
exitfunc rc
elseif strcmp(tkn$,"set")==0 then
rc = ExtractIntToken(urtcmd$,reg)
rc = ExtractStrToken(urtcmd$,stringVal$)
rc = LORAMACSetOption(reg, stringVal$)
exitfunc rc
elseif strcmp(tkn$,"debug")==0 then
rc = ExtractIntTokens(urtcmd$,0,3)
if rc == 0 then
rc = LORAMACSetDebug(i[0],i[1],i[2])
endif
exitfunc rc
endif
endfunc 5
//==============================================================================
function OnUartCmd() as integer
dim inputVoltage_mV
rc=1 //Assume there is an error
tlen = ExtractStrToken(urtcmd$,tkn$) //Get first token
if tlen == 0 then
rc=0
elseif tlen > 0 then
if strcmp(tkn$,"lora") == 0 then
rc = _Lora()
elseif strcmp(tkn$,"volts") == 0 then
inputVoltage_mV = ReadPwrSupplyMv()
print "Input voltage = ";inputVoltage_mV;"mV\n"
rc=0
elseif strcmp(tkn$,"exit") == 0 then
rc=0
exitfunc 0
endif
endif
//Send a response back to the user
UartRsp(rc)
endfunc 1
//******************************************************************************
// Handler definitions
//******************************************************************************
//==============================================================================
// This handler is called when data has arrived at the serial port
//==============================================================================
function HandlerUartRxCmd() as integer
dim nMatch
if urts < 0 then
//UART parser is suspended
exitfunc 1
endif
//Check if CR has been received
nMatch=UartReadMatch(stRsp$,13)
if nMatch!=0 then
//CR exists in the input buffer
urtcmd$ = strsplitleft$(stRsp$,nMatch)
exitfunc OnUartCmd()
endif
endfunc 1
//==============================================================================
// This handler is called when there is availale duty cycle on one of sub-band to send data
//==============================================================================
FUNCTION HandlerNextTx() As Integer
okToTx = 1
endfunc 1
//==============================================================================
// This handler is called when there is a LoRa TX Complete event
//==============================================================================
function HandlerLoRaTxComp() as integer
print "\nLoRa TX Complete Event"
endfunc 1
//==============================================================================
// This handler is called when there is a LoRa RX Complete event
//==============================================================================
function HandlerLoRaRxComp() as integer
print "\nLoRa RX Complete Event"
endfunc 1
//==============================================================================
// This handler is called when the LoRa Join procedure starts
//==============================================================================
function HandlerLoRaJoining() as integer
print "\nAttempting to join the LoRa network"
endfunc 1
//==============================================================================
// This handler is called when there is a LoRa Join Success Complete event
//==============================================================================
function HandlerLoRaJoined() as integer
print "\nSuccessfully joined the LoRa network"
//Change data rate to DR2 to allow bigger payload
str$ = "2"
rc = LORAMACSetOption(LORAMAC_OPT_DATA_RATE, str$)
if (AUTO_JOIN == 1) then
print "\nNow RM1xx will send data to Cayenne periodically"
TimerStart(0, 30000, 1)
endif
endfunc 1
//==============================================================================
// This handler is called when there is a LoRa Join Fail event
//==============================================================================
function HandlerLoRaJoinFailed() as integer
str$ = "\nFailed to join the LoRa network"
PrintMsg(str$)
endfunc 1
//==============================================================================
// This handler is called when there is a LoRa Tx Timeout event
//==============================================================================
function HandlerLoRaTxTimeout() as integer
str$ = "\nLoRa TX Timeout"
PrintMsg(str$)
endfunc 1
//==============================================================================
// This handler is called when there is a LoRa Rx Timeout event
//==============================================================================
function HandlerLoRaRxTimeout() as integer
str$ = "\nLoRa RX Timeout"
PrintMsg(str$)
endfunc 1
//==============================================================================
// This handler is called when there is an error in the receive path
//==============================================================================
function HandlerLoRaRxError() as integer
str$ = "\nLoRa RX Error"
PrintMsg(str$)
endfunc 1
//==============================================================================
// This handler is called when the TxDone signal has been received in the module
//==============================================================================
function HandlerLoRaTxDone() As Integer
str$ = "\nTx Done"
PrintMsg(str$)
endfunc 1
//==============================================================================
// This handler is called an RxWindow has faied to revceive a sync pulse
//==============================================================================
function HandlerLoRaNoSync() As Integer
str$ = "\nNo Sync pulse"
PrintMsg(str$)
endfunc 1
//==============================================================================
// This handler is called when An ADR command has been receive as part of a downlink.
//==============================================================================
function HandlerLoRaAdr(PacketType, FramePending) As Integer
str$ = "\n\nADR received"
PrintMsg(str$)
rc = LORAMACGetOption(LORAMAC_OPT_TX_POWER, str$)
print "\nLORAMAC_OPT_TX_POWER: ";str$;"\n"
rc = LORAMACGetOption(LORAMAC_OPT_DATA_RATE, str$)
print "LORAMAC_OPT_DATA_RATE: ";str$;"\n"
rc = LORAMACGetOption(LORAMAC_OPT_CHANNELMASK, str$)
print "LORAMAC_OPT_CHANNELMASK: ";str$;
endfunc 1
//==============================================================================
// This handler is called when Button2 is pressed
//==============================================================================
function Button2Pressed()
if (led == 0) then
gpiowrite(LED5,1) //Turns LED5 on
led = 1
str$ = "\nButton2 pressed - LED5 turned on\n"
PrintMsg(str$)
else
gpiowrite(LED5,0) //Turns LED5 off
led = 0
str$ = "\nButton2 pressed - LED5 turned off\n"
PrintMsg(str$)
endif
endfunc 1
//******************************************************************************
// Equivalent to main() in C
//******************************************************************************
//==============================================================================
// Enable synchronous event handlers
//==============================================================================
OnEvent EVUARTRX call HandlerUartRxCmd
OnEvent EVLORAMACTXCOMPLETE call HandlerLoRaTxComp
OnEvent EVLORAMACRXCOMPLETE call HandlerLoRaRxComp
OnEvent EVLORAMACJOINING call HandlerLoRaJoining
OnEvent EVLORAMACJOINED call HandlerLoRaJoined
OnEvent EVLORAMACJOINFAIL call HandlerLoRaJoinFailed
OnEvent EVLORAMACTXTIMEOUT call HandlerLoRaTxTimeout
OnEvent EVLORAMACRXTIMEOUT call HandlerLoRaRxTimeout
OnEvent EVLORAMACRXERROR call HandlerLoRaRxError
OnEvent EVLORAMACTXDONE call HandlerLoRaTxDone
OnEvent EVLORAMACNOSYNC call HandlerLoRaNoSync
OnEvent EVLORAMACADR call HandlerLoRaAdr
OnEvent EVLORAMACNEXTTX call HandlerNextTx
OnEvent EVTMR0 call LoraPost
OnEvent EVGPIOCHAN0 call Button2Pressed
//==============================================================================
// Set up GPIO and SPI for temperature sensor, Button1, Button2, LED5
// and join LoRa network
//==============================================================================
sub Initialise()
dim wr$
//Set the temperature sensor up with 1/3 scaling
rc = GpioSetFunc(TEMP_SENS_PIN, 3, 0x13)
//Setting for Button1
rc=GpioSetFunc(SPI_CS_PIN, 2, 1)
GpioWrite(SPI_CS_PIN, 1)
rc=SpiOpen(0, 125000, 0, SPIHandle)
//Configure Button1 as output by writing(opcode 0x40) 0x10 to register 0x00
wr$="\40\00\10"
GpioWrite(SPI_CS_PIN, 0)
rc=SpiWrite(wr$)
GpioWrite(SPI_CS_PIN, 1)
//Enable a weak pull-up on the button1 (GPPU 0x06)
wr$="\40\06\10"
GpioWrite(SPI_CS_PIN, 0)
rc=SpiWrite(wr$)
GpioWrite(SPI_CS_PIN, 1)
//Set LED5 as GIDITAL_OUT with initial output low
rc = gpiosetfunc(LED5,2,0)
//Setting BUTTON2 as Digial_IN with weak pull-up
rc = gpiosetfunc(BUTTON2,1,2)
//Link Button2 to LED5
rc = GpioBindEvent(0, BUTTON2 , 1) //Binds a gpio transition low to EVGPIOCHAN0.
//Automatically join the LoRa network if set
if (AUTO_JOIN == 1) then
rc = LORAMACJoin(JOIN_TYPE)
endif
endsub
Initialise()
//Send an OK response
UartRsp(0)
//Wait for a synchronous event.
WaitEvent