-
-
Notifications
You must be signed in to change notification settings - Fork 14
/
iot.html
1878 lines (1805 loc) · 105 KB
/
iot.html
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
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="rustdoc">
<title>Build an IoT App with Zig and LoRaWAN</title>
<!-- Begin scripts/articles/*-header.html: Article Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<meta property="og:title"
content="Build an IoT App with Zig and LoRaWAN"
data-rh="true">
<meta property="og:description"
content="Let's build a complex IoT App with Zig and LoRaWAN... And run it on RISC-V BL602 with Apache NuttX RTOS"
data-rh="true">
<meta property="og:image"
content="https://lupyuen.github.io/images/iot-title.jpg">
<meta property="og:type"
content="article" data-rh="true">
<link rel="canonical" href="https://lupyuen.org/articles/iot.html" />
<!-- End scripts/articles/*-header.html -->
<!-- Begin scripts/rustdoc-header.html: Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<link rel="alternate" type="application/rss+xml" title="RSS Feed for lupyuen" href="/rss.xml" />
<link rel="stylesheet" type="text/css" href="../normalize.css">
<link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle">
<link rel="stylesheet" type="text/css" href="../dark.css">
<link rel="stylesheet" type="text/css" href="../light.css" id="themeStyle">
<link rel="stylesheet" type="text/css" href="../prism.css">
<script src="../storage.js"></script><noscript>
<link rel="stylesheet" href="../noscript.css"></noscript>
<link rel="shortcut icon" href="../favicon.ico">
<style type="text/css">
#crate-search {
background-image: url("../down-arrow.svg");
}
</style>
<!-- End scripts/rustdoc-header.html -->
</head>
<body class="rustdoc">
<!--[if lte IE 8]>
<div class="warning">
This old browser is unsupported and will most likely display funky
things.
</div>
<![endif]-->
<!-- Begin scripts/rustdoc-before.html: Pre-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
<!-- Begin Theme Picker -->
<div class="theme-picker" style="left: 0"><button id="theme-picker" aria-label="Pick another theme!"><img src="../brush.svg"
width="18" alt="Pick another theme!"></button>
<div id="theme-choices"></div>
</div>
<!-- Theme Picker -->
<!-- End scripts/rustdoc-before.html -->
<h1 class="title">Build an IoT App with Zig and LoRaWAN</h1>
<nav id="rustdoc"><ul>
<li><a href="#lorawan-network-stack" title="LoRaWAN Network Stack">1 LoRaWAN Network Stack</a><ul></ul></li>
<li><a href="#import-lorawan-library" title="Import LoRaWAN Library">2 Import LoRaWAN Library</a><ul></ul></li>
<li><a href="#main-function" title="Main Function">3 Main Function</a><ul></ul></li>
<li><a href="#convert-integer-type" title="Convert Integer Type">4 Convert Integer Type</a><ul></ul></li>
<li><a href="#transmit-data-packet" title="Transmit Data Packet">5 Transmit Data Packet</a><ul></ul></li>
<li><a href="#logging" title="Logging">6 Logging</a><ul></ul></li>
<li><a href="#compile-zig-app" title="Compile Zig App">7 Compile Zig App</a><ul></ul></li>
<li><a href="#run-zig-app" title="Run Zig App">8 Run Zig App</a><ul></ul></li>
<li><a href="#safety-checks" title="Safety Checks">9 Safety Checks</a><ul></ul></li>
<li><a href="#zig-outcomes" title="Zig Outcomes">10 Zig Outcomes</a><ul></ul></li>
<li><a href="#whats-next" title="What’s Next">11 What’s Next</a><ul></ul></li>
<li><a href="#notes" title="Notes">12 Notes</a><ul></ul></li>
<li><a href="#appendix-handle-lorawan-events" title="Appendix: Handle LoRaWAN Events">13 Appendix: Handle LoRaWAN Events</a><ul></ul></li>
<li><a href="#appendix-logging" title="Appendix: Logging">14 Appendix: Logging</a><ul></ul></li>
<li><a href="#appendix-panic-handler" title="Appendix: Panic Handler">15 Appendix: Panic Handler</a><ul></ul></li>
<li><a href="#appendix-zig-compiler-as-drop-in-replacement-for-gcc" title="Appendix: Zig Compiler as Drop-In Replacement for GCC">16 Appendix: Zig Compiler as Drop-In Replacement for GCC</a><ul></ul></li>
<li><a href="#appendix-lorawan-library-for-nuttx" title="Appendix: LoRaWAN Library for NuttX">17 Appendix: LoRaWAN Library for NuttX</a><ul></ul></li>
<li><a href="#appendix-lorawan-app-for-nuttx" title="Appendix: LoRaWAN App for NuttX">18 Appendix: LoRaWAN App for NuttX</a><ul></ul></li>
<li><a href="#appendix-auto-translate-lorawan-app-to-zig" title="Appendix: Auto-Translate LoRaWAN App to Zig">19 Appendix: Auto-Translate LoRaWAN App to Zig</a><ul></ul></li>
<li><a href="#appendix-opaque-type-error" title="Appendix: Opaque Type Error">20 Appendix: Opaque Type Error</a><ul></ul></li>
<li><a href="#appendix-fix-opaque-type" title="Appendix: Fix Opaque Type">21 Appendix: Fix Opaque Type</a><ul></ul></li>
<li><a href="#appendix-macro-error" title="Appendix: Macro Error">22 Appendix: Macro Error</a><ul></ul></li>
<li><a href="#appendix-struct-initialisation-error" title="Appendix: Struct Initialisation Error">23 Appendix: Struct Initialisation Error</a><ul></ul></li></ul></nav><p>📝 <em>15 Jun 2022</em></p>
<p><img src="https://lupyuen.github.io/images/iot-title.jpg" alt="Pine64 PineDio Stack BL604 RISC-V Board (left) talking LoRaWAN on Zig to RAKwireless WisGate LoRaWAN Gateway (right)" /></p>
<p><em>Pine64 PineDio Stack BL604 RISC-V Board (left) talking LoRaWAN on Zig to RAKwireless WisGate LoRaWAN Gateway (right)</em></p>
<p>In our last article we learnt to run barebones <strong>Zig on a Microcontroller</strong> (RISC-V BL602) with a <strong>Real-Time Operating System</strong> (Apache NuttX RTOS)…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/zig"><strong>“Zig on RISC-V BL602: Quick Peek with Apache NuttX RTOS”</strong></a></li>
</ul>
<p><em>But can we do something way more sophisticated with Zig?</em></p>
<p>Yes we can! Today we shall run a complex <strong>IoT Application</strong> with <strong>Zig and LoRaWAN</strong>…</p>
<ul>
<li>
<p>Join a <a href="https://makezine.com/2021/05/24/go-long-with-lora-radio/"><strong>LoRaWAN Wireless Network</strong></a></p>
</li>
<li>
<p>Transmit a <strong>Data Packet</strong> to the LoRaWAN Network at regular intervals</p>
</li>
</ul>
<p>Which is the typical firmware we would run on <strong>IoT Sensors</strong>.</p>
<p><em>Will this run on any device?</em></p>
<p>We’ll do this on Pine64’s <a href="https://lupyuen.github.io/articles/pinedio2"><strong>PineDio Stack BL604</strong></a> RISC-V Board.</p>
<p>But the steps should be similar for BL602, ESP32-C3, Arm Cortex-M and other 32-bit microcontrollers supported by Zig.</p>
<p><em>Why are we doing this?</em></p>
<p>I always dreaded maintaining and extending complex <strong>IoT Apps in C</strong>. <a href="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c">(Like this one)</a></p>
<p>Will Zig make this a little less painful? Let’s find out!</p>
<p>This is the Zig source code that we’ll study today…</p>
<ul>
<li><a href="https://github.com/lupyuen/zig-bl602-nuttx"><strong>lupyuen/zig-bl602-nuttx</strong></a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/spi2-title.jpg" alt="Pine64 PineCone BL602 Board (right) connected to Semtech SX1262 LoRa Transceiver (left). This works too!" /></p>
<p><a href="https://lupyuen.github.io/articles/spi2"><em>Pine64 PineCone BL602 Board (right) connected to Semtech SX1262 LoRa Transceiver (left). This works too!</em></a></p>
<h1 id="lorawan-network-stack"><a class="doc-anchor" href="#lorawan-network-stack">§</a>1 LoRaWAN Network Stack</h1>
<p><em>What’s a LoRaWAN Network Stack?</em></p>
<p>To talk to a LoRaWAN Wireless Network, our IoT Gadget needs 3 things…</p>
<ul>
<li>
<p><strong>LoRa Radio Transceiver</strong></p>
<p><a href="https://www.semtech.com/products/wireless-rf/lora-core/sx1262">(Like PineDio Stack’s onboard Semtech SX1262 Transceiver)</a></p>
</li>
<li>
<p><strong>LoRa Driver</strong> that will transmit and receive raw LoRa Packets</p>
<p>(By controlling the LoRa Transceiver over SPI)</p>
</li>
<li>
<p><strong>LoRaWAN Driver</strong> that will join a LoRaWAN Network and transmit LoRaWAN Data Packets</p>
<p>(By calling the LoRa Driver)</p>
</li>
</ul>
<p>Together, the LoRa Driver and LoRaWAN Driver make up the <strong>LoRaWAN Network Stack</strong>.</p>
<p><em>Which LoRaWAN Stack will we use?</em></p>
<p>We’ll use <strong>Semtech’s Reference Implementation</strong> of the LoRaWAN Stack…</p>
<ul>
<li>
<p><a href="https://github.com/Lora-net/LoRaMac-node"><strong>Lora-net/LoRaMac-node</strong></a></p>
<p><a href="https://stackforce.github.io/LoRaMac-doc/LoRaMac-doc-v4.6.0/index.html">(LoRaMAC Documentation)</a></p>
</li>
</ul>
<p>That we’ve ported to PineDio Stack BL604 with <strong>Apache NuttX RTOS</strong>…</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/lorawan3"><strong>“LoRaWAN on Apache NuttX OS”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/sx1262"><strong>“LoRa SX1262 on Apache NuttX OS”</strong></a></p>
</li>
</ul>
<p>The same LoRaWAN Stack is available on many other platforms, including <a href="https://docs.zephyrproject.org/latest/connectivity/lora_lorawan/index.html"><strong>Zephyr OS</strong></a> and <a href="https://github.com/beegee-tokyo/SX126x-Arduino"><strong>Arduino</strong></a>.</p>
<p><a href="https://codeberg.org/JF002/loramac-node">(My good friend JF is porting the LoRaWAN Stack to Linux)</a></p>
<p><em>But the LoRaWAN Stack is in C! Will it work with Zig?</em></p>
<p>Yep no worries, Zig will happily <strong>import the LoRaWAN Stack from C</strong> without any wrappers or modifications.</p>
<p>And we’ll call the LoRaWAN Stack as though it were a Zig Library.</p>
<p><em>So we’re not rewriting the LoRaWAN Stack in Zig?</em></p>
<p>Rewriting the LoRaWAN Stack in Zig (or another language) sounds risky because the LoRaWAN Stack is still under <a href="https://github.com/Lora-net/LoRaMac-node/commits/master"><strong>Active Development</strong></a>. It can change at any moment!</p>
<p>We’ll stick with the <strong>C Implementation</strong> of the LoRaWAN Stack so that our Zig IoT App will enjoy the latest LoRaWAN updates and features.</p>
<p><a href="https://lupyuen.github.io/articles/zig#why-zig">(More about this)</a></p>
<p><em>Why is our Zig IoT App so complex anyway?</em></p>
<p>That’s because…</p>
<ul>
<li>
<p>LoRaWAN Wireless Protocol is <strong>Time-Critical</strong>. If we’re late by 1 second, LoRaWAN just won’t work. <a href="https://gist.github.com/lupyuen/1d96b24c6bf5164cba652d903eedb9d1">(See this)</a></p>
</li>
<li>
<p>Our app controls the <strong>LoRa Radio Transceiver</strong> over SPI and GPIO. <a href="https://lupyuen.github.io/articles/sx1262#spi-interface">(See this)</a></p>
</li>
<li>
<p>And it needs to handle <strong>GPIO Interrupts</strong> from the LoRa Transceiver whenever a LoRa Packet is received. <a href="https://lupyuen.github.io/articles/sx1262#handle-dio1-interrupt">(See this)</a></p>
</li>
<li>
<p>Which means our app needs to do <strong>Multithreading with Timers and Message Queues</strong> efficiently. <a href="https://lupyuen.github.io/articles/sx1262#multithreading-with-nimble-porting-layer">(See this)</a></p>
</li>
</ul>
<p>Great way to test if Zig can really handle Complex Embedded Apps!</p>
<p><img src="https://lupyuen.github.io/images/iot-code2a.png" alt="Import LoRaWAN Library" /></p>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L5-L48">(Source)</a></p>
<h1 id="import-lorawan-library"><a class="doc-anchor" href="#import-lorawan-library">§</a>2 Import LoRaWAN Library</h1>
<p>Let’s dive into our Zig IoT App. We import the <a href="https://lupyuen.github.io/articles/zig#import-standard-library"><strong>Zig Standard Library</strong></a> at the top of our app: <a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L5-L48">lorawan_test.zig</a></p>
<div class="example-wrap"><pre class="language-zig"><code>/// Import the Zig Standard Library
const std = @import("std");</code></pre></div>
<p>Then we call <a href="https://ziglang.org/documentation/master/#cImport"><strong>@cImport</strong></a> to import the <strong>C Macros and C Header Files</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code>/// Import the LoRaWAN Library from C
const c = @cImport({
// Define C Macros for NuttX on RISC-V, equivalent to...
// #define __NuttX__
// #define NDEBUG
// #define ARCH_RISCV
@cDefine("__NuttX__", "");
@cDefine("NDEBUG", "");
@cDefine("ARCH_RISCV", "");</code></pre></div>
<p>The code above defines the <strong>C Macros</strong> that will be called by the C Header Files coming up.</p>
<p>Next comes a workaround for a <strong>C Macro Error</strong> that appears on Zig with Apache NuttX RTOS…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Workaround for "Unable to translate macro: undefined identifier `LL`"
@cDefine("LL", "");
@cDefine("__int_c_join(a, b)", "a"); // Bypass zig/lib/include/stdint.h</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/iot#appendix-macro-error">(More about this)</a></p>
<p>We import the <strong>C Header Files</strong> for Apache NuttX RTOS…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Import the NuttX Header Files from C, equivalent to...
// #include <arch/types.h>
// #include <../../nuttx/include/limits.h>
// #include <stdio.h>
@cInclude("arch/types.h");
@cInclude("../../nuttx/include/limits.h");
@cInclude("stdio.h");</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/iot#appendix-zig-compiler-as-drop-in-replacement-for-gcc">(More about the includes)</a></p>
<p>Followed by the C Header Files for our <strong>LoRaWAN Library</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Import LoRaWAN Header Files from C, based on
// https://github.com/Lora-net/LoRaMac-node/blob/master/src/apps/LoRaMac/fuota-test-01/B-L072Z-LRWAN1/main.c#L24-L40
@cInclude("firmwareVersion.h");
@cInclude("../libs/liblorawan/src/apps/LoRaMac/common/githubVersion.h");
@cInclude("../libs/liblorawan/src/boards/utilities.h");
@cInclude("../libs/liblorawan/src/mac/region/RegionCommon.h");
@cInclude("../libs/liblorawan/src/apps/LoRaMac/common/Commissioning.h");
@cInclude("../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/LmHandler.h");
@cInclude("../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpCompliance.h");
@cInclude("../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpClockSync.h");
@cInclude("../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpRemoteMcastSetup.h");
@cInclude("../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpFragmentation.h");
@cInclude("../libs/liblorawan/src/apps/LoRaMac/common/LmHandlerMsgDisplay.h");
});</code></pre></div>
<p><a href="https://github.com/Lora-net/LoRaMac-node/blob/master/src/apps/LoRaMac/fuota-test-01/B-L072Z-LRWAN1/main.c#L24-L40">(Based on this C code)</a></p>
<p>The LoRaWAN Library is ready to be called by our Zig App!</p>
<p>This is how we reference the LoRaWAN Library to define our <a href="https://www.thethingsnetwork.org/docs/lorawan/frequencies-by-country/"><strong>LoRaWAN Region</strong></a>…</p>
<div class="example-wrap"><pre class="language-zig"><code>/// LoRaWAN Region
const ACTIVE_REGION = c.LORAMAC_REGION_AS923;</code></pre></div>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L44-L86">(Source)</a></p>
<p><em>Why the “<strong><code>c.</code></strong>” in <code>c.LORAMAC_REGION_AS923</code>?</em></p>
<p>Remember that we imported the LoRaWAN Library under the <strong>Namespace “<code>c</code>”</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code>/// Import the LoRaWAN Library under Namespace "c"
const c = @cImport({ ... });</code></pre></div>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L5-L48">(Source)</a></p>
<p>Hence we use “<code>c.something</code>” to refer to the Constants and Functions defined in the LoRaWAN Library.</p>
<p><em>Why did we define the C Macros like <code>__NuttX__</code>?</em></p>
<p>These C Macros are needed by the <strong>NuttX Header Files</strong>.</p>
<p>Without the macros, the NuttX Header Files won’t be imported correctly into Zig. <a href="https://lupyuen.github.io/articles/iot#appendix-zig-compiler-as-drop-in-replacement-for-gcc">(See this)</a></p>
<p><em>Why did we import “arch/types.h”?</em></p>
<p>This fixes a problem with the <strong>NuttX Types</strong>. <a href="https://lupyuen.github.io/articles/iot#appendix-zig-compiler-as-drop-in-replacement-for-gcc">(See this)</a></p>
<p>Let’s head over to the Main Function…</p>
<p><img src="https://lupyuen.github.io/images/iot-code3a.png" alt="Zig App calls LoRaWAN Library imported from C" /></p>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L90-L158">(Source)</a></p>
<h1 id="main-function"><a class="doc-anchor" href="#main-function">§</a>3 Main Function</h1>
<p>This is the <a href="https://lupyuen.github.io/articles/zig#main-function"><strong>Main Function</strong></a> for our Zig App: <a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L90-L158">lorawan_test.zig</a></p>
<div class="example-wrap"><pre class="language-zig"><code>/// Main Function that will be called by NuttX.
/// We call the LoRaWAN Library to join a
/// LoRaWAN Network and send a Data Packet.
pub export fn lorawan_test_main(
_argc: c_int,
_argv: [*]const [*]const u8
) c_int {
_ = _argc;
_ = _argv;
// Init the Timer Struct at startup
TxTimer = std.mem.zeroes(c.TimerEvent_t);</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/iot#appendix-struct-initialisation-error">(We init <strong>TxTimer</strong> here because of this)</a></p>
<p>We begin by computing the randomised <strong>interval between transmissions</strong> of LoRaWAN Data Packets…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Compute the interval between transmissions based on Duty Cycle
TxPeriodicity = @intCast(u32, // Cast to u32 because randr() can be negative
APP_TX_DUTYCYCLE +
c.randr(
-APP_TX_DUTYCYCLE_RND,
APP_TX_DUTYCYCLE_RND
)
);</code></pre></div>
<p>(We’ll talk about <strong>@intCast</strong> in a while)</p>
<p>Our app sends LoRaWAN Data Packets every <strong>40 seconds</strong> (roughly). <a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L52-L59">(See this)</a></p>
<p>Next we show the <strong>App Version</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Show the Firmware and GitHub Versions
const appVersion = c.Version_t {
.Value = c.FIRMWARE_VERSION,
};
const gitHubVersion = c.Version_t {
.Value = c.GITHUB_VERSION,
};
c.DisplayAppInfo("Zig LoRaWAN Test", &appVersion, &gitHubVersion);</code></pre></div>
<p>Then we <strong>initialise the LoRaWAN Library</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Init LoRaWAN
if (LmHandlerInit(&LmHandlerCallbacks, &LmHandlerParams)
!= c.LORAMAC_HANDLER_SUCCESS) {
std.log.err("LoRaMac wasn't properly initialized", .{});
// Fatal error, endless loop.
while (true) {}
}</code></pre></div>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L651-L682">(<strong>LmHandlerCallbacks</strong> and <strong>LmHandlerParams</strong> are defined here)</a></p>
<p>(We’ll explain “<code>.{}</code>” in a while)</p>
<p>We set the <strong>Max Tolerated Receive Error</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Set system maximum tolerated rx error in milliseconds
_ = c.LmHandlerSetSystemMaxRxError(20);</code></pre></div>
<p>And we load some packages for <strong>LoRaWAN Compliance</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code> // The LoRa-Alliance Compliance protocol package should always be initialized and activated.
_ = c.LmHandlerPackageRegister(c.PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams);
_ = c.LmHandlerPackageRegister(c.PACKAGE_ID_CLOCK_SYNC, null);
_ = c.LmHandlerPackageRegister(c.PACKAGE_ID_REMOTE_MCAST_SETUP, null);
_ = c.LmHandlerPackageRegister(c.PACKAGE_ID_FRAGMENTATION, &FragmentationParams);</code></pre></div>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L684-L709">(<strong>LmhpComplianceParams</strong> and <strong>FragmentationParams</strong> are defined here)</a></p>
<p>Everything is hunky dory! We can now transmit a LoRaWAN Request to <strong>join the LoRaWAN Network</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Init the Clock Sync and File Transfer status
IsClockSynched = false;
IsFileTransferDone = false;
// Join the LoRaWAN Network
c.LmHandlerJoin();</code></pre></div>
<p><a href="https://github.com/Lora-net/LoRaMac-node/blob/master/src/peripherals/soft-se/se-identity.h">(LoRaWAN Keys and EUIs are defined here)</a></p>
<p>We start the <strong>Transmit Timer</strong> that will send a LoRaWAN Data Packet at periodic intervals (right after we join the LoRaWAN Network)…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Set the Transmit Timer
StartTxProcess(LmHandlerTxEvents_t.LORAMAC_HANDLER_TX_ON_TIMER);</code></pre></div>
<p>Finally we loop forever handling <strong>LoRaWAN Events</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Handle LoRaWAN Events
handle_event_queue(); // Never returns
return 0;
}</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/iot#appendix-handle-lorawan-events">(<strong>handle_event_queue</strong> is explained in the Appendix)</a></p>
<p>That’s all for the Main Function of our Zig App!</p>
<p><img src="https://lupyuen.github.io/images/lorawan3-flow.jpg" alt="Our LoRaWAN Zig App" /></p>
<p><em>Wait… Our Zig Code looks familiar?</em></p>
<p>Yep our Zig Code is largely identical to the <strong>C Code in the Demo App</strong> for the LoRaWAN Stack…</p>
<ul>
<li>
<p><a href="https://github.com/Lora-net/LoRaMac-node/blob/master/src/apps/LoRaMac/fuota-test-01/B-L072Z-LRWAN1/main.c#L314-L390"><strong>LoRaMac/fuota-test-01/main.c</strong></a></p>
<p>(Pic below)</p>
</li>
</ul>
<p><strong>Converting C Code to Zig</strong> looks rather straightforward. In a while we’ll talk about the tricky parts we encountered during the conversion.</p>
<p><em>Why did we call <strong>LmHandlerInit</strong> instead of <strong>c.LmHandlerInit</strong>?</em></p>
<p>That’s one of the tricky parts of our C-to-Zig conversion, as explained here…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/iot#appendix-fix-opaque-type"><strong>“Fix Opaque Type”</strong></a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/iot-code1a.png" alt="Demo App for the LoRaWAN Stack" /></p>
<p><a href="https://github.com/Lora-net/LoRaMac-node/blob/master/src/apps/LoRaMac/fuota-test-01/B-L072Z-LRWAN1/main.c#L314-L390">(Source)</a></p>
<h1 id="convert-integer-type"><a class="doc-anchor" href="#convert-integer-type">§</a>4 Convert Integer Type</h1>
<p>Earlier we saw this computation of the randomised <strong>interval between transmissions</strong> of LoRaWAN Data Packets: <a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L106-L113">lorawan_test.zig</a></p>
<div class="example-wrap"><pre class="language-zig"><code>// In Zig: Compute the interval between transmissions based on Duty Cycle.
// TxPeriodicity is an unsigned integer (32-bit).
// We cast to u32 because randr() can be negative.
TxPeriodicity = @intCast(u32,
APP_TX_DUTYCYCLE +
c.randr(
-APP_TX_DUTYCYCLE_RND,
APP_TX_DUTYCYCLE_RND
)
);</code></pre></div>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L52-L59">(Roughly 40 seconds)</a></p>
<p>Let’s find out why <a href="https://ziglang.org/documentation/master/#intCast"><strong>@intCast</strong></a> is needed.</p>
<p>In the Original C Code we compute the interval <strong>without any Explicit Type Conversion</strong>…</p>
<div class="example-wrap"><pre class="language-c"><code>// In C: Compute the interval between transmissions based on Duty Cycle.
// TxPeriodicity is an unsigned integer (32-bit).
// Remember that randr() can be negative.
TxPeriodicity =
APP_TX_DUTYCYCLE +
randr(
-APP_TX_DUTYCYCLE_RND,
APP_TX_DUTYCYCLE_RND
);</code></pre></div>
<p><a href="https://github.com/Lora-net/LoRaMac-node/blob/master/src/apps/LoRaMac/fuota-test-01/B-L072Z-LRWAN1/main.c#L330-L333">(Source)</a></p>
<p><em>What happens if we compile this in Zig?</em></p>
<p>Zig Compiler shows this error…</p>
<div class="example-wrap"><pre class="language-text"><code>unsigned 32-bit int cannot represent
all possible signed 32-bit values</code></pre></div>
<p><em>What does it mean?</em></p>
<p>Well <strong>TxPeriodicity</strong> is an <strong>Unsigned Integer</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code>/// Random interval between transmissions
var TxPeriodicity: u32 = 0;</code></pre></div>
<p>But <a href="https://github.com/Lora-net/LoRaMac-node/blob/master/src/boards/utilities.h#L94-L101"><strong>randr()</strong></a> returns a <strong>Signed Integer</strong>…</p>
<div class="example-wrap"><pre class="language-c"><code>/// Computes a random number between min and max
int32_t randr(int32_t min, int32_t max);</code></pre></div>
<p>Mixing <strong>Signed and Unsigned Integers</strong> is a Bad Sign (pun intended)…</p>
<p><strong>randr()</strong> could potentially cause <strong>TxPeriodicity</strong> to underflow!</p>
<p><em>How does @intCast fix this?</em></p>
<p>When we write this with <a href="https://ziglang.org/documentation/master/#intCast"><strong>@intCast</strong></a>…</p>
<div class="example-wrap"><pre class="language-zig"><code>TxPeriodicity = @intCast(u32,
APP_TX_DUTYCYCLE +
c.randr(
-APP_TX_DUTYCYCLE_RND,
APP_TX_DUTYCYCLE_RND
)
);</code></pre></div>
<p>We’re telling the Zig Compiler to convert the <strong>Signed Result to an Unsigned Integer</strong>.</p>
<p><a href="https://ziglang.org/documentation/master/#intCast">(More about <strong>@intCast</strong>)</a></p>
<p><em>What happens if there’s an underflow?</em></p>
<p>The Signed-to-Unsigned Conversion fails and we’ll see a <strong>Runtime Error</strong>…</p>
<div class="example-wrap"><pre class="language-text"><code>!ZIG PANIC!
attempt to cast negative value to unsigned integer
Stack Trace:
0x23016dba</code></pre></div>
<p>Great to have Zig watching our backs… When we do risky things! 👍</p>
<p><a href="https://lupyuen.github.io/articles/iot#appendix-panic-handler">(How we implemented a Custom Panic Handler)</a></p>
<h1 id="transmit-data-packet"><a class="doc-anchor" href="#transmit-data-packet">§</a>5 Transmit Data Packet</h1>
<p>Back to our Zig App: This is how we <strong>transmit a Data Packet</strong> to the LoRaWAN Network: <a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L163-L205">lorawan_test.zig</a></p>
<div class="example-wrap"><pre class="language-zig"><code>/// Prepare the payload of a Data Packet
/// and transmit it
fn PrepareTxFrame() void {
// If we haven't joined the LoRaWAN Network...
if (c.LmHandlerIsBusy()) {
// Try again later
return;
}</code></pre></div>
<p>LoRaWAN won’t let us transmit data unless we’ve <strong>joined the LoRaWAN Network</strong>. So we check this first.</p>
<p>Next we prepare the <strong>message to be sent</strong> <em>(“Hi NuttX”)</em>…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Message to be sent to LoRaWAN
const msg: []const u8 = "Hi NuttX\x00"; // 9 bytes including null
debug("PrepareTxFrame: Transmit to LoRaWAN ({} bytes): {s}", .{
msg.len, msg
});</code></pre></div>
<p>(We’ll talk about <strong>debug</strong> in a while)</p>
<p>That’s <strong>9 bytes</strong>, including the Terminating Null.</p>
<p><em>Why so smol?</em></p>
<p>The first LoRaWAN message needs to be <strong>11 bytes</strong> or smaller, subsequent messages can be up to <strong>53 bytes</strong>.</p>
<p>This depends on the <strong>LoRaWAN Data Rate</strong> and the LoRaWAN Region. <a href="https://lupyuen.github.io/articles/lorawan3#message-size">(See this)</a></p>
<p>Then we copy the message into the <strong>LoRaWAN Buffer</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Copy message into LoRaWAN buffer
std.mem.copy(
u8, // Type
&AppDataBuffer, // Destination
msg // Source
);</code></pre></div>
<p><a href="https://ziglang.org/documentation/master/std/#std;mem.copy">(<strong>std.mem.copy</strong> is documented here)</a></p>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L732-L737">(<strong>AppDataBuffer</strong> is defined here)</a></p>
<p>We compose the <strong>LoRaWAN Transmit Request</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Compose the transmit request
var appData = c.LmHandlerAppData_t {
.Buffer = &AppDataBuffer,
.BufferSize = msg.len,
.Port = 1,
};</code></pre></div>
<p>Remember that the <a href="https://lupyuen.github.io/articles/lorawan3#message-size"><strong>Max Message Size</strong></a> depends on the LoRaWAN Data Rate and the LoRaWAN Region?</p>
<p>This is how we <strong>validate the Message Size</strong> to make sure that our message isn’t too large…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Validate the message size and check if it can be transmitted
var txInfo: c.LoRaMacTxInfo_t = undefined;
const status = c.LoRaMacQueryTxPossible(
appData.BufferSize, // Message Size
&txInfo // Unused
);
assert(status == c.LORAMAC_STATUS_OK);</code></pre></div>
<p>Finally we <strong>transmit the message</strong> to the LoRaWAN Network…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Transmit the message
const sendStatus = c.LmHandlerSend(
&appData, // Transmit Request
LmHandlerParams.IsTxConfirmed // False (No acknowledge required)
);
assert(sendStatus == c.LORAMAC_HANDLER_SUCCESS);
debug("PrepareTxFrame: Transmit OK", .{});
}</code></pre></div>
<p>And that’s how <a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L163-L205"><strong>PrepareTxFrame</strong></a> transmits a Data Packet over LoRaWAN.</p>
<p><em>How is PrepareTxFrame called?</em></p>
<p>After we have joined the LoRaWAN Network, our LoRaWAN Event Loop calls <a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L222-L232"><strong>UplinkProcess</strong></a>…</p>
<div class="example-wrap"><pre class="language-zig"><code>/// LoRaWAN Event Loop that dequeues Events from
/// the Event Queue and processes the Events
fn handle_event_queue() void {
// Loop forever handling Events from the Event Queue
while (true) {
// Omitted: Handle the next Event from the Event Queue
...
// If we have joined the network, do the uplink
if (!c.LmHandlerIsBusy()) {
UplinkProcess();
}</code></pre></div>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L451-L492">(Source)</a></p>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L222-L232"><strong>UplinkProcess</strong></a> then calls <a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L163-L205"><strong>PrepareTxFrame</strong></a> to transmit a Data Packet, when the Transmit Timer has expired.</p>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L222-L232">(<strong>UplinkProcess</strong> is defined here)</a></p>
<p><a href="https://lupyuen.github.io/articles/iot#appendix-handle-lorawan-events">(<strong>handle_event_queue</strong> is explained in the Appendix)</a></p>
<p><img src="https://lupyuen.github.io/images/lorawan3-chirpstack6.png" alt="ChirpStack LoRaWAN Gateway receives Data Packet from our Zig App" /></p>
<p><em>ChirpStack LoRaWAN Gateway receives Data Packet from our Zig App</em></p>
<h1 id="logging"><a class="doc-anchor" href="#logging">§</a>6 Logging</h1>
<p>Earlier we saw this code for printing a <strong>Debug Message</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code>// Message to be sent
const msg: []const u8 = "Hi NuttX\x00"; // 9 bytes including null
// Print the message
debug("Transmit to LoRaWAN ({} bytes): {s}", .{
msg.len, msg
});</code></pre></div>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L170-L176">(Source)</a></p>
<p>The code above prints this <strong>Formatted Message</strong> to the console…</p>
<div class="example-wrap"><pre class="language-text"><code>Transmit to LoRaWAN (9 bytes): Hi NuttX</code></pre></div>
<p>The <strong>Format Specifiers</strong> <code>{}</code> and <code>{s}</code> embedded in the Format String are explained here…</p>
<ul>
<li>
<p><a href="https://ziglearn.org/chapter-2/#formatting"><strong>Zig Formatting</strong></a></p>
</li>
<li>
<p><a href="https://github.com/ziglang/zig/blob/master/lib/std/fmt.zig#L27-L72"><strong>Format Specifiers</strong></a></p>
</li>
</ul>
<p><em>What’s <code>.{ ... }</code>?</em></p>
<p><code>.{ ... }</code> creates an <a href="https://ziglearn.org/chapter-1/#anonymous-structs"><strong>Anonymous Struct</strong></a> with a variable number of arguments that will be passed to the <strong>debug</strong> function for formatting.</p>
<p><em>And if we have no arguments?</em></p>
<p>Then we do this…</p>
<div class="example-wrap"><pre class="language-zig"><code>// Print the message without formatting
debug("Transmit to LoRaWAN", .{});</code></pre></div>
<p>We discuss the implementation of <strong>Zig Logging</strong> in the Appendix…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/iot#appendix-logging"><strong>“Appendix: Logging”</strong></a></li>
</ul>
<h1 id="compile-zig-app"><a class="doc-anchor" href="#compile-zig-app">§</a>7 Compile Zig App</h1>
<p>Now that we understand the code, we’re ready to <strong>compile our LoRaWAN Zig App</strong>!</p>
<p>First we download the latest version of <strong>Zig Compiler</strong> (0.10.0 or later), extract it and add to PATH…</p>
<ul>
<li><a href="https://ziglang.org/download/"><strong>Zig Compiler Downloads</strong></a></li>
</ul>
<p>Then we download and compile <strong>Apache NuttX RTOS</strong> for PineDio Stack BL604…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/pinedio2#build-nuttx"><strong>“Build NuttX”</strong></a></li>
</ul>
<p>Before compiling NuttX, configure the <strong>LoRaWAN App Key, Device EUI and Join EUI</strong> in the LoRaWAN Library…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/lorawan3#device-eui-join-eui-and-app-key"><strong>“Device EUI, Join EUI and App Key”</strong></a></li>
</ul>
<p>After building NuttX, we download and compile our <strong>LoRaWAN Zig App</strong>…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Download our LoRaWAN Zig App for NuttX
git clone --recursive https://github.com/lupyuen/zig-bl602-nuttx
cd zig-bl602-nuttx
## TODO: Edit lorawan_test.zig and set the LoRaWAN Region...
## const ACTIVE_REGION = c.LORAMAC_REGION_AS923;
## Compile the Zig App for BL602
## (RV32IMACF with Hardware Floating-Point)
## TODO: Change "$HOME/nuttx" to your NuttX Project Directory
zig build-obj \
--verbose-cimport \
-target riscv32-freestanding-none \
-mcpu=baseline_rv32-d \
-isystem "$HOME/nuttx/nuttx/include" \
-I "$HOME/nuttx/apps/examples/lorawan_test" \
lorawan_test.zig</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/b29186ad4ad870bcaaace704fd3def7d">(See the Compile Log)</a></p>
<p>Note that <strong>target</strong> and <strong>mcpu</strong> are specific to BL602…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/zig#zig-target"><strong>“Zig Target”</strong></a></li>
</ul>
<p><em>How did we get the Compiler Options <code>-isystem</code> and <code>-I</code>?</em></p>
<p>Remember that we’ll link our Compiled Zig App with <strong>Apache NuttX RTOS.</strong></p>
<p>Hence the <strong>Zig Compiler Options must be the same</strong> as the GCC Options used to compile NuttX.</p>
<p><a href="https://lupyuen.github.io/articles/iot#appendix-zig-compiler-as-drop-in-replacement-for-gcc">(See the GCC Options for NuttX)</a></p>
<p>Next comes a quirk specific to BL602: We must <strong>patch the ELF Header</strong> from Software Floating-Point ABI to Hardware Floating-Point ABI…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Patch the ELF Header of `lorawan_test.o`
## from Soft-Float ABI to Hard-Float ABI
xxd -c 1 lorawan_test.o \
| sed 's/00000024: 01/00000024: 03/' \
| xxd -r -c 1 - lorawan_test2.o
cp lorawan_test2.o lorawan_test.o</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/zig#patch-elf-header">(More about this)</a></p>
<p>Finally we inject our <strong>Compiled Zig App</strong> into the NuttX Project Directory and link it into the <strong>NuttX Firmware</strong>…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Copy the compiled app to NuttX and overwrite `lorawan_test.o`
## TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cp lorawan_test.o $HOME/nuttx/apps/examples/lorawan_test/*lorawan_test.o
## Build NuttX to link the Zig Object from `lorawan_test.o`
## TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME/nuttx/nuttx
make
## For WSL: Copy the NuttX Firmware to c:\blflash for flashing
mkdir /mnt/c/blflash
cp nuttx.bin /mnt/c/blflash</code></pre></div>
<p>We’re ready to run our Zig App!</p>
<p><img src="https://lupyuen.github.io/images/lorawan3-flow.jpg" alt="Running our LoRaWAN Zig App" /></p>
<h1 id="run-zig-app"><a class="doc-anchor" href="#run-zig-app">§</a>8 Run Zig App</h1>
<p>Follow these steps to <strong>flash and boot NuttX</strong> (with our Zig App inside) on PineDio Stack…</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/pinedio2#flash-pinedio-stack"><strong>“Flash PineDio Stack”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/pinedio2#boot-pinedio-stack"><strong>“Boot PineDio Stack”</strong></a></p>
</li>
</ul>
<p>In the NuttX Shell, enter this command to start our Zig App…</p>
<div class="example-wrap"><pre class="language-bash"><code>lorawan_test</code></pre></div>
<p>Our Zig App starts and transmits a LoRaWAN Request to <strong>join the LoRaWAN Network</strong> (by controlling the LoRa Transceiver over SPI)…</p>
<div class="example-wrap"><pre class="language-text"><code>Application name : Zig LoRaWAN Test
###### =========== MLME-Request ============ ######
###### MLME_JOIN ######
###### ===================================== ######</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/0871ac515b18d9d68d3aacf831fd0f5b">(See the complete log)</a></p>
<p>5 seconds later, our app receives the <strong>Join Accept Response</strong> from our ChirpStack LoRaWAN Gateway (by handling the GPIO Interrupt triggered by the LoRa Transceiver)…</p>
<div class="example-wrap"><pre class="language-text"><code>###### =========== MLME-Confirm ============ ######
STATUS : OK
###### =========== JOINED ============ ######
OTAA
DevAddr : 00D803AB
DATA RATE : DR_2</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/0871ac515b18d9d68d3aacf831fd0f5b">(Source)</a></p>
<p>We have successfully joined the LoRaWAN Network!</p>
<p>Every 40 seconds, our app transmits a <strong>Data Packet</strong> <em>(“Hi NuttX”)</em> to the LoRaWAN Network…</p>
<div class="example-wrap"><pre class="language-text"><code>PrepareTxFrame: Transmit to LoRaWAN (9 bytes): Hi NuttX
###### =========== MCPS-Confirm ============ ######
STATUS : OK
###### ===== UPLINK FRAME 1 ===== ######
CLASS : A
TX PORT : 1
TX DATA : UNCONFIRMED
48 69 20 4E 75 74 74 58 00
DATA RATE : DR_3
U/L FREQ : 923200000
TX POWER : 0
CHANNEL MASK: 0003</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/0871ac515b18d9d68d3aacf831fd0f5b">(Source)</a></p>
<p>The Data Packet appears in our <strong>LoRaWAN Gateway</strong> (ChirpStack), like in the pic below.</p>
<p>Yep our LoRaWAN Zig App has successfully transmitted a Data Packet to the LoRaWAN Network! 🎉</p>
<p><img src="https://lupyuen.github.io/images/lorawan3-chirpstack6.png" alt="ChirpStack LoRaWAN Gateway receives Data Packet from our Zig App" /></p>
<p><em>Can we test our app without a LoRaWAN Gateway?</em></p>
<p>Our app will work fine with <a href="https://lupyuen.github.io/articles/ttn"><strong>The Things Network</strong></a>, the worldwide free-to-use LoRaWAN Network.</p>
<p>Check the Network Coverage here…</p>
<ul>
<li><a href="https://www.thethingsnetwork.org/map"><strong>The Things Network Coverage Map</strong></a></li>
</ul>
<p>And set the <a href="https://lupyuen.github.io/articles/lorawan3#device-eui-join-eui-and-app-key"><strong>LoRaWAN Parameters</strong></a> like so…</p>
<ul>
<li>
<p><strong>LORAWAN_DEVICE_EUI</strong>: Set this to the <strong>DevEUI</strong> from The Things Network</p>
</li>
<li>
<p><strong>LORAWAN_JOIN_EUI</strong>: Set this to <code>{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }</code></p>
</li>
<li>
<p><strong>APP_KEY, NWK_KEY</strong>: Set both to the <strong>AppKey</strong> from The Things Network</p>
</li>
</ul>
<p>To get the <strong>DevEUI</strong> and <strong>AppKey</strong> from The Things Network…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/ttn#add-device-to-the-things-network"><strong>“Add Device to The Things Network”</strong></a></li>
</ul>
<p>(I don’t think <strong>NWK_KEY</strong> is used)</p>
<p><img src="https://lupyuen.github.io/images/lorawan3-ttn.png" alt="The Things Network receives Data Packet from our LoRaWAN App" /></p>
<p><em>The Things Network receives Data Packet from our LoRaWAN App</em></p>
<h1 id="safety-checks"><a class="doc-anchor" href="#safety-checks">§</a>9 Safety Checks</h1>
<p><em>Our IoT App is now in Zig instead of C. Do we gain anything with Zig?</em></p>
<p>We claimed earlier that <strong>Zig is watching our backs</strong> (in case we do something risky)…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/iot#convert-integer-type"><strong>“Convert Integer Type”</strong></a></li>
</ul>
<p>Let’s dig for more evidence that Zig really tries to protect our programs…</p>
<p>This <strong>C Code</strong> (from the original LoRaWAN Demo) copies an array, byte by byte…</p>
<div class="example-wrap"><pre class="language-c"><code>static int8_t FragDecoderWrite(uint32_t addr, uint8_t *data, uint32_t size) {
for (uint32_t i = 0; i < size; i++ ) {
UnfragmentedData[addr + i] = data[i];
}</code></pre></div>
<p><a href="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L539-L550">(Source)</a></p>
<p>Our Zig Compiler has a fascinating feature: It can <strong>translate C programs into Zig</strong>!</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/iot#appendix-auto-translate-lorawan-app-to-zig"><strong>“Auto-Translate LoRaWAN App from C to Zig”</strong></a></li>
</ul>
<p>When we feed the above C Code into Zig’s Auto-Translator, it produces this functionally-equivalent <strong>Zig Code</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code>pub fn FragDecoderWrite(addr: u32, data: [*c]u8, size: u32) callconv(.C) i8 {
var i: u32 = 0;
while (i < size) : (i +%= 1) {
UnfragmentedData[addr +% i] = data[i];
}</code></pre></div>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/translated/lorawan_test_main.zig#L4335-L4349">(Source)</a></p>
<p><em>Hmmm something looks different?</em></p>
<p>Yep the <strong>Array Indexing</strong> in C…</p>
<div class="example-wrap"><pre class="language-c"><code>// Array Indexing in C...
UnfragmentedData[addr + i]</code></pre></div>
<p>Gets translated to this in Zig…</p>
<div class="example-wrap"><pre class="language-zig"><code>// Array Indexing in Zig...
UnfragmentedData[addr +% i]</code></pre></div>
<p>“<strong><code>+</code></strong>” in C becomes “<strong><code>+%</code></strong>” in Zig!</p>
<p><em>What’s “<code>+%</code>” in Zig?</em></p>
<p>That’s the Zig Operator for <a href="https://ziglang.org/documentation/master/#Wrapping-Operations"><strong>Wraparound Addition</strong></a>.</p>
<p>Which means that the result <strong>wraps back to 0</strong> (and beyond) if the addition overflows the integer.</p>
<p><em>Exactly how we expect C to work right?</em></p>
<p>Yep the Zig Compiler has faithfully translated the Wraparound Addition from C to Zig.</p>
<p>But this isn’t what we intended, since we don’t expect the addition to overflow.</p>
<p>That’s why in our final converted Zig code, we <strong>revert “<code>+%</code>” back to “<code>+</code>”</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code>export fn FragDecoderWrite(addr: u32, data: [*c]u8, size: u32) i8 {
var i: u32 = 0;
while (i < size) : (i += 1) {
// We changed `+%` back to `+`
UnfragmentedData[addr + i] = data[i];
}</code></pre></div>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L403-L412">(Source)</a></p>
<p><em>But what happens if the addition overflows?</em></p>
<p>We’ll see a Runtime Error…</p>
<div class="example-wrap"><pre class="language-text"><code>panic: integer overflow</code></pre></div>
<p><a href="https://ziglang.org/documentation/master/#Integer-Overflow">(Source)</a></p>
<p>Which is probably a good thing, to ensure that our values are sensible.</p>
<p><em>What if our Array Index goes out of bounds?</em></p>
<p>We’ll get another Runtime Error…</p>
<div class="example-wrap"><pre class="language-text"><code>panic: index out of bounds</code></pre></div>
<p><a href="https://ziglang.org/documentation/master/#Index-out-of-Bounds">(Source)</a></p>
<p>We handle Runtime Errors in our <strong>Custom Panic Handler</strong>, as explained here…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/iot#appendix-panic-handler"><strong>“Zig Panic Handler”</strong></a></li>
</ul>
<p><em>So Zig watches for underflow / overflow / out-of-bounds errors at runtime. Anything else?</em></p>
<p>Here’s the list of <strong>Safety Checks</strong> done by Zig at runtime…</p>
<ul>
<li><a href="https://ziglang.org/documentation/master/#Undefined-Behavior"><strong>“Zig Undefined Behavior”</strong></a></li>
</ul>
<p>Thus indeed, Zig tries very hard to catch all kinds of problems at runtime.</p>
<p>And that’s super helpful for a complex app like ours.</p>
<p><em>Can we turn off the Safety Checks?</em></p>
<p>If we prefer to live a little recklessly (momentarily), this is how we <strong>disable the Safety Checks</strong>…</p>
<ul>
<li><a href="https://ziglang.org/documentation/master/#setRuntimeSafety"><strong>@setRuntimeSafety</strong></a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/spi2-pinedio10a.jpg" alt="PineDio Stack BL604" /></p>
<h1 id="zig-outcomes"><a class="doc-anchor" href="#zig-outcomes">§</a>10 Zig Outcomes</h1>
<p><em>Once again… Why are we doing this in Zig?</em></p>
<p>Let’s recap: We have a <strong>complex chunk of firmware</strong> that needs to run on an IoT gadget (PineDio Stack)…</p>
<ul>
<li>
<p>It talks SPI to the <strong>LoRa Radio Transceiver</strong> to transmit packets</p>
</li>
<li>
<p>It handles <strong>GPIO Interrupts</strong> from the LoRa Transceiver to receive packets</p>
</li>
<li>
<p>It needs <strong>Multithreading, Timers and Event Queues</strong> because the LoRaWAN Protocol is complicated and time-critical</p>
</li>
</ul>
<p>We wished we could <strong>rewrite the LoRaWAN Stack</strong> in a modern, memory-safe language… But we can’t. <a href="https://lupyuen.github.io/articles/zig#why-zig">(Because LoRaWAN changes)</a></p>
<p><em>But we can do this partially in Zig right?</em></p>
<p>Yes it seems the best we can do today is to…</p>
<ul>
<li>
<p>Code the <strong>High-Level Parts in Zig</strong></p>
<p>(Event Loop and Data Transmission)</p>
</li>
<li>
<p>Leave the <strong>Low-Level Parts in C</strong></p>
<p>(LoRaWAN Stack and Apache NuttX RTOS)</p>
</li>
</ul>
<p>And Zig Compiler will do the <strong>Zig-to-C</strong> plumbing for us. (As we’ve seen)</p>
<p><em>Zig Compiler calls Clang to import the C Header Files. But NuttX compiles with GCC. Won’t we have problems with code compatibility?</em></p>
<p>We have validated Zig Compiler’s <strong>Clang as a drop-in replacement</strong> for GCC…</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/iot#appendix-zig-compiler-as-drop-in-replacement-for-gcc"><strong>“Zig Compiler as Drop-In Replacement for GCC”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/iot#appendix-lorawan-library-for-nuttx"><strong>“LoRaWAN Library (compiled with zig cc)”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/iot#appendix-lorawan-app-for-nuttx"><strong>“LoRaWAN App (compiled with zig cc)”</strong></a></p>
</li>
</ul>
<p>Hence we’re confident that <strong>Zig will interoperate correctly</strong> with the LoRaWAN Stack and Apache NuttX RTOS.</p>
<p>(Well for BL602 NuttX at least)</p>
<p><em>Were there problems with Zig-to-C interoperability?</em></p>
<p>We hit some <strong>minor interoperability issues</strong> and we found workarounds…</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/iot#appendix-opaque-type-error"><strong>“Opaque Type Error”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/iot#appendix-fix-opaque-type"><strong>“Fix Opaque Type”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/iot#appendix-macro-error"><strong>“Macro Error”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/iot#appendix-struct-initialisation-error"><strong>“Struct Initialisation Error”</strong></a></p>
</li>
</ul>
<p>No showstoppers, so our Zig App is good to go!</p>
<p><em>Is Zig effective in managing the complexity of our firmware?</em></p>
<p>I think it is! Zig has plenty of <strong>Safety Checks</strong> to help ensure that we’re doing the right thing…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/iot#safety-checks"><strong>“Safety Checks”</strong></a></li>
</ul>
<p>Now I feel confident that I can <strong>safely extend</strong> our Zig App to do more meaningful IoT things…</p>
<ul>
<li>
<p>Read BL602’s <strong>Internal Temperature Sensor</strong> <a href="https://github.com/lupyuen/bl602_adc_test">(Like this)</a></p>
</li>
<li>
<p>Compress the Temperature Sensor Data with <strong>CBOR</strong> <a href="https://lupyuen.github.io/articles/cbor2">(Like this)</a></p>
</li>
<li>
<p>Transmit over LoRaWAN to <strong>The Things Network</strong> <a href="https://lupyuen.github.io/articles/ttn">(Like this)</a></p>
</li>
<li>
<p>Monitor the Sensor Data with <strong>Prometheus and Grafana</strong> <a href="https://lupyuen.github.io/articles/prometheus">(Like this)</a></p>
</li>
</ul>
<p>We’ll extend our Zig App the modular way thanks to <a href="https://zig.news/mattnite/import-and-packages-23mb"><strong>@import</strong></a></p>
<p><img src="https://lupyuen.github.io/images/prometheus-title.jpg" alt="Extending our Zig App with CBOR, The Things Network, Prometheus and Grafana" /></p>
<p><em>Is there anything else that might benefit from Zig?</em></p>
<p><a href="https://lupyuen.github.io/articles/pinedio2#nuttx-apps"><strong>LVGL Touchscreen Apps</strong></a> might be easier to maintain when we code them in Zig.</p>
<p>(Since LVGL looks as complicated as LoRaWAN)</p>
<p>Someday I’ll try LVGL on Zig… And we might possibly combine it with LoRaWAN in a single Zig App! Check the updates here…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/lvgl"><strong>“Build an LVGL Touchscreen App with Zig”</strong></a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/pinedio2-title.jpg" alt="LVGL Touchscreen Apps might benefit from Zig" /></p>
<p><em>LVGL Touchscreen Apps might benefit from Zig</em></p>
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>11 What’s Next</h1>
<p>I hope this article has inspired you to create IoT apps in Zig!</p>
<p>In the coming weeks I shall flesh out our Zig App, so that it works like a real <strong>IoT Sensor Device.</strong></p>
<p>(With Temperature Sensor, CBOR Encoding, The Things Network, …)</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/sensor"><strong>“Read NuttX Sensor Data with Zig”</strong></a></li>
</ul>
<p>For the next article we’ll take a quick detour and explore <strong>Zig on PinePhone</strong>…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/pinephone"><strong>“Build a PinePhone App with Zig and zgt”</strong></a></li>
</ul>
<p>And then back to <strong>Zig on Apache NuttX RTOS</strong>…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/lvgl"><strong>“Build an LVGL Touchscreen App with Zig”</strong></a></li>
</ul>
<p>Many Thanks to my <a href="https://lupyuen.github.io/articles/sponsor"><strong>GitHub Sponsors</strong></a> for supporting my work! This article wouldn’t have been possible without your support.</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/sponsor"><strong>Sponsor me a coffee</strong></a></p>
</li>
<li>
<p><a href="https://www.reddit.com/r/Zig/comments/vbvj9e/build_an_iot_app_with_zig_and_lorawan/"><strong>Discuss this article on Reddit</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/book"><strong>Read “The RISC-V BL602 / BL604 Book”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io"><strong>Check out my articles</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/rss.xml"><strong>RSS Feed</strong></a></p>
</li>
</ul>
<p><em>Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…</em></p>
<p><a href="https://github.com/lupyuen/lupyuen.github.io/blob/master/src/iot.md"><strong><code>lupyuen.github.io/src/iot.md</code></strong></a></p>
<h1 id="notes"><a class="doc-anchor" href="#notes">§</a>12 Notes</h1>
<ol>
<li>
<p>This article is the expanded version of <a href="https://twitter.com/MisterTechBlog/status/1533595486577258496"><strong>this Twitter Thread</strong></a></p>
</li>
<li>
<p>This article was inspired by a question from my <a href="https://lupyuen.github.io/articles/sponsor"><strong>GitHub Sponsor</strong></a>: “Can we run Zig on BL602 with Apache NuttX RTOS?”</p>
</li>
<li>
<p>These articles were super helpful for <strong>Zig-to-C Interoperability</strong>…</p>
<p><a href="https://ziglearn.org/chapter-4/"><strong>“Working with C”</strong></a></p>
<p><a href="https://zig.news/kristoff/compile-a-c-c-project-with-zig-368j"><strong>“Compile a C/C++ Project with Zig”</strong></a></p>
<p><a href="https://zig.news/kristoff/extend-a-c-c-project-with-zig-55di"><strong>“Extend a C/C++ Project with Zig”</strong></a></p>
<p><a href="https://kristoff.it/blog/maintain-it-with-zig"><strong>“Maintain it With Zig”</strong></a></p>
</li>
<li>
<p>Can we use <a href="https://ziglang.org/documentation/master/#Async-Functions"><strong>Zig Async Functions</strong></a> to simplify our Zig IoT App?</p>
<p>Interesting idea, let’s explore that! <a href="https://www.reddit.com/r/Zig/comments/vbvj9e/comment/iclmwr9/?utm_source=share&utm_medium=web2x&context=3">(See this)</a></p>
</li>
<li>
<p>I’m now using <a href="https://ziglang.org/documentation/master/#typeInfo"><strong>Zig Type Reflection</strong></a> to document the internals of the LoRaWAN Library…</p>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx#zig-type-reflection"><strong>“Zig Type Reflection for LoRaWAN Library”</strong></a></p>
<p>The LoRaWAN Library is a popular library that runs on many platforms, would be great if Zig can create helpful docs for the complicated multithreaded library.</p>
</li>
</ol>
<p><img src="https://lupyuen.github.io/images/sx1262-handler.jpg" alt="Handle LoRaWAN Events with NimBLE Porting Layer" /></p>
<h1 id="appendix-handle-lorawan-events"><a class="doc-anchor" href="#appendix-handle-lorawan-events">§</a>13 Appendix: Handle LoRaWAN Events</h1>
<p>Let’s look at the <strong>Event Loop</strong> that handles the LoRa and LoRaWAN Events in our app.</p>
<p><em>Our Event Loop looks different from the Original LoRaWAN Demo App?</em></p>
<p>Yep the Original LoRaWAN Demo App handles LoRaWAN Events in a <strong>Busy-Wait Loop</strong>. <a href="https://github.com/Lora-net/LoRaMac-node/blob/master/src/apps/LoRaMac/fuota-test-01/B-L072Z-LRWAN1/main.c#L366-L389">(See this)</a></p>
<p>But since our Zig App runs on a Real-Time Operating System (RTOS), we can use the <strong>Multithreading Features</strong> (Timers and Event Queues) provided by the RTOS.</p>
<p><em>So we’re directly calling the Timers and Event Queues from Apache NuttX RTOS?</em></p>
<p>Not quite. We’re calling the Timers and Event Queues provided by <a href="https://lupyuen.github.io/articles/sx1262#multithreading-with-nimble-porting-layer"><strong>NimBLE Porting Layer</strong></a>.</p>
<p>NimBLE Porting Layer is a <a href="https://github.com/apache/mynewt-nimble/tree/master/porting/npl"><strong>Portable Multitasking Library</strong></a> that works on multiple operating systems: FreeRTOS, Linux, Mynewt, NuttX, RIOT.</p>
<p>By calling NimBLE Porting Layer, our modded LoRaWAN Stack will run on all of these operating systems (hopefully).</p>
<p><a href="https://lupyuen.github.io/articles/sx1262#multithreading-with-nimble-porting-layer">(More about NimBLE Porting Layer)</a></p>
<p><em>Alright let’s see the code!</em></p>
<p>Our Event Loop forever reads LoRa and LoRaWAN Events from an <strong>Event Queue</strong> and handles them.</p>
<p>The Event Queue is created in our LoRa SX1262 Library as explained here…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/sx1262#event-queue"><strong>“Event Queue”</strong></a></li>
</ul>
<p>The Main Function of our LoRaWAN App calls this function to run the <strong>Event Loop</strong>: <a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L451-L492">lorawan_test.zig</a></p>
<div class="example-wrap"><pre class="language-zig"><code>/// LoRaWAN Event Loop that dequeues Events from the Event Queue and processes the Events
fn handle_event_queue() void {
// Loop forever handling Events from the Event Queue
while (true) {
// Get the next Event from the Event Queue
var ev: [*c]c.ble_npl_event = c.ble_npl_eventq_get(
&event_queue, // Event Queue
c.BLE_NPL_TIME_FOREVER // No Timeout (Wait forever for event)
);</code></pre></div>
<p>This code runs in the <strong>Foreground Thread</strong> of our app.</p>
<p>Here we loop forever, <strong>waiting for Events</strong> from the Event Queue.</p>
<p>When we receive an Event, we <strong>remove the Event</strong> from the Event Queue…</p>
<div class="example-wrap"><pre class="language-zig"><code> // If no Event due to timeout, wait for next Event.
// Should never happen since we wait forever for an Event.
if (ev == null) { debug("handle_event_queue: timeout", .{}); continue; }
debug("handle_event_queue: ev=0x{x}", .{ @ptrToInt(ev) });
// Remove the Event from the Event Queue
c.ble_npl_eventq_remove(&event_queue, ev);</code></pre></div>
<p>We call the <strong>Event Handler Function</strong> that was registered with the Event…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Trigger the Event Handler Function
c.ble_npl_event_run(ev);</code></pre></div>
<ul>
<li>
<p>For SX1262 Interrupts: We call <a href="https://lupyuen.github.io/articles/sx1262#radioondioirq"><strong>RadioOnDioIrq</strong></a> to handle the packet transmitted / received notification</p>
</li>
<li>
<p>For Timer Events: We call the <strong>Timeout Function</strong> defined in the Timer</p>
</li>
</ul>
<p>The rest of the Event Loop handles <strong>LoRaWAN Events</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code> // Process the LoRaMac events
c.LmHandlerProcess();</code></pre></div>
<p><strong>LmHandlerProcess</strong> handles <strong>Join Network Events</strong> in the LoRaMAC Layer of our LoRaWAN Library.</p>
<p>If we have joined the LoRaWAN Network, we <strong>transmit data</strong> to the network…</p>
<div class="example-wrap"><pre class="language-zig"><code> // If we have joined the network, do the uplink
if (!c.LmHandlerIsBusy()) {
UplinkProcess();
}</code></pre></div>
<p>(<a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L220-L230"><strong>UplinkProcess</strong></a> calls <a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L163-L203"><strong>PrepareTxFrame</strong></a> to transmit a Data Packet, which we have seen earlier)</p>
<p>The last part of the Event Loop will handle Low Power Mode in future…</p>
<div class="example-wrap"><pre class="language-zig"><code> // TODO: CRITICAL_SECTION_BEGIN();
if (IsMacProcessPending == 1) {
// Clear flag and prevent MCU to go into low power mode
IsMacProcessPending = 0;
} else {
// The MCU wakes up through events
// TODO: BoardLowPowerHandler();
}
// TODO: CRITICAL_SECTION_END();
}
}</code></pre></div>
<p>And we loop back perpetually, waiting for Events and handling them.</p>
<p>That’s how we handle LoRa and LoRaWAN Events with NimBLE Porting Layer!</p>
<h1 id="appendix-logging"><a class="doc-anchor" href="#appendix-logging">§</a>14 Appendix: Logging</h1>
<p>We have implemented Zig Debug Logging <strong>std.log.debug</strong> that’s described here…</p>
<ul>
<li><a href="https://gist.github.com/leecannon/d6f5d7e5af5881c466161270347ce84d"><strong>“A simple overview of Zig’s std.log”</strong></a></li>
</ul>
<p>Here’s how we call <strong>std.log.debug</strong> to print a log message…</p>
<div class="example-wrap"><pre class="language-zig"><code>// Create a short alias named `debug`
const debug = std.log.debug;
// Message with 8 bytes
const msg: []const u8 = "Hi NuttX";
// Print the message
debug("Transmit to LoRaWAN ({} bytes): {s}", .{
msg.len, msg
});
// Prints: Transmit to LoRaWAN (8 bytes): Hi NuttX</code></pre></div>
<p><code>.{ ... }</code> creates an <a href="https://ziglearn.org/chapter-1/#anonymous-structs"><strong>Anonymous Struct</strong></a> with a variable number of arguments that will be passed to <strong>std.log.debug</strong> for formatting.</p>
<p>Below is our implementation of <strong>std.log.debug</strong>…</p>
<div class="example-wrap"><pre class="language-zig"><code>/// Called by Zig for `std.log.debug`, `std.log.info`, `std.log.err`, ...
/// https://gist.github.com/leecannon/d6f5d7e5af5881c466161270347ce84d
pub fn log(
comptime _message_level: std.log.Level,
comptime _scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: anytype,
) void {
_ = _message_level;
_ = _scope;
// Format the message
var buf: [100]u8 = undefined; // Limit to 100 chars
var slice = std.fmt.bufPrint(&buf, format, args)
catch { _ = puts("*** log error: buf too small"); return; };
// Terminate the formatted message with a null
var buf2: [buf.len + 1 : 0]u8 = undefined;
std.mem.copy(
u8,
buf2[0..slice.len],
slice[0..slice.len]
);
buf2[slice.len] = 0;
// Print the formatted message
_ = puts(&buf2);
}</code></pre></div>
<p><a href="https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L519-L546">(Source)</a></p>
<p>This implementation calls <strong>puts()</strong>, which is supported by Apache NuttX RTOS since it’s <a href="https://nuttx.apache.org/docs/latest/introduction/inviolables.html#strict-posix-compliance"><strong>POSIX-Compliant</strong></a>.</p>
<h1 id="appendix-panic-handler"><a class="doc-anchor" href="#appendix-panic-handler">§</a>15 Appendix: Panic Handler</h1>
<p><em>Some debug features don’t seem to be working? Like <strong>unreachable</strong>, <strong>std.debug.assert</strong> and <strong>std.debug.panic</strong>?</em></p>
<p>That’s because for Embedded Platforms (like Apache NuttX RTOS) we need to implement our own <strong>Panic Handler</strong>…</p>
<ul>
<li>
<p><a href="https://andrewkelley.me/post/zig-stack-traces-kernel-panic-bare-bones-os.html"><strong>“Using Zig to Provide Stack Traces on Kernel Panic for a Bare Bones Operating System”</strong></a></p>
</li>
<li>
<p><a href="https://github.com/ziglang/zig/blob/master/lib/std/builtin.zig#L763-L847"><strong>Default Panic Handler: std.debug.default_panic</strong></a></p>
</li>
</ul>
<p>With our own Panic Handler, this Assertion Failure…</p>
<div class="example-wrap"><pre class="language-zig"><code>// Create a short alias named `assert`
const assert = std.debug.assert;
// Assertion Failure
assert(TxPeriodicity != 0);</code></pre></div>