generated from sirlilpanda/kicad-project-template-actionless
Compare commits
9 Commits
7d752f2534
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a06855f4e | |||
| 1ff9b657a1 | |||
|
|
5c45a3313f | ||
| e239240178 | |||
| 3f4bef7cbf | |||
|
|
4d334124a7 | ||
| baf2f0be9e | |||
| 52aac06b2c | |||
| f21f909a71 |
@@ -1,7 +1,7 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(version 20251024)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
(generator_version "10.0")
|
||||
(symbol "DOZ50N03"
|
||||
(pin_names
|
||||
(offset 0)
|
||||
@@ -10,8 +10,12 @@
|
||||
(exclude_from_sim no)
|
||||
(in_bom yes)
|
||||
(on_board yes)
|
||||
(in_pos_files yes)
|
||||
(duplicate_pin_numbers_are_jumpers no)
|
||||
(property "Reference" "Q4"
|
||||
(at 6.35 1.2701 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -21,6 +25,8 @@
|
||||
)
|
||||
(property "Value" "DOZ50N03"
|
||||
(at 6.35 -1.2699 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -30,74 +36,90 @@
|
||||
)
|
||||
(property "Footprint" "Package_SON:VSON-8_3.3x3.3mm_P0.65mm_NexFET"
|
||||
(at 5.08 2.54 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" "https://www.lcsc.com/datasheet/C36499165.pdf"
|
||||
(at 0 0 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Description" "C36499165"
|
||||
(at 0 0 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "NOTES" ""
|
||||
(at 0 0 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Part Number" ""
|
||||
(at 0 0 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Supplier PN" ""
|
||||
(at 0 0 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Supplier link" ""
|
||||
(at 0 0 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "ki_keywords" "transistor NMOS N-MOS N-MOSFET"
|
||||
(at 0 0 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(symbol "DOZ50N03_0_1"
|
||||
@@ -268,6 +290,62 @@
|
||||
)
|
||||
)
|
||||
(symbol "DOZ50N03_1_1"
|
||||
(pin passive line
|
||||
(at 2.54 -5.08 90)
|
||||
(length 2.54)
|
||||
(name "D"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "1"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin passive line
|
||||
(at 2.54 -5.08 90)
|
||||
(length 2.54)
|
||||
(hide yes)
|
||||
(name "D"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "2"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin passive line
|
||||
(at 2.54 -5.08 90)
|
||||
(length 2.54)
|
||||
(hide yes)
|
||||
(name "D"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "3"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin input line
|
||||
(at -5.08 0 0)
|
||||
(length 5.08)
|
||||
@@ -289,62 +367,6 @@
|
||||
(pin passive line
|
||||
(at 2.54 5.08 270)
|
||||
(length 2.54)
|
||||
(name "D"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "1"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin passive line
|
||||
(at 2.54 5.08 270)
|
||||
(length 2.54)
|
||||
(hide yes)
|
||||
(name "D"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "2"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin passive line
|
||||
(at 2.54 5.08 270)
|
||||
(length 2.54)
|
||||
(hide yes)
|
||||
(name "D"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "3"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin passive line
|
||||
(at 2.54 -5.08 90)
|
||||
(length 2.54)
|
||||
(name "S"
|
||||
(effects
|
||||
(font
|
||||
@@ -367,8 +389,12 @@
|
||||
(exclude_from_sim no)
|
||||
(in_bom yes)
|
||||
(on_board yes)
|
||||
(in_pos_files yes)
|
||||
(duplicate_pin_numbers_are_jumpers no)
|
||||
(property "Reference" "U"
|
||||
(at 13.208 -26.67 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -377,6 +403,8 @@
|
||||
)
|
||||
(property "Value" "TI DRV8701ERGER"
|
||||
(at -10.922 26.924 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -385,31 +413,37 @@
|
||||
)
|
||||
(property "Footprint" "Package_DFN_QFN:VQFN-24-1EP_4x4mm_P0.5mm_EP2.45x2.45mm_ThermalVias"
|
||||
(at 9.144 -28.956 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify left)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" "kicad-embed://drv8701.pdf"
|
||||
(at 9.398 -30.734 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify left)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Description" ""
|
||||
(at 0 0 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(symbol "DRV8701E_1_1"
|
||||
@@ -424,6 +458,24 @@
|
||||
(type background)
|
||||
)
|
||||
)
|
||||
(pin power_in line
|
||||
(at 0 27.94 270)
|
||||
(length 2.54)
|
||||
(name "VM"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "1"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin power_out line
|
||||
(at -16.51 22.86 0)
|
||||
(length 2.54)
|
||||
@@ -478,17 +530,35 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin power_out line
|
||||
(at -16.51 12.7 0)
|
||||
(pin power_in line
|
||||
(at 0 -27.94 90)
|
||||
(length 2.54)
|
||||
(name "DVDD"
|
||||
(name "GND"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "8"
|
||||
(number "5"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin input line
|
||||
(at -16.51 7.62 0)
|
||||
(length 2.54)
|
||||
(name "VREF"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "6"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -514,17 +584,107 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin input line
|
||||
(at -16.51 7.62 0)
|
||||
(pin power_out line
|
||||
(at -16.51 12.7 0)
|
||||
(length 2.54)
|
||||
(name "VREF"
|
||||
(name "DVDD"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "6"
|
||||
(number "8"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin open_collector line
|
||||
(at -16.51 -22.86 0)
|
||||
(length 2.54)
|
||||
(name "nFAULT"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "9"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin open_collector line
|
||||
(at -16.51 -20.32 0)
|
||||
(length 2.54)
|
||||
(name "SNSOUT"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "10"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin output line
|
||||
(at -16.51 -3.81 0)
|
||||
(length 2.54)
|
||||
(name "SO"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "11"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin input line
|
||||
(at -16.51 -12.7 0)
|
||||
(length 2.54)
|
||||
(name "IDRIVE"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "12"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin input line
|
||||
(at -16.51 -10.16 0)
|
||||
(length 2.54)
|
||||
(name "nSLEEP"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "13"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -568,114 +728,6 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin output line
|
||||
(at -16.51 -3.81 0)
|
||||
(length 2.54)
|
||||
(name "SO"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "11"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin input line
|
||||
(at -16.51 -10.16 0)
|
||||
(length 2.54)
|
||||
(name "nSLEEP"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "13"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin input line
|
||||
(at -16.51 -12.7 0)
|
||||
(length 2.54)
|
||||
(name "IDRIVE"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "12"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin open_collector line
|
||||
(at -16.51 -20.32 0)
|
||||
(length 2.54)
|
||||
(name "SNSOUT"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "10"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin open_collector line
|
||||
(at -16.51 -22.86 0)
|
||||
(length 2.54)
|
||||
(name "nFAULT"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "9"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin power_in line
|
||||
(at 0 27.94 270)
|
||||
(length 2.54)
|
||||
(name "VM"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "1"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin power_in line
|
||||
(at 0 -27.94 90)
|
||||
(length 2.54)
|
||||
@@ -695,43 +747,6 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin power_in line
|
||||
(at 0 -27.94 90)
|
||||
(length 2.54)
|
||||
(hide yes)
|
||||
(name "GND"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "25"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin power_in line
|
||||
(at 0 -27.94 90)
|
||||
(length 2.54)
|
||||
(name "GND"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "5"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin output line
|
||||
(at 16.51 17.78 180)
|
||||
(length 2.54)
|
||||
@@ -786,17 +801,17 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin output line
|
||||
(at 16.51 2.54 180)
|
||||
(pin input line
|
||||
(at 16.51 -17.78 180)
|
||||
(length 2.54)
|
||||
(name "GH2"
|
||||
(name "SN"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "24"
|
||||
(number "20"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -805,16 +820,16 @@
|
||||
)
|
||||
)
|
||||
(pin input line
|
||||
(at 16.51 0 180)
|
||||
(at 16.51 -12.7 180)
|
||||
(length 2.54)
|
||||
(name "SH2"
|
||||
(name "SP"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "23"
|
||||
(number "21"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -841,16 +856,16 @@
|
||||
)
|
||||
)
|
||||
(pin input line
|
||||
(at 16.51 -12.7 180)
|
||||
(at 16.51 0 180)
|
||||
(length 2.54)
|
||||
(name "SP"
|
||||
(name "SH2"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "21"
|
||||
(number "23"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -858,17 +873,36 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin input line
|
||||
(at 16.51 -17.78 180)
|
||||
(pin output line
|
||||
(at 16.51 2.54 180)
|
||||
(length 2.54)
|
||||
(name "SN"
|
||||
(name "GH2"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "20"
|
||||
(number "24"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin power_in line
|
||||
(at 0 -27.94 90)
|
||||
(length 2.54)
|
||||
(hide yes)
|
||||
(name "GND"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "25"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
|
||||
@@ -5,6 +5,129 @@
|
||||
(uuid "1a26a228-6eb3-45fe-98b2-d5a826508636")
|
||||
(paper "A4")
|
||||
(lib_symbols
|
||||
(symbol "Connector:TestPoint"
|
||||
(pin_numbers
|
||||
(hide yes)
|
||||
)
|
||||
(pin_names
|
||||
(offset 0.762)
|
||||
(hide yes)
|
||||
)
|
||||
(exclude_from_sim no)
|
||||
(in_bom yes)
|
||||
(on_board yes)
|
||||
(in_pos_files yes)
|
||||
(duplicate_pin_numbers_are_jumpers no)
|
||||
(property "Reference" "TP"
|
||||
(at 0 6.858 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Value" "TestPoint"
|
||||
(at 0 5.08 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Footprint" ""
|
||||
(at 5.08 0 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" ""
|
||||
(at 5.08 0 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Description" "test point"
|
||||
(at 0 0 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "ki_keywords" "test point tp"
|
||||
(at 0 0 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "ki_fp_filters" "Pin* Test*"
|
||||
(at 0 0 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(hide yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol "TestPoint_0_1"
|
||||
(circle
|
||||
(center 0 3.302)
|
||||
(radius 0.762)
|
||||
(stroke
|
||||
(width 0)
|
||||
(type default)
|
||||
)
|
||||
(fill
|
||||
(type none)
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol "TestPoint_1_1"
|
||||
(pin passive line
|
||||
(at 0 0 90)
|
||||
(length 2.54)
|
||||
(name "1"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "1"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(embedded_fonts no)
|
||||
)
|
||||
(symbol "Connector_Generic:Conn_01x03"
|
||||
(pin_names
|
||||
(offset 1.016)
|
||||
@@ -4575,14 +4698,6 @@
|
||||
(at 100.33 153.67)
|
||||
(uuid "41d4dceb-a576-4429-a735-b52754f3ea41")
|
||||
)
|
||||
(no_connect
|
||||
(at 100.33 176.53)
|
||||
(uuid "4bf4f931-f3ba-4556-b73b-79e6d8d15ac3")
|
||||
)
|
||||
(no_connect
|
||||
(at 100.33 173.99)
|
||||
(uuid "699d7af2-89c6-4387-ae88-0b98473f9d89")
|
||||
)
|
||||
(no_connect
|
||||
(at 100.33 156.21)
|
||||
(uuid "bdfe21fa-bdf8-4547-8a8c-980823f6d305")
|
||||
@@ -7575,6 +7690,129 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "Connector:TestPoint")
|
||||
(at 100.33 176.53 270)
|
||||
(unit 1)
|
||||
(body_style 1)
|
||||
(exclude_from_sim no)
|
||||
(in_bom no)
|
||||
(on_board yes)
|
||||
(in_pos_files yes)
|
||||
(dnp no)
|
||||
(uuid "28380973-eb1f-42c1-bccb-bafa1d41b1aa")
|
||||
(property "Reference" "TP2"
|
||||
(at 105.664 176.784 90)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify left)
|
||||
)
|
||||
)
|
||||
(property "Value" "TestPoint"
|
||||
(at 105.41 177.7999 90)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify left)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "TestPoint:TestPoint_Pad_2.0x2.0mm"
|
||||
(at 100.33 181.61 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" ""
|
||||
(at 100.33 181.61 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Description" "test point"
|
||||
(at 100.33 176.53 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "NOTES" ""
|
||||
(at 100.33 176.53 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Supplier link" ""
|
||||
(at 100.33 176.53 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Supplier PN" ""
|
||||
(at 100.33 176.53 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Part Number" ""
|
||||
(at 100.33 176.53 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin "1"
|
||||
(uuid "d341d7c3-98a3-412a-a5d0-041d92e49f6f")
|
||||
)
|
||||
(instances
|
||||
(project "esp32-racer"
|
||||
(path "/1d580319-54a9-438f-a4a3-ea2f61f357b8/2d868a8b-8f53-45e0-b871-4d23b4a3af01"
|
||||
(reference "TP2")
|
||||
(unit 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "Device:FerriteBead")
|
||||
(at 33.02 83.82 180)
|
||||
@@ -11912,6 +12150,129 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "Connector:TestPoint")
|
||||
(at 100.33 173.99 270)
|
||||
(unit 1)
|
||||
(body_style 1)
|
||||
(exclude_from_sim no)
|
||||
(in_bom no)
|
||||
(on_board yes)
|
||||
(in_pos_files yes)
|
||||
(dnp no)
|
||||
(uuid "9516846e-5e4a-46fa-b6c1-34b8862062c3")
|
||||
(property "Reference" "TP1"
|
||||
(at 105.664 174.244 90)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify left)
|
||||
)
|
||||
)
|
||||
(property "Value" "TestPoint"
|
||||
(at 105.41 175.2599 90)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify left)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "TestPoint:TestPoint_Pad_2.0x2.0mm"
|
||||
(at 100.33 179.07 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" ""
|
||||
(at 100.33 179.07 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Description" "test point"
|
||||
(at 100.33 173.99 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "NOTES" ""
|
||||
(at 100.33 173.99 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Supplier link" ""
|
||||
(at 100.33 173.99 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Supplier PN" ""
|
||||
(at 100.33 173.99 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Part Number" ""
|
||||
(at 100.33 173.99 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin "1"
|
||||
(uuid "7d0a6099-dd13-4b83-8a74-b0d6a3b9291c")
|
||||
)
|
||||
(instances
|
||||
(project "esp32-racer"
|
||||
(path "/1d580319-54a9-438f-a4a3-ea2f61f357b8/2d868a8b-8f53-45e0-b871-4d23b4a3af01"
|
||||
(reference "TP1")
|
||||
(unit 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "LED:SK6812")
|
||||
(at 173.99 46.99 0)
|
||||
|
||||
@@ -1168,7 +1168,7 @@
|
||||
)
|
||||
(symbol "DOZ50N03_1_1"
|
||||
(pin passive line
|
||||
(at 2.54 5.08 270)
|
||||
(at 2.54 -5.08 90)
|
||||
(length 2.54)
|
||||
(name "D"
|
||||
(effects
|
||||
@@ -1186,7 +1186,7 @@
|
||||
)
|
||||
)
|
||||
(pin passive line
|
||||
(at 2.54 5.08 270)
|
||||
(at 2.54 -5.08 90)
|
||||
(length 2.54)
|
||||
(hide yes)
|
||||
(name "D"
|
||||
@@ -1205,7 +1205,7 @@
|
||||
)
|
||||
)
|
||||
(pin passive line
|
||||
(at 2.54 5.08 270)
|
||||
(at 2.54 -5.08 90)
|
||||
(length 2.54)
|
||||
(hide yes)
|
||||
(name "D"
|
||||
@@ -1242,7 +1242,7 @@
|
||||
)
|
||||
)
|
||||
(pin passive line
|
||||
(at 2.54 -5.08 90)
|
||||
(at 2.54 5.08 270)
|
||||
(length 2.54)
|
||||
(name "S"
|
||||
(effects
|
||||
|
||||
@@ -4654,16 +4654,6 @@
|
||||
)
|
||||
(uuid "3db405c1-a0f1-4f1e-9c58-0efe3c3ec006")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 48.26 111.76) (xy 52.07 111.76)
|
||||
)
|
||||
(stroke
|
||||
(width 0)
|
||||
(type default)
|
||||
)
|
||||
(uuid "3de2c821-682f-430a-98ea-21355f1cca93")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 160.02 85.09) (xy 171.45 85.09)
|
||||
@@ -5434,6 +5424,16 @@
|
||||
)
|
||||
(uuid "ae958790-b0ec-44e1-b41a-85c846afe8b3")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 48.26 111.76) (xy 52.07 111.76)
|
||||
)
|
||||
(stroke
|
||||
(width 0)
|
||||
(type default)
|
||||
)
|
||||
(uuid "af47384e-fad6-42f7-bbf7-62e24015d21d")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 262.89 30.48) (xy 265.43 30.48)
|
||||
@@ -8689,7 +8689,7 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "Package_DFN_QFN:DFN-12-1EP_4x4mm_P0.65mm_EP2.64x3.54mm"
|
||||
(property "Footprint" "Package_DFN_QFN:DFN-12-1EP_3x3mm_P0.45mm_EP1.65x2.38mm"
|
||||
(at 76.454 151.384 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
@@ -11717,6 +11717,150 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "Device:R")
|
||||
(at 44.45 111.76 90)
|
||||
(unit 1)
|
||||
(body_style 1)
|
||||
(exclude_from_sim no)
|
||||
(in_bom yes)
|
||||
(on_board yes)
|
||||
(in_pos_files yes)
|
||||
(dnp no)
|
||||
(uuid "48259959-a703-42ba-b75a-5bc82d7fd3c6")
|
||||
(property "Reference" "R65"
|
||||
(at 44.45 114.3 90)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Value" "0"
|
||||
(at 44.45 111.76 90)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "Resistor_SMD:R_0603_1608Metric"
|
||||
(at 44.45 113.538 90)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" ""
|
||||
(at 44.45 111.76 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Description" "Resistor"
|
||||
(at 44.45 111.76 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Notes" ""
|
||||
(at 44.45 111.76 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "PN" " RC0603JR-070RL "
|
||||
(at 44.45 111.76 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "supplier link" "https://nz.mouser.com/ProductDetail/YAGEO/RC0603JR-070RL?qs=2cAdsCoAWRGWyPcuXgGiXQ%3D%3D"
|
||||
(at 44.45 111.76 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Manufactor Part Number" ""
|
||||
(at 44.45 111.76 90)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Supplier Part Number" ""
|
||||
(at 44.45 111.76 90)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Sipplier PN" " 603-RC0603JR-070RL "
|
||||
(at 44.45 111.76 90)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin "1"
|
||||
(uuid "30f894a3-f030-447e-9d08-27ea33f7253c")
|
||||
)
|
||||
(pin "2"
|
||||
(uuid "5218291b-cca8-47cd-a89b-66918ed5422b")
|
||||
)
|
||||
(instances
|
||||
(project "esp32-racer"
|
||||
(path "/1d580319-54a9-438f-a4a3-ea2f61f357b8/12408af9-30e9-42ec-a388-fbb9bc2a9d16"
|
||||
(reference "R65")
|
||||
(unit 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "power:GND")
|
||||
(at 158.75 137.16 0)
|
||||
@@ -13606,7 +13750,7 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "Package_DFN_QFN:DFN-12-1EP_4x4mm_P0.65mm_EP2.64x3.54mm"
|
||||
(property "Footprint" "Package_DFN_QFN:DFN-12-1EP_3x3mm_P0.45mm_EP1.65x2.38mm"
|
||||
(at 76.454 89.154 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
@@ -22899,150 +23043,6 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "Device:R")
|
||||
(at 44.45 111.76 90)
|
||||
(unit 1)
|
||||
(body_style 1)
|
||||
(exclude_from_sim no)
|
||||
(in_bom yes)
|
||||
(on_board yes)
|
||||
(in_pos_files yes)
|
||||
(dnp no)
|
||||
(uuid "f47a2fca-cf94-47c5-a084-fd9e13dd2fe7")
|
||||
(property "Reference" "R65"
|
||||
(at 44.45 114.3 90)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Value" "0"
|
||||
(at 44.45 111.76 90)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "Resistor_SMD:R_0603_1608Metric"
|
||||
(at 44.45 113.538 90)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" ""
|
||||
(at 44.45 111.76 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Description" "Resistor"
|
||||
(at 44.45 111.76 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Notes" ""
|
||||
(at 44.45 111.76 0)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "PN" " RC0603JR-070RL "
|
||||
(at 44.45 111.76 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "supplier link" "https://nz.mouser.com/ProductDetail/YAGEO/RC0603JR-070RL?qs=2cAdsCoAWRGWyPcuXgGiXQ%3D%3D"
|
||||
(at 44.45 111.76 0)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Manufactor Part Number" ""
|
||||
(at 44.45 111.76 90)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Supplier Part Number" ""
|
||||
(at 44.45 111.76 90)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Sipplier PN" " 603-RC0603JR-070RL "
|
||||
(at 44.45 111.76 90)
|
||||
(hide yes)
|
||||
(show_name no)
|
||||
(do_not_autoplace no)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin "1"
|
||||
(uuid "eb34f315-6354-4968-8713-977aa1d091bd")
|
||||
)
|
||||
(pin "2"
|
||||
(uuid "ed918021-4fcb-42dc-95ad-2bdea1b77164")
|
||||
)
|
||||
(instances
|
||||
(project "esp32-racer"
|
||||
(path "/1d580319-54a9-438f-a4a3-ea2f61f357b8/12408af9-30e9-42ec-a388-fbb9bc2a9d16"
|
||||
(reference "R65")
|
||||
(unit 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "Device:R")
|
||||
(at 160.02 88.9 0)
|
||||
|
||||
@@ -3084,7 +3084,7 @@
|
||||
)
|
||||
(symbol "DOZ50N03_1_1"
|
||||
(pin passive line
|
||||
(at 2.54 5.08 270)
|
||||
(at 2.54 -5.08 90)
|
||||
(length 2.54)
|
||||
(name "D"
|
||||
(effects
|
||||
@@ -3102,7 +3102,7 @@
|
||||
)
|
||||
)
|
||||
(pin passive line
|
||||
(at 2.54 5.08 270)
|
||||
(at 2.54 -5.08 90)
|
||||
(length 2.54)
|
||||
(hide yes)
|
||||
(name "D"
|
||||
@@ -3121,7 +3121,7 @@
|
||||
)
|
||||
)
|
||||
(pin passive line
|
||||
(at 2.54 5.08 270)
|
||||
(at 2.54 -5.08 90)
|
||||
(length 2.54)
|
||||
(hide yes)
|
||||
(name "D"
|
||||
@@ -3158,7 +3158,7 @@
|
||||
)
|
||||
)
|
||||
(pin passive line
|
||||
(at 2.54 -5.08 90)
|
||||
(at 2.54 5.08 270)
|
||||
(length 2.54)
|
||||
(name "S"
|
||||
(effects
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"board": {
|
||||
"active_layer": 5,
|
||||
"active_layer": 0,
|
||||
"active_layer_preset": "",
|
||||
"auto_track_width": true,
|
||||
"hidden_netclasses": [],
|
||||
|
||||
@@ -769,7 +769,7 @@
|
||||
"uuid": "1d580319-54a9-438f-a4a3-ea2f61f357b8"
|
||||
}
|
||||
],
|
||||
"used_designators": "",
|
||||
"used_designators": "R65,TP1-2",
|
||||
"variants": []
|
||||
},
|
||||
"sheets": [
|
||||
|
||||
22
software/lsm6dso_example/CMakeLists.txt
Normal file
22
software/lsm6dso_example/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(ENV{IDF_COMPONENT_MANAGER} "0")
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
# add the component directories that we want to use
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
"components/"
|
||||
)
|
||||
|
||||
set(
|
||||
COMPONENTS
|
||||
"main esptool_py i2c lsm6dso filters"
|
||||
CACHE STRING
|
||||
"List of components to include"
|
||||
)
|
||||
|
||||
project(lsm6dso_example)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
50
software/lsm6dso_example/README.md
Normal file
50
software/lsm6dso_example/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# LSM6DSO Example
|
||||
|
||||
This example demonstrates how to use the espp LSM6DSO 6-axis IMU driver with the
|
||||
ESP-IDF. The example is modeled after the ICM42607 example and shows how to
|
||||
configure the IMU, read accelerometer and gyroscope data, and use orientation
|
||||
filtering (e.g., Madgwick filter).
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
- I2C communication with the LSM6DSO
|
||||
- Configurable accelerometer and gyroscope range and output data rate
|
||||
- Periodic reading of accelerometer, gyroscope, and temperature data
|
||||
- Orientation filtering using Madgwick filter
|
||||
|
||||
## Usage
|
||||
- Configure the I2C pins and address in `sdkconfig` or via Kconfig options
|
||||
- Build and flash the example to your ESP32/ESP-IDF target
|
||||
- The example will print IMU data and orientation to the serial console
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view
|
||||
serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||

|
||||

|
||||
|
||||
## Example Code
|
||||
See `main/lsm6dso_example.cpp` for the full example source code.
|
||||
|
||||
## Configuration
|
||||
- Default I2C address: 0x6A (can be changed in Kconfig or via config struct)
|
||||
- Example I2C pins: SDA = 21, SCL = 22
|
||||
|
||||
## Documentation
|
||||
See the [documentation](https://esp-cpp.github.io/espp/imu/lsm6dso.html) for
|
||||
full API details.
|
||||
2
software/lsm6dso_example/main/CMakeLists.txt
Normal file
2
software/lsm6dso_example/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
INCLUDE_DIRS ".")
|
||||
46
software/lsm6dso_example/main/Kconfig.projbuild
Normal file
46
software/lsm6dso_example/main/Kconfig.projbuild
Normal file
@@ -0,0 +1,46 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
choice EXAMPLE_HARDWARE
|
||||
prompt "Hardware"
|
||||
default EXAMPLE_HARDWARE_QTPYPICO
|
||||
help
|
||||
Select the hardware to run this example on.
|
||||
|
||||
config EXAMPLE_HARDWARE_QTPYPICO
|
||||
depends on IDF_TARGET_ESP32
|
||||
bool "Qt Py PICO"
|
||||
|
||||
config EXAMPLE_HARDWARE_QTPYS3
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
bool "Qt Py S3"
|
||||
|
||||
config EXAMPLE_HARDWARE_CUSTOM
|
||||
bool "Custom"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_I2C_SCL_GPIO
|
||||
int "SCL GPIO Num"
|
||||
range 0 50
|
||||
default 19 if EXAMPLE_HARDWARE_QTPYPICO
|
||||
default 40 if EXAMPLE_HARDWARE_QTPYS3
|
||||
default 19 if EXAMPLE_HARDWARE_CUSTOM
|
||||
help
|
||||
GPIO number for I2C Master clock line.
|
||||
|
||||
config EXAMPLE_I2C_SDA_GPIO
|
||||
int "SDA GPIO Num"
|
||||
range 0 50
|
||||
default 22 if EXAMPLE_HARDWARE_QTPYPICO
|
||||
default 41 if EXAMPLE_HARDWARE_QTPYS3
|
||||
default 22 if EXAMPLE_HARDWARE_CUSTOM
|
||||
help
|
||||
GPIO number for I2C Master data line.
|
||||
|
||||
config EXAMPLE_I2C_CLOCK_SPEED_HZ
|
||||
int "I2C Clock Speed"
|
||||
range 100 1000000
|
||||
default 400000
|
||||
help
|
||||
I2C clock speed in Hz.
|
||||
|
||||
endmenu
|
||||
199
software/lsm6dso_example/main/lsm6dso_example.cpp
Normal file
199
software/lsm6dso_example/main/lsm6dso_example.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#include "i2c.hpp"
|
||||
#include "kalman_filter.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "lsm6dso.hpp"
|
||||
#include "madgwick_filter.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
extern "C" void app_main(void) {
|
||||
espp::Logger logger({.tag = "LSM6DSO Example", .level = espp::Logger::Verbosity::INFO});
|
||||
logger.info("Starting LSM6DSO example!");
|
||||
|
||||
//! [lsm6dso example]
|
||||
using Imu = espp::Lsm6dso<espp::lsm6dso::Interface::I2C>;
|
||||
|
||||
// I2C config (customize as needed)
|
||||
static constexpr auto i2c_port = I2C_NUM_0;
|
||||
static constexpr auto i2c_clock_speed = CONFIG_EXAMPLE_I2C_CLOCK_SPEED_HZ; // Set in sdkconfig
|
||||
static constexpr gpio_num_t i2c_sda = (gpio_num_t)CONFIG_EXAMPLE_I2C_SDA_GPIO; // Set in sdkconfig
|
||||
static constexpr gpio_num_t i2c_scl = (gpio_num_t)CONFIG_EXAMPLE_I2C_SCL_GPIO; // Set in sdkconfig
|
||||
espp::I2c i2c({.port = i2c_port,
|
||||
.sda_io_num = i2c_sda,
|
||||
.scl_io_num = i2c_scl,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.clk_speed = i2c_clock_speed});
|
||||
|
||||
// make the orientation filter to compute orientation from accel + gyro
|
||||
static constexpr float angle_noise = 0.001f;
|
||||
static constexpr float rate_noise = 0.1f;
|
||||
static espp::KalmanFilter<2> kf;
|
||||
kf.set_process_noise(rate_noise);
|
||||
kf.set_measurement_noise(angle_noise);
|
||||
|
||||
auto kalman_filter_fn = [](float dt, const Imu::Value &accel,
|
||||
const Imu::Value &gyro) -> Imu::Value {
|
||||
// Apply Kalman filter
|
||||
float accelRoll = atan2(accel.y, accel.z);
|
||||
float accelPitch = atan2(-accel.x, sqrt(accel.y * accel.y + accel.z * accel.z));
|
||||
kf.predict({espp::deg_to_rad(gyro.x), espp::deg_to_rad(gyro.y)}, dt);
|
||||
kf.update({accelRoll, accelPitch});
|
||||
float roll, pitch;
|
||||
std::tie(roll, pitch) = kf.get_state();
|
||||
// return the computed orientation
|
||||
Imu::Value orientation{};
|
||||
orientation.roll = roll;
|
||||
orientation.pitch = pitch;
|
||||
orientation.yaw = 0.0f;
|
||||
return orientation;
|
||||
};
|
||||
|
||||
// Madgwick filter for orientation
|
||||
static constexpr float beta = 0.1f;
|
||||
static espp::MadgwickFilter madgwick(beta);
|
||||
auto madgwick_filter_fn = [](float dt, const Imu::Value &accel,
|
||||
const Imu::Value &gyro) -> Imu::Value {
|
||||
madgwick.update(dt, accel.x, accel.y, accel.z, espp::deg_to_rad(gyro.x),
|
||||
espp::deg_to_rad(gyro.y), espp::deg_to_rad(gyro.z));
|
||||
float roll, pitch, yaw;
|
||||
madgwick.get_euler(roll, pitch, yaw);
|
||||
Imu::Value orientation{};
|
||||
orientation.roll = espp::deg_to_rad(roll);
|
||||
orientation.pitch = espp::deg_to_rad(pitch);
|
||||
orientation.yaw = espp::deg_to_rad(yaw);
|
||||
return orientation;
|
||||
};
|
||||
|
||||
// IMU config
|
||||
Imu::Config config{
|
||||
.device_address = Imu::DEFAULT_I2C_ADDRESS,
|
||||
.write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2,
|
||||
std::placeholders::_3),
|
||||
.read = std::bind(&espp::I2c::read, &i2c, std::placeholders::_1, std::placeholders::_2,
|
||||
std::placeholders::_3),
|
||||
.imu_config =
|
||||
{
|
||||
.accel_range = Imu::AccelRange::RANGE_2G,
|
||||
.accel_odr = Imu::AccelODR::ODR_416_HZ,
|
||||
.gyro_range = Imu::GyroRange::DPS_2000,
|
||||
.gyro_odr = Imu::GyroODR::ODR_416_HZ,
|
||||
},
|
||||
.orientation_filter = kalman_filter_fn,
|
||||
.auto_init = true,
|
||||
.log_level = espp::Logger::Verbosity::INFO,
|
||||
};
|
||||
|
||||
logger.info("Creating LSM6DSO IMU");
|
||||
Imu imu(config);
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
// set the accel / gyro on-chip filters
|
||||
static constexpr uint8_t accel_filter_bandwidth = 0b001; // ODR / 10
|
||||
static constexpr uint8_t gyro_lpf_bandwidth = 0b001; // ODR / 3
|
||||
static constexpr bool gyro_hpf_enabled = false; // disable high-pass filter on gyro
|
||||
static constexpr auto gyro_hpf_bandwidth = Imu::GyroHPF::HPF_0_26_HZ; // 0.26Hz
|
||||
if (!imu.set_accelerometer_filter(accel_filter_bandwidth, Imu::AccelFilter::LOWPASS, ec)) {
|
||||
logger.error("Failed to set accelerometer filter: {}", ec.message());
|
||||
}
|
||||
// set the gyroscope filter to have lowpass
|
||||
if (!imu.set_gyroscope_filter(gyro_lpf_bandwidth, gyro_hpf_enabled, gyro_hpf_bandwidth, ec)) {
|
||||
logger.error("Failed to set gyroscope filter: {}", ec.message());
|
||||
}
|
||||
|
||||
// make a task to read out the IMU data and print it to console
|
||||
espp::Task imu_task({.callback = [&](std::mutex &m, std::condition_variable &cv) -> bool {
|
||||
static auto start = std::chrono::steady_clock::now();
|
||||
|
||||
auto now = esp_timer_get_time(); // time in microseconds
|
||||
static auto t0 = now;
|
||||
auto t1 = now;
|
||||
float dt = (t1 - t0) / 1'000'000.0f; // convert us to s
|
||||
t0 = t1;
|
||||
|
||||
std::error_code ec;
|
||||
// update the imu data
|
||||
if (!imu.update(dt, ec)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get accel
|
||||
auto accel = imu.get_accelerometer();
|
||||
auto gyro = imu.get_gyroscope();
|
||||
auto temp = imu.get_temperature();
|
||||
auto orientation = imu.get_orientation();
|
||||
auto gravity_vector = imu.get_gravity_vector();
|
||||
|
||||
[[maybe_unused]] auto t2 = esp_timer_get_time(); // time in microseconds
|
||||
|
||||
auto madgwick_orientation = madgwick_filter_fn(dt, accel, gyro);
|
||||
float roll = madgwick_orientation.roll;
|
||||
float pitch = madgwick_orientation.pitch;
|
||||
float yaw = madgwick_orientation.yaw;
|
||||
float vx = sin(pitch);
|
||||
float vy = -cos(pitch) * sin(roll);
|
||||
float vz = -cos(pitch) * cos(roll);
|
||||
|
||||
// print time and raw IMU data
|
||||
std::string text = "";
|
||||
text += fmt::format("{:.3f},", now / 1'000'000.0f);
|
||||
text += fmt::format("{:02.3f},{:02.3f},{:02.3f},", (float)accel.x,
|
||||
(float)accel.y, (float)accel.z);
|
||||
text += fmt::format("{:03.3f},{:03.3f},{:03.3f},", (float)gyro.x,
|
||||
(float)gyro.y, (float)gyro.z);
|
||||
text += fmt::format("{:02.1f},", temp);
|
||||
// print kalman filter outputs
|
||||
text += fmt::format("{:03.3f},{:03.3f},{:03.3f},", (float)orientation.x,
|
||||
(float)orientation.y, (float)orientation.z);
|
||||
text += fmt::format("{:03.3f},{:03.3f},{:03.3f},", (float)gravity_vector.x,
|
||||
(float)gravity_vector.y, (float)gravity_vector.z);
|
||||
// print madgwick filter outputs
|
||||
text += fmt::format("{:03.3f},{:03.3f},{:03.3f},", roll, pitch, yaw);
|
||||
text += fmt::format("{:03.3f},{:03.3f},{:03.3f}", vx, vy, vz);
|
||||
|
||||
fmt::print("{}\n", text);
|
||||
|
||||
// fmt::print("IMU update took {:.3f} ms\n", (t2 - t0) / 1000.0f);
|
||||
|
||||
// sleep first in case we don't get IMU data and need to exit early
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
cv.wait_until(lock, start + 10ms);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
.task_config = {
|
||||
.name = "IMU",
|
||||
.stack_size_bytes = 6 * 1024,
|
||||
.priority = 10,
|
||||
.core_id = 0,
|
||||
}});
|
||||
|
||||
// print the header for the IMU data (for plotting)
|
||||
fmt::print("% Time (s), "
|
||||
// raw IMU data (accel, gyro, temp)
|
||||
"Accel X (m/s^2), Accel Y (m/s^2), Accel Z (m/s^2), "
|
||||
"Gyro X (rad/s), Gyro Y (rad/s), Gyro Z (rad/s), "
|
||||
"Temp (C), "
|
||||
// kalman filter outputs
|
||||
"Kalman Roll (rad), Kalman Pitch (rad), Kalman Yaw (rad), "
|
||||
"Kalman Gravity X, Kalman Gravity Y, Kalman Gravity Z, "
|
||||
// madgwick filter outputs
|
||||
"Madgwick Roll (rad), Madgwick Pitch (rad), Madgwick Yaw (rad), "
|
||||
"Madgwick Gravity X, Madgwick Gravity Y, Madgwick Gravity Z\n");
|
||||
|
||||
logger.info("Starting IMU task");
|
||||
imu_task.start();
|
||||
|
||||
// loop forever
|
||||
while (true) {
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
//! [lsm6dso example]
|
||||
}
|
||||
2081
software/lsm6dso_example/sdkconfig
Normal file
2081
software/lsm6dso_example/sdkconfig
Normal file
File diff suppressed because it is too large
Load Diff
20
software/lsm6dso_example/sdkconfig.defaults
Normal file
20
software/lsm6dso_example/sdkconfig.defaults
Normal file
@@ -0,0 +1,20 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
|
||||
# set compiler optimization level to -O2 (compile for performance)
|
||||
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||
|
||||
# ESP32-specific
|
||||
#
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240
|
||||
|
||||
# Common ESP-related
|
||||
#
|
||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384
|
||||
|
||||
# Set esp-timer task stack size to 6KB
|
||||
CONFIG_ESP_TIMER_TASK_STACK_SIZE=6144
|
||||
|
||||
# set the functions into IRAM
|
||||
CONFIG_SPI_MASTER_IN_IRAM=y
|
||||
1948
software/lsm6dso_example/sdkconfig.old
Normal file
1948
software/lsm6dso_example/sdkconfig.old
Normal file
File diff suppressed because it is too large
Load Diff
6
software/wacky_bully/CMakeLists.txt
Normal file
6
software/wacky_bully/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(wacky_bully)
|
||||
3
software/wacky_bully/components/drv8701/CMakeLists.txt
Normal file
3
software/wacky_bully/components/drv8701/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "drv8701.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES esp_driver_mcpwm esp_driver_gpio)
|
||||
105
software/wacky_bully/components/drv8701/drv8701.c
Normal file
105
software/wacky_bully/components/drv8701/drv8701.c
Normal file
@@ -0,0 +1,105 @@
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include "drv8701.h"
|
||||
#include "driver/mcpwm_cmpr.h"
|
||||
#include "driver/mcpwm_gen.h"
|
||||
#include "driver/mcpwm_oper.h"
|
||||
#include "driver/mcpwm_timer.h"
|
||||
#include "driver/mcpwm_types.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_err.h"
|
||||
#include "freertos/idf_additions.h"
|
||||
#include "hal/gpio_types.h"
|
||||
#include "portmacro.h"
|
||||
|
||||
#define PWM_RESOLUTION_HZ 2000000
|
||||
#define PWM_TIMEBASE_PERIOD PWM_RESOLUTION_HZ/20000
|
||||
|
||||
|
||||
void drv8701_task(void *args) {
|
||||
drv8701_task_config_t *motor_cfg = ((drv8701_task_config_t *)args);
|
||||
motor_cfg->in_ctrl_queue = xQueueCreate(3, sizeof(float));
|
||||
motor_cfg->current_mutex = xSemaphoreCreateMutex();
|
||||
|
||||
// --------------------- Configure PWM -------------------------
|
||||
mcpwm_timer_handle_t timer = NULL;
|
||||
mcpwm_timer_config_t timer_cfg = {
|
||||
.group_id = motor_cfg->pwm_group_num,
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = PWM_RESOLUTION_HZ,
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
|
||||
.period_ticks = PWM_TIMEBASE_PERIOD,
|
||||
};
|
||||
ESP_ERROR_CHECK(mcpwm_new_timer(&timer_cfg, &timer));
|
||||
|
||||
mcpwm_oper_handle_t oper = NULL;
|
||||
mcpwm_operator_config_t oper_cfg = {
|
||||
.group_id = motor_cfg->pwm_group_num,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(mcpwm_new_operator(&oper_cfg, &oper));
|
||||
ESP_ERROR_CHECK(mcpwm_operator_connect_timer(oper, timer));
|
||||
|
||||
mcpwm_cmpr_handle_t cmpr = NULL;
|
||||
mcpwm_comparator_config_t cmpr_cfg = {
|
||||
.flags.update_cmp_on_tep = true,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(mcpwm_new_comparator(oper, &cmpr_cfg, &cmpr));
|
||||
|
||||
mcpwm_gen_handle_t gen = NULL;
|
||||
mcpwm_generator_config_t gen_cfg = {
|
||||
.gen_gpio_num = motor_cfg->en_pin,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(mcpwm_new_generator(oper, &gen_cfg, &gen));
|
||||
ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(cmpr, 0));
|
||||
|
||||
ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(gen,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH)));
|
||||
ESP_ERROR_CHECK(mcpwm_generator_set_action_on_compare_event(gen,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpr, MCPWM_GEN_ACTION_LOW)));
|
||||
|
||||
ESP_ERROR_CHECK(mcpwm_timer_enable(timer));
|
||||
ESP_ERROR_CHECK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
|
||||
// --------------------- Configure Direction -------------------------
|
||||
|
||||
printf("Configuring Direction\n");
|
||||
gpio_config_t phase_cfg = {
|
||||
.pin_bit_mask = 1ULL << motor_cfg->ph_pin,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE
|
||||
};
|
||||
gpio_config(&phase_cfg);
|
||||
|
||||
// Main Loop
|
||||
float target_velocity = 0.0;
|
||||
while(1) {
|
||||
|
||||
// Get new velocity if it has changed
|
||||
if(xQueueReceive(motor_cfg->in_ctrl_queue, &target_velocity, 0)){
|
||||
target_velocity = fmaxf(-1.0, fminf(1.0, target_velocity));
|
||||
}
|
||||
|
||||
|
||||
uint32_t abs_scaled_vel = (uint32_t) (fabsf(target_velocity) * ((float)PWM_TIMEBASE_PERIOD));
|
||||
mcpwm_comparator_set_compare_value(cmpr, abs_scaled_vel);
|
||||
|
||||
if (target_velocity > 0.0) {
|
||||
gpio_set_level(motor_cfg->ph_pin, 1);
|
||||
}
|
||||
else {
|
||||
gpio_set_level(motor_cfg->ph_pin, 0);
|
||||
}
|
||||
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void set_motor_vel(drv8701_task_config_t *motor_cfg, float velocity) {
|
||||
xQueueSend(motor_cfg->in_ctrl_queue, &velocity, 0);
|
||||
}
|
||||
|
||||
38
software/wacky_bully/components/drv8701/include/drv8701.h
Normal file
38
software/wacky_bully/components/drv8701/include/drv8701.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef DRV8701_H
|
||||
#define DRV8701_H
|
||||
|
||||
#include "freertos/idf_additions.h"
|
||||
|
||||
// Enable PWM for speed control
|
||||
// Phase chooses direction
|
||||
|
||||
typedef struct {
|
||||
uint8_t en_pin;
|
||||
uint8_t ph_pin;
|
||||
|
||||
uint8_t pwm_group_num;
|
||||
|
||||
// Do not populate, done as part of task creation
|
||||
QueueHandle_t in_ctrl_queue;
|
||||
SemaphoreHandle_t current_mutex;
|
||||
|
||||
} drv8701_task_config_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Launch the motor driver task
|
||||
*
|
||||
* @param motor_cfg The configuration for the motor driver
|
||||
*/
|
||||
void drv8701_task(void *args);
|
||||
|
||||
/**
|
||||
* @brief Set the velocity of a specific motor driver
|
||||
*
|
||||
* @param motor_cfg The configuration for the specific motor driver
|
||||
* @param velocity -1.0 to 1.0
|
||||
*/
|
||||
void set_motor_vel(drv8701_task_config_t *motor_cfg, float velocity);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "xbox_controller.c"
|
||||
PRIV_REQUIRES bt esp_hid driver mbedtls nvs_flash
|
||||
INCLUDE_DIRS "include")
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef XBOX_CONTROLLER_H
|
||||
#define XBOX_CONTROLLER_H
|
||||
|
||||
void init_controller(void);
|
||||
void print_state(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,673 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include "xbox_controller.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_hidh.h"
|
||||
#include "esp_hid_common.h"
|
||||
#include "esp_gattc_api.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_bt.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "freertos/idf_additions.h"
|
||||
|
||||
#define HIDH_IDLE_MODE 0x00
|
||||
#define HIDH_BLE_MODE 0x01
|
||||
#define HIDH_BT_MODE 0x02
|
||||
#define HIDH_BTDM_MODE 0x03
|
||||
|
||||
|
||||
#define XBOX_CONTROLLER_INDEX_BUTTONS_DIR 12
|
||||
#define XBOX_CONTROLLER_INDEX_BUTTONS_MAIN 13
|
||||
#define XBOX_CONTROLLER_INDEX_BUTTONS_CENTER 14
|
||||
#define XBOX_CONTROLLER_INDEX_BUTTONS_SHARE 15
|
||||
|
||||
typedef struct {
|
||||
bool A;
|
||||
bool B;
|
||||
bool X;
|
||||
bool Y;
|
||||
bool UP;
|
||||
bool DOWN;
|
||||
bool LEFT;
|
||||
bool RIGHT;
|
||||
bool RB;
|
||||
bool LB;
|
||||
bool START;
|
||||
bool SELECT;
|
||||
bool XBOX_BUT;
|
||||
bool RS;
|
||||
bool LS;
|
||||
|
||||
uint16_t joyLX;
|
||||
uint16_t joyLY;
|
||||
uint16_t joyRX;
|
||||
uint16_t joyRY;
|
||||
uint16_t trigL;
|
||||
uint16_t trigR;
|
||||
}controller_state_t;
|
||||
|
||||
controller_state_t xbox_state;
|
||||
|
||||
void print_state(void) {
|
||||
ESP_LOGI("xbox_ctrl_output", "A:%1d,B:%1d,X:%1d,Y:%1d,U:%1d,D:%1d,L%1d,R:%1d\n",
|
||||
xbox_state.A, xbox_state.B, xbox_state.X, xbox_state.Y,
|
||||
xbox_state.UP, xbox_state.DOWN, xbox_state.LEFT, xbox_state.RIGHT);
|
||||
ESP_LOGI("xbox_ctrl_output", "RB:%1d,LB:%1d,RS:%1d,LS:%1d,START:%1d,SELECT:%1d,XBOX:%1d",
|
||||
xbox_state.RB, xbox_state.LB, xbox_state.RS, xbox_state.LS,
|
||||
xbox_state.START, xbox_state.SELECT, xbox_state.XBOX_BUT);
|
||||
ESP_LOGI("xbox_ctrl_output", "jLX:%5d,jLY:%5d,jRX:%5d,jRY:%5d,tL:%5d,tR:%5d",
|
||||
xbox_state.joyLX, xbox_state.joyLY, xbox_state.joyRX, xbox_state.joyRY,
|
||||
xbox_state.trigL, xbox_state.trigR);
|
||||
}
|
||||
|
||||
const char *esp_ble_key_type_str(esp_ble_key_type_t key_type)
|
||||
{
|
||||
const char *key_str = NULL;
|
||||
switch (key_type) {
|
||||
case ESP_LE_KEY_NONE:
|
||||
key_str = "ESP_LE_KEY_NONE";
|
||||
break;
|
||||
case ESP_LE_KEY_PENC:
|
||||
key_str = "ESP_LE_KEY_PENC";
|
||||
break;
|
||||
case ESP_LE_KEY_PID:
|
||||
key_str = "ESP_LE_KEY_PID";
|
||||
break;
|
||||
case ESP_LE_KEY_PCSRK:
|
||||
key_str = "ESP_LE_KEY_PCSRK";
|
||||
break;
|
||||
case ESP_LE_KEY_PLK:
|
||||
key_str = "ESP_LE_KEY_PLK";
|
||||
break;
|
||||
case ESP_LE_KEY_LLK:
|
||||
key_str = "ESP_LE_KEY_LLK";
|
||||
break;
|
||||
case ESP_LE_KEY_LENC:
|
||||
key_str = "ESP_LE_KEY_LENC";
|
||||
break;
|
||||
case ESP_LE_KEY_LID:
|
||||
key_str = "ESP_LE_KEY_LID";
|
||||
break;
|
||||
case ESP_LE_KEY_LCSRK:
|
||||
key_str = "ESP_LE_KEY_LCSRK";
|
||||
break;
|
||||
default:
|
||||
key_str = "INVALID BLE KEY TYPE";
|
||||
break;
|
||||
|
||||
}
|
||||
return key_str;
|
||||
}
|
||||
|
||||
|
||||
void controller_msg_callback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data) {
|
||||
|
||||
esp_hidh_event_t event = (esp_hidh_event_t)id;
|
||||
esp_hidh_event_data_t *param = (esp_hidh_event_data_t *) event_data;
|
||||
switch (event)
|
||||
{
|
||||
case ESP_HIDH_OPEN_EVENT:
|
||||
{
|
||||
if (param->open.status == ESP_OK)
|
||||
{
|
||||
const uint8_t *bda = esp_hidh_dev_bda_get(param->open.dev);
|
||||
ESP_LOGI("xbox_ctrl", ESP_BD_ADDR_STR " OPEN: %s", ESP_BD_ADDR_HEX(bda), esp_hidh_dev_name_get(param->open.dev));
|
||||
esp_hidh_dev_dump(param->open.dev, stdout);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE("xbox_ctrl", " OPEN failed!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_BATTERY_EVENT:
|
||||
{
|
||||
const uint8_t *bda = esp_hidh_dev_bda_get(param->battery.dev);
|
||||
ESP_LOGI("xbox_ctrl", ESP_BD_ADDR_STR " BATTERY: %d%%", ESP_BD_ADDR_HEX(bda), param->battery.level);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_INPUT_EVENT:
|
||||
{
|
||||
uint8_t btnBits;
|
||||
btnBits = param->input.data[XBOX_CONTROLLER_INDEX_BUTTONS_MAIN];
|
||||
|
||||
xbox_state.A = (btnBits & (1 << 0) << 0);
|
||||
xbox_state.B = (btnBits & (1 << 1) << 1);
|
||||
xbox_state.X = (btnBits & (1 << 2) << 2);
|
||||
xbox_state.Y = (btnBits & (1 << 3) << 3);
|
||||
xbox_state.LB = (btnBits & (1 << 6) << 6);
|
||||
xbox_state.RB = (btnBits & (1 << 7) << 7);
|
||||
|
||||
btnBits = param->input.data[XBOX_CONTROLLER_INDEX_BUTTONS_CENTER];
|
||||
|
||||
xbox_state.SELECT = (btnBits & (1 << 2) << 2);
|
||||
xbox_state.START = (btnBits & (1 << 3) << 3);
|
||||
xbox_state.XBOX_BUT = (btnBits & (1 << 4) << 4);
|
||||
xbox_state.LS = (btnBits & (1 << 5) << 5);
|
||||
xbox_state.RS = (btnBits & (1 << 6) << 6);
|
||||
|
||||
btnBits = param->input.data[XBOX_CONTROLLER_INDEX_BUTTONS_DIR];
|
||||
xbox_state.UP = btnBits == 1 || btnBits == 2 || btnBits == 8;
|
||||
xbox_state.RIGHT = 2 <= btnBits && btnBits <= 4;
|
||||
xbox_state.DOWN = 4 <= btnBits && btnBits <= 6;
|
||||
xbox_state.LEFT = 6 <= btnBits && btnBits <= 8;
|
||||
|
||||
|
||||
xbox_state.joyLX = (uint16_t)param->input.data[0] | ((uint16_t)param->input.data[1] << 8); // 0-65535
|
||||
xbox_state.joyLY = (uint16_t)param->input.data[2] | ((uint16_t)param->input.data[3] << 8);
|
||||
xbox_state.joyRX = (uint16_t)param->input.data[4] | ((uint16_t)param->input.data[5] << 8);
|
||||
xbox_state.joyRY = (uint16_t)param->input.data[6] | ((uint16_t)param->input.data[7] << 8);
|
||||
|
||||
xbox_state.trigL = (uint16_t)param->input.data[8] | ((uint16_t)param->input.data[9] << 8); // 0-1024
|
||||
xbox_state.trigR = (uint16_t)param->input.data[10] | ((uint16_t)param->input.data[11] << 8);
|
||||
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_FEATURE_EVENT:
|
||||
{
|
||||
const uint8_t *bda = esp_hidh_dev_bda_get(param->feature.dev);
|
||||
ESP_LOGI("xbox_ctrl", ESP_BD_ADDR_STR " FEATURE: %8s, MAP: %2u, ID: %3u, Len: %d", ESP_BD_ADDR_HEX(bda),
|
||||
esp_hid_usage_str(param->feature.usage), param->feature.map_index, param->feature.report_id,
|
||||
param->feature.length);
|
||||
ESP_LOG_BUFFER_HEX("xbox_ctrl", param->feature.data, param->feature.length);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_CLOSE_EVENT:
|
||||
{
|
||||
const uint8_t *bda = esp_hidh_dev_bda_get(param->close.dev);
|
||||
ESP_LOGI("xbox_ctrl", ESP_BD_ADDR_STR " CLOSE: %s", ESP_BD_ADDR_HEX(bda), esp_hidh_dev_name_get(param->close.dev));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_LOGI("xbox_ctrl", "EVENT: %d", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *ble_addr_type_names[] = {"PUBLIC", "RANDOM", "RPA_PUBLIC", "RPA_RANDOM"};
|
||||
|
||||
typedef struct esp_hidh_scan_result_s {
|
||||
struct esp_hidh_scan_result_s *next;
|
||||
|
||||
esp_bd_addr_t bda;
|
||||
const char *name;
|
||||
int8_t rssi;
|
||||
esp_hid_usage_t usage;
|
||||
esp_hid_transport_t transport; //BT, BLE or USB
|
||||
union {
|
||||
struct {
|
||||
esp_bt_uuid_t uuid;
|
||||
} bt;
|
||||
struct {
|
||||
esp_ble_addr_type_t addr_type;
|
||||
uint16_t appearance;
|
||||
} ble;
|
||||
};
|
||||
} esp_hid_scan_result_t;
|
||||
|
||||
static esp_hid_scan_result_t *bt_scan_results = NULL;
|
||||
static size_t num_bt_scan_results = 0;
|
||||
|
||||
static esp_hid_scan_result_t *ble_scan_results = NULL;
|
||||
static size_t num_ble_scan_results = 0;
|
||||
|
||||
static SemaphoreHandle_t bt_hidh_cb_semaphore = NULL;
|
||||
#define WAIT_BT_CB() xSemaphoreTake(bt_hidh_cb_semaphore, portMAX_DELAY)
|
||||
#define SEND_BT_CB() xSemaphoreGive(bt_hidh_cb_semaphore)
|
||||
|
||||
static SemaphoreHandle_t ble_hidh_cb_semaphore = NULL;
|
||||
#define WAIT_BLE_CB() xSemaphoreTake(ble_hidh_cb_semaphore, portMAX_DELAY)
|
||||
#define SEND_BLE_CB() xSemaphoreGive(ble_hidh_cb_semaphore)
|
||||
|
||||
#define SIZEOF_ARRAY(a) (sizeof(a)/sizeof(*a))
|
||||
|
||||
static esp_ble_scan_params_t hid_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
.scan_interval = 0x50,
|
||||
.scan_window = 0x30,
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_ENABLE,
|
||||
};
|
||||
|
||||
static esp_err_t start_ble_scan(uint32_t seconds)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
if ((ret = esp_ble_gap_set_scan_params(&hid_scan_params)) != ESP_OK) {
|
||||
ESP_LOGE("xbox_ctrl", "esp_ble_gap_set_scan_params failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
WAIT_BLE_CB();
|
||||
if ((ret = esp_ble_gap_start_scanning(seconds)) != ESP_OK) {
|
||||
ESP_LOGE("xbox_ctrl", "esp_ble_gap_start_scanning failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_result_t **results)
|
||||
{
|
||||
if (num_bt_scan_results || bt_scan_results || num_ble_scan_results || ble_scan_results) {
|
||||
ESP_LOGE("xbox_ctrl", "There are old scan results. Free them first!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (start_ble_scan(seconds) == ESP_OK) {
|
||||
WAIT_BLE_CB();
|
||||
} else {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
*num_results = num_bt_scan_results + num_ble_scan_results;
|
||||
*results = bt_scan_results;
|
||||
if (num_bt_scan_results) {
|
||||
while (bt_scan_results->next != NULL) {
|
||||
bt_scan_results = bt_scan_results->next;
|
||||
}
|
||||
bt_scan_results->next = ble_scan_results;
|
||||
} else {
|
||||
*results = ble_scan_results;
|
||||
}
|
||||
|
||||
num_bt_scan_results = 0;
|
||||
bt_scan_results = NULL;
|
||||
num_ble_scan_results = 0;
|
||||
ble_scan_results = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type)
|
||||
{
|
||||
if (ble_addr_type > BLE_ADDR_TYPE_RPA_RANDOM) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return ble_addr_type_names[ble_addr_type];
|
||||
}
|
||||
|
||||
void esp_hid_scan_results_free(esp_hid_scan_result_t *results)
|
||||
{
|
||||
esp_hid_scan_result_t *r = NULL;
|
||||
while (results) {
|
||||
r = results;
|
||||
results = results->next;
|
||||
if (r->name != NULL) {
|
||||
free((char *)r->name);
|
||||
}
|
||||
free(r);
|
||||
}
|
||||
}
|
||||
|
||||
#define SCAN_DURATION_SECONDS 10
|
||||
void hid_task(void *pvParameters)
|
||||
{
|
||||
size_t results_len = 0;
|
||||
esp_hid_scan_result_t *results = NULL;
|
||||
ESP_LOGI("xbox_ctrl", "SCAN...");
|
||||
// start scan for HID devices
|
||||
esp_hid_scan(SCAN_DURATION_SECONDS, &results_len, &results);
|
||||
ESP_LOGI("xbox_ctrl", "SCAN: %u results", results_len);
|
||||
if (results_len)
|
||||
{
|
||||
esp_hid_scan_result_t *r = results;
|
||||
esp_hid_scan_result_t *cr = NULL;
|
||||
while (r)
|
||||
{
|
||||
printf(" %s: " ESP_BD_ADDR_STR ", ", (r->transport == ESP_HID_TRANSPORT_BLE) ? "BLE" : "BT ", ESP_BD_ADDR_HEX(r->bda));
|
||||
printf("RSSI: %d, ", r->rssi);
|
||||
printf("USAGE: %s, ", esp_hid_usage_str(r->usage));
|
||||
|
||||
if (r->transport == ESP_HID_TRANSPORT_BLE)
|
||||
{
|
||||
if (r->ble.appearance == 0x03c4)
|
||||
cr = r;
|
||||
printf("APPEARANCE: 0x%04x, ", r->ble.appearance);
|
||||
printf("ADDR_TYPE: '%s', ", ble_addr_type_str(r->ble.addr_type));
|
||||
}
|
||||
|
||||
printf("NAME: %s ", r->name ? r->name : "");
|
||||
printf("\n");
|
||||
r = r->next;
|
||||
}
|
||||
if (cr)
|
||||
{
|
||||
// open the last result
|
||||
esp_hidh_dev_open(cr->bda, cr->transport, cr->ble.addr_type);
|
||||
}
|
||||
// free the results
|
||||
esp_hid_scan_results_free(results);
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
#define GAP_DBG_PRINTF(...) printf(__VA_ARGS__)
|
||||
|
||||
static esp_hid_scan_result_t *find_scan_result(esp_bd_addr_t bda, esp_hid_scan_result_t *results)
|
||||
{
|
||||
esp_hid_scan_result_t *r = results;
|
||||
while (r) {
|
||||
if (memcmp(bda, r->bda, sizeof(esp_bd_addr_t)) == 0) {
|
||||
return r;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void add_ble_scan_result(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type, uint16_t appearance, uint8_t *name, uint8_t name_len, int rssi)
|
||||
{
|
||||
if (find_scan_result(bda, ble_scan_results)) {
|
||||
ESP_LOGW("xbox_controller", "Result already exists!");
|
||||
return;
|
||||
}
|
||||
esp_hid_scan_result_t *r = (esp_hid_scan_result_t *)malloc(sizeof(esp_hid_scan_result_t));
|
||||
if (r == NULL) {
|
||||
ESP_LOGE("xbox_controller", "Malloc ble_hidh_scan_result_t failed!");
|
||||
return;
|
||||
}
|
||||
r->transport = ESP_HID_TRANSPORT_BLE;
|
||||
memcpy(r->bda, bda, sizeof(esp_bd_addr_t));
|
||||
r->ble.appearance = appearance;
|
||||
r->ble.addr_type = addr_type;
|
||||
r->usage = esp_hid_usage_from_appearance(appearance);
|
||||
r->rssi = rssi;
|
||||
r->name = NULL;
|
||||
if (name_len && name) {
|
||||
char *name_s = (char *)malloc(name_len + 1);
|
||||
if (name_s == NULL) {
|
||||
free(r);
|
||||
ESP_LOGE("xbox_controller", "Malloc result name failed!");
|
||||
return;
|
||||
}
|
||||
memcpy(name_s, name, name_len);
|
||||
name_s[name_len] = 0;
|
||||
r->name = (const char *)name_s;
|
||||
}
|
||||
r->next = ble_scan_results;
|
||||
ble_scan_results = r;
|
||||
num_ble_scan_results++;
|
||||
}
|
||||
|
||||
|
||||
static void handle_ble_device_result(struct ble_scan_result_evt_param *scan_rst)
|
||||
{
|
||||
|
||||
uint16_t uuid = 0;
|
||||
uint16_t appearance = 0;
|
||||
char name[64] = {0};
|
||||
|
||||
uint8_t uuid_len = 0;
|
||||
uint8_t *uuid_d = esp_ble_resolve_adv_data(scan_rst->ble_adv, ESP_BLE_AD_TYPE_16SRV_CMPL, &uuid_len);
|
||||
if (uuid_d != NULL && uuid_len) {
|
||||
uuid = uuid_d[0] + (uuid_d[1] << 8);
|
||||
}
|
||||
|
||||
uint8_t appearance_len = 0;
|
||||
uint8_t *appearance_d = esp_ble_resolve_adv_data(scan_rst->ble_adv, ESP_BLE_AD_TYPE_APPEARANCE, &appearance_len);
|
||||
if (appearance_d != NULL && appearance_len) {
|
||||
appearance = appearance_d[0] + (appearance_d[1] << 8);
|
||||
}
|
||||
|
||||
uint8_t adv_name_len = 0;
|
||||
uint8_t *adv_name = esp_ble_resolve_adv_data(scan_rst->ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
|
||||
|
||||
if (adv_name == NULL) {
|
||||
adv_name = esp_ble_resolve_adv_data(scan_rst->ble_adv, ESP_BLE_AD_TYPE_NAME_SHORT, &adv_name_len);
|
||||
}
|
||||
|
||||
if (adv_name != NULL && adv_name_len) {
|
||||
memcpy(name, adv_name, adv_name_len);
|
||||
name[adv_name_len] = 0;
|
||||
}
|
||||
|
||||
GAP_DBG_PRINTF("BLE: " ESP_BD_ADDR_STR ", ", ESP_BD_ADDR_HEX(scan_rst->bda));
|
||||
GAP_DBG_PRINTF("RSSI: %d, ", scan_rst->rssi);
|
||||
GAP_DBG_PRINTF("UUID: 0x%04x, ", uuid);
|
||||
GAP_DBG_PRINTF("APPEARANCE: 0x%04x, ", appearance);
|
||||
GAP_DBG_PRINTF("ADDR_TYPE: '%s'", ble_addr_type_str(scan_rst->ble_addr_type));
|
||||
if (adv_name_len) {
|
||||
GAP_DBG_PRINTF(", NAME: '%s'", name);
|
||||
}
|
||||
GAP_DBG_PRINTF("\n");
|
||||
|
||||
if (uuid == ESP_GATT_UUID_HID_SVC) {
|
||||
add_ble_scan_result(scan_rst->bda, scan_rst->ble_addr_type, appearance, adv_name, adv_name_len, scan_rst->rssi);
|
||||
}
|
||||
}
|
||||
static const char *ble_gap_evt_names[] = { "ADV_DATA_SET_COMPLETE", "SCAN_RSP_DATA_SET_COMPLETE", "SCAN_PARAM_SET_COMPLETE", "SCAN_RESULT", "ADV_DATA_RAW_SET_COMPLETE", "SCAN_RSP_DATA_RAW_SET_COMPLETE", "ADV_START_COMPLETE", "SCAN_START_COMPLETE", "AUTH_CMPL", "KEY", "SEC_REQ", "PASSKEY_NOTIF", "PASSKEY_REQ", "OOB_REQ", "LOCAL_IR", "LOCAL_ER", "NC_REQ", "ADV_STOP_COMPLETE", "SCAN_STOP_COMPLETE", "SET_STATIC_RAND_ADDR", "UPDATE_CONN_PARAMS", "SET_PKT_LENGTH_COMPLETE", "SET_LOCAL_PRIVACY_COMPLETE", "REMOVE_BOND_DEV_COMPLETE", "CLEAR_BOND_DEV_COMPLETE", "GET_BOND_DEV_COMPLETE", "READ_RSSI_COMPLETE", "UPDATE_WHITELIST_COMPLETE"};
|
||||
|
||||
|
||||
const char *ble_gap_evt_str(uint8_t event)
|
||||
{
|
||||
if (event >= SIZEOF_ARRAY(ble_gap_evt_names)) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return ble_gap_evt_names[event];
|
||||
}
|
||||
|
||||
|
||||
static void ble_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
|
||||
{
|
||||
switch (event) {
|
||||
/*
|
||||
* SCAN
|
||||
* */
|
||||
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: {
|
||||
ESP_LOGV("xbox_controller", "BLE GAP EVENT SCAN_PARAM_SET_COMPLETE");
|
||||
SEND_BLE_CB();
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_SCAN_RESULT_EVT: {
|
||||
esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
|
||||
switch (scan_result->scan_rst.search_evt) {
|
||||
case ESP_GAP_SEARCH_INQ_RES_EVT: {
|
||||
handle_ble_device_result(&scan_result->scan_rst);
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_SEARCH_INQ_CMPL_EVT:
|
||||
ESP_LOGV("xbox_controller", "BLE GAP EVENT SCAN DONE: %d", scan_result->scan_rst.num_resps);
|
||||
SEND_BLE_CB();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: {
|
||||
ESP_LOGV("xbox_controller", "BLE GAP EVENT SCAN CANCELED");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* ADVERTISEMENT
|
||||
* */
|
||||
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
|
||||
ESP_LOGV("xbox_controller", "BLE GAP ADV_DATA_SET_COMPLETE");
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
|
||||
ESP_LOGV("xbox_controller", "BLE GAP ADV_START_COMPLETE");
|
||||
break;
|
||||
|
||||
/*
|
||||
* AUTHENTICATION
|
||||
* */
|
||||
case ESP_GAP_BLE_AUTH_CMPL_EVT:
|
||||
if (!param->ble_security.auth_cmpl.success) {
|
||||
ESP_LOGE("xbox_controller", "BLE GAP AUTH ERROR: 0x%x", param->ble_security.auth_cmpl.fail_reason);
|
||||
} else {
|
||||
ESP_LOGI("xbox_controller", "BLE GAP AUTH SUCCESS");
|
||||
}
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_KEY_EVT: //shows the ble key info share with peer device to the user.
|
||||
ESP_LOGI("xbox_controller", "BLE GAP KEY type = %s", esp_ble_key_type_str(param->ble_security.ble_key.key_type));
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: // ESP_IO_CAP_OUT
|
||||
// The app will receive this evt when the IO has Output capability and the peer device IO has Input capability.
|
||||
// Show the passkey number to the user to input it in the peer device.
|
||||
ESP_LOGI("xbox_controller", "BLE GAP PASSKEY_NOTIF passkey:%"PRIu32, param->ble_security.key_notif.passkey);
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_NC_REQ_EVT: // ESP_IO_CAP_IO
|
||||
// The app will receive this event when the IO has DisplayYesNO capability and the peer device IO also has DisplayYesNo capability.
|
||||
// show the passkey number to the user to confirm it with the number displayed by peer device.
|
||||
ESP_LOGI("xbox_controller", "BLE GAP NC_REQ passkey:%"PRIu32, param->ble_security.key_notif.passkey);
|
||||
esp_ble_confirm_reply(param->ble_security.key_notif.bd_addr, true);
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_PASSKEY_REQ_EVT: // ESP_IO_CAP_IN
|
||||
// The app will receive this evt when the IO has Input capability and the peer device IO has Output capability.
|
||||
// See the passkey number on the peer device and send it back.
|
||||
ESP_LOGI("xbox_controller", "BLE GAP PASSKEY_REQ");
|
||||
//esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, 1234);
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_SEC_REQ_EVT:
|
||||
ESP_LOGI("xbox_controller", "BLE GAP SEC_REQ");
|
||||
// Send the positive(true) security response to the peer device to accept the security request.
|
||||
// If not accept the security request, should send the security response with negative(false) accept value.
|
||||
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGV("xbox_controller", "BLE GAP EVENT %s", ble_gap_evt_str(event));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t init_ble_gap(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
if ((ret = esp_ble_gap_register_callback(ble_gap_event_handler)) != ESP_OK) {
|
||||
ESP_LOGE("xbox_controller", "esp_ble_gap_register_callback failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t init_low_level(uint8_t mode)
|
||||
{
|
||||
esp_err_t ret;
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
|
||||
{
|
||||
ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
||||
if (ret) {
|
||||
ESP_LOGE("xbox_controller", "esp_bt_controller_mem_release failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = esp_bt_controller_init(&bt_cfg);
|
||||
if (ret) {
|
||||
ESP_LOGE("xbox_controller", "esp_bt_controller_init failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = esp_bt_controller_enable(mode);
|
||||
if (ret) {
|
||||
ESP_LOGE("xbox_controller", "esp_bt_controller_enable failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = esp_bluedroid_init();
|
||||
if (ret) {
|
||||
ESP_LOGE("xbox_controller", "esp_bluedroid_init failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = esp_bluedroid_enable();
|
||||
if (ret) {
|
||||
ESP_LOGE("xbox_controller", "esp_bluedroid_enable failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
if (mode & ESP_BT_MODE_BLE) {
|
||||
ret = init_ble_gap();
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_hid_gap_init(uint8_t mode)
|
||||
{
|
||||
esp_err_t ret;
|
||||
if (!mode || mode > ESP_BT_MODE_BTDM) {
|
||||
ESP_LOGE("xbox_controller", "Invalid mode given!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (bt_hidh_cb_semaphore != NULL) {
|
||||
ESP_LOGE("xbox_controller", "Already initialised");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
bt_hidh_cb_semaphore = xSemaphoreCreateBinary();
|
||||
if (bt_hidh_cb_semaphore == NULL) {
|
||||
ESP_LOGE("xbox_controller", "xSemaphoreCreateMutex failed!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ble_hidh_cb_semaphore = xSemaphoreCreateBinary();
|
||||
if (ble_hidh_cb_semaphore == NULL) {
|
||||
ESP_LOGE("xbox_controller", "xSemaphoreCreateMutex failed!");
|
||||
vSemaphoreDelete(bt_hidh_cb_semaphore);
|
||||
bt_hidh_cb_semaphore = NULL;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ret = init_low_level(mode);
|
||||
if (ret != ESP_OK) {
|
||||
vSemaphoreDelete(bt_hidh_cb_semaphore);
|
||||
bt_hidh_cb_semaphore = NULL;
|
||||
vSemaphoreDelete(ble_hidh_cb_semaphore);
|
||||
ble_hidh_cb_semaphore = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
void init_controller(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ESP_ERROR_CHECK(esp_hid_gap_init(HIDH_BLE_MODE));
|
||||
|
||||
|
||||
ble_hidh_cb_semaphore = xSemaphoreCreateBinary();
|
||||
ESP_ERROR_CHECK(esp_ble_gattc_register_callback(esp_hidh_gattc_event_handler));
|
||||
|
||||
|
||||
esp_hidh_config_t cfg = {
|
||||
.callback = controller_msg_callback,
|
||||
.event_stack_size = 4096,
|
||||
.callback_arg = NULL
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_hidh_init(&cfg));
|
||||
xTaskCreate(&hid_task, "hid_task", 6 * 1024, NULL, 2, NULL);
|
||||
}
|
||||
10
software/wacky_bully/dependencies.lock
Normal file
10
software/wacky_bully/dependencies.lock
Normal file
@@ -0,0 +1,10 @@
|
||||
dependencies:
|
||||
idf:
|
||||
source:
|
||||
type: idf
|
||||
version: 6.1.0
|
||||
direct_dependencies:
|
||||
- idf
|
||||
manifest_hash: e44bf68eca6b7b264ddae08cd014cd3294c0473230381b6d6f88ed18ec879038
|
||||
target: esp32s3
|
||||
version: 3.0.0
|
||||
3
software/wacky_bully/main/CMakeLists.txt
Normal file
3
software/wacky_bully/main/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "wacky_bully.c"
|
||||
REQUIRES bt "drv8701" freertos xbox_controller
|
||||
INCLUDE_DIRS ".")
|
||||
16
software/wacky_bully/main/idf_component.yml
Normal file
16
software/wacky_bully/main/idf_component.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf:
|
||||
version: ">=4.1.0"
|
||||
# # Put list of dependencies here
|
||||
# # For components maintained by Espressif:
|
||||
# component: "~1.0.0"
|
||||
# # For 3rd party components:
|
||||
# username/component: ">=1.0.0,<2.0.0"
|
||||
# username2/component2:
|
||||
# version: "~1.0.0"
|
||||
# # For transient dependencies `public` flag can be set.
|
||||
# # `public` flag doesn't have an effect dependencies of the `main` component.
|
||||
# # All dependencies of `main` are public by default.
|
||||
# public: true
|
||||
55
software/wacky_bully/main/wacky_bully.c
Normal file
55
software/wacky_bully/main/wacky_bully.c
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <stdio.h>
|
||||
#include "drv8701.h"
|
||||
#include "freertos/idf_additions.h"
|
||||
#include "portmacro.h"
|
||||
#include "soc/gpio_num.h"
|
||||
#include "xbox_controller.h"
|
||||
|
||||
|
||||
drv8701_task_config_t motor1 = {
|
||||
.ph_pin = GPIO_NUM_13,
|
||||
.en_pin = GPIO_NUM_14,
|
||||
.pwm_group_num = 0,
|
||||
};
|
||||
drv8701_task_config_t motor2 = {
|
||||
.ph_pin = GPIO_NUM_21,
|
||||
.en_pin = GPIO_NUM_45,
|
||||
.pwm_group_num = 0,
|
||||
};
|
||||
drv8701_task_config_t motor3 = {
|
||||
.ph_pin = GPIO_NUM_9,
|
||||
.en_pin = GPIO_NUM_10,
|
||||
.pwm_group_num = 1,
|
||||
};
|
||||
drv8701_task_config_t motor4 = {
|
||||
.ph_pin = GPIO_NUM_11,
|
||||
.en_pin = GPIO_NUM_12,
|
||||
.pwm_group_num = 1,
|
||||
};
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("Starting\n");
|
||||
|
||||
init_controller();
|
||||
|
||||
xTaskCreate(drv8701_task, "Motor1 Task", 2048, (void *)&motor1, 4, NULL);
|
||||
xTaskCreate(drv8701_task, "Motor2 Task", 2048, (void *)&motor2, 4, NULL);
|
||||
xTaskCreate(drv8701_task, "Motor3 Task", 2048, (void *)&motor3, 4, NULL);
|
||||
xTaskCreate(drv8701_task, "Motor4 Task", 2048, (void *)&motor4, 4, NULL);
|
||||
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||
float motor1_vel = 0.0;
|
||||
while(true) {
|
||||
print_state();
|
||||
motor1_vel = motor1_vel + 0.1;
|
||||
if (motor1_vel > 1.0)
|
||||
motor1_vel = -1.0;
|
||||
printf("Motor1 velocity: %0.2f\n", motor1_vel);
|
||||
set_motor_vel(&motor1, motor1_vel);
|
||||
set_motor_vel(&motor2, motor1_vel);
|
||||
set_motor_vel(&motor3, motor1_vel);
|
||||
set_motor_vel(&motor4, motor1_vel);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
4863
software/wacky_bully/sdkconfig
Normal file
4863
software/wacky_bully/sdkconfig
Normal file
File diff suppressed because it is too large
Load Diff
4455
software/wacky_bully/sdkconfig.old
Normal file
4455
software/wacky_bully/sdkconfig.old
Normal file
File diff suppressed because it is too large
Load Diff
71
software/zig_main/.devcontainer/Dockerfile
Normal file
71
software/zig_main/.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,71 @@
|
||||
ARG DOCKER_TAG=latest
|
||||
FROM espressif/idf:${DOCKER_TAG}
|
||||
|
||||
ARG DEBIAN_FRONTEND=nointeractive
|
||||
ARG CONTAINER_USER=esp
|
||||
ARG USER_UID=1050
|
||||
ARG USER_GID=$USER_UID
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt install -y -q \
|
||||
cmake \
|
||||
git \
|
||||
libglib2.0-0 \
|
||||
libnuma1 \
|
||||
libpixman-1-0 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# QEMU
|
||||
ENV QEMU_REL=esp_develop_9.2.2_20250817
|
||||
ENV QEMU_SHA256=588bfaccd0f929650655d10a580f020c6ba9c131712d8fa519280081b8d126eb
|
||||
ENV QEMU_DIST=qemu-xtensa-softmmu-${QEMU_REL}-x86_64-linux-gnu.tar.xz
|
||||
ENV QEMU_URL=https://github.com/espressif/qemu/releases/download/esp-develop-9.2.2-20250817/${QEMU_DIST}
|
||||
|
||||
# Zig v0.16.0 - xtensa
|
||||
ENV ZIG_REL=zig-relsafe-x86_64-linux-musl-baseline
|
||||
ENV ZIG_SHA256=411f1858a9610803af28cd271acf4548873545ccc866f4b9060903ff0d4b6e8e
|
||||
ENV ZIG_DIST=${ZIG_REL}.tar.xz
|
||||
ENV ZIG_URL=https://github.com/kassane/zig-espressif-bootstrap/releases/download/0.16.0-xtensa/${ZIG_DIST}
|
||||
|
||||
# ZLS v0.16.0
|
||||
ENV ZLS_REL=zls-x86_64-linux-musl-baseline
|
||||
ENV ZLS_SHA256=993192ee0e212334df0cc5ae4fc5e5bdfded395054414a4a7b0202e4dd3c74dd
|
||||
ENV ZLS_DIST=${ZLS_REL}.tar.xz
|
||||
ENV ZLS_URL=https://github.com/kassane/zig-espressif-bootstrap/releases/download/0.16.0-xtensa-dev/${ZLS_DIST}
|
||||
|
||||
ENV LC_ALL=C.UTF-8
|
||||
ENV LANG=C.UTF-8
|
||||
|
||||
RUN wget --no-verbose ${QEMU_URL} \
|
||||
&& echo "${QEMU_SHA256} *${QEMU_DIST}" | sha256sum --check --strict - \
|
||||
&& tar -xf $QEMU_DIST -C /opt \
|
||||
&& rm ${QEMU_DIST}
|
||||
|
||||
RUN wget --no-verbose ${ZIG_URL} \
|
||||
&& echo "${ZIG_SHA256} *${ZIG_DIST}" | sha256sum --check --strict - \
|
||||
&& tar -xf $ZIG_DIST -C /opt \
|
||||
&& rm ${ZIG_DIST}
|
||||
|
||||
RUN wget --no-verbose ${ZLS_URL} \
|
||||
&& echo "${ZLS_SHA256} *${ZLS_DIST}" | sha256sum --check --strict - \
|
||||
&& mkdir -p /opt/zls \
|
||||
&& tar -xf $ZLS_DIST -C /opt/zls \
|
||||
&& rm ${ZLS_DIST}
|
||||
|
||||
ENV PATH=/opt/qemu/bin:/opt/${ZIG_REL}:/opt/zls/bin:${PATH}
|
||||
|
||||
RUN groupadd --gid $USER_GID $CONTAINER_USER \
|
||||
&& adduser --uid $USER_UID --gid $USER_GID --disabled-password --gecos "" ${CONTAINER_USER} \
|
||||
&& usermod -a -G root $CONTAINER_USER && usermod -a -G dialout $CONTAINER_USER
|
||||
|
||||
RUN chmod -R 775 /opt/esp/python_env/
|
||||
|
||||
USER ${CONTAINER_USER}
|
||||
ENV USER=${CONTAINER_USER}
|
||||
WORKDIR /home/${CONTAINER_USER}
|
||||
|
||||
RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc
|
||||
|
||||
ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]
|
||||
|
||||
CMD ["/bin/bash", "-c"]
|
||||
51
software/zig_main/.devcontainer/devcontainer.json
Normal file
51
software/zig_main/.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "ESP-IDF QEMU",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"args": {
|
||||
"DOCKER_TAG": "release-v6.0"
|
||||
}
|
||||
},
|
||||
"mounts": [
|
||||
// Unfortunately this mount only works for linux users:
|
||||
"source=/dev/bus/usb,target=/dev/bus/usb,type=bind,consistency=cached"
|
||||
],
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"terminal.integrated.defaultProfile.linux": "bash",
|
||||
"idf.espIdfPath": "/opt/esp/idf",
|
||||
"idf.customExtraPaths": "",
|
||||
"idf.pythonBinPath": "/opt/esp/python_env/idf6.0_py3.14_env/bin/python",
|
||||
"idf.toolsPath": "/opt/esp",
|
||||
"zig.zls.path": "/opt/zls/bin/zls",
|
||||
"zig.path": "/opt/zig-relsafe-x86_64-linux-musl-baseline/zig",
|
||||
"idf.gitPath": "/usr/bin/git"
|
||||
},
|
||||
"extensions": [
|
||||
"espressif.esp-idf-extension",
|
||||
"ziglang.vscode-zig"
|
||||
]
|
||||
},
|
||||
"codespaces": {
|
||||
"settings": {
|
||||
"terminal.integrated.defaultProfile.linux": "bash",
|
||||
"idf.espIdfPath": "/opt/esp/idf",
|
||||
"idf.customExtraPaths": "",
|
||||
"idf.pythonBinPath": "/opt/esp/python_env/idf6.0_py3.14_env/bin/python",
|
||||
"idf.toolsPath": "/opt/esp",
|
||||
"zig.zls.path": "/opt/zls/bin/zls",
|
||||
"zig.path": "/opt/zig-relsafe-x86_64-linux-musl-baseline/zig",
|
||||
"idf.gitPath": "/usr/bin/git"
|
||||
},
|
||||
"extensions": [
|
||||
"espressif.esp-idf-extension",
|
||||
"ziglang.vscode-zig",
|
||||
"espressif.esp-idf-web"
|
||||
]
|
||||
}
|
||||
},
|
||||
"runArgs": [
|
||||
"--privileged"
|
||||
]
|
||||
}
|
||||
13
software/zig_main/.gitignore
vendored
Normal file
13
software/zig_main/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
build/
|
||||
*zig-*/
|
||||
.vscode/
|
||||
.cache/
|
||||
sdkconfig
|
||||
*.old
|
||||
*.lock
|
||||
managed_components/
|
||||
imports/idf-sys.zig
|
||||
imports/helpers.zig
|
||||
imports/c_builtins.zig
|
||||
include/bindings.h
|
||||
!flake.lock
|
||||
10
software/zig_main/CMakeLists.txt
Normal file
10
software/zig_main/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose default type of build (Debug)" FORCE)
|
||||
endif()
|
||||
project(zig-sample-idf)
|
||||
201
software/zig_main/LICENSE-APACHE
Normal file
201
software/zig_main/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
16
software/zig_main/LICENSE-MIT
Normal file
16
software/zig_main/LICENSE-MIT
Normal file
@@ -0,0 +1,16 @@
|
||||
MIT No Attribution
|
||||
|
||||
Copyright 2024 all Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
software and associated documentation files (the "Software"), to deal in the Software
|
||||
without restriction, including without limitation the rights to use, copy, modify,
|
||||
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
150
software/zig_main/README.md
Normal file
150
software/zig_main/README.md
Normal file
@@ -0,0 +1,150 @@
|
||||
[](https://deepwiki.com/kassane/zig-esp-idf-sample)
|
||||
|
||||
# Using Zig Language & Toolchain with ESP-IDF
|
||||
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-H4 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
## STATUS: Experimental
|
||||
|
||||
## Description
|
||||
|
||||
This project aims to integrate Zig language and toolchain with the [Espressif IoT Development Framework](https://github.com/espressif/esp-idf) for enhanced development capabilities on ESP32 and its variants.
|
||||
|
||||
More information about building and using Zig with ESP-IDF can be found in the [documentation](docs/getting-started.md).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Zig](https://ziglang.org/download) toolchain - v0.16.0 or master
|
||||
- [ESP-IDF](https://github.com/espressif/esp-idf) - v5.x or v6.x or master
|
||||
|
||||
### Targets Allowed
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Target</th>
|
||||
<th>Architecture</th>
|
||||
<th>Features</th>
|
||||
<th>Zig Build Configuration</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>ESP32</strong></td>
|
||||
<td>Xtensa LX6</td>
|
||||
<td>Dual-core, WiFi, BT Classic, BLE</td>
|
||||
<td><code>-Dtarget=xtensa-freestanding-none -Dcpu=esp32</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>ESP32-S2</strong></td>
|
||||
<td>Xtensa LX7</td>
|
||||
<td>Single-core, WiFi, USB OTG</td>
|
||||
<td><code>-Dtarget=xtensa-freestanding-none -Dcpu=esp32s2</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>ESP32-S3</strong></td>
|
||||
<td>Xtensa LX7</td>
|
||||
<td>Dual-core, WiFi, BLE 5.0, USB OTG, AI</td>
|
||||
<td><code>-Dtarget=xtensa-freestanding-none -Dcpu=esp32s3</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>ESP32-C2</strong></td>
|
||||
<td>RISC-V</td>
|
||||
<td>Single-core, WiFi, BLE 5.0, Low-cost</td>
|
||||
<td rowspan="2"><code>-Dtarget=riscv32-freestanding-none -Dcpu=generic_rv32+m+c+zicsr+zifencei</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>ESP32-C3</strong></td>
|
||||
<td>RISC-V</td>
|
||||
<td>Single-core, WiFi, BLE 5.0, Low-power</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>ESP32-C5</strong></td>
|
||||
<td>RISC-V</td>
|
||||
<td>Single-core, WiFi 6, BLE 5.0</td>
|
||||
<td rowspan="5"><code>-Dtarget=riscv32-freestanding-none -Dcpu=generic_rv32+m+a+c+zicsr+zifencei</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>ESP32-C6</strong></td>
|
||||
<td>RISC-V</td>
|
||||
<td>Single-core, WiFi 6, BLE 5.0, Zigbee, Thread</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>ESP32-C61</strong></td>
|
||||
<td>RISC-V</td>
|
||||
<td>Single-core, WiFi 6, BLE 5.0, Low-cost</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>ESP32-H2</strong></td>
|
||||
<td>RISC-V</td>
|
||||
<td>BLE 5.0, Zigbee 3.0, Thread, No WiFi</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>ESP32-H21</strong></td>
|
||||
<td>RISC-V</td>
|
||||
<td>BLE 5.0, Zigbee 3.0, Thread, No WiFi</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>ESP32-H4</strong></td>
|
||||
<td>RISC-V</td>
|
||||
<td>BLE 5.2, Zigbee, Thread, FPU, No WiFi</td>
|
||||
<td><code>-Dtarget=riscv32-freestanding-eabihf -Dcpu=esp32h4</code> (Espressif fork) / <code>generic_rv32+m+a+c+f+zicsr+zifencei</code> (upstream)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>ESP32-P4</strong></td>
|
||||
<td>RISC-V</td>
|
||||
<td>Dual-core, AI, DSP, FPU, No WiFi/BT</td>
|
||||
<td><code>-Dtarget=riscv32-freestanding-eabihf -Dcpu=esp32p4</code> (Espressif fork) / <code>generic_rv32+m+a+c+f+zicsr+zifencei</code> (upstream)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
> [!WARNING]
|
||||
> **Xtensa Architecture Support**
|
||||
>
|
||||
> The upstream [Zig compiler](https://ziglang.org/download) (LLVM backend) does not support Xtensa architecture. For ESP32, ESP32-S2, and ESP32-S3 targets, you must use the [Espressif Zig fork](https://github.com/kassane/zig-espressif-bootstrap/releases).
|
||||
>
|
||||
> - **RISC-V targets (all variants):** Works with upstream Zig ✅ (uses generic_rv32 fallback); named CPU models available with Espressif fork
|
||||
> - **Xtensa targets (ESP32/S2/S3):** Requires [zig-xtensa](https://github.com/kassane/zig-espressif-bootstrap/releases) (auto-downloaded)
|
||||
>
|
||||
> The build system automatically downloads the correct toolchain for your target.
|
||||
|
||||
|
||||
### Key Features:
|
||||
|
||||
- **Zig Language Integration**: Use the Zig programming language to write firmware code. It provides modern language features such as comptime, meta-programming, and error handling.
|
||||
|
||||
- **Zig Toolchain Integration**: The Zig toolchain can be used to build zig libraries and executables, and can also be integrated with the ESP-IDF build system. Also, system compiler and linker can be replaced to `zig cc`/`zig c++`.
|
||||
- **Note:** For C++ support, zig toolchain uses `llvm-libc++` ABI by default.
|
||||
|
||||
- **ESP-IDF Compatibility**: Seamlessly integrate Zig with the ESP-IDF framework, allowing developers to leverage the rich set of APIs and functionalities provided by ESP-IDF for IoT development.
|
||||
|
||||
- **Build System Configuration**: Using CMake to build Zig libraries allows easy integration with existing ESP-IDF projects while providing efficient dependency management and build configuration.
|
||||
|
||||
- **Cross-Platform Development**: Facilitate development across various ESP32 variants including ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-H2, ESP32-P4, ESP32-S2, and ESP32-S3, ensuring broad compatibility and versatility.
|
||||
|
||||
|
||||
### About Allocators
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Asserts allocations are within `@alignOf(std.c.max_align_t)` and directly calls
|
||||
> `malloc`/`free`. Does not attempt to utilize `malloc_usable_size`.
|
||||
>
|
||||
> - `std.heap.raw_c_allocator` allocator is safe to use as the backing allocator with `std.heap.ArenaAllocator` for example and is more optimal in such a case than `std.heap.c_allocator`. - ref.: [std-doc](https://ziglang.org/documentation/0.15.2/std/#std.heap.raw_c_allocator)
|
||||
>
|
||||
> - `std.heap.ArenaAllocator` takes an existing allocator, wraps it, and provides an interface where you can allocate without freeing, and then free it all together. - ref.: [std-doc](https://ziglang.org/documentation/master/std/#std.heap.ArenaAllocator)
|
||||
>
|
||||
> **Custom Allocators** (based on `std.heap.raw_c_allocator`)
|
||||
>
|
||||
> - `idf.heap.HeapCapsAllocator` - ref.: [espressif-doc](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/mem_alloc.html)
|
||||
> - `idf.heap.MultiHeapAllocator` - ref.: [espressif-doc](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/mem_alloc.html)
|
||||
> - `idf.heap.VPortAllocator` - ref.: [FreeRTOS-doc](https://www.freertos.org/a00111.html)
|
||||
|
||||
|
||||
### License
|
||||
|
||||
This project is licensed twice:
|
||||
- [Apache](LICENSE-APACHE)
|
||||
- [MIT-0](LICENSE-MIT)
|
||||
258
software/zig_main/build.zig
Normal file
258
software/zig_main/build.zig
Normal file
@@ -0,0 +1,258 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{
|
||||
.whitelist = espressif_targets,
|
||||
.default_target = espressif_targets[0],
|
||||
});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const example = b.option([]const u8, "example", "Relative path (from project root) to the Zig source file") orelse "main/app.zig";
|
||||
|
||||
const obj = b.addObject(.{
|
||||
.name = "app_zig",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path(example),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.link_libc = true,
|
||||
}),
|
||||
});
|
||||
obj.root_module.addImport("esp_idf", idf_wrapped_modules(b));
|
||||
|
||||
const obj_install = b.addInstallArtifact(obj, .{
|
||||
.dest_dir = .{
|
||||
.override = .{
|
||||
.custom = "obj",
|
||||
},
|
||||
},
|
||||
});
|
||||
b.getInstallStep().dependOn(&obj_install.step);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Module descriptor table
|
||||
//
|
||||
// To add a new module:
|
||||
// 1. Add a row here with the .zig source file and any deps by name.
|
||||
// 2. Add the name to the `esp_idf` entry's deps list if it should be
|
||||
// re-exported through the top-level "esp_idf" namespace module.
|
||||
//
|
||||
// To add a new dependency inside an existing .zig source file:
|
||||
// 1. Add the dep name to the matching row's `deps` field here.
|
||||
// That is the *only* place you need to edit — no other blocks to touch.
|
||||
// ---------------------------------------------------------------------------
|
||||
const ModuleSpec = struct {
|
||||
/// Name used for @import("…") and as the key in the resolver map.
|
||||
name: []const u8,
|
||||
/// Path relative to the `imports/` directory.
|
||||
file: []const u8,
|
||||
/// Names of other modules (must appear earlier in this table).
|
||||
deps: []const []const u8 = &.{},
|
||||
};
|
||||
|
||||
/// Ordered list — a module may only reference deps that appear before it.
|
||||
const module_specs = [_]ModuleSpec{
|
||||
// ── leaf (no deps) ──────────────────────────────────────────────────
|
||||
.{ .name = "sys", .file = "idf-sys.zig" },
|
||||
// ── depend on sys only ──────────────────────────────────────────────
|
||||
.{ .name = "error", .file = "error.zig", .deps = &.{"sys"} },
|
||||
.{ .name = "log", .file = "logger.zig", .deps = &.{"sys"} },
|
||||
.{ .name = "ver", .file = "version.zig", .deps = &.{"sys"} },
|
||||
.{ .name = "heap", .file = "heap.zig", .deps = &.{"sys"} },
|
||||
.{ .name = "bootloader", .file = "bootloader.zig", .deps = &.{"sys"} },
|
||||
.{ .name = "lwip", .file = "lwip.zig", .deps = &.{"sys"} },
|
||||
.{ .name = "mqtt", .file = "mqtt.zig", .deps = &.{"sys"} },
|
||||
.{ .name = "phy", .file = "phy.zig", .deps = &.{"sys"} },
|
||||
.{ .name = "segger", .file = "segger.zig", .deps = &.{"sys"} },
|
||||
.{ .name = "crc", .file = "crc.zig", .deps = &.{"sys"} },
|
||||
// ── depend on sys + error ───────────────────────────────────────────
|
||||
.{ .name = "bluetooth", .file = "bluetooth.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "led", .file = "led-strip.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "wifi", .file = "wifi.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "gpio", .file = "gpio.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "uart", .file = "uart.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "i2c", .file = "i2c.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "i2s", .file = "i2s.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "spi", .file = "spi.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "now", .file = "now.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "pulse", .file = "pcnt.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "http", .file = "http.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "dsp", .file = "dsp.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "hosted", .file = "hosted.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "wifi_remote", .file = "wifi_remote.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "pthread", .file = "pthread.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "timer", .file = "timer.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "ledc", .file = "ledc.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "twai", .file = "twai.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "pm", .file = "pm.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "matter", .file = "matter.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "rtos", .file = "rtos.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "nvs", .file = "nvs.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "partition", .file = "partition.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "sleep", .file = "sleep.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "event", .file = "event.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "wdt", .file = "wdt.zig", .deps = &.{ "sys", "error" } },
|
||||
.{ .name = "nimble", .file = "nimble.zig", .deps = &.{ "sys", "error" } },
|
||||
// ── depend on sys + log ────────────────────────
|
||||
.{ .name = "panic", .file = "panic.zig", .deps = &.{ "sys", "log" } },
|
||||
};
|
||||
|
||||
/// Names re-exported by the top-level "esp_idf" umbrella module (idf.zig).
|
||||
const esp_idf_exports = [_][]const u8{
|
||||
"sys", "error", "log", "ver", "heap", "bootloader", "lwip", "mqtt",
|
||||
"phy", "segger", "crc", "bluetooth", "led", "wifi", "gpio", "uart",
|
||||
"i2c", "i2s", "spi", "now", "pulse", "http", "dsp", "panic",
|
||||
"rtos", "nvs", "partition", "sleep", "event", "wdt", "nimble", "hosted",
|
||||
"wifi_remote", "timer", "ledc", "twai", "pm", "pthread", "matter",
|
||||
};
|
||||
|
||||
pub fn idf_wrapped_modules(b: *std.Build) *std.Build.Module {
|
||||
const src_path = std.fs.path.dirname(@src().file) orelse b.pathResolve(&.{"."});
|
||||
const imports_dir = b.pathJoin(&.{ src_path, "imports" });
|
||||
|
||||
// Build a name → *Module map so deps can be looked up by name.
|
||||
var map = std.StringHashMap(*std.Build.Module).init(b.allocator);
|
||||
defer map.deinit();
|
||||
|
||||
inline for (module_specs) |spec| {
|
||||
// Collect this module's imports from the already-resolved map.
|
||||
var imports: std.ArrayList(std.Build.Module.Import) = .empty;
|
||||
defer imports.deinit(b.allocator);
|
||||
|
||||
inline for (spec.deps) |dep_name| {
|
||||
const dep_mod = map.get(dep_name) orelse
|
||||
@panic("dep '" ++ dep_name ++ "' not yet resolved — check ordering in module_specs");
|
||||
imports.append(b.allocator, .{ .name = dep_name, .module = dep_mod }) catch @panic("OOM");
|
||||
}
|
||||
|
||||
const mod = b.addModule(spec.name, .{
|
||||
.root_source_file = b.path(b.pathJoin(&.{ imports_dir, spec.file })),
|
||||
.imports = imports.items,
|
||||
});
|
||||
map.put(spec.name, mod) catch @panic("OOM");
|
||||
}
|
||||
|
||||
// Build the esp_idf umbrella module's import list.
|
||||
var top_imports: std.ArrayList(std.Build.Module.Import) = .empty;
|
||||
defer top_imports.deinit(b.allocator);
|
||||
|
||||
inline for (esp_idf_exports) |name| {
|
||||
const mod = map.get(name) orelse
|
||||
@panic("export '" ++ name ++ "' not found in module_specs");
|
||||
top_imports.append(b.allocator, .{ .name = name, .module = mod }) catch @panic("OOM");
|
||||
}
|
||||
|
||||
return b.addModule("esp_idf", .{
|
||||
.root_source_file = b.path(b.pathJoin(&.{ imports_dir, "idf.zig" })),
|
||||
.imports = top_imports.items,
|
||||
});
|
||||
}
|
||||
|
||||
pub const espressif_targets: []const std.Target.Query =
|
||||
if (hasEspXtensaSupport()) riscv_targets ++ xtensa_targets else riscv_targets;
|
||||
|
||||
const riscv_targets: []const std.Target.Query = blk: {
|
||||
var result: []const std.Target.Query = &.{};
|
||||
|
||||
// Named ESP32 RISC-V CPU models — available when built with the Espressif Zig fork.
|
||||
// MC group (c2, c3): m+c+zicsr+zifencei abi=none
|
||||
// MAC group (c5, c6, c61, h2, h21): m+a+c+zicsr+zifencei abi=none
|
||||
// MACF group (h4, s31, p4, p4eco4): m+a+c+f+zicsr+zifencei abi=eabihf
|
||||
const plain_models = .{
|
||||
"esp32c2", "esp32c3",
|
||||
"esp32c5", "esp32c6",
|
||||
"esp32c61", "esp32c61eco0",
|
||||
"esp32h2", "esp32h21",
|
||||
};
|
||||
for (plain_models) |name| {
|
||||
if (@hasDecl(std.Target.riscv.cpu, name)) {
|
||||
result = result ++ &[_]std.Target.Query{.{
|
||||
.cpu_arch = .riscv32,
|
||||
.cpu_model = .{ .explicit = &@field(std.Target.riscv.cpu, name) },
|
||||
.os_tag = .freestanding,
|
||||
.abi = .none,
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
// MACF group: has FPU — use eabihf ABI.
|
||||
const macf_models = .{
|
||||
"esp32h4", "esp32p4",
|
||||
"esp32p4eco4", "esp32s31",
|
||||
};
|
||||
for (macf_models) |name| {
|
||||
if (@hasDecl(std.Target.riscv.cpu, name)) {
|
||||
result = result ++ &[_]std.Target.Query{.{
|
||||
.cpu_arch = .riscv32,
|
||||
.cpu_model = .{ .explicit = &@field(std.Target.riscv.cpu, name) },
|
||||
.os_tag = .freestanding,
|
||||
.abi = .eabihf,
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
// Generic fallback targets for standard Zig builds without named ESP models.
|
||||
if (result.len == 0) {
|
||||
result = &[_]std.Target.Query{
|
||||
// MC: c2, c3
|
||||
.{
|
||||
.cpu_arch = .riscv32,
|
||||
.cpu_model = .{ .explicit = &std.Target.riscv.cpu.generic_rv32 },
|
||||
.os_tag = .freestanding,
|
||||
.abi = .none,
|
||||
.cpu_features_add = std.Target.riscv.featureSet(&.{ .m, .c, .zifencei, .zicsr }),
|
||||
},
|
||||
// MAC: c5, c6, c61, h2, h21
|
||||
.{
|
||||
.cpu_arch = .riscv32,
|
||||
.cpu_model = .{ .explicit = &std.Target.riscv.cpu.generic_rv32 },
|
||||
.os_tag = .freestanding,
|
||||
.abi = .none,
|
||||
.cpu_features_add = std.Target.riscv.featureSet(&.{ .m, .a, .c, .zifencei, .zicsr }),
|
||||
},
|
||||
// MACF: h4, s31, p4, p4eco4
|
||||
.{
|
||||
.cpu_arch = .riscv32,
|
||||
.cpu_model = .{ .explicit = &std.Target.riscv.cpu.generic_rv32 },
|
||||
.os_tag = .freestanding,
|
||||
.abi = .eabihf,
|
||||
.cpu_features_add = std.Target.riscv.featureSet(&.{ .m, .a, .c, .f, .zifencei, .zicsr }),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
break :blk result;
|
||||
};
|
||||
|
||||
const xtensa_targets: []const std.Target.Query = &.{
|
||||
// ESP32: Requires Espressif LLVM fork
|
||||
.{
|
||||
.cpu_arch = .xtensa,
|
||||
.cpu_model = .{ .explicit = &std.Target.xtensa.cpu.esp32 },
|
||||
.os_tag = .freestanding,
|
||||
.abi = .none,
|
||||
},
|
||||
// ESP32-S2: Requires Espressif LLVM fork
|
||||
.{
|
||||
.cpu_arch = .xtensa,
|
||||
.cpu_model = .{ .explicit = &std.Target.xtensa.cpu.esp32s2 },
|
||||
.os_tag = .freestanding,
|
||||
.abi = .none,
|
||||
},
|
||||
// ESP32-S3: Requires Espressif LLVM fork
|
||||
.{
|
||||
.cpu_arch = .xtensa,
|
||||
.cpu_model = .{ .explicit = &std.Target.xtensa.cpu.esp32s3 },
|
||||
.os_tag = .freestanding,
|
||||
.abi = .none,
|
||||
},
|
||||
};
|
||||
|
||||
/// Checks if the Zig compiler has Espressif Xtensa support enabled
|
||||
fn hasEspXtensaSupport() bool {
|
||||
for (std.Target.Cpu.Arch.xtensa.allCpuModels()) |model| {
|
||||
if (std.mem.startsWith(u8, model.name, "esp")) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
54
software/zig_main/build.zig.zon
Normal file
54
software/zig_main/build.zig.zon
Normal file
@@ -0,0 +1,54 @@
|
||||
.{
|
||||
.name = .esp_idf_sample,
|
||||
.version = "0.1.0",
|
||||
.fingerprint = 0xbe5a38cf3bc380ae,
|
||||
// This field is optional.
|
||||
// Each dependency must either provide a `url` and `hash`, or a `path`.
|
||||
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
|
||||
// Once all dependencies are fetched, `zig build` no longer requires
|
||||
// internet connectivity.
|
||||
.dependencies = .{
|
||||
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
|
||||
//.example = .{
|
||||
// // When updating this field to a new URL, be sure to delete the corresponding
|
||||
// // `hash`, otherwise you are communicating that you expect to find the old hash at
|
||||
// // the new URL.
|
||||
// .url = "https://example.com/foo.tar.gz",
|
||||
//
|
||||
// // This is computed from the file contents of the directory of files that is
|
||||
// // obtained after fetching `url` and applying the inclusion rules given by
|
||||
// // `paths`.
|
||||
// //
|
||||
// // This field is the source of truth; packages do not come from a `url`; they
|
||||
// // come from a `hash`. `url` is just one of many possible mirrors for how to
|
||||
// // obtain a package matching this `hash`.
|
||||
// //
|
||||
// // Uses the [multihash](https://multiformats.io/multihash/) format.
|
||||
// .hash = "...",
|
||||
//
|
||||
// // When this is provided, the package is found in a directory relative to the
|
||||
// // build root. In this case the package's hash is irrelevant and therefore not
|
||||
// // computed. This field and `url` are mutually exclusive.
|
||||
// .path = "foo",
|
||||
//},
|
||||
},
|
||||
// Specifies the set of files and directories that are included in this package.
|
||||
// Only files and directories listed here are included in the `hash` that
|
||||
// is computed for this package.
|
||||
// Paths are relative to the build root. Use the empty string (`""`) to refer to
|
||||
// the build root itself.
|
||||
// A directory listed here means that all files within, recursively, are included.
|
||||
.paths = .{
|
||||
// This makes *all* files, recursively, included in this package. It is generally
|
||||
// better to explicitly list the files and directories instead, to insure that
|
||||
// fetching from tarballs, file system paths, and version control all result
|
||||
// in the same contents hash.
|
||||
"main",
|
||||
"imports",
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"LICENSE-APACHE",
|
||||
"LICENSE-MIT",
|
||||
"README.md",
|
||||
},
|
||||
}
|
||||
158
software/zig_main/cmake/bindgen-standalone.cmake
Normal file
158
software/zig_main/cmake/bindgen-standalone.cmake
Normal file
@@ -0,0 +1,158 @@
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Download translate_c-standalone
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
# Determine platform triple for translate_c-standalone naming
|
||||
if(ZIG_PLATFORM STREQUAL "linux-musl")
|
||||
if(ZIG_ARCH STREQUAL "aarch64")
|
||||
set(TRANSLATE_C_TRIPLE "aarch64-linux-musl")
|
||||
set(HASH_SUM "7d5968a41a02064d90035db95ef102fc2ab5e71a2eb48ba6cf028eb79b4ae43d")
|
||||
elseif(ZIG_ARCH STREQUAL "x86_64")
|
||||
set(TRANSLATE_C_TRIPLE "x86_64-linux-musl")
|
||||
set(HASH_SUM "d098e2675fc6706914b6e53dd72f6536895420460f287e73e19772c979fabb84")
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported architecture for translate_c-standalone: ${ZIG_ARCH}")
|
||||
endif()
|
||||
set(ARCHIVE_EXT "tar.xz")
|
||||
elseif(ZIG_PLATFORM STREQUAL "windows")
|
||||
if(ZIG_ARCH STREQUAL "x86_64")
|
||||
set(TRANSLATE_C_TRIPLE "x86_64-windows-gnu")
|
||||
set(HASH_SUM "8e53a03ee44983eea2786c2ba028e83a816b9a993a768b3f4ca8c6e3525923e2")
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported architecture for translate_c-standalone: ${ZIG_ARCH}")
|
||||
endif()
|
||||
set(ARCHIVE_EXT "zip")
|
||||
elseif(ZIG_PLATFORM STREQUAL "macos")
|
||||
if(ZIG_ARCH STREQUAL "aarch64")
|
||||
set(TRANSLATE_C_TRIPLE "aarch64-macos")
|
||||
set(HASH_SUM "8b3d2360d09add35f764ac138e0ec497503dc3de788a01e93fd7b9923a44369c")
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported architecture for translate_c-standalone: ${ZIG_ARCH}")
|
||||
endif()
|
||||
set(ARCHIVE_EXT "tar.xz")
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported platform for translate_c-standalone: ${ZIG_PLATFORM}")
|
||||
endif()
|
||||
|
||||
set(TRANSLATE_C_DIR "${CMAKE_BINARY_DIR}/translate_c-standalone")
|
||||
set(TRANSLATE_C_ARCHIVE "${CMAKE_BINARY_DIR}/translate_c-standalone-${TRANSLATE_C_TRIPLE}-baseline.${ARCHIVE_EXT}")
|
||||
set(TRANSLATE_C_BIN "${TRANSLATE_C_DIR}/bin")
|
||||
|
||||
# Download if not already extracted
|
||||
if(NOT EXISTS "${TRANSLATE_C_BIN}")
|
||||
set(TRANSLATE_C_URL "https://github.com/kassane/zig-espressif-bootstrap/releases/download/0.16.0-xtensa-dev/translate_c-standalone-${TRANSLATE_C_TRIPLE}-baseline.${ARCHIVE_EXT}")
|
||||
|
||||
message(STATUS "Downloading translate_c-standalone (${TRANSLATE_C_TRIPLE}):")
|
||||
message(STATUS " => ${TRANSLATE_C_ARCHIVE}")
|
||||
|
||||
file(DOWNLOAD "${TRANSLATE_C_URL}" "${TRANSLATE_C_ARCHIVE}"
|
||||
TLS_VERIFY ON
|
||||
EXPECTED_HASH SHA256=${HASH_SUM}
|
||||
STATUS download_status
|
||||
LOG download_log
|
||||
)
|
||||
|
||||
list(GET download_status 0 dl_code)
|
||||
if(NOT dl_code EQUAL 0)
|
||||
message(FATAL_ERROR "Download failed:\n${download_log}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Extracting ${ARCHIVE_EXT} ...")
|
||||
file(MAKE_DIRECTORY "${TRANSLATE_C_DIR}")
|
||||
|
||||
if(HOST_OS_LOWER MATCHES "windows|win")
|
||||
execute_process(
|
||||
COMMAND powershell -NoProfile -ExecutionPolicy Bypass
|
||||
-Command "Expand-Archive -Path '${TRANSLATE_C_ARCHIVE}' -DestinationPath '${TRANSLATE_C_DIR}' -Force"
|
||||
RESULT_VARIABLE extract_result
|
||||
)
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E tar xf "${TRANSLATE_C_ARCHIVE}"
|
||||
WORKING_DIRECTORY "${TRANSLATE_C_DIR}"
|
||||
RESULT_VARIABLE extract_result
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT extract_result EQUAL 0)
|
||||
message(FATAL_ERROR "Extraction failed (code ${extract_result})")
|
||||
endif()
|
||||
|
||||
file(REMOVE "${TRANSLATE_C_ARCHIVE}")
|
||||
|
||||
# Make executable on Unix
|
||||
if(NOT HOST_OS_LOWER MATCHES "windows|win")
|
||||
execute_process(
|
||||
COMMAND chmod +x "${TRANSLATE_C_BIN}"
|
||||
RESULT_VARIABLE chmod_result
|
||||
)
|
||||
if(NOT chmod_result EQUAL 0)
|
||||
message(WARNING "Failed to set executable bit on translate_c-standalone")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "translate_c-standalone ready at: ${TRANSLATE_C_BIN}")
|
||||
else()
|
||||
message(STATUS "Using cached translate_c-standalone: ${TRANSLATE_C_BIN}")
|
||||
endif()
|
||||
set(BINDGEN "${TRANSLATE_C_BIN}/translate-c")
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Helper to invoke translate-c (mirrors zig_run from zig-runner.cmake)
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
function(bindgen_run)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 ARG
|
||||
"VERBATIM;ALLOW_FAIL"
|
||||
"WORKING_DIRECTORY;RESULT_VARIABLE;OUTPUT_VARIABLE;ERROR_VARIABLE;OUTPUT_FILE;TIMEOUT"
|
||||
"COMMAND"
|
||||
)
|
||||
if(NOT ARG_COMMAND)
|
||||
message(FATAL_ERROR "bindgen_run: COMMAND list is required")
|
||||
endif()
|
||||
if(NOT DEFINED ARG_WORKING_DIRECTORY)
|
||||
set(ARG_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
endif()
|
||||
if(NOT DEFINED ARG_TIMEOUT)
|
||||
set(ARG_TIMEOUT 300)
|
||||
endif()
|
||||
set(extra_args)
|
||||
if(ARG_VERBATIM)
|
||||
list(APPEND extra_args VERBATIM)
|
||||
endif()
|
||||
if(ARG_OUTPUT_FILE)
|
||||
list(APPEND extra_args OUTPUT_FILE "${ARG_OUTPUT_FILE}")
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND "${BINDGEN}" ${ARG_COMMAND}
|
||||
WORKING_DIRECTORY "${ARG_WORKING_DIRECTORY}"
|
||||
RESULT_VARIABLE result
|
||||
OUTPUT_VARIABLE output
|
||||
ERROR_VARIABLE error
|
||||
TIMEOUT ${ARG_TIMEOUT}
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_STRIP_TRAILING_WHITESPACE
|
||||
${extra_args}
|
||||
)
|
||||
if(DEFINED ARG_RESULT_VARIABLE)
|
||||
set(${ARG_RESULT_VARIABLE} "${result}" PARENT_SCOPE)
|
||||
endif()
|
||||
if(DEFINED ARG_OUTPUT_VARIABLE)
|
||||
set(${ARG_OUTPUT_VARIABLE} "${output}" PARENT_SCOPE)
|
||||
endif()
|
||||
if(DEFINED ARG_ERROR_VARIABLE)
|
||||
set(${ARG_ERROR_VARIABLE} "${error}" PARENT_SCOPE)
|
||||
endif()
|
||||
if(NOT ARG_ALLOW_FAIL)
|
||||
if(result MATCHES "timeout")
|
||||
message(FATAL_ERROR
|
||||
"translate-c timed out after ${ARG_TIMEOUT}s — Aro deadlock on stub headers (issue #61)\n"
|
||||
" Command: ${BINDGEN} ${ARG_COMMAND}\n"
|
||||
"--- stderr ---\n${error}")
|
||||
elseif(NOT result EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"translate-c failed (code ${result}):\n"
|
||||
" ${BINDGEN} ${ARG_COMMAND}\n"
|
||||
"--- stderr ---\n${error}")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
32
software/zig_main/cmake/bindings.cmake
Normal file
32
software/zig_main/cmake/bindings.cmake
Normal file
@@ -0,0 +1,32 @@
|
||||
# ────────────────────────────────────────────────────────────────────────────────
|
||||
# Helper for downloading / referencing esp-rs/esp-idf-sys bindings.h
|
||||
#
|
||||
# This file is part of a Zig + ESP-IDF project using translate-c.
|
||||
# It downloads the official bindings.h mega-header from esp-rs/esp-idf-sys
|
||||
# as a reference (helps when improving stubs.h or comparing generated bindings).
|
||||
#
|
||||
# Huge thanks to the esp-rs community for maintaining esp-idf-sys
|
||||
# Repository: https://github.com/esp-rs/esp-idf-sys
|
||||
# bindings.h: https://github.com/esp-rs/esp-idf-sys/blob/master/src/include/esp-idf/bindings.h
|
||||
# ────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
set(BINDINGS_URL
|
||||
"https://raw.githubusercontent.com/esp-rs/esp-idf-sys/refs/heads/master/src/include/esp-idf/bindings.h"
|
||||
)
|
||||
set(BINDINGS_ESP_RS "${CMAKE_SOURCE_DIR}/include/bindings.h")
|
||||
if(NOT EXISTS "${BINDINGS_ESP_RS}")
|
||||
file(DOWNLOAD ${BINDINGS_URL} ${BINDINGS_ESP_RS}
|
||||
TLS_VERIFY ON
|
||||
STATUS download_status
|
||||
LOG download_log
|
||||
TIMEOUT 15
|
||||
)
|
||||
list(GET download_status 0 status_code)
|
||||
list(GET download_status 1 error_msg)
|
||||
if(NOT status_code EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to download esp-idf/bindings.h\nStatus: ${status_code}\nError: ${error_msg}\nLog:\n${download_log}")
|
||||
endif()
|
||||
message(STATUS "Downloaded esp-idf/bindings.h => ${BINDINGS_ESP_RS}")
|
||||
else()
|
||||
message(STATUS "esp-idf/bindings.h already exists => ${BINDINGS_ESP_RS}")
|
||||
endif()
|
||||
227
software/zig_main/cmake/extra-components.cmake
Normal file
227
software/zig_main/cmake/extra-components.cmake
Normal file
@@ -0,0 +1,227 @@
|
||||
# ─── Optional/Managed Components Detection ───────────────────────────────────
|
||||
idf_build_get_property(BUILD_COMPS BUILD_COMPONENTS)
|
||||
|
||||
# Get IDF_PATH from environment
|
||||
set(IDF_PATH $ENV{IDF_PATH})
|
||||
|
||||
# Include version.cmake to get IDF_VERSION_MAJOR, IDF_VERSION_MINOR, etc.
|
||||
if(EXISTS "${IDF_PATH}/tools/cmake/version.cmake")
|
||||
include("${IDF_PATH}/tools/cmake/version.cmake")
|
||||
else()
|
||||
message(FATAL_ERROR "Failed to find version.cmake in ${IDF_PATH}/tools/cmake")
|
||||
endif()
|
||||
|
||||
# Helper function to check and add component
|
||||
macro(check_unmanaged_component COMPONENT_NAME VENDOR PACKAGE DEFINE_NAME)
|
||||
set(COMP_PATHS "")
|
||||
set(COMP_BASE "${CMAKE_SOURCE_DIR}/components/${VENDOR}__${PACKAGE}")
|
||||
|
||||
if("${PACKAGE}" STREQUAL "esp-dsp")
|
||||
list(APPEND COMP_PATHS
|
||||
"${COMP_BASE}/modules/common/include"
|
||||
"${COMP_BASE}/modules/dotprod/include"
|
||||
"${COMP_BASE}/modules/support/include"
|
||||
"${COMP_BASE}/modules/support/mem/include"
|
||||
"${COMP_BASE}/modules/windows/include"
|
||||
"${COMP_BASE}/modules/windows/hann/include"
|
||||
"${COMP_BASE}/modules/windows/blackman/include"
|
||||
"${COMP_BASE}/modules/windows/blackman_harris/include"
|
||||
"${COMP_BASE}/modules/windows/blackman_nuttall/include"
|
||||
"${COMP_BASE}/modules/windows/nuttall/include"
|
||||
"${COMP_BASE}/modules/windows/flat_top/include"
|
||||
"${COMP_BASE}/modules/iir/include"
|
||||
"${COMP_BASE}/modules/fir/include"
|
||||
"${COMP_BASE}/modules/math/include"
|
||||
"${COMP_BASE}/modules/math/add/include"
|
||||
"${COMP_BASE}/modules/math/sub/include"
|
||||
"${COMP_BASE}/modules/math/mul/include"
|
||||
"${COMP_BASE}/modules/math/addc/include"
|
||||
"${COMP_BASE}/modules/math/mulc/include"
|
||||
"${COMP_BASE}/modules/math/sqrt/include"
|
||||
"${COMP_BASE}/modules/matrix/include"
|
||||
"${COMP_BASE}/modules/matrix/mul/include"
|
||||
"${COMP_BASE}/modules/matrix/add/include"
|
||||
"${COMP_BASE}/modules/matrix/addc/include"
|
||||
"${COMP_BASE}/modules/matrix/mulc/include"
|
||||
"${COMP_BASE}/modules/matrix/sub/include"
|
||||
"${COMP_BASE}/modules/fft/include"
|
||||
"${COMP_BASE}/modules/dct/include"
|
||||
"${COMP_BASE}/modules/conv/include"
|
||||
"${COMP_BASE}/modules/kalman/ekf/include"
|
||||
"${COMP_BASE}/modules/kalman/ekf_imu13states/include"
|
||||
)
|
||||
elseif("${PACKAGE}" STREQUAL "esp_wifi_remote")
|
||||
# Add base include if exists
|
||||
set(BASE_INCLUDE "${COMP_BASE}/include")
|
||||
if(EXISTS "${BASE_INCLUDE}")
|
||||
list(APPEND COMP_PATHS "${BASE_INCLUDE}")
|
||||
endif()
|
||||
|
||||
# Add version-specific include using MAJOR.MINOR
|
||||
set(VERSION_SUBDIR "idf_v${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}")
|
||||
set(VERSION_PATH "${COMP_BASE}/${VERSION_SUBDIR}/include")
|
||||
if(EXISTS "${VERSION_PATH}")
|
||||
list(APPEND COMP_PATHS "${VERSION_PATH}")
|
||||
endif()
|
||||
elseif("${PACKAGE}" STREQUAL "esp_hosted")
|
||||
# Add host and api include paths for esp_hosted
|
||||
set(HOST_PATH "${COMP_BASE}/host")
|
||||
if(EXISTS "${HOST_PATH}")
|
||||
list(APPEND COMP_PATHS "${HOST_PATH}")
|
||||
endif()
|
||||
set(API_PATH "${HOST_PATH}/api/include")
|
||||
if(EXISTS "${API_PATH}")
|
||||
list(APPEND COMP_PATHS "${API_PATH}")
|
||||
endif()
|
||||
elseif("${PACKAGE}" STREQUAL "esp_matter")
|
||||
# esp_matter is a C++ (CHIP SDK) library.
|
||||
# matter_stubs.h provides the C interface for translate-c instead.
|
||||
list(APPEND COMP_PATHS "${COMP_BASE}/components/esp_matter")
|
||||
else()
|
||||
list(APPEND COMP_PATHS "${COMP_BASE}/include")
|
||||
endif()
|
||||
|
||||
# Check if component is in build
|
||||
if("${VENDOR}__${PACKAGE}" IN_LIST BUILD_COMPS OR "${PACKAGE}" IN_LIST BUILD_COMPS)
|
||||
# Verify at least one path exists (check the base directory)
|
||||
if(EXISTS "${COMP_BASE}")
|
||||
message(STATUS "${COMPONENT_NAME} component found")
|
||||
set(${DEFINE_NAME} 1)
|
||||
|
||||
# Add all include paths that actually exist
|
||||
set(VALID_PATHS 0)
|
||||
foreach(COMP_PATH IN LISTS COMP_PATHS)
|
||||
if(EXISTS "${COMP_PATH}")
|
||||
list(APPEND INCLUDE_DIRS "${COMP_PATH}")
|
||||
math(EXPR VALID_PATHS "${VALID_PATHS} + 1")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(VALID_PATHS GREATER 0)
|
||||
message(STATUS " Added ${VALID_PATHS} include paths for ${COMPONENT_NAME}")
|
||||
else()
|
||||
message(WARNING "${COMPONENT_NAME} base exists but no include paths found")
|
||||
set(${DEFINE_NAME} 0)
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "${COMPONENT_NAME} in components but not found: ${COMP_BASE}")
|
||||
set(${DEFINE_NAME} 0)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "${COMPONENT_NAME} not in build. To add: idf.py add-dependency ${VENDOR}/${PACKAGE}")
|
||||
set(${DEFINE_NAME} 0)
|
||||
endif()
|
||||
|
||||
list(APPEND EXTRA_DEFINE_FLAGS "-D${DEFINE_NAME}=${${DEFINE_NAME}}")
|
||||
endmacro()
|
||||
|
||||
# Helper function to check and add component
|
||||
macro(check_managed_component COMPONENT_NAME VENDOR PACKAGE DEFINE_NAME)
|
||||
set(COMP_PATHS "")
|
||||
set(COMP_BASE "${CMAKE_SOURCE_DIR}/managed_components/${VENDOR}__${PACKAGE}")
|
||||
|
||||
if("${PACKAGE}" STREQUAL "esp-dsp")
|
||||
list(APPEND COMP_PATHS
|
||||
"${COMP_BASE}/modules/common/include"
|
||||
"${COMP_BASE}/modules/dotprod/include"
|
||||
"${COMP_BASE}/modules/support/include"
|
||||
"${COMP_BASE}/modules/support/mem/include"
|
||||
"${COMP_BASE}/modules/windows/include"
|
||||
"${COMP_BASE}/modules/windows/hann/include"
|
||||
"${COMP_BASE}/modules/windows/blackman/include"
|
||||
"${COMP_BASE}/modules/windows/blackman_harris/include"
|
||||
"${COMP_BASE}/modules/windows/blackman_nuttall/include"
|
||||
"${COMP_BASE}/modules/windows/nuttall/include"
|
||||
"${COMP_BASE}/modules/windows/flat_top/include"
|
||||
"${COMP_BASE}/modules/iir/include"
|
||||
"${COMP_BASE}/modules/fir/include"
|
||||
"${COMP_BASE}/modules/math/include"
|
||||
"${COMP_BASE}/modules/math/add/include"
|
||||
"${COMP_BASE}/modules/math/sub/include"
|
||||
"${COMP_BASE}/modules/math/mul/include"
|
||||
"${COMP_BASE}/modules/math/addc/include"
|
||||
"${COMP_BASE}/modules/math/mulc/include"
|
||||
"${COMP_BASE}/modules/math/sqrt/include"
|
||||
"${COMP_BASE}/modules/matrix/include"
|
||||
"${COMP_BASE}/modules/matrix/mul/include"
|
||||
"${COMP_BASE}/modules/matrix/add/include"
|
||||
"${COMP_BASE}/modules/matrix/addc/include"
|
||||
"${COMP_BASE}/modules/matrix/mulc/include"
|
||||
"${COMP_BASE}/modules/matrix/sub/include"
|
||||
"${COMP_BASE}/modules/fft/include"
|
||||
"${COMP_BASE}/modules/dct/include"
|
||||
"${COMP_BASE}/modules/conv/include"
|
||||
"${COMP_BASE}/modules/kalman/ekf/include"
|
||||
"${COMP_BASE}/modules/kalman/ekf_imu13states/include"
|
||||
)
|
||||
elseif("${PACKAGE}" STREQUAL "esp_wifi_remote")
|
||||
# Add base include if exists
|
||||
set(BASE_INCLUDE "${COMP_BASE}/include")
|
||||
if(EXISTS "${BASE_INCLUDE}")
|
||||
list(APPEND COMP_PATHS "${BASE_INCLUDE}")
|
||||
endif()
|
||||
|
||||
# Add version-specific include using MAJOR.MINOR
|
||||
set(VERSION_SUBDIR "idf_v${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}")
|
||||
set(VERSION_PATH "${COMP_BASE}/${VERSION_SUBDIR}/include")
|
||||
if(EXISTS "${VERSION_PATH}")
|
||||
list(APPEND COMP_PATHS "${VERSION_PATH}")
|
||||
endif()
|
||||
elseif("${PACKAGE}" STREQUAL "esp_hosted")
|
||||
# Add host and api include paths for esp_hosted
|
||||
set(HOST_PATH "${COMP_BASE}/host")
|
||||
if(EXISTS "${HOST_PATH}")
|
||||
list(APPEND COMP_PATHS "${HOST_PATH}")
|
||||
endif()
|
||||
set(API_PATH "${HOST_PATH}/api/include")
|
||||
if(EXISTS "${API_PATH}")
|
||||
list(APPEND COMP_PATHS "${API_PATH}")
|
||||
endif()
|
||||
elseif("${PACKAGE}" STREQUAL "esp_matter")
|
||||
# esp_matter is a C++ (CHIP SDK) library.
|
||||
# matter_stubs.h provides the C interface for translate-c instead.
|
||||
list(APPEND COMP_PATHS "${COMP_BASE}/components/esp_matter")
|
||||
else()
|
||||
list(APPEND COMP_PATHS "${COMP_BASE}/include")
|
||||
endif()
|
||||
|
||||
# Check if component is in build
|
||||
if("${VENDOR}__${PACKAGE}" IN_LIST BUILD_COMPS OR "${PACKAGE}" IN_LIST BUILD_COMPS)
|
||||
# Verify at least one path exists (check the base directory)
|
||||
if(EXISTS "${COMP_BASE}")
|
||||
message(STATUS "${COMPONENT_NAME} component found")
|
||||
set(${DEFINE_NAME} 1)
|
||||
|
||||
# Add all include paths that actually exist
|
||||
set(VALID_PATHS 0)
|
||||
foreach(COMP_PATH IN LISTS COMP_PATHS)
|
||||
if(EXISTS "${COMP_PATH}")
|
||||
list(APPEND INCLUDE_DIRS "${COMP_PATH}")
|
||||
math(EXPR VALID_PATHS "${VALID_PATHS} + 1")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(VALID_PATHS GREATER 0)
|
||||
message(STATUS " Added ${VALID_PATHS} include paths for ${COMPONENT_NAME}")
|
||||
else()
|
||||
message(WARNING "${COMPONENT_NAME} base exists but no include paths found")
|
||||
set(${DEFINE_NAME} 0)
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "${COMPONENT_NAME} in components but not found: ${COMP_BASE}")
|
||||
set(${DEFINE_NAME} 0)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "${COMPONENT_NAME} not in build. To add: idf.py add-dependency ${VENDOR}/${PACKAGE}")
|
||||
set(${DEFINE_NAME} 0)
|
||||
endif()
|
||||
|
||||
list(APPEND EXTRA_DEFINE_FLAGS "-D${DEFINE_NAME}=${${DEFINE_NAME}}")
|
||||
endmacro()
|
||||
|
||||
# Add your components here
|
||||
check_unmanaged_component("LED Strip" "espressif" "led_strip" "HAS_LED_STRIP")
|
||||
check_managed_component("ESP-DSP" "espressif" "esp-dsp" "HAS_ESP_DSP")
|
||||
check_managed_component("ESP Wifi Remote" "espressif" "esp_wifi_remote" "HAS_ESP_WIFI_REMOTE")
|
||||
check_managed_component("ESP Hosted" "espressif" "esp_hosted" "HAS_ESP_HOSTED")
|
||||
check_managed_component("ESP Matter" "espressif" "esp_matter" "HAS_ESP_MATTER")
|
||||
162
software/zig_main/cmake/patch.cmake
Normal file
162
software/zig_main/cmake/patch.cmake
Normal file
@@ -0,0 +1,162 @@
|
||||
# ============================================================================
|
||||
# Zig Bindings Patcher
|
||||
# Removes and replaces problematic structs/functions from ESP-IDF bindings
|
||||
# ============================================================================
|
||||
|
||||
message(STATUS "Patching Zig bindings: ${TARGET_FILE}")
|
||||
|
||||
# ============================================================================
|
||||
# Read file content
|
||||
# ============================================================================
|
||||
file(READ "${TARGET_FILE}" FILE_CONTENT)
|
||||
|
||||
# ============================================================================
|
||||
# Determine WiFi support based on target
|
||||
# ============================================================================
|
||||
if(CONFIG_IDF_TARGET_ESP32P4 OR CONFIG_IDF_TARGET_ESP32H2 OR CONFIG_IDF_TARGET_ESP32H21 OR CONFIG_IDF_TARGET_ESP32H4)
|
||||
set(WIFI_SUPPORTED FALSE)
|
||||
else()
|
||||
set(WIFI_SUPPORTED TRUE)
|
||||
endif()
|
||||
|
||||
# ============================================================================
|
||||
# Component status (passed from zig-config.cmake)
|
||||
# ============================================================================
|
||||
if(NOT DEFINED HAS_LED_STRIP)
|
||||
set(HAS_LED_STRIP 0)
|
||||
endif()
|
||||
if(NOT DEFINED HAS_ESP_DSP)
|
||||
set(HAS_ESP_DSP 0)
|
||||
endif()
|
||||
|
||||
# message(STATUS "Component detection:")
|
||||
# message(STATUS " HAS_LED_STRIP: ${HAS_LED_STRIP}")
|
||||
# message(STATUS " HAS_ESP_DSP: ${HAS_ESP_DSP}")
|
||||
|
||||
# ============================================================================
|
||||
# Remove problematic definitions
|
||||
# ============================================================================
|
||||
|
||||
# WiFi patches (only for WiFi-enabled targets)
|
||||
if(WIFI_SUPPORTED)
|
||||
string(REGEX REPLACE "pub const wifi_sta_config_t[^;]*;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
string(REGEX REPLACE "pub const wifi_ap_config_t[^;]*;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
endif()
|
||||
|
||||
# Remove portTICK_PERIOD_MS (will be replaced with custom version)
|
||||
string(REGEX REPLACE "pub const portTICK_PERIOD_MS[^;]*;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
|
||||
# ============================================================================
|
||||
# Handle bitfield-related opaque types and unions
|
||||
# ============================================================================
|
||||
string(REGEX REPLACE "(//[^\n]*\n?)*const struct_unnamed_[0-9]+ = opaque {};( //[^\n]*\n?)*" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
string(REGEX REPLACE "(//[^\n]*\n?)*const union_unnamed_[0-9]+ = extern union \\{\n unnamed_0: struct_unnamed_[0-9]+,\n val: u32,\n\\};( //[^\n]*\n?)*" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
string(REGEX REPLACE "unnamed_0: struct_unnamed_[0-9]+,\n?" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
string(REGEX REPLACE "([a-zA-Z0-9_]+): ((\[[0-9]+\])?)(struct|union)_unnamed_[0-9]+" "\\1: \\2u32" FILE_CONTENT "${FILE_CONTENT}")
|
||||
string(REGEX REPLACE "zeroes\\((struct|union)_unnamed_[0-9]+\\)" "zeroes(u32)" FILE_CONTENT "${FILE_CONTENT}")
|
||||
string(REGEX REPLACE "zeroes\\((\\[[0-9]+\\])(struct|union)_unnamed_[0-9]+\\)" "zeroes(\\1u32)" FILE_CONTENT "${FILE_CONTENT}")
|
||||
|
||||
# ============================================================================
|
||||
# Fix peripheral register structs (lp_*, gpio_dev_t, i2c_dev_t, etc.)
|
||||
# ============================================================================
|
||||
# 1. Aggressively remove ALL gpio_dev_t and lp_* declarations (including opaque and aliases)
|
||||
string(REGEX REPLACE "pub const (struct_)?gpio_dev_t = [^;]+;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
string(REGEX REPLACE "pub const (struct_)?lp_[a-z0-9_]*_dev_t = [^;]+;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
string(REGEX REPLACE "pub const (struct_)?i2c_dev_t = [^;]+;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
# 2. Also remove any remaining aliases that point to struct_ versions
|
||||
string(REGEX REPLACE "pub const (lp_[a-z0-9_]+_dev_t|gpio_dev_t|i2c_dev_t) = struct_[^;]+;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
# 3. Append clean definitions with correct sizes
|
||||
string(APPEND FILE_CONTENT "
|
||||
pub const lp_io_dev_t = extern struct { reserved: [1024]u8 };
|
||||
pub const lp_clkrst_dev_t = extern struct { reserved: [1024]u8 };
|
||||
pub const lp_i2c_dev_t = extern struct { reserved: [0x184]u8 };
|
||||
pub const lp_timer_dev_t = extern struct { reserved: [1024]u8 };
|
||||
pub const lp_uart_dev_t = extern struct { reserved: [0xa0]u8 };
|
||||
pub const lp_wdt_dev_t = extern struct { reserved: [1024]u8 };
|
||||
pub const lp_io_mux_dev_t = extern struct { reserved: [512]u8 };
|
||||
pub const lp_iomux_dev_t = extern struct { reserved: [84]u8 };
|
||||
pub const lp_aonclkrst_dev_t = extern struct { reserved: [1024]u8 };
|
||||
pub const i2c_dev_t = extern struct { reserved: [388]u8 };
|
||||
")
|
||||
# 4. Add gpio_dev_t with target-specific size (this must come AFTER the general remove)
|
||||
if(CONFIG_IDF_TARGET_ESP32C61 OR CONFIG_IDF_TARGET_ESP32C5 OR CONFIG_IDF_TARGET_ESP32H4)
|
||||
string(APPEND FILE_CONTENT "\npub const gpio_dev_t = extern struct { reserved: [3584]u8 };")
|
||||
elseif(CONFIG_IDF_TARGET_ESP32P4)
|
||||
string(APPEND FILE_CONTENT "\npub const gpio_dev_t = extern struct { reserved: [2048]u8 };")
|
||||
elseif(CONFIG_IDF_TARGET_ESP32C2 OR CONFIG_IDF_TARGET_ESP32C6 OR CONFIG_IDF_TARGET_ESP32H2)
|
||||
string(APPEND FILE_CONTENT "\npub const gpio_dev_t = extern struct { reserved: [1792]u8 };")
|
||||
else()
|
||||
# Fallback
|
||||
endif()
|
||||
if(CONFIG_IDF_TARGET_ESP32P4)
|
||||
string(APPEND FILE_CONTENT "\npub const lp_gpio_dev_t = extern struct { reserved: [308]u8 };")
|
||||
else()
|
||||
string(APPEND FILE_CONTENT "\npub const lp_gpio_dev_t = extern struct { reserved: [1024]u8 };")
|
||||
endif()
|
||||
|
||||
# ESP32-P4 specific: Remove xPortCanYield function
|
||||
if(CONFIG_IDF_TARGET_ESP32P4)
|
||||
string(REGEX REPLACE "pub fn xPortCanYield\\([^)]*\\) callconv\\(\\.c\\) bool \\{([^{}]|\\{[^{}]*\\})*\\}" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
endif()
|
||||
|
||||
# LED Strip component patches (if enabled)
|
||||
if(HAS_LED_STRIP EQUAL 1)
|
||||
message(STATUS " Applying LED Strip patches")
|
||||
string(REGEX REPLACE "pub const struct_led_strip_rmt_extra_config_20[^;]*;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
string(REGEX REPLACE "pub const struct_format_layout_15[^;]*;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
string(REGEX REPLACE "pub const led_strip_rmt_config_t[^;]*;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
string(REGEX REPLACE "pub const led_color_component_format_t[^;]*;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
string(REGEX REPLACE "pub const led_strip_config_t = extern struct \\{[^}]*\\};" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||
endif()
|
||||
|
||||
# ============================================================================
|
||||
# Append custom patch files
|
||||
# ============================================================================
|
||||
set(PATCH_DIR "${CMAKE_SOURCE_DIR}/../../../patches")
|
||||
|
||||
# Define patches to apply
|
||||
set(PATCH_FILES
|
||||
"porttick_period_ms.zig"
|
||||
)
|
||||
|
||||
# Add target-specific patches
|
||||
if(CONFIG_IDF_TARGET_ESP32P4)
|
||||
list(APPEND PATCH_FILES "xport_can_yield.zig")
|
||||
endif()
|
||||
|
||||
# Add WiFi patches
|
||||
if(WIFI_SUPPORTED)
|
||||
list(APPEND PATCH_FILES
|
||||
"wifi/wifi_sta_config_t.zig"
|
||||
"wifi/wifi_ap_config_t.zig"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Add LED Strip patches
|
||||
if(HAS_LED_STRIP EQUAL 1)
|
||||
list(APPEND PATCH_FILES
|
||||
"led_strip/led_strip_rmt_extra_config_20.zig"
|
||||
"led_strip/led_strip_struct_format_layout_15.zig"
|
||||
"led_strip/led_color_component_format_t.zig"
|
||||
"led_strip/led_strip_rmt_config_t.zig"
|
||||
"led_strip/led_strip_config_t.zig"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Apply each patch file
|
||||
foreach(PATCH_FILE IN LISTS PATCH_FILES)
|
||||
set(PATCH_PATH "${PATCH_DIR}/${PATCH_FILE}")
|
||||
if(EXISTS "${PATCH_PATH}")
|
||||
message(STATUS " Applying patch: ${PATCH_FILE}")
|
||||
file(READ "${PATCH_PATH}" PATCH_CONTENT)
|
||||
string(APPEND FILE_CONTENT "\n${PATCH_CONTENT}")
|
||||
else()
|
||||
message(WARNING " Patch file not found: ${PATCH_FILE}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# ============================================================================
|
||||
# Write patched content
|
||||
# ============================================================================
|
||||
file(WRITE "${TARGET_FILE}" "${FILE_CONTENT}")
|
||||
message(STATUS "Patching completed: ${TARGET_FILE}")
|
||||
488
software/zig_main/cmake/zig-config.cmake
Normal file
488
software/zig_main/cmake/zig-config.cmake
Normal file
@@ -0,0 +1,488 @@
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Zig configuration for ESP-IDF (esp32 xtensa/riscv targets)
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
set(ZIG_MIN_VERSION "0.16.0")
|
||||
|
||||
# ─── Host platform & architecture detection ──────────────────────────────────
|
||||
cmake_host_system_information(RESULT HOST_OS QUERY OS_NAME)
|
||||
string(TOLOWER "${HOST_OS}" HOST_OS_LOWER)
|
||||
|
||||
set(HOST_ARCH "${CMAKE_HOST_SYSTEM_PROCESSOR}")
|
||||
|
||||
# Normalize to Zig-style triple names
|
||||
if(HOST_ARCH MATCHES "^(AMD64|amd64|x86_64|X64)$")
|
||||
set(ZIG_ARCH "x86_64")
|
||||
elseif(HOST_ARCH MATCHES "^(aarch64|arm64|ARM64)$")
|
||||
set(ZIG_ARCH "aarch64")
|
||||
elseif(HOST_ARCH MATCHES "^(x86|i686|i386|i[345]86)$")
|
||||
set(ZIG_ARCH "x86")
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported host architecture '${HOST_ARCH}'.\n"
|
||||
"Please report:\n"
|
||||
" CMAKE_HOST_SYSTEM_PROCESSOR = ${CMAKE_HOST_SYSTEM_PROCESSOR}\n"
|
||||
" cmake --system-information | findstr PROCESSOR")
|
||||
endif()
|
||||
|
||||
# Platform + extension
|
||||
if(HOST_OS_LOWER MATCHES "(linux|unix)")
|
||||
set(ZIG_PLATFORM "linux-musl")
|
||||
set(ARCHIVE_EXT "tar.xz")
|
||||
elseif(HOST_OS_LOWER MATCHES "windows|win")
|
||||
set(ZIG_PLATFORM "windows")
|
||||
set(ARCHIVE_EXT "zip")
|
||||
elseif(HOST_OS_LOWER MATCHES "darwin|mac|osx")
|
||||
set(ZIG_PLATFORM "macos")
|
||||
set(ARCHIVE_EXT "tar.xz")
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported host OS: ${HOST_OS}")
|
||||
endif()
|
||||
|
||||
set(ZIG_TRIPLET "${ZIG_ARCH}-${ZIG_PLATFORM}-baseline")
|
||||
set(ZIG_DIR "${CMAKE_BINARY_DIR}/zig-relsafe-${ZIG_TRIPLET}")
|
||||
set(ZIG_ARCHIVE "${ZIG_DIR}.${ARCHIVE_EXT}")
|
||||
|
||||
# ─── Decide which zig to use ─────────────────────────────────────────────────
|
||||
find_program(ZIG_FOUND zig)
|
||||
|
||||
set(USE_ZIG_ESPRESSIF_BOOTSTRAP TRUE)
|
||||
if(ZIG_FOUND AND CONFIG_IDF_TARGET_ARCH_RISCV)
|
||||
# For most RISC-V prefer system zig (except P4 & H4)
|
||||
if(NOT (CONFIG_IDF_TARGET_ESP32P4 OR CONFIG_IDF_TARGET_ESP32H4))
|
||||
set(USE_ZIG_ESPRESSIF_BOOTSTRAP FALSE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ─── Download & extract espressif zig if needed ──────────────────────────────
|
||||
if(USE_ZIG_ESPRESSIF_BOOTSTRAP)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/zig-download.cmake)
|
||||
else()
|
||||
if(NOT ZIG_FOUND)
|
||||
message(FATAL_ERROR "System 'zig' not found and espressif bootstrap is disabled.")
|
||||
endif()
|
||||
set(ZIG_BIN "${ZIG_FOUND}")
|
||||
endif()
|
||||
message(STATUS "Using Zig executable: ${ZIG_BIN}")
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────────────
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/zig-runner.cmake)
|
||||
# ─────── get Zig version ────────────────────────────────────────────────────────────
|
||||
zig_run(COMMAND version OUTPUT_VARIABLE ZIG_VERSION)
|
||||
if("${ZIG_VERSION}" VERSION_LESS "${ZIG_MIN_VERSION}")
|
||||
message(FATAL_ERROR "Zig version too old: ${ZIG_VERSION} < ${ZIG_MIN_VERSION}")
|
||||
endif()
|
||||
message(STATUS "Zig version: ${ZIG_VERSION}")
|
||||
|
||||
# Detect whether this Zig build is the Espressif fork (has named ESP32 RISC-V CPU models).
|
||||
# Upstream Zig only ships generic RISC-V models; the fork adds esp32c3, esp32c6, etc.
|
||||
zig_run(
|
||||
COMMAND build-obj --show-builtin -target riscv32-freestanding -mcpu=esp32c3
|
||||
RESULT_VARIABLE _zig_esp_riscv_probe
|
||||
OUTPUT_VARIABLE _zig_esp_riscv_probe_out
|
||||
ERROR_VARIABLE _zig_esp_riscv_probe_err
|
||||
ALLOW_FAIL
|
||||
)
|
||||
if(_zig_esp_riscv_probe EQUAL 0)
|
||||
set(ZIG_HAS_ESP_RISCV_MODELS ON)
|
||||
message(STATUS "Zig RISC-V: Espressif fork detected — using named ESP CPU models")
|
||||
else()
|
||||
set(ZIG_HAS_ESP_RISCV_MODELS OFF)
|
||||
message(STATUS "Zig RISC-V: upstream Zig detected — falling back to generic_rv32 models")
|
||||
endif()
|
||||
|
||||
# ====================================================================================
|
||||
|
||||
# Determine target model from CONFIG_IDF_TARGET
|
||||
string(TOLOWER "${CONFIG_IDF_TARGET}" TARGET_IDF_MODEL)
|
||||
|
||||
# Target architecture configuration lookup table
|
||||
set(RISCV_TARGETS
|
||||
"esp32c2" "esp32c3" "esp32c5" "esp32c6" "esp32c61" "esp32c61eco0"
|
||||
"esp32h2" "esp32h21" "esp32h4" "esp32p4" "esp32p4eco4" "esp32s31")
|
||||
set(XTENSA_TARGETS
|
||||
"esp32" "esp32s2" "esp32s3")
|
||||
|
||||
if(TARGET_IDF_MODEL IN_LIST RISCV_TARGETS)
|
||||
set(TARGET_IDF_ARCH "riscv")
|
||||
|
||||
# MACF group (FPU): use eabihf ABI. All others use none.
|
||||
set(_rv_macf_targets "esp32h4" "esp32s31" "esp32p4" "esp32p4eco4")
|
||||
if(TARGET_IDF_MODEL IN_LIST _rv_macf_targets)
|
||||
set(ZIG_TARGET "riscv32-freestanding-eabihf")
|
||||
else()
|
||||
set(ZIG_TARGET "riscv32-freestanding-none")
|
||||
endif()
|
||||
|
||||
if(ZIG_HAS_ESP_RISCV_MODELS)
|
||||
# Espressif Zig fork: use named CPU model (feature set is already encoded in model).
|
||||
set(TARGET_CPU_MODEL "${TARGET_IDF_MODEL}")
|
||||
else()
|
||||
# Upstream Zig: fall back to generic_rv32 with explicit feature sets.
|
||||
# MC (c2, c3): m+c+zicsr+zifencei abi=none
|
||||
# MAC (c5, c6, c61, h2, h21): m+a+c+zicsr+zifencei abi=none
|
||||
# MACF (h4, s31, p4, p4eco4): m+a+c+f+zicsr+zifencei abi=eabihf
|
||||
set(_rv_mc_targets "esp32c2" "esp32c3")
|
||||
set(_rv_mac_targets "esp32c5" "esp32c6" "esp32c61" "esp32c61eco0" "esp32h2" "esp32h21")
|
||||
if(TARGET_IDF_MODEL IN_LIST _rv_mc_targets)
|
||||
set(TARGET_CPU_MODEL "generic_rv32+m+c+zicsr+zifencei")
|
||||
elseif(TARGET_IDF_MODEL IN_LIST _rv_mac_targets)
|
||||
set(TARGET_CPU_MODEL "generic_rv32+m+a+c+zicsr+zifencei")
|
||||
elseif(TARGET_IDF_MODEL IN_LIST _rv_macf_targets)
|
||||
set(TARGET_CPU_MODEL "generic_rv32+m+a+c+f+zicsr+zifencei")
|
||||
else()
|
||||
set(TARGET_CPU_MODEL "generic_rv32+m+c+zicsr+zifencei")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
elseif(TARGET_IDF_MODEL IN_LIST XTENSA_TARGETS)
|
||||
set(TARGET_IDF_ARCH "xtensa")
|
||||
set(ZIG_TARGET "xtensa-freestanding-none")
|
||||
set(TARGET_CPU_MODEL "${TARGET_IDF_MODEL}")
|
||||
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported IDF target: ${CONFIG_IDF_TARGET}")
|
||||
endif()
|
||||
|
||||
message(STATUS "ESP-IDF Target: ${TARGET_IDF_MODEL}")
|
||||
message(STATUS "Architecture: ${TARGET_IDF_ARCH}")
|
||||
message(STATUS "Zig Target: ${ZIG_TARGET}")
|
||||
message(STATUS "CPU Model: ${TARGET_CPU_MODEL}")
|
||||
|
||||
# Check Toolchain version
|
||||
get_filename_component(TOOLCHAIN_BIN_DIR "${CMAKE_C_COMPILER}" DIRECTORY)
|
||||
get_filename_component(TOOLCHAIN_VERSION_DIR "${TOOLCHAIN_BIN_DIR}" DIRECTORY)
|
||||
if("${TOOLCHAIN_VERSION_DIR}" MATCHES "esp-([0-9]+\\.[0-9]+\\.[0-9]+_[0-9]+)")
|
||||
set(TOOLCHAIN_VERSION "${CMAKE_MATCH_1}")
|
||||
message(STATUS "Detected ESP toolchain version: ${TOOLCHAIN_VERSION}")
|
||||
else()
|
||||
message(WARNING "Standard ESP version pattern not found in: ${TOOLCHAIN_VERSION_DIR}")
|
||||
endif()
|
||||
|
||||
if(CONFIG_IDF_TARGET_ARCH_RISCV)
|
||||
set(ARCH "riscv")
|
||||
set(TRIPLE "riscv32-esp-elf")
|
||||
set(ARCH_DEFINE "__riscv")
|
||||
elseif(CONFIG_IDF_TARGET_ARCH_XTENSA)
|
||||
set(ARCH "xtensa")
|
||||
set(TRIPLE "xtensa-esp-elf")
|
||||
set(ARCH_DEFINE "__XTENSA__")
|
||||
endif()
|
||||
# Get toolchain includes with fallback paths
|
||||
set(POSSIBLE_INCLUDE_PATHS
|
||||
"${TOOLCHAIN_VERSION_DIR}/${TRIPLE}/include"
|
||||
"${TOOLCHAIN_BIN_DIR}/../${TRIPLE}/include"
|
||||
"${TOOLCHAIN_VERSION_DIR}/${TRIPLE}/${TRIPLE}/include"
|
||||
)
|
||||
# Find the toolchain include directory
|
||||
set(TOOLCHAIN_ELF_INCLUDE "")
|
||||
foreach(PATH ${POSSIBLE_INCLUDE_PATHS})
|
||||
if(IS_DIRECTORY "${PATH}")
|
||||
set(TOOLCHAIN_ELF_INCLUDE "${PATH}")
|
||||
message(STATUS "Found toolchain include at: ${TOOLCHAIN_ELF_INCLUDE}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
# sys-include should be sys-include directory OR the same as regular include
|
||||
# (since sys headers are typically under include/sys/)
|
||||
set(POSSIBLE_SYS_INCLUDE_PATHS
|
||||
"${TOOLCHAIN_VERSION_DIR}/${TRIPLE}/sys-include"
|
||||
"${TOOLCHAIN_BIN_DIR}/../${TRIPLE}/sys-include"
|
||||
"${TOOLCHAIN_VERSION_DIR}/${TRIPLE}/${TRIPLE}/sys-include"
|
||||
"${TOOLCHAIN_VERSION_DIR}/${TRIPLE}/${TRIPLE}/include"
|
||||
"${TOOLCHAIN_ELF_INCLUDE}"
|
||||
)
|
||||
# Find the first existing sys-include directory
|
||||
set(TOOLCHAIN_SYS_INCLUDE "")
|
||||
foreach(PATH ${POSSIBLE_SYS_INCLUDE_PATHS})
|
||||
if(IS_DIRECTORY "${PATH}")
|
||||
set(TOOLCHAIN_SYS_INCLUDE "${PATH}")
|
||||
message(STATUS "Found sys-include at: ${TOOLCHAIN_SYS_INCLUDE}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
if(NOT IS_DIRECTORY "${TOOLCHAIN_ELF_INCLUDE}")
|
||||
message(WARNING "Toolchain include directory not found: ${TOOLCHAIN_ELF_INCLUDE}")
|
||||
endif()
|
||||
if(NOT IS_DIRECTORY "${TOOLCHAIN_SYS_INCLUDE}")
|
||||
message(WARNING "Toolchain sys-include directory not found: ${TOOLCHAIN_SYS_INCLUDE}")
|
||||
endif()
|
||||
|
||||
# components list
|
||||
set(INCLUDE_DIRS
|
||||
# Wrapper dir MUST come first — redirects "freertos/portmacro.h" through a
|
||||
# single canonical path to fix Windows mixed-separator #pragma once failure.
|
||||
# See include/freertos-compat/freertos/portmacro.h for details (issue #50).
|
||||
"${CMAKE_SOURCE_DIR}/include/freertos-compat"
|
||||
"${IDF_PATH}/components/freertos/FreeRTOS-Kernel-SMP/portable/${ARCH}/include/freertos"
|
||||
"${IDF_PATH}/components/freertos/FreeRTOS-Kernel/include"
|
||||
"${IDF_PATH}/components/freertos/config/include"
|
||||
"${IDF_PATH}/components/freertos/config/include/freertos"
|
||||
"${IDF_PATH}/components/freertos/esp_additions/include"
|
||||
"${IDF_PATH}/components/freertos/config/${ARCH}/include"
|
||||
"${IDF_PATH}/components/esp_hw_support/include"
|
||||
"${IDF_PATH}/components/soc/include"
|
||||
"${IDF_PATH}/components/soc/${TARGET_IDF_MODEL}/include"
|
||||
"${IDF_PATH}/components/esp_common/include"
|
||||
"${IDF_PATH}/components/hal/include"
|
||||
"${IDF_PATH}/components/bootloader_support/include"
|
||||
"${IDF_PATH}/components/${ARCH}/include"
|
||||
"${IDF_PATH}/components/bt/include/${TARGET_IDF_MODEL}/include"
|
||||
"${IDF_PATH}/components/bt/host/nimble/nimble/nimble/include"
|
||||
"${IDF_PATH}/components/bt/host/bluedroid/api/include"
|
||||
"${IDF_PATH}/components/bt/host/bluedroid/api/include/api"
|
||||
"${IDF_PATH}/components/${ARCH}/${TARGET_IDF_MODEL}/include"
|
||||
"${IDF_PATH}/components/${ARCH}/${TARGET_IDF_MODEL}/include/${ARCH}"
|
||||
"${IDF_PATH}/components/${ARCH}/${TARGET_IDF_MODEL}/include/${ARCH}/config"
|
||||
"${IDF_PATH}/components/${ARCH}/${TARGET_IDF_MODEL}/include/${ARCH}/core"
|
||||
"${IDF_PATH}/components/soc/${TARGET_IDF_MODEL}/register"
|
||||
"${IDF_PATH}/components/esp_system/include"
|
||||
"${IDF_PATH}/components/esp_hw_support/etm/include"
|
||||
"${IDF_PATH}/components/esp_hw_support/ldo/include"
|
||||
"${IDF_PATH}/components/esp_hal_ana_cmpr/include"
|
||||
"${IDF_PATH}/components/esp_hal_ana_conv/include"
|
||||
"${IDF_PATH}/components/esp_hal_cam/include"
|
||||
"${IDF_PATH}/components/esp_hal_dma/include"
|
||||
"${IDF_PATH}/components/esp_hal_gpio/include"
|
||||
"${IDF_PATH}/components/esp_hal_gpio/${TARGET_IDF_MODEL}/include"
|
||||
"${IDF_PATH}/components/esp_hal_gpspi/include"
|
||||
"${IDF_PATH}/components/esp_hal_i2c/include"
|
||||
"${IDF_PATH}/components/esp_hal_i2s/include"
|
||||
"${IDF_PATH}/components/esp_hal_lcd/include"
|
||||
"${IDF_PATH}/components/esp_hal_ledc/include"
|
||||
"${IDF_PATH}/components/esp_hal_mcpwm/include"
|
||||
"${IDF_PATH}/components/esp_hal_mspi/include"
|
||||
"${IDF_PATH}/components/esp_hal_parlio/include"
|
||||
"${IDF_PATH}/components/esp_hal_pcnt/include"
|
||||
"${IDF_PATH}/components/esp_hal_ppa/include"
|
||||
"${IDF_PATH}/components/esp_hal_rmt/include"
|
||||
"${IDF_PATH}/components/esp_hal_timg/include"
|
||||
"${IDF_PATH}/components/esp_hal_touch_sens/include"
|
||||
"${IDF_PATH}/components/esp_hal_twai/include"
|
||||
"${IDF_PATH}/components/esp_hal_uart/include"
|
||||
"${IDF_PATH}/components/esp_hal_usb/include"
|
||||
"${IDF_PATH}/components/esp_hal_wdt/include"
|
||||
"${IDF_PATH}/components/esp_driver_ana_cmpr/include"
|
||||
"${IDF_PATH}/components/esp_driver_bitscrambler/include"
|
||||
"${IDF_PATH}/components/esp_driver_cam/include"
|
||||
"${IDF_PATH}/components/esp_driver_dac/include"
|
||||
"${IDF_PATH}/components/esp_driver_gpio/include"
|
||||
"${IDF_PATH}/components/esp_driver_gptimer/include"
|
||||
"${IDF_PATH}/components/esp_driver_i2c/include"
|
||||
"${IDF_PATH}/components/esp_driver_i2s/include"
|
||||
"${IDF_PATH}/components/esp_driver_i3c/include"
|
||||
"${IDF_PATH}/components/esp_driver_isp/include"
|
||||
"${IDF_PATH}/components/esp_driver_jpeg/include"
|
||||
"${IDF_PATH}/components/esp_driver_ledc/include"
|
||||
"${IDF_PATH}/components/esp_driver_mcpwm/include"
|
||||
"${IDF_PATH}/components/esp_driver_parlio/include"
|
||||
"${IDF_PATH}/components/esp_driver_pcnt/include"
|
||||
"${IDF_PATH}/components/esp_driver_ppa/include"
|
||||
"${IDF_PATH}/components/esp_driver_rmt/include"
|
||||
"${IDF_PATH}/components/esp_driver_sd_intf/include"
|
||||
"${IDF_PATH}/components/esp_driver_sdio/include"
|
||||
"${IDF_PATH}/components/esp_driver_sdm/include"
|
||||
"${IDF_PATH}/components/esp_driver_sdmmc/include"
|
||||
"${IDF_PATH}/components/esp_driver_sdspi/include"
|
||||
"${IDF_PATH}/components/esp_driver_spi/include"
|
||||
"${IDF_PATH}/components/esp_driver_touch_sens/include"
|
||||
"${IDF_PATH}/components/esp_driver_tsens/include"
|
||||
"${IDF_PATH}/components/esp_driver_twai/include"
|
||||
"${IDF_PATH}/components/esp_driver_uart/include"
|
||||
"${IDF_PATH}/components/esp_driver_usb_serial_jtag/include"
|
||||
"${IDF_PATH}/components/esp_phy/include"
|
||||
"${IDF_PATH}/components/esp_tee/include"
|
||||
"${IDF_PATH}/components/esp_timer/include"
|
||||
"${IDF_PATH}/components/esp_coex/include"
|
||||
"${IDF_PATH}/components/esp_psram/include"
|
||||
"${IDF_PATH}/components/esp_security/include"
|
||||
"${IDF_PATH}/components/esp_trace/include"
|
||||
"${IDF_PATH}/components/esp_blockdev/include"
|
||||
"${IDF_PATH}/components/pthread/include"
|
||||
"${IDF_PATH}/components/hal/platform_port/include"
|
||||
"${IDF_PATH}/components/heap/include"
|
||||
"${IDF_PATH}/components/ieee802154/include"
|
||||
"${IDF_PATH}/components/openthread/include"
|
||||
"${IDF_PATH}/components/openthread/openthread/third_party/mbedtls/repo/include"
|
||||
"${IDF_PATH}/components/esp_rom/${TARGET_IDF_MODEL}/include/${TARGET_IDF_MODEL}"
|
||||
"${IDF_PATH}/components/esp_rom/include"
|
||||
"${IDF_PATH}/components/esp_wifi/include"
|
||||
"${IDF_PATH}/components/esp_bootloader_format/include"
|
||||
"${IDF_PATH}/components/esp_app_format/include"
|
||||
"${IDF_PATH}/components/esp_pm/include"
|
||||
"${IDF_PATH}/components/esp_lcd/include"
|
||||
"${IDF_PATH}/components/esp_lcd/interface"
|
||||
"${IDF_PATH}/components/esp_lcd/dsi/include"
|
||||
"${IDF_PATH}/components/esp_lcd/rgb/include"
|
||||
"${IDF_PATH}/components/mbedtls/mbedtls/tf-psa-crypto/drivers/builtin/include"
|
||||
"${IDF_PATH}/components/mbedtls/mbedtls/tf-psa-crypto/include"
|
||||
"${IDF_PATH}/components/mbedtls/esp_crt_bundle/include"
|
||||
"${IDF_PATH}/components/mbedtls/port/include"
|
||||
"${IDF_PATH}/components/mbedtls/mbedtls/include"
|
||||
"${IDF_PATH}/components/http_parser"
|
||||
"${IDF_PATH}/components/esp-tls"
|
||||
"${IDF_PATH}/components/esp_https_ota/include"
|
||||
"${IDF_PATH}/components/esp_http_server/include"
|
||||
"${IDF_PATH}/components/esp_https_server/include"
|
||||
"${IDF_PATH}/components/esp_http_client/include"
|
||||
"${IDF_PATH}/components/log/include"
|
||||
"${IDF_PATH}/components/vfs/include"
|
||||
"${IDF_PATH}/components/wpa_supplicant/esp_supplicant/include"
|
||||
"${IDF_PATH}/components/nvs_flash/include"
|
||||
"${IDF_PATH}/components/esp_partition/include"
|
||||
"${IDF_PATH}/components/esp_netif/include"
|
||||
"${IDF_PATH}/components/esp_event/include"
|
||||
"${IDF_PATH}/components/driver/i2c/include"
|
||||
"${IDF_PATH}/components/driver/deprecated"
|
||||
"${IDF_PATH}/components/driver/touch_sensor/${TARGET_IDF_MODEL}/include"
|
||||
"${IDF_PATH}/components/driver/touch_sensor/include"
|
||||
"${IDF_PATH}/components/driver/twai/include"
|
||||
"${IDF_PATH}/components/spi_flash/include"
|
||||
"${IDF_PATH}/components/esp_vfs_console/include"
|
||||
"${IDF_PATH}/components/esp_ringbuf/include"
|
||||
"${IDF_PATH}/components/esp_usb_cdc_rom_console/include"
|
||||
"${CMAKE_SOURCE_DIR}/build/config"
|
||||
)
|
||||
# Toolchain system includes (separate from regular includes)
|
||||
set(SYSTEM_INCLUDE_DIRS
|
||||
"${TOOLCHAIN_SYS_INCLUDE}"
|
||||
"${TOOLCHAIN_ELF_INCLUDE}"
|
||||
"${IDF_PATH}/components/newlib"
|
||||
"${IDF_PATH}/components/newlib/platform_include"
|
||||
"${IDF_PATH}/components/esp_libc/platform_include"
|
||||
"${IDF_PATH}/components/lwip/lwip/src/include"
|
||||
"${IDF_PATH}/components/lwip/port/esp32xx/include"
|
||||
"${IDF_PATH}/components/lwip/port/freertos/include"
|
||||
"${IDF_PATH}/components/lwip/port/include"
|
||||
"${IDF_PATH}/components/lwip/include"
|
||||
"${IDF_PATH}/components/lwip/include/apps"
|
||||
)
|
||||
if(CONFIG_IDF_TARGET_ESP32P4)
|
||||
list(APPEND INCLUDE_DIRS "${IDF_PATH}/components/soc/${TARGET_IDF_MODEL}/register/hw_ver3")
|
||||
elseif(CONFIG_IDF_TARGET_ESP32H4)
|
||||
list(APPEND INCLUDE_DIRS "${IDF_PATH}/components/soc/${TARGET_IDF_MODEL}/register/hw_ver_mp")
|
||||
endif()
|
||||
|
||||
# get esp-idf C Macros
|
||||
idf_build_get_property(all_defines COMPILE_DEFINITIONS)
|
||||
|
||||
set(EXTRA_DEFINE_FLAGS "")
|
||||
foreach(def ${all_defines})
|
||||
string(STRIP "${def}" def_clean)
|
||||
if(NOT def_clean STREQUAL "")
|
||||
if(NOT def_clean MATCHES "^-D")
|
||||
list(APPEND EXTRA_DEFINE_FLAGS "-D${def_clean}")
|
||||
else()
|
||||
list(APPEND EXTRA_DEFINE_FLAGS "${def_clean}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
string(TOUPPER "${TARGET_IDF_ARCH}" TARGET_IDF_ARCH_UPPER)
|
||||
string(TOUPPER "${TARGET_IDF_MODEL}" TARGET_IDF_MODEL_UPPER)
|
||||
set(DEFINE_FLAGS
|
||||
"-D__${TARGET_IDF_ARCH}"
|
||||
"-Dcpu_${TARGET_CPU_MODEL}"
|
||||
"-D${ARCH_DEFINE}"
|
||||
"-D__${TARGET_IDF_ARCH_UPPER}_EL__"
|
||||
"-DCONFIG_IDF_TARGET_${TARGET_IDF_MODEL_UPPER}"
|
||||
"-D__COUNTER__=0"
|
||||
"-DIRAM_ATTR="
|
||||
"-D_SECTION_ATTR_IMPL\\(x,y\\)="
|
||||
"-DSOC_MMU_PAGE_SIZE=0x8000"
|
||||
"-DLWIP_NO_UNISTD_H="
|
||||
)
|
||||
string(JOIN " " DEFINE_FLAGS_STR ${DEFINE_FLAGS})
|
||||
|
||||
if(ARCH_DEFINE)
|
||||
set(DEFINE_FLAGS "${DEFINE_FLAGS} -D${ARCH_DEFINE}")
|
||||
endif()
|
||||
set(IDF_SYS_ZIG "${CMAKE_SOURCE_DIR}/imports/idf-sys.zig")
|
||||
set(IDF_SYS_C "${CMAKE_SOURCE_DIR}/include/stubs.h")
|
||||
|
||||
# get esp-rs bindings.h
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/bindings.cmake)
|
||||
|
||||
# add extra-components
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/extra-components.cmake)
|
||||
|
||||
set(INCLUDE_FLAGS "")
|
||||
foreach(dir ${INCLUDE_DIRS})
|
||||
# Normalize to forward slashes (prevents Windows mixed-separator double-include in translate-c)
|
||||
file(TO_CMAKE_PATH "${dir}" dir)
|
||||
set(INCLUDE_FLAGS "${INCLUDE_FLAGS} -I\"${dir}\"")
|
||||
endforeach()
|
||||
|
||||
# Build system include flags (for toolchain)
|
||||
foreach(dir ${SYSTEM_INCLUDE_DIRS})
|
||||
file(TO_CMAKE_PATH "${dir}" dir)
|
||||
set(INCLUDE_FLAGS "${INCLUDE_FLAGS} -isystem \"${dir}\"")
|
||||
endforeach()
|
||||
if(NOT WIN32)
|
||||
separate_arguments(INCLUDE_FLAGS UNIX_COMMAND "${INCLUDE_FLAGS}")
|
||||
else()
|
||||
separate_arguments(INCLUDE_FLAGS WINDOWS_COMMAND "${INCLUDE_FLAGS}")
|
||||
endif()
|
||||
|
||||
# Run `translate-c` to generate `idf-sys.zig`
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/bindgen-standalone.cmake)
|
||||
bindgen_run(
|
||||
COMMAND
|
||||
${IDF_SYS_C} -target ${ZIG_TARGET} -mcpu ${TARGET_CPU_MODEL}
|
||||
${DEFINE_FLAGS} ${EXTRA_DEFINE_FLAGS} ${INCLUDE_FLAGS}
|
||||
OUTPUT_FILE ${IDF_SYS_ZIG}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/imports
|
||||
DEPENDS ${IDF_SYS_C}
|
||||
)
|
||||
|
||||
set(PATCHES_DONE "${CMAKE_BINARY_DIR}/patches_applied.done")
|
||||
add_custom_command(
|
||||
OUTPUT "${PATCHES_DONE}"
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-D TARGET_FILE=${IDF_SYS_ZIG}
|
||||
-D CONFIG_IDF_TARGET_ESP32H2=${CONFIG_IDF_TARGET_ESP32H2}
|
||||
-D CONFIG_IDF_TARGET_ESP32H21=${CONFIG_IDF_TARGET_ESP32H21}
|
||||
-D CONFIG_IDF_TARGET_ESP32H4=${CONFIG_IDF_TARGET_ESP32H4}
|
||||
-D CONFIG_IDF_TARGET_ESP32P4=${CONFIG_IDF_TARGET_ESP32P4}
|
||||
-D CONFIG_IDF_TARGET_ESP32C2=${CONFIG_IDF_TARGET_ESP32C2}
|
||||
-D CONFIG_IDF_TARGET_ESP32C5=${CONFIG_IDF_TARGET_ESP32C5}
|
||||
-D CONFIG_IDF_TARGET_ESP32C6=${CONFIG_IDF_TARGET_ESP32C6}
|
||||
-D CONFIG_IDF_TARGET_ESP32C61=${CONFIG_IDF_TARGET_ESP32C61}
|
||||
-D HAS_LED_STRIP=${HAS_LED_STRIP}
|
||||
-D HAS_ESP_DSP=${HAS_ESP_DSP}
|
||||
-P ${CMAKE_SOURCE_DIR}/cmake/patch.cmake
|
||||
COMMAND ${CMAKE_COMMAND} -E touch "${PATCHES_DONE}"
|
||||
DEPENDS "${IDF_SYS_ZIG}"
|
||||
COMMENT "Patching idf-sys.zig"
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(translate_c ALL DEPENDS "${PATCHES_DONE}")
|
||||
message(STATUS "IDF_SYS_ZIG is set to: ${IDF_SYS_ZIG}")
|
||||
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(ZIG_BUILD_TYPE "Debug")
|
||||
else()
|
||||
set(ZIG_BUILD_TYPE "ReleaseSafe")
|
||||
endif()
|
||||
|
||||
add_custom_target(zig_build
|
||||
${ZIG_BIN} build
|
||||
--build-file ${CMAKE_SOURCE_DIR}/build.zig
|
||||
-Doptimize=${ZIG_BUILD_TYPE}
|
||||
-Dtarget=${ZIG_TARGET}
|
||||
-Dcpu=${TARGET_CPU_MODEL}
|
||||
${ZIG_EXAMPLE_ARG}
|
||||
-freference-trace
|
||||
--cache-dir ${CMAKE_BINARY_DIR}/../.zig-cache
|
||||
--prefix ${CMAKE_BINARY_DIR}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
BYPRODUCTS ${CMAKE_BINARY_DIR}/obj/app_zig.o
|
||||
VERBATIM)
|
||||
|
||||
add_dependencies(zig_build translate_c)
|
||||
add_dependencies(${COMPONENT_LIB} zig_build)
|
||||
|
||||
target_sources(${COMPONENT_LIB}
|
||||
PRIVATE
|
||||
${CMAKE_BINARY_DIR}/obj/app_zig.o
|
||||
)
|
||||
58
software/zig_main/cmake/zig-download.cmake
Normal file
58
software/zig_main/cmake/zig-download.cmake
Normal file
@@ -0,0 +1,58 @@
|
||||
if(NOT EXISTS "${ZIG_DIR}/zig")
|
||||
if(ZIG_PLATFORM STREQUAL "linux-musl")
|
||||
if(ZIG_ARCH STREQUAL "aarch64")
|
||||
set(HASH_SUM "d3e4930bbac053b40860290cdec2ad1e052418172aa452e590b242c081a01f94")
|
||||
elseif(ZIG_ARCH STREQUAL "x86_64")
|
||||
set(HASH_SUM "411f1858a9610803af28cd271acf4548873545ccc866f4b9060903ff0d4b6e8e")
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported architecture: ${ZIG_ARCH}")
|
||||
endif()
|
||||
elseif(ZIG_PLATFORM STREQUAL "windows")
|
||||
if(ZIG_ARCH STREQUAL "x86_64")
|
||||
set(HASH_SUM "4d5b66d857e790d068e408e6bdd0054f9e78ca2d0ab96b5ce25f63a125f1701e")
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported architecture: ${ZIG_ARCH}")
|
||||
endif()
|
||||
elseif(ZIG_PLATFORM STREQUAL "macos")
|
||||
if(ZIG_ARCH STREQUAL "aarch64")
|
||||
set(HASH_SUM "f47ac7927ae44f3b8290aa818d3158b0f9adc12742e03b7496ad440b6d89d38e")
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported architecture: ${ZIG_ARCH}")
|
||||
endif()
|
||||
endif()
|
||||
set(ZIG_URL "https://github.com/kassane/zig-espressif-bootstrap/releases/download/0.16.0-xtensa/zig-relsafe-${ZIG_TRIPLET}.${ARCHIVE_EXT}")
|
||||
message(STATUS "Downloading Zig (espressif variant):")
|
||||
message(STATUS " => ${ZIG_ARCHIVE}")
|
||||
file(DOWNLOAD "${ZIG_URL}" "${ZIG_ARCHIVE}"
|
||||
TLS_VERIFY ON
|
||||
EXPECTED_HASH SHA256=${HASH_SUM}
|
||||
STATUS download_status
|
||||
LOG download_log
|
||||
# SHOW_PROGRESS
|
||||
)
|
||||
list(GET download_status 0 dl_code)
|
||||
if(NOT dl_code EQUAL 0)
|
||||
message(FATAL_ERROR "Download failed:\n${download_log}")
|
||||
endif()
|
||||
message(STATUS "Extracting ${ARCHIVE_EXT} ...")
|
||||
if(HOST_OS_LOWER MATCHES "windows|win")
|
||||
execute_process(
|
||||
COMMAND powershell -NoProfile -ExecutionPolicy Bypass
|
||||
-Command "Expand-Archive -Path '${ZIG_ARCHIVE}' -DestinationPath '${CMAKE_BINARY_DIR}' -Force"
|
||||
RESULT_VARIABLE extract_result
|
||||
)
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E tar xf "${ZIG_ARCHIVE}"
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
RESULT_VARIABLE extract_result
|
||||
)
|
||||
endif()
|
||||
if(NOT extract_result EQUAL 0)
|
||||
message(FATAL_ERROR "Extraction failed (code ${extract_result})")
|
||||
endif()
|
||||
file(REMOVE "${ZIG_ARCHIVE}")
|
||||
else()
|
||||
message(STATUS "Using cached espressif zig: ${ZIG_DIR}/zig")
|
||||
endif()
|
||||
set(ZIG_BIN "${ZIG_DIR}/zig")
|
||||
63
software/zig_main/cmake/zig-runner.cmake
Normal file
63
software/zig_main/cmake/zig-runner.cmake
Normal file
@@ -0,0 +1,63 @@
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# HELPER to run zig cli (build - translate-c - fmt - ...)
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
function(zig_run)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 ARG
|
||||
"VERBATIM;ALLOW_FAIL"
|
||||
"WORKING_DIRECTORY;RESULT_VARIABLE;OUTPUT_VARIABLE;ERROR_VARIABLE;OUTPUT_FILE;TIMEOUT"
|
||||
"COMMAND"
|
||||
)
|
||||
if(NOT ARG_COMMAND)
|
||||
message(FATAL_ERROR "zig_run: COMMAND list is required")
|
||||
endif()
|
||||
if(NOT DEFINED ARG_WORKING_DIRECTORY)
|
||||
set(ARG_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
# Default timeout: 300 s. translate-c normally finishes in seconds;
|
||||
if(NOT DEFINED ARG_TIMEOUT)
|
||||
set(ARG_TIMEOUT 300)
|
||||
endif()
|
||||
set(extra_args)
|
||||
if(ARG_VERBATIM)
|
||||
list(APPEND extra_args VERBATIM)
|
||||
endif()
|
||||
if(ARG_OUTPUT_FILE)
|
||||
list(APPEND extra_args OUTPUT_FILE "${ARG_OUTPUT_FILE}")
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND "${ZIG_BIN}" ${ARG_COMMAND}
|
||||
WORKING_DIRECTORY "${ARG_WORKING_DIRECTORY}"
|
||||
RESULT_VARIABLE result
|
||||
OUTPUT_VARIABLE output
|
||||
ERROR_VARIABLE error
|
||||
TIMEOUT ${ARG_TIMEOUT}
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_STRIP_TRAILING_WHITESPACE
|
||||
${extra_args}
|
||||
)
|
||||
# Propagate results to parent scope if requested
|
||||
if(DEFINED ARG_RESULT_VARIABLE)
|
||||
set(${ARG_RESULT_VARIABLE} ${result} PARENT_SCOPE)
|
||||
endif()
|
||||
if(DEFINED ARG_OUTPUT_VARIABLE)
|
||||
set(${ARG_OUTPUT_VARIABLE} "${output}" PARENT_SCOPE)
|
||||
endif()
|
||||
if(DEFINED ARG_ERROR_VARIABLE)
|
||||
set(${ARG_ERROR_VARIABLE} "${error}" PARENT_SCOPE)
|
||||
endif()
|
||||
if(NOT ARG_ALLOW_FAIL)
|
||||
if(result MATCHES "timeout")
|
||||
message(FATAL_ERROR
|
||||
"Zig command timed out after ${ARG_TIMEOUT}s\n"
|
||||
" Command: ${ZIG_BIN} ${ARG_COMMAND}\n"
|
||||
"--- stdout ---\n${output}\n"
|
||||
"--- stderr ---\n${error}")
|
||||
elseif(NOT result EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"Zig command failed (code ${result}):\n"
|
||||
" ${ZIG_BIN} ${ARG_COMMAND}\n"
|
||||
"--- stdout ---\n${output}\n"
|
||||
"--- stderr ---\n${error}")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1 @@
|
||||
28621486f77229aaf81c71f5e15d6fbf36c2949cf11094e07090593e659e7639
|
||||
@@ -0,0 +1,67 @@
|
||||
## 3.0.3
|
||||
|
||||
- Support WS2816 with 16-bit color
|
||||
|
||||
## 3.0.1
|
||||
|
||||
- Support WS2811 bit timing
|
||||
|
||||
## 3.0.0
|
||||
|
||||
- Discontinued support for ESP-IDF v4.x
|
||||
- Added configuration for user-defined color component format
|
||||
|
||||
## 2.5.5
|
||||
|
||||
- Simplified the led_strip component dependency, the time of full build with ESP-IDF v5.3 can now be shorter.
|
||||
|
||||
## 2.5.4
|
||||
|
||||
- Inserted extra delay when initialize the SPI LED device, to ensure all LEDs are in the reset state correctly
|
||||
|
||||
## 2.5.3
|
||||
|
||||
- Extend reset time (280us) to support WS2812B-V5
|
||||
|
||||
## 2.5.2
|
||||
|
||||
- Added API reference doc (api.md)
|
||||
|
||||
## 2.5.0
|
||||
|
||||
- Enabled support for IDF4.4 and above
|
||||
- with RMT backend only
|
||||
- Added API `led_strip_set_pixel_hsv`
|
||||
|
||||
## 2.4.0
|
||||
|
||||
- Support configurable SPI mode to control leds
|
||||
- recommend enabling DMA when using SPI mode
|
||||
|
||||
## 2.3.0
|
||||
|
||||
- Support configurable RMT channel size by setting `mem_block_symbols`
|
||||
|
||||
## 2.2.0
|
||||
|
||||
- Support for 4 components RGBW leds (SK6812):
|
||||
- in led_strip_config_t new fields
|
||||
led_pixel_format, controlling byte format (LED_PIXEL_FORMAT_GRB, LED_PIXEL_FORMAT_GRBW)
|
||||
led_model, used to configure bit timing (LED_MODEL_WS2812, LED_MODEL_SK6812)
|
||||
- new API led_strip_set_pixel_rgbw
|
||||
- new interface type set_pixel_rgbw
|
||||
|
||||
## 2.1.0
|
||||
|
||||
- Support DMA feature, which offloads the CPU by a lot when it comes to drive a bunch of LEDs
|
||||
- Support various RMT clock sources
|
||||
- Acquire and release the power management lock before and after each refresh
|
||||
- New driver flag: `invert_out` which can invert the led control signal by hardware
|
||||
|
||||
## 2.0.0
|
||||
|
||||
- Reimplemented the driver using the new RMT driver (`driver/rmt_tx.h`)
|
||||
|
||||
## 1.0.0
|
||||
|
||||
- Initial driver version, based on the legacy RMT driver (`driver/rmt.h`)
|
||||
@@ -0,0 +1 @@
|
||||
{"version":"1.0","algorithm":"sha256","created_at":"2026-02-04T21:14:01.842522+00:00","files":[{"path":"CHANGELOG.md","size":1667,"hash":"b445b45b8ce496848e247b569090efc3ea1a8680b8e69a7309dbdeeb97eb9d51"},{"path":"CMakeLists.txt","size":917,"hash":"038cbe6ba04c27101892e51d9d6a0627d64130f666f5d61b1f097462f982955b"},{"path":"LICENSE","size":11358,"hash":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"},{"path":"README.md","size":2072,"hash":"12e83a316c51d85c6c1ee2e5eecfb46691f6be42ce685eece2ce063a9c949001"},{"path":"idf_component.yml","size":492,"hash":"f4f09e02e4e53be5b49defeab1b6da6cdb9acfe377df892dfef5a0c370a7f88e"},{"path":"docs/Doxyfile","size":738,"hash":"7f64bdef18c3ed6f2e3d6397066e2fad4b5e31c2052744ca9631f34f69fdff79"},{"path":"docs/book.toml","size":297,"hash":"5d66624796168a4b8d0d87631c438c392b973206f4f7c53d9897a0b7ca7ce5b4"},{"path":"include/led_strip.h","size":4313,"hash":"36344aae936d7e0e764954188d77160c357d3ae1990ede1c7451e2bd1c0e85eb"},{"path":"include/led_strip_rmt.h","size":1630,"hash":"c63a152ab4aa187080b8d29cdb49365a9ea03b6ca7c41c66920e5c58ac0d0c52"},{"path":"include/led_strip_spi.h","size":1599,"hash":"cf0dcd5c748a7f11bf55077325b68a64ea826e55fc8e7b38aaad6fc0eb5345e5"},{"path":"include/led_strip_types.h","size":4276,"hash":"168b30b21ecbc5789903204753958c781304324e6a44d40a5fcb8676f12cb29c"},{"path":"interface/led_strip_interface.h","size":2934,"hash":"5b7d0c326d0d0d9748830d4aec46d765400e1446055d4a1197c83111e937d74c"},{"path":"src/led_strip_api.c","size":3841,"hash":"61968f0dde1cf1720f6b00bf3cb5d2c2b990aac6de00c90d9465746afc3e03d5"},{"path":"src/led_strip_rmt_dev.c","size":8925,"hash":"0e69d4743e65956ef2494d1347b81b99f283e0a79d6d341733fd1d58c1c6e97e"},{"path":"src/led_strip_rmt_encoder.c","size":8080,"hash":"4ab03dfbba4a90f392af3234665f76ccd21c004c61f5575e1e428354512d1748"},{"path":"src/led_strip_rmt_encoder.h","size":977,"hash":"690381c35ace2703a5c7156f6547a8524f4cbfe5bef40be619e2097960120a40"},{"path":"src/led_strip_spi_dev.c","size":11600,"hash":"79a4e4c42185afdbdf2163bd945a6339bf09965a85fb20525b3d7b106d695ab2"},{"path":"examples/led_strip_rmt_ws2812/CMakeLists.txt","size":140,"hash":"526f16308e57fafd25d0fd79d872152a9214c28967f78aa9c94ebe9e73040940"},{"path":"examples/led_strip_rmt_ws2812/README.md","size":1200,"hash":"a5f39b31c5f7cbf548ee31b61ab22e430a6c823404c0ddb113703512bcb3ad3c"},{"path":"examples/led_strip_spi_ws2812/CMakeLists.txt","size":140,"hash":"61255dc48f295f09e84abd7895ae5767763ac3decb4b4584e38681ea877427e8"},{"path":"examples/led_strip_spi_ws2812/README.md","size":1201,"hash":"2c02a29197cd1f2d4af4c4c9cd44677e303b0e168a1773eef9fc3fdb39377d27"},{"path":"examples/led_strip_spi_ws2812/main/CMakeLists.txt","size":99,"hash":"34e7f83d26bca924c629ea2012e6f200b415d486907863fe936d94872ff739eb"},{"path":"examples/led_strip_spi_ws2812/main/idf_component.yml","size":68,"hash":"a0c6b9b94056e8459a9acb8d7828540b36b4f7fe9ced9011ea97ba23b2fc96d4"},{"path":"examples/led_strip_spi_ws2812/main/led_strip_spi_ws2812_main.c","size":2808,"hash":"ef7ee688e7e1f451879a7b238b2a7133ccf880adb6d0e551328150acf86f656d"},{"path":"examples/led_strip_rmt_ws2812/main/CMakeLists.txt","size":99,"hash":"8960b68811805d3aa40e1a7f44ddf7400c0d0731829b6d2b3b1584d8dcd3b392"},{"path":"examples/led_strip_rmt_ws2812/main/idf_component.yml","size":53,"hash":"d52c7e09ecb7a6e4946fb6e697d6d7127918d4334858973f8c7434b1d2f120f0"},{"path":"examples/led_strip_rmt_ws2812/main/led_strip_rmt_ws2812_main.c","size":3253,"hash":"8835bd39d38dac8fb27c5e1298cb12ddf4c6ed430b4a2a1e061334f56d77f470"},{"path":"docs/src/SUMMARY.md","size":110,"hash":"b3a38ed25d2e5187928554682b1bd7154444e1bc1ce8183e6a3d328e720f7b61"},{"path":"docs/src/api.md","size":128,"hash":"d06c809c85c02f6ae22bd090331e1150dad89bd57034f056dbf3df0449cdc22b"},{"path":"docs/src/index.md","size":2967,"hash":"db944dabd24b1faa4d61a8f8db4f734334cefc2d1efb6d023a51fb94d1c3311f"}]}
|
||||
@@ -0,0 +1,26 @@
|
||||
include($ENV{IDF_PATH}/tools/cmake/version.cmake)
|
||||
|
||||
set(srcs "src/led_strip_api.c")
|
||||
set(public_requires)
|
||||
|
||||
if(CONFIG_SOC_RMT_SUPPORTED)
|
||||
list(APPEND srcs "src/led_strip_rmt_dev.c" "src/led_strip_rmt_encoder.c")
|
||||
endif()
|
||||
|
||||
# the SPI backend driver relies on some feature that was available in IDF 5.1
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.1")
|
||||
if(CONFIG_SOC_GPSPI_SUPPORTED)
|
||||
list(APPEND srcs "src/led_strip_spi_dev.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Starting from esp-idf v5.3, the RMT and SPI drivers are moved to separate components
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3")
|
||||
list(APPEND public_requires "esp_driver_rmt" "esp_driver_spi")
|
||||
else()
|
||||
list(APPEND public_requires "driver")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "include" "interface"
|
||||
REQUIRES ${public_requires})
|
||||
202
software/zig_main/components/espressif__led_strip/LICENSE
Normal file
202
software/zig_main/components/espressif__led_strip/LICENSE
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
23
software/zig_main/components/espressif__led_strip/README.md
Normal file
23
software/zig_main/components/espressif__led_strip/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# LED Strip Driver
|
||||
|
||||
[](https://components.espressif.com/components/espressif/led_strip)
|
||||
|
||||
This driver is designed for addressable LEDs like [WS2812](http://www.world-semi.com/Certifications/WS2812B.html), where each LED is controlled by a single data line.
|
||||
|
||||
## Supported Backend Peripherals
|
||||
|
||||
The LED strip driver supports two different backend peripherals to generate the timing signals required by addressable LEDs:
|
||||
|
||||
### The [RMT](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/rmt.html) Peripheral
|
||||
|
||||
This is the most economical way to drive the LEDs because it only consumes one RMT channel, leaving other channels free to use. However, the memory usage increases dramatically with the number of LEDs. If the RMT hardware can't be assist by DMA, the driver will going into interrupt very frequently, thus result in a high CPU usage. What's worse, if the RMT interrupt is delayed or not serviced in time (e.g. if Wi-Fi interrupt happens on the same CPU core), the RMT transaction will be corrupted and the LEDs will display incorrect colors. If you want to use RMT to drive a large number of LEDs, you'd better to enable the DMA feature if possible [^1].
|
||||
|
||||
### The [SPI](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html) Peripheral
|
||||
|
||||
SPI peripheral can also be used to generate the timing required by the LED strip, in a so-called "Clock-less" mode. However this backend is not as economical as the RMT one, because it will take up the whole **bus**. You **CANNOT** connect other devices to the same SPI bus if it's been used by the led_strip, because the led_strip doesn't have the concept of "Chip Select".
|
||||
|
||||
## Documentation
|
||||
|
||||
For detailed information about the LED Strip component, including API reference and user guides, please visit:
|
||||
|
||||
- **Programming Guide & API Reference**: [LED Strip Documentation](https://espressif.github.io/idf-extra-components/latest/led_strip/index.html)
|
||||
@@ -0,0 +1,30 @@
|
||||
# Set this to the header file you want
|
||||
INPUT = \
|
||||
../include/ \
|
||||
../interface/
|
||||
|
||||
# The output directory for the generated XML documentation
|
||||
OUTPUT_DIRECTORY = doxygen_output
|
||||
|
||||
# Warning-related settings, it's recommended to keep them enabled
|
||||
WARN_IF_UNDOC_ENUM_VAL = YES
|
||||
WARN_AS_ERROR = YES
|
||||
|
||||
# Other common settings
|
||||
FULL_PATH_NAMES = YES
|
||||
STRIP_FROM_PATH = ../
|
||||
STRIP_FROM_INC_PATH = ../
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = YES
|
||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
EXTRACT_ALL = YES
|
||||
PREDEFINED = $(ENV_DOXYGEN_DEFINES)
|
||||
HAVE_DOT = NO
|
||||
GENERATE_XML = YES
|
||||
XML_OUTPUT = xml
|
||||
GENERATE_HTML = NO
|
||||
HAVE_DOT = NO
|
||||
GENERATE_LATEX = NO
|
||||
QUIET = YES
|
||||
MARKDOWN_SUPPORT = YES
|
||||
@@ -0,0 +1,8 @@
|
||||
[book]
|
||||
title = "LED Strip Documentation"
|
||||
language = "en"
|
||||
|
||||
[output.html]
|
||||
default-theme = "light"
|
||||
git-repository-url = "https://github.com/espressif/idf-extra-components/tree/master/led_strip"
|
||||
edit-url-template = "https://github.com/espressif/idf-extra-components/edit/master/led_strip/docs/{path}"
|
||||
@@ -0,0 +1,13 @@
|
||||
# Summary
|
||||
|
||||
---
|
||||
|
||||
# Programming Guide
|
||||
|
||||
- [LED Strip](index.md)
|
||||
|
||||
---
|
||||
|
||||
# API Reference
|
||||
|
||||
- [API Reference](api.md)
|
||||
@@ -0,0 +1,9 @@
|
||||
# API Reference
|
||||
|
||||
<div class="warning">
|
||||
|
||||
This file is automatically generated by esp-doxybook.
|
||||
|
||||
DO NOT edit it manually.
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,75 @@
|
||||
# LED Strip Programming Guide
|
||||
|
||||
## Allocate LED Strip Object with RMT Backend
|
||||
|
||||
```c
|
||||
#define BLINK_GPIO 0
|
||||
|
||||
/// LED strip common configuration
|
||||
led_strip_config_t strip_config = {
|
||||
.strip_gpio_num = BLINK_GPIO, // The GPIO that connected to the LED strip's data line
|
||||
.max_leds = 1, // The number of LEDs in the strip,
|
||||
.led_model = LED_MODEL_WS2812, // LED strip model, it determines the bit timing
|
||||
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, // The color component format is G-R-B
|
||||
.flags = {
|
||||
.invert_out = false, // don't invert the output signal
|
||||
}
|
||||
};
|
||||
|
||||
/// RMT backend specific configuration
|
||||
led_strip_rmt_config_t rmt_config = {
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||
.resolution_hz = 10 * 1000 * 1000, // RMT counter clock frequency: 10MHz
|
||||
.mem_block_symbols = 64, // the memory size of each RMT channel, in words (4 bytes)
|
||||
.flags = {
|
||||
.with_dma = false, // DMA feature is available on chips like ESP32-S3/P4
|
||||
}
|
||||
};
|
||||
|
||||
/// Create the LED strip object
|
||||
led_strip_handle_t led_strip = NULL;
|
||||
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
You can create multiple LED strip objects with different GPIOs and pixel numbers. The backend driver will automatically allocate sufficient RMT channels for you wherever possible. If the RMT channels are not enough, the [led_strip_new_rmt_device](api.md#function-led_strip_new_rmt_device) will return an error.
|
||||
|
||||
## Allocate LED Strip Object with SPI Backend
|
||||
|
||||
```c
|
||||
#define BLINK_GPIO 0
|
||||
|
||||
/// LED strip common configuration
|
||||
led_strip_config_t strip_config = {
|
||||
.strip_gpio_num = BLINK_GPIO, // The GPIO that connected to the LED strip's data line
|
||||
.max_leds = 1, // The number of LEDs in the strip,
|
||||
.led_model = LED_MODEL_WS2812, // LED strip model, it determines the bit timing
|
||||
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, // The color component format is G-R-B
|
||||
.flags = {
|
||||
.invert_out = false, // don't invert the output signal
|
||||
}
|
||||
};
|
||||
|
||||
/// SPI backend specific configuration
|
||||
led_strip_spi_config_t spi_config = {
|
||||
.clk_src = SPI_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||
.spi_bus = SPI2_HOST, // SPI bus ID
|
||||
.flags = {
|
||||
.with_dma = true, // Using DMA can improve performance and help drive more LEDs
|
||||
}
|
||||
};
|
||||
|
||||
/// Create the LED strip object
|
||||
led_strip_handle_t led_strip = NULL;
|
||||
ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
The number of LED strip objects can be created depends on how many free SPI controllers are free to use in your project.
|
||||
|
||||
## FAQ
|
||||
|
||||
- How to set the brightness of the LED strip?
|
||||
- You can tune the brightness by scaling the value of each R-G-B element with a **same** factor. But pay attention to the overflow of the value.
|
||||
@@ -0,0 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(COMPONENTS main)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(led_strip_rmt_ws2812)
|
||||
@@ -0,0 +1,31 @@
|
||||
# LED Strip Example (RMT backend + WS2812)
|
||||
|
||||
This example demonstrates how to blink the WS2812 LED using the [led_strip](https://components.espressif.com/component/espressif/led_strip) component.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with Espressif SoC
|
||||
* A USB cable for Power supply and programming
|
||||
* WS2812 LED strip
|
||||
|
||||
### Configure the Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`. Then assign the proper GPIO in the [source file](main/led_strip_rmt_ws2812_main.c). If your led strip has multiple LEDs, don't forget update the number.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```text
|
||||
I (299) gpio: GPIO[8]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (309) example: Created LED strip object with RMT backend
|
||||
I (309) example: Start blinking LED strip
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "led_strip_rmt_ws2812_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
espressif/led_strip:
|
||||
version: ^3
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "led_strip.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
// Set to 1 to use DMA for driving the LED strip, 0 otherwise
|
||||
// Please note the RMT DMA feature is only available on chips e.g. ESP32-S3/P4
|
||||
#define LED_STRIP_USE_DMA 0
|
||||
|
||||
#if LED_STRIP_USE_DMA
|
||||
// Numbers of the LED in the strip
|
||||
#define LED_STRIP_LED_COUNT 256
|
||||
#define LED_STRIP_MEMORY_BLOCK_WORDS 1024 // this determines the DMA block size
|
||||
#else
|
||||
// Numbers of the LED in the strip
|
||||
#define LED_STRIP_LED_COUNT 24
|
||||
#define LED_STRIP_MEMORY_BLOCK_WORDS 0 // let the driver choose a proper memory block size automatically
|
||||
#endif // LED_STRIP_USE_DMA
|
||||
|
||||
// GPIO assignment
|
||||
#define LED_STRIP_GPIO_PIN 2
|
||||
|
||||
// 10MHz resolution, 1 tick = 0.1us (led strip needs a high resolution)
|
||||
#define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000)
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
led_strip_handle_t configure_led(void)
|
||||
{
|
||||
// LED strip general initialization, according to your led board design
|
||||
led_strip_config_t strip_config = {
|
||||
.strip_gpio_num = LED_STRIP_GPIO_PIN, // The GPIO that connected to the LED strip's data line
|
||||
.max_leds = LED_STRIP_LED_COUNT, // The number of LEDs in the strip,
|
||||
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, // The color order of the strip: GRB
|
||||
.flags = {
|
||||
.invert_out = false, // don't invert the output signal
|
||||
}
|
||||
};
|
||||
|
||||
// LED strip backend configuration: RMT
|
||||
led_strip_rmt_config_t rmt_config = {
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||
.resolution_hz = LED_STRIP_RMT_RES_HZ, // RMT counter clock frequency
|
||||
.mem_block_symbols = LED_STRIP_MEMORY_BLOCK_WORDS, // the memory block size used by the RMT channel
|
||||
.flags = {
|
||||
.with_dma = LED_STRIP_USE_DMA, // Using DMA can improve performance when driving more LEDs
|
||||
}
|
||||
};
|
||||
|
||||
// LED Strip object handle
|
||||
led_strip_handle_t led_strip;
|
||||
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
|
||||
ESP_LOGI(TAG, "Created LED strip object with RMT backend");
|
||||
return led_strip;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
led_strip_handle_t led_strip = configure_led();
|
||||
bool led_on_off = false;
|
||||
|
||||
ESP_LOGI(TAG, "Start blinking LED strip");
|
||||
while (1) {
|
||||
if (led_on_off) {
|
||||
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
|
||||
for (int i = 0; i < LED_STRIP_LED_COUNT; i++) {
|
||||
ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i, 5, 5, 5));
|
||||
}
|
||||
/* Refresh the strip to send data */
|
||||
ESP_ERROR_CHECK(led_strip_refresh(led_strip));
|
||||
ESP_LOGI(TAG, "LED ON!");
|
||||
} else {
|
||||
/* Set all LED off to clear all pixels */
|
||||
ESP_ERROR_CHECK(led_strip_clear(led_strip));
|
||||
ESP_LOGI(TAG, "LED OFF!");
|
||||
}
|
||||
|
||||
led_on_off = !led_on_off;
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(COMPONENTS main)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(led_strip_spi_ws2812)
|
||||
@@ -0,0 +1,31 @@
|
||||
# LED Strip Example (SPI backend + WS2812)
|
||||
|
||||
This example demonstrates how to blink the WS2812 LED using the [led_strip](https://components.espressif.com/component/espressif/led_strip) component.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with Espressif SoC
|
||||
* A USB cable for Power supply and programming
|
||||
* WS2812 LED strip
|
||||
|
||||
### Configure the Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`. Then assign the proper GPIO in the [source file](main/led_strip_spi_ws2812_main.c). If your led strip has multiple LEDs, don't forget update the number.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```text
|
||||
I (299) gpio: GPIO[14]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (309) example: Created LED strip object with SPI backend
|
||||
I (309) example: Start blinking LED strip
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "led_strip_spi_ws2812_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
espressif/led_strip:
|
||||
version: ^3
|
||||
idf: '>=5.1'
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "led_strip.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
// GPIO assignment
|
||||
#define LED_STRIP_GPIO_PIN 2
|
||||
// Numbers of the LED in the strip
|
||||
#define LED_STRIP_LED_COUNT 24
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
led_strip_handle_t configure_led(void)
|
||||
{
|
||||
// LED strip general initialization, according to your led board design
|
||||
led_strip_config_t strip_config = {
|
||||
.strip_gpio_num = LED_STRIP_GPIO_PIN, // The GPIO that connected to the LED strip's data line
|
||||
.max_leds = LED_STRIP_LED_COUNT, // The number of LEDs in the strip,
|
||||
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||
// set the color order of the strip: GRB
|
||||
.color_component_format = {
|
||||
.format = {
|
||||
.r_pos = 1, // red is the second byte in the color data
|
||||
.g_pos = 0, // green is the first byte in the color data
|
||||
.b_pos = 2, // blue is the third byte in the color data
|
||||
.num_components = 3, // total 3 color components
|
||||
},
|
||||
},
|
||||
.flags = {
|
||||
.invert_out = false, // don't invert the output signal
|
||||
}
|
||||
};
|
||||
|
||||
// LED strip backend configuration: SPI
|
||||
led_strip_spi_config_t spi_config = {
|
||||
.clk_src = SPI_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||
.spi_bus = SPI2_HOST, // SPI bus ID
|
||||
.flags = {
|
||||
.with_dma = true, // Using DMA can improve performance and help drive more LEDs
|
||||
}
|
||||
};
|
||||
|
||||
// LED Strip object handle
|
||||
led_strip_handle_t led_strip;
|
||||
ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
|
||||
ESP_LOGI(TAG, "Created LED strip object with SPI backend");
|
||||
return led_strip;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
led_strip_handle_t led_strip = configure_led();
|
||||
bool led_on_off = false;
|
||||
|
||||
ESP_LOGI(TAG, "Start blinking LED strip");
|
||||
while (1) {
|
||||
if (led_on_off) {
|
||||
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
|
||||
for (int i = 0; i < LED_STRIP_LED_COUNT; i++) {
|
||||
ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i, 5, 5, 5));
|
||||
}
|
||||
/* Refresh the strip to send data */
|
||||
ESP_ERROR_CHECK(led_strip_refresh(led_strip));
|
||||
ESP_LOGI(TAG, "LED ON!");
|
||||
} else {
|
||||
/* Set all LED off to clear all pixels */
|
||||
ESP_ERROR_CHECK(led_strip_clear(led_strip));
|
||||
ESP_LOGI(TAG, "LED OFF!");
|
||||
}
|
||||
|
||||
led_on_off = !led_on_off;
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
dependencies:
|
||||
idf: '>=5.0'
|
||||
description: Driver for Addressable LED Strip (WS2812, etc)
|
||||
documentation: https://espressif.github.io/idf-extra-components/latest/led_strip/index.html
|
||||
issues: https://github.com/espressif/idf-extra-components/issues
|
||||
repository: git://github.com/espressif/idf-extra-components.git
|
||||
repository_info:
|
||||
commit_sha: 7cd447361ca2f0a1c01aa3089e3031f6171b6c7e
|
||||
path: led_strip
|
||||
url: https://github.com/espressif/idf-extra-components/tree/master/led_strip
|
||||
version: 3.0.3
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "led_strip_rmt.h"
|
||||
#include "led_strip_spi.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Set RGB for a specific pixel
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param red: red part of color
|
||||
* @param green: green part of color
|
||||
* @param blue: blue part of color
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set RGB for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
|
||||
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
|
||||
|
||||
/**
|
||||
* @brief Set RGBW for a specific pixel
|
||||
*
|
||||
* @note Only call this function if your led strip does have the white component (e.g. SK6812-RGBW)
|
||||
* @note Also see `led_strip_set_pixel` if you only want to specify the RGB part of the color and bypass the white component
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param red: red part of color
|
||||
* @param green: green part of color
|
||||
* @param blue: blue part of color
|
||||
* @param white: separate white component
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set RGBW color for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
|
||||
* - ESP_FAIL: Set RGBW color for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white);
|
||||
|
||||
/**
|
||||
* @brief Set HSV for a specific pixel
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param hue: hue part of color (0 - 360)
|
||||
* @param saturation: saturation part of color (0 - 255, rescaled from 0 - 1. e.g. saturation = 0.5, rescaled to 127)
|
||||
* @param value: value part of color (0 - 255, rescaled from 0 - 1. e.g. value = 0.5, rescaled to 127)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set HSV color for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set HSV color for a specific pixel failed because of an invalid argument
|
||||
* - ESP_FAIL: Set HSV color for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value);
|
||||
|
||||
/**
|
||||
* @brief Set HSV for a specific pixel in 16-bit resolution
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param hue: hue part of color (0 - 360)
|
||||
* @param saturation: saturation part of color (0 - 65535, rescaled from 0 - 1. e.g. saturation = 0.5, rescaled to 32767)
|
||||
* @param value: value part of color (0 - 65535, rescaled from 0 - 1. e.g. value = 0.5, rescaled to 32767)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set HSV color for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set HSV color for a specific pixel failed because of an invalid argument
|
||||
* - ESP_FAIL: Set HSV color for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t led_strip_set_pixel_hsv_16(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint16_t saturation, uint16_t value);
|
||||
|
||||
/**
|
||||
* @brief Refresh memory colors to LEDs
|
||||
*
|
||||
* @param strip: LED strip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Refresh successfully
|
||||
* - ESP_FAIL: Refresh failed because some other error occurred
|
||||
*
|
||||
* @note:
|
||||
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
|
||||
*/
|
||||
esp_err_t led_strip_refresh(led_strip_handle_t strip);
|
||||
|
||||
/**
|
||||
* @brief Clear LED strip (turn off all LEDs)
|
||||
*
|
||||
* @param strip: LED strip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Clear LEDs successfully
|
||||
* - ESP_FAIL: Clear LEDs failed because some other error occurred
|
||||
*/
|
||||
esp_err_t led_strip_clear(led_strip_handle_t strip);
|
||||
|
||||
/**
|
||||
* @brief Free LED strip resources
|
||||
*
|
||||
* @param strip: LED strip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Free resources successfully
|
||||
* - ESP_FAIL: Free resources failed because error occurred
|
||||
*/
|
||||
esp_err_t led_strip_del(led_strip_handle_t strip);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "led_strip_types.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "driver/rmt_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LED Strip RMT specific configuration
|
||||
*/
|
||||
typedef struct {
|
||||
rmt_clock_source_t clk_src; /*!< RMT clock source */
|
||||
uint32_t resolution_hz; /*!< RMT tick resolution, if set to zero, a default resolution (10MHz) will be applied */
|
||||
size_t mem_block_symbols; /*!< How many RMT symbols can one RMT channel hold at one time. Set to 0 will fallback to use the default size. */
|
||||
/*!< Extra RMT specific driver flags */
|
||||
struct led_strip_rmt_extra_config {
|
||||
uint32_t with_dma: 1; /*!< Use DMA to transmit data */
|
||||
} flags; /*!< Extra driver flags */
|
||||
} led_strip_rmt_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create LED strip based on RMT TX channel
|
||||
*
|
||||
* @param led_config LED strip configuration
|
||||
* @param rmt_config RMT specific configuration
|
||||
* @param ret_strip Returned LED strip handle
|
||||
* @return
|
||||
* - ESP_OK: create LED strip handle successfully
|
||||
* - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory
|
||||
* - ESP_FAIL: create LED strip handle failed because some other error
|
||||
*/
|
||||
esp_err_t led_strip_new_rmt_device(const led_strip_config2_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "led_strip_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LED Strip SPI specific configuration
|
||||
*/
|
||||
typedef struct {
|
||||
spi_clock_source_t clk_src; /*!< SPI clock source */
|
||||
spi_host_device_t spi_bus; /*!< SPI bus ID. Which buses are available depends on the specific chip */
|
||||
struct {
|
||||
uint32_t with_dma: 1; /*!< Use DMA to transmit data */
|
||||
} flags; /*!< Extra driver flags */
|
||||
} led_strip_spi_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create LED strip based on SPI MOSI channel
|
||||
*
|
||||
* @note Although only the MOSI line is used for generating the signal, the whole SPI bus can't be used for other purposes.
|
||||
*
|
||||
* @param led_config LED strip configuration
|
||||
* @param spi_config SPI specific configuration
|
||||
* @param ret_strip Returned LED strip handle
|
||||
* @return
|
||||
* - ESP_OK: create LED strip handle successfully
|
||||
* - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument
|
||||
* - ESP_ERR_NOT_SUPPORTED: create LED strip handle failed because of unsupported configuration
|
||||
* - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory
|
||||
* - ESP_FAIL: create LED strip handle failed because some other error
|
||||
*/
|
||||
esp_err_t led_strip_new_spi_device(const led_strip_config2_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type of LED strip handle
|
||||
*/
|
||||
typedef struct led_strip_t *led_strip_handle_t;
|
||||
|
||||
/**
|
||||
* @brief LED strip model
|
||||
* @note Different led model may have different timing parameters, so we need to distinguish them.
|
||||
*/
|
||||
typedef enum {
|
||||
LED_MODEL_WS2812, /*!< LED strip model: WS2812 */
|
||||
LED_MODEL_SK6812, /*!< LED strip model: SK6812 */
|
||||
LED_MODEL_WS2811, /*!< LED strip model: WS2811 */
|
||||
LED_MODEL_WS2816, /*!< LED strip model: WS2816 */
|
||||
LED_MODEL_INVALID /*!< Invalid LED strip model */
|
||||
} led_model_t;
|
||||
|
||||
/**
|
||||
* @brief LED color component format
|
||||
* @note The format is used to specify the order of color components in each pixel, also the number of color components.
|
||||
*/
|
||||
typedef union {
|
||||
struct format_layout {
|
||||
uint32_t r_pos: 2; /*!< Position of the red channel in the color order: 0~3 */
|
||||
uint32_t g_pos: 2; /*!< Position of the green channel in the color order: 0~3 */
|
||||
uint32_t b_pos: 2; /*!< Position of the blue channel in the color order: 0~3 */
|
||||
uint32_t w_pos: 2; /*!< Position of the white channel in the color order: 0~3 */
|
||||
uint32_t reserved: 19; /*!< Reserved */
|
||||
uint32_t bytes_per_color: 2; /*!< Bytes per color component: 1 or 2. If set to 0, it will fallback to 1 */
|
||||
uint32_t num_components: 3; /*!< Number of color components per pixel: 3 or 4. If set to 0, it will fallback to 3 */
|
||||
} format; /*!< Format layout */
|
||||
uint32_t format_id; /*!< Format ID */
|
||||
} led_color_component_format_t;
|
||||
|
||||
/// Helper macros to set the color component format
|
||||
#define LED_STRIP_COLOR_COMPONENT_FMT_GRB (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 1, .num_components = 3}}
|
||||
#define LED_STRIP_COLOR_COMPONENT_FMT_GRB_16 (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 2, .num_components = 3}}
|
||||
#define LED_STRIP_COLOR_COMPONENT_FMT_GRBW (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 1, .num_components = 4}}
|
||||
#define LED_STRIP_COLOR_COMPONENT_FMT_GRBW_16 (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 2, .num_components = 4}}
|
||||
#define LED_STRIP_COLOR_COMPONENT_FMT_RGB (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 1, .num_components = 3}}
|
||||
#define LED_STRIP_COLOR_COMPONENT_FMT_RGB_16 (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 2, .num_components = 3}}
|
||||
#define LED_STRIP_COLOR_COMPONENT_FMT_RGBW (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 1, .num_components = 4}}
|
||||
#define LED_STRIP_COLOR_COMPONENT_FMT_RGBW_16 (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 2, .num_components = 4}}
|
||||
|
||||
/**
|
||||
* @brief LED Strip common configurations
|
||||
* The common configurations are not specific to any backend peripheral.
|
||||
*/
|
||||
typedef struct {
|
||||
int strip_gpio_num; /*!< GPIO number that used by LED strip */
|
||||
uint32_t max_leds; /*!< Maximum number of LEDs that can be controlled in a single strip */
|
||||
led_model_t led_model; /*!< Specifies the LED strip model (e.g., WS2812, SK6812) */
|
||||
led_color_component_format_t color_component_format; /*!< Specifies the order of color components in each pixel.
|
||||
Use helper macros like `LED_STRIP_COLOR_COMPONENT_FMT_GRB` to set the format */
|
||||
/*!< LED strip extra driver flags */
|
||||
struct led_strip_extra_flags {
|
||||
uint32_t invert_out: 1; /*!< Invert output signal */
|
||||
} flags; /*!< Extra driver flags */
|
||||
} led_strip_config2_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct led_strip_t led_strip_t; /*!< Type of LED strip */
|
||||
|
||||
/**
|
||||
* @brief LED strip interface definition
|
||||
*/
|
||||
struct led_strip_t {
|
||||
/**
|
||||
* @brief Set RGB for a specific pixel
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param red: red part of color
|
||||
* @param green: green part of color
|
||||
* @param blue: blue part of color
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set RGB for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
|
||||
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
|
||||
|
||||
/**
|
||||
* @brief Set RGBW for a specific pixel. Similar to `set_pixel` but also set the white component
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param red: red part of color
|
||||
* @param green: green part of color
|
||||
* @param blue: blue part of color
|
||||
* @param white: separate white component
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set RGBW color for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
|
||||
* - ESP_FAIL: Set RGBW color for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t (*set_pixel_rgbw)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white);
|
||||
|
||||
/**
|
||||
* @brief Refresh memory colors to LEDs
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param timeout_ms: timeout value for refreshing task
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Refresh successfully
|
||||
* - ESP_FAIL: Refresh failed because some other error occurred
|
||||
*
|
||||
* @note:
|
||||
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
|
||||
*/
|
||||
esp_err_t (*refresh)(led_strip_t *strip);
|
||||
|
||||
/**
|
||||
* @brief Clear LED strip (turn off all LEDs)
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param timeout_ms: timeout value for clearing task
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Clear LEDs successfully
|
||||
* - ESP_FAIL: Clear LEDs failed because some other error occurred
|
||||
*/
|
||||
esp_err_t (*clear)(led_strip_t *strip);
|
||||
|
||||
/**
|
||||
* @brief Free LED strip resources
|
||||
*
|
||||
* @param strip: LED strip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Free resources successfully
|
||||
* - ESP_FAIL: Free resources failed because error occurred
|
||||
*/
|
||||
esp_err_t (*del)(led_strip_t *strip);
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "led_strip.h"
|
||||
#include "led_strip_interface.h"
|
||||
|
||||
static const char *TAG = "led_strip";
|
||||
|
||||
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->set_pixel(strip, index, red, green, blue);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
uint32_t red = 0;
|
||||
uint32_t green = 0;
|
||||
uint32_t blue = 0;
|
||||
|
||||
uint32_t rgb_max = value;
|
||||
uint32_t rgb_min = rgb_max * (255 - saturation) / 255;
|
||||
|
||||
uint32_t i = hue / 60;
|
||||
uint32_t diff = hue % 60;
|
||||
|
||||
// RGB adjustment amount by hue
|
||||
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
red = rgb_max;
|
||||
green = rgb_min + rgb_adj;
|
||||
blue = rgb_min;
|
||||
break;
|
||||
case 1:
|
||||
red = rgb_max - rgb_adj;
|
||||
green = rgb_max;
|
||||
blue = rgb_min;
|
||||
break;
|
||||
case 2:
|
||||
red = rgb_min;
|
||||
green = rgb_max;
|
||||
blue = rgb_min + rgb_adj;
|
||||
break;
|
||||
case 3:
|
||||
red = rgb_min;
|
||||
green = rgb_max - rgb_adj;
|
||||
blue = rgb_max;
|
||||
break;
|
||||
case 4:
|
||||
red = rgb_min + rgb_adj;
|
||||
green = rgb_min;
|
||||
blue = rgb_max;
|
||||
break;
|
||||
default:
|
||||
red = rgb_max;
|
||||
green = rgb_min;
|
||||
blue = rgb_max - rgb_adj;
|
||||
break;
|
||||
}
|
||||
|
||||
return strip->set_pixel(strip, index, red, green, blue);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_set_pixel_hsv_16(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint16_t saturation, uint16_t value)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
uint32_t red = 0;
|
||||
uint32_t green = 0;
|
||||
uint32_t blue = 0;
|
||||
|
||||
uint32_t rgb_max = value;
|
||||
uint32_t rgb_min = rgb_max * (65535 - saturation) / 65535;
|
||||
|
||||
uint32_t i = hue / 60;
|
||||
uint32_t diff = hue % 60;
|
||||
|
||||
// RGB adjustment amount by hue
|
||||
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
red = rgb_max;
|
||||
green = rgb_min + rgb_adj;
|
||||
blue = rgb_min;
|
||||
break;
|
||||
case 1:
|
||||
red = rgb_max - rgb_adj;
|
||||
green = rgb_max;
|
||||
blue = rgb_min;
|
||||
break;
|
||||
case 2:
|
||||
red = rgb_min;
|
||||
green = rgb_max;
|
||||
blue = rgb_min + rgb_adj;
|
||||
break;
|
||||
case 3:
|
||||
red = rgb_min;
|
||||
green = rgb_max - rgb_adj;
|
||||
blue = rgb_max;
|
||||
break;
|
||||
case 4:
|
||||
red = rgb_min + rgb_adj;
|
||||
green = rgb_min;
|
||||
blue = rgb_max;
|
||||
break;
|
||||
default:
|
||||
red = rgb_max;
|
||||
green = rgb_min;
|
||||
blue = rgb_max - rgb_adj;
|
||||
break;
|
||||
}
|
||||
|
||||
return strip->set_pixel(strip, index, red, green, blue);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->set_pixel_rgbw(strip, index, red, green, blue, white);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_refresh(led_strip_handle_t strip)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->refresh(strip);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_clear(led_strip_handle_t strip)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->clear(strip);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_del(led_strip_handle_t strip)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->del(strip);
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/rmt_tx.h"
|
||||
#include "led_strip.h"
|
||||
#include "led_strip_interface.h"
|
||||
#include "led_strip_rmt_encoder.h"
|
||||
|
||||
#define LED_STRIP_RMT_DEFAULT_RESOLUTION 10000000 // 10MHz resolution
|
||||
#define LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE 4
|
||||
// the memory size of each RMT channel, in words (4 bytes)
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64
|
||||
#else
|
||||
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48
|
||||
#endif
|
||||
|
||||
static const char *TAG = "led_strip_rmt";
|
||||
|
||||
typedef struct {
|
||||
led_strip_t base;
|
||||
rmt_channel_handle_t rmt_chan;
|
||||
rmt_encoder_handle_t strip_encoder;
|
||||
uint32_t strip_len;
|
||||
uint8_t bytes_per_pixel;
|
||||
led_color_component_format_t component_fmt;
|
||||
uint8_t pixel_buf[];
|
||||
} led_strip_rmt_obj;
|
||||
|
||||
static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
|
||||
struct format_layout format = rmt_strip->component_fmt.format;
|
||||
uint32_t start = index * rmt_strip->bytes_per_pixel;
|
||||
uint8_t *pixel_buf = rmt_strip->pixel_buf;
|
||||
uint8_t pos_bytes = format.bytes_per_color;
|
||||
|
||||
for (uint8_t i = 0; i < format.bytes_per_color; i++) {
|
||||
uint8_t color_shift = 8 * (format.bytes_per_color - 1 - i);
|
||||
pixel_buf[start + format.r_pos * pos_bytes + i] = (red >> color_shift) & 0xFF;
|
||||
pixel_buf[start + format.g_pos * pos_bytes + i] = (green >> color_shift) & 0xFF;
|
||||
pixel_buf[start + format.b_pos * pos_bytes + i] = (blue >> color_shift) & 0xFF;
|
||||
if (format.num_components > 3) {
|
||||
pixel_buf[start + format.w_pos * pos_bytes + i] = 0;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
struct format_layout format = rmt_strip->component_fmt.format;
|
||||
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
ESP_RETURN_ON_FALSE(format.num_components == 4, ESP_ERR_INVALID_ARG, TAG, "led doesn't have 4 components");
|
||||
|
||||
uint32_t start = index * rmt_strip->bytes_per_pixel;
|
||||
uint8_t *pixel_buf = rmt_strip->pixel_buf;
|
||||
uint8_t pos_bytes = format.bytes_per_color;
|
||||
|
||||
for (uint8_t i = 0; i < format.bytes_per_color; i++) {
|
||||
uint8_t color_shift = 8 * (format.bytes_per_color - 1 - i);
|
||||
pixel_buf[start + format.r_pos * pos_bytes + i] = (red >> color_shift) & 0xFF;
|
||||
pixel_buf[start + format.g_pos * pos_bytes + i] = (green >> color_shift) & 0xFF;
|
||||
pixel_buf[start + format.b_pos * pos_bytes + i] = (blue >> color_shift) & 0xFF;
|
||||
pixel_buf[start + format.w_pos * pos_bytes + i] = (white >> color_shift) & 0xFF;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_refresh(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
rmt_transmit_config_t tx_conf = {
|
||||
.loop_count = 0,
|
||||
};
|
||||
|
||||
ESP_RETURN_ON_ERROR(rmt_enable(rmt_strip->rmt_chan), TAG, "enable RMT channel failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_transmit(rmt_strip->rmt_chan, rmt_strip->strip_encoder, rmt_strip->pixel_buf,
|
||||
rmt_strip->strip_len * rmt_strip->bytes_per_pixel, &tx_conf), TAG, "transmit pixels by RMT failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(rmt_strip->rmt_chan, -1), TAG, "flush RMT channel failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_disable(rmt_strip->rmt_chan), TAG, "disable RMT channel failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_clear(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
// Write zero to turn off all leds
|
||||
memset(rmt_strip->pixel_buf, 0, rmt_strip->strip_len * rmt_strip->bytes_per_pixel);
|
||||
return led_strip_rmt_refresh(strip);
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_del(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_ERROR(rmt_del_channel(rmt_strip->rmt_chan), TAG, "delete RMT channel failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_del_encoder(rmt_strip->strip_encoder), TAG, "delete strip encoder failed");
|
||||
free(rmt_strip);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t led_strip_new_rmt_device(const led_strip_config2_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = NULL;
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(led_config && rmt_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
led_color_component_format_t component_fmt = led_config->color_component_format;
|
||||
// If R/G/B order is not specified, set default GRB order as fallback
|
||||
if (component_fmt.format_id == 0) {
|
||||
component_fmt = LED_STRIP_COLOR_COMPONENT_FMT_GRB;
|
||||
}
|
||||
if (led_config->led_model == LED_MODEL_WS2816) {
|
||||
component_fmt.format.bytes_per_color = 2;
|
||||
}
|
||||
if (component_fmt.format.bytes_per_color == 0) {
|
||||
component_fmt.format.bytes_per_color = 1;
|
||||
}
|
||||
// check the validation of the color component format
|
||||
uint8_t mask = 0;
|
||||
if (component_fmt.format.num_components == 3) {
|
||||
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos);
|
||||
// Check for invalid values
|
||||
ESP_RETURN_ON_FALSE(mask == 0x07, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
||||
} else if (component_fmt.format.num_components == 4) {
|
||||
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos) | BIT(component_fmt.format.w_pos);
|
||||
// Check for invalid values
|
||||
ESP_RETURN_ON_FALSE(mask == 0x0F, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
||||
} else {
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "invalid number of color components: %d", component_fmt.format.num_components);
|
||||
}
|
||||
uint8_t bytes_per_pixel = component_fmt.format.num_components;
|
||||
if (component_fmt.format.bytes_per_color > 1) {
|
||||
bytes_per_pixel *= component_fmt.format.bytes_per_color;
|
||||
}
|
||||
rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * bytes_per_pixel);
|
||||
ESP_GOTO_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for rmt strip");
|
||||
uint32_t resolution = rmt_config->resolution_hz ? rmt_config->resolution_hz : LED_STRIP_RMT_DEFAULT_RESOLUTION;
|
||||
|
||||
// for backward compatibility, if the user does not set the clk_src, use the default value
|
||||
rmt_clock_source_t clk_src = RMT_CLK_SRC_DEFAULT;
|
||||
if (rmt_config->clk_src) {
|
||||
clk_src = rmt_config->clk_src;
|
||||
}
|
||||
size_t mem_block_symbols = LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS;
|
||||
// override the default value if the user sets it
|
||||
if (rmt_config->mem_block_symbols) {
|
||||
mem_block_symbols = rmt_config->mem_block_symbols;
|
||||
}
|
||||
rmt_tx_channel_config_t rmt_chan_config = {
|
||||
.clk_src = clk_src,
|
||||
.gpio_num = led_config->strip_gpio_num,
|
||||
.mem_block_symbols = mem_block_symbols,
|
||||
.resolution_hz = resolution,
|
||||
.trans_queue_depth = LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE,
|
||||
.flags.with_dma = rmt_config->flags.with_dma,
|
||||
.flags.invert_out = led_config->flags.invert_out,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&rmt_chan_config, &rmt_strip->rmt_chan), err, TAG, "create RMT TX channel failed");
|
||||
|
||||
led_strip_encoder_config_t strip_encoder_conf = {
|
||||
.resolution = resolution,
|
||||
.led_model = led_config->led_model
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_led_strip_encoder(&strip_encoder_conf, &rmt_strip->strip_encoder), err, TAG, "create LED strip encoder failed");
|
||||
|
||||
rmt_strip->component_fmt = component_fmt;
|
||||
rmt_strip->bytes_per_pixel = bytes_per_pixel;
|
||||
rmt_strip->strip_len = led_config->max_leds;
|
||||
rmt_strip->base.set_pixel = led_strip_rmt_set_pixel;
|
||||
rmt_strip->base.set_pixel_rgbw = led_strip_rmt_set_pixel_rgbw;
|
||||
rmt_strip->base.refresh = led_strip_rmt_refresh;
|
||||
rmt_strip->base.clear = led_strip_rmt_clear;
|
||||
rmt_strip->base.del = led_strip_rmt_del;
|
||||
|
||||
*ret_strip = &rmt_strip->base;
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (rmt_strip) {
|
||||
if (rmt_strip->rmt_chan) {
|
||||
rmt_del_channel(rmt_strip->rmt_chan);
|
||||
}
|
||||
if (rmt_strip->strip_encoder) {
|
||||
rmt_del_encoder(rmt_strip->strip_encoder);
|
||||
}
|
||||
free(rmt_strip);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_attr.h"
|
||||
#include "led_strip_rmt_encoder.h"
|
||||
|
||||
static const char *TAG = "led_rmt_encoder";
|
||||
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0)
|
||||
#if CONFIG_RMT_ISR_IRAM_SAFE
|
||||
#define RMT_ENCODER_FUNC_ATTR IRAM_ATTR
|
||||
#else
|
||||
#define RMT_ENCODER_FUNC_ATTR
|
||||
#endif // CONFIG_RMT_ISR_IRAM_SAFE
|
||||
#endif // ESP_IDF_VERSION
|
||||
|
||||
typedef struct {
|
||||
rmt_encoder_t base;
|
||||
rmt_encoder_t *bytes_encoder;
|
||||
rmt_encoder_t *copy_encoder;
|
||||
int state;
|
||||
rmt_symbol_word_t reset_code;
|
||||
} rmt_led_strip_encoder_t;
|
||||
|
||||
RMT_ENCODER_FUNC_ATTR
|
||||
static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder;
|
||||
rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder;
|
||||
rmt_encode_state_t session_state = 0;
|
||||
rmt_encode_state_t state = 0;
|
||||
size_t encoded_symbols = 0;
|
||||
switch (led_encoder->state) {
|
||||
case 0: // send RGB data
|
||||
encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state);
|
||||
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||
led_encoder->state = 1; // switch to next state when current encoding session finished
|
||||
}
|
||||
if (session_state & RMT_ENCODING_MEM_FULL) {
|
||||
state |= RMT_ENCODING_MEM_FULL;
|
||||
goto out; // yield if there's no free space for encoding artifacts
|
||||
}
|
||||
// fall-through
|
||||
case 1: // send reset code
|
||||
encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code,
|
||||
sizeof(led_encoder->reset_code), &session_state);
|
||||
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||
led_encoder->state = 0; // back to the initial encoding session
|
||||
state |= RMT_ENCODING_COMPLETE;
|
||||
}
|
||||
if (session_state & RMT_ENCODING_MEM_FULL) {
|
||||
state |= RMT_ENCODING_MEM_FULL;
|
||||
goto out; // yield if there's no free space for encoding artifacts
|
||||
}
|
||||
}
|
||||
out:
|
||||
*ret_state = state;
|
||||
return encoded_symbols;
|
||||
}
|
||||
|
||||
static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_del_encoder(led_encoder->bytes_encoder);
|
||||
rmt_del_encoder(led_encoder->copy_encoder);
|
||||
free(led_encoder);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
RMT_ENCODER_FUNC_ATTR
|
||||
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_encoder_reset(led_encoder->bytes_encoder);
|
||||
rmt_encoder_reset(led_encoder->copy_encoder);
|
||||
led_encoder->state = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
rmt_led_strip_encoder_t *led_encoder = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(config->led_model < LED_MODEL_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led model");
|
||||
led_encoder = calloc(1, sizeof(rmt_led_strip_encoder_t));
|
||||
ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder");
|
||||
led_encoder->base.encode = rmt_encode_led_strip;
|
||||
led_encoder->base.del = rmt_del_led_strip_encoder;
|
||||
led_encoder->base.reset = rmt_led_strip_encoder_reset;
|
||||
rmt_bytes_encoder_config_t bytes_encoder_config;
|
||||
uint32_t reset_ticks = config->resolution / 1000000 * 280 / 2; // reset code duration defaults to 280us to accommodate WS2812B-V5
|
||||
if (config->led_model == LED_MODEL_SK6812) {
|
||||
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||
.bit0 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
|
||||
},
|
||||
.bit1 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.6 * config->resolution / 1000000, // T1H=0.6us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.6 * config->resolution / 1000000, // T1L=0.6us
|
||||
},
|
||||
.flags.msb_first = 1 // SK6812 transfer bit order: G7...G0R7...R0B7...B0(W7...W0)
|
||||
};
|
||||
} else if (config->led_model == LED_MODEL_WS2812) {
|
||||
// different led strip might have its own timing requirements, following parameter is for WS2812
|
||||
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||
.bit0 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
|
||||
},
|
||||
.bit1 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.9 * config->resolution / 1000000, // T1H=0.9us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.3 * config->resolution / 1000000, // T1L=0.3us
|
||||
},
|
||||
.flags.msb_first = 1 // WS2812 transfer bit order: G7...G0R7...R0B7...B0
|
||||
};
|
||||
} else if (config->led_model == LED_MODEL_WS2811) {
|
||||
// different led strip might have its own timing requirements, following parameter is for WS2811
|
||||
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||
.bit0 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.5 * config->resolution / 1000000., // T0H=0.5us
|
||||
.level1 = 0,
|
||||
.duration1 = 2.0 * config->resolution / 1000000., // T0L=2.0us
|
||||
},
|
||||
.bit1 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 1.2 * config->resolution / 1000000., // T1H=1.2us
|
||||
.level1 = 0,
|
||||
.duration1 = 1.3 * config->resolution / 1000000., // T1L=1.3us
|
||||
},
|
||||
.flags.msb_first = 1
|
||||
};
|
||||
reset_ticks = config->resolution / 1000000 * 50 / 2; // divide by 2... signal is sent twice
|
||||
} else if (config->led_model == LED_MODEL_WS2816) {
|
||||
// different led strip might have its own timing requirements, following parameter is for WS2816
|
||||
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||
.bit0 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.95 * config->resolution / 1000000, // T0L=0.95us
|
||||
},
|
||||
.bit1 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.75 * config->resolution / 1000000, // T1H=0.75us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.5 * config->resolution / 1000000, // T1L=0.5us
|
||||
},
|
||||
.flags.msb_first = 1
|
||||
};
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed");
|
||||
rmt_copy_encoder_config_t copy_encoder_config = {};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed");
|
||||
|
||||
led_encoder->reset_code = (rmt_symbol_word_t) {
|
||||
.level0 = 0,
|
||||
.duration0 = reset_ticks,
|
||||
.level1 = 0,
|
||||
.duration1 = reset_ticks,
|
||||
};
|
||||
*ret_encoder = &led_encoder->base;
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (led_encoder) {
|
||||
if (led_encoder->bytes_encoder) {
|
||||
rmt_del_encoder(led_encoder->bytes_encoder);
|
||||
}
|
||||
if (led_encoder->copy_encoder) {
|
||||
rmt_del_encoder(led_encoder->copy_encoder);
|
||||
}
|
||||
free(led_encoder);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "driver/rmt_encoder.h"
|
||||
#include "led_strip_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type of led strip encoder configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t resolution; /*!< Encoder resolution, in Hz */
|
||||
led_model_t led_model; /*!< LED model */
|
||||
} led_strip_encoder_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create RMT encoder for encoding LED strip pixels into RMT symbols
|
||||
*
|
||||
* @param[in] config Encoder configuration
|
||||
* @param[out] ret_encoder Returned encoder handle
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG for any invalid arguments
|
||||
* - ESP_ERR_NO_MEM out of memory when creating led strip encoder
|
||||
* - ESP_OK if creating encoder successfully
|
||||
*/
|
||||
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "soc/spi_periph.h"
|
||||
#include "led_strip.h"
|
||||
#include "led_strip_interface.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
#define LED_STRIP_SPI_DEFAULT_RESOLUTION (2.5 * 1000 * 1000) // 2.5MHz resolution
|
||||
#define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4
|
||||
|
||||
#define SPI_BYTES_PER_COLOR_BYTE 3
|
||||
#define SPI_BITS_PER_COLOR_BYTE (SPI_BYTES_PER_COLOR_BYTE * 8)
|
||||
|
||||
static const char *TAG = "led_strip_spi";
|
||||
|
||||
typedef struct {
|
||||
led_strip_t base;
|
||||
spi_host_device_t spi_host;
|
||||
spi_device_handle_t spi_device;
|
||||
uint32_t strip_len;
|
||||
uint8_t bytes_per_pixel;
|
||||
led_color_component_format_t component_fmt;
|
||||
uint8_t pixel_buf[];
|
||||
} led_strip_spi_obj;
|
||||
|
||||
// please make sure to zero-initialize the buf before calling this function
|
||||
static void __led_strip_spi_bit(uint8_t data, uint8_t *buf)
|
||||
{
|
||||
// Each color of 1 bit is represented by 3 bits of SPI, low_level:100 ,high_level:110
|
||||
// So a color byte occupies 3 bytes of SPI.
|
||||
*(buf + 2) |= data & BIT(0) ? BIT(2) | BIT(1) : BIT(2);
|
||||
*(buf + 2) |= data & BIT(1) ? BIT(5) | BIT(4) : BIT(5);
|
||||
*(buf + 2) |= data & BIT(2) ? BIT(7) : 0x00;
|
||||
*(buf + 1) |= BIT(0);
|
||||
*(buf + 1) |= data & BIT(3) ? BIT(3) | BIT(2) : BIT(3);
|
||||
*(buf + 1) |= data & BIT(4) ? BIT(6) | BIT(5) : BIT(6);
|
||||
*(buf + 0) |= data & BIT(5) ? BIT(1) | BIT(0) : BIT(1);
|
||||
*(buf + 0) |= data & BIT(6) ? BIT(4) | BIT(3) : BIT(4);
|
||||
*(buf + 0) |= data & BIT(7) ? BIT(7) | BIT(6) : BIT(7);
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
// 3 pixels take 72bits(9bytes)
|
||||
uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
|
||||
uint8_t *pixel_buf = spi_strip->pixel_buf;
|
||||
struct format_layout format = spi_strip->component_fmt.format;
|
||||
memset(pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||
|
||||
uint8_t pos_bytes = format.bytes_per_color;
|
||||
for (uint8_t i = 0; i < format.bytes_per_color; i++) {
|
||||
uint8_t color_shift = 8 * (format.bytes_per_color - 1 - i);
|
||||
__led_strip_spi_bit((red >> color_shift) & 0xFF, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.r_pos * pos_bytes + i)]);
|
||||
__led_strip_spi_bit((green >> color_shift) & 0xFF, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.g_pos * pos_bytes + i)]);
|
||||
__led_strip_spi_bit((blue >> color_shift) & 0xFF, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.b_pos * pos_bytes + i)]);
|
||||
if (format.num_components > 3) {
|
||||
__led_strip_spi_bit(0, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.w_pos * pos_bytes + i)]);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
struct format_layout format = spi_strip->component_fmt.format;
|
||||
ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
ESP_RETURN_ON_FALSE(format.num_components == 4, ESP_ERR_INVALID_ARG, TAG, "led doesn't have 4 components");
|
||||
|
||||
// LED_PIXEL_FORMAT_GRBW takes 96bits(12bytes)
|
||||
uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
|
||||
uint8_t *pixel_buf = spi_strip->pixel_buf;
|
||||
memset(pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||
|
||||
uint8_t pos_bytes = format.bytes_per_color;
|
||||
for (uint8_t i = 0; i < format.bytes_per_color; i++) {
|
||||
uint8_t color_shift = 8 * (format.bytes_per_color - 1 - i);
|
||||
__led_strip_spi_bit((red >> color_shift) & 0xFF, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.r_pos * pos_bytes + i)]);
|
||||
__led_strip_spi_bit((green >> color_shift) & 0xFF, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.g_pos * pos_bytes + i)]);
|
||||
__led_strip_spi_bit((blue >> color_shift) & 0xFF, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.b_pos * pos_bytes + i)]);
|
||||
__led_strip_spi_bit((white >> color_shift) & 0xFF, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.w_pos * pos_bytes + i)]);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_refresh(led_strip_t *strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
spi_transaction_t tx_conf;
|
||||
memset(&tx_conf, 0, sizeof(tx_conf));
|
||||
|
||||
tx_conf.length = spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BITS_PER_COLOR_BYTE;
|
||||
tx_conf.tx_buffer = spi_strip->pixel_buf;
|
||||
tx_conf.rx_buffer = NULL;
|
||||
ESP_RETURN_ON_ERROR(spi_device_transmit(spi_strip->spi_device, &tx_conf), TAG, "transmit pixels by SPI failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_clear(led_strip_t *strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
//Write zero to turn off all leds
|
||||
memset(spi_strip->pixel_buf, 0, spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||
uint8_t *buf = spi_strip->pixel_buf;
|
||||
for (int index = 0; index < spi_strip->strip_len * spi_strip->bytes_per_pixel; index++) {
|
||||
__led_strip_spi_bit(0, buf);
|
||||
buf += SPI_BYTES_PER_COLOR_BYTE;
|
||||
}
|
||||
|
||||
return led_strip_spi_refresh(strip);
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_del(led_strip_t *strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
|
||||
ESP_RETURN_ON_ERROR(spi_bus_remove_device(spi_strip->spi_device), TAG, "delete spi device failed");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_free(spi_strip->spi_host), TAG, "free spi bus failed");
|
||||
|
||||
free(spi_strip);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t led_strip_new_spi_device(const led_strip_config2_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = NULL;
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(led_config && spi_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
led_color_component_format_t component_fmt = led_config->color_component_format;
|
||||
// If R/G/B order is not specified, set default GRB order as fallback
|
||||
if (component_fmt.format_id == 0) {
|
||||
component_fmt = LED_STRIP_COLOR_COMPONENT_FMT_GRB;
|
||||
}
|
||||
if (led_config->led_model == LED_MODEL_WS2816) {
|
||||
component_fmt.format.bytes_per_color = 2;
|
||||
}
|
||||
if (component_fmt.format.bytes_per_color == 0) {
|
||||
component_fmt.format.bytes_per_color = 1;
|
||||
}
|
||||
uint8_t mask = 0;
|
||||
if (component_fmt.format.num_components == 3) {
|
||||
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos);
|
||||
// Check for invalid values
|
||||
ESP_RETURN_ON_FALSE(mask == 0x07, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
||||
} else if (component_fmt.format.num_components == 4) {
|
||||
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos) | BIT(component_fmt.format.w_pos);
|
||||
// Check for invalid values
|
||||
ESP_RETURN_ON_FALSE(mask == 0x0F, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
||||
} else {
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "invalid number of color components: %d", component_fmt.format.num_components);
|
||||
}
|
||||
uint8_t bytes_per_pixel = component_fmt.format.num_components;
|
||||
if (component_fmt.format.bytes_per_color > 1) {
|
||||
bytes_per_pixel *= component_fmt.format.bytes_per_color;
|
||||
}
|
||||
uint32_t mem_caps = MALLOC_CAP_DEFAULT;
|
||||
if (spi_config->flags.with_dma) {
|
||||
// DMA buffer must be placed in internal SRAM
|
||||
mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
|
||||
}
|
||||
spi_strip = heap_caps_calloc(1, sizeof(led_strip_spi_obj) + led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE, mem_caps);
|
||||
|
||||
ESP_GOTO_ON_FALSE(spi_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for spi strip");
|
||||
|
||||
spi_strip->spi_host = spi_config->spi_bus;
|
||||
// for backward compatibility, if the user does not set the clk_src, use the default value
|
||||
spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT;
|
||||
if (spi_config->clk_src) {
|
||||
clk_src = spi_config->clk_src;
|
||||
}
|
||||
|
||||
spi_bus_config_t spi_bus_cfg = {
|
||||
.mosi_io_num = led_config->strip_gpio_num,
|
||||
//Only use MOSI to generate the signal, set -1 when other pins are not used.
|
||||
.miso_io_num = -1,
|
||||
.sclk_io_num = -1,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(spi_bus_initialize(spi_strip->spi_host, &spi_bus_cfg, spi_config->flags.with_dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED), err, TAG, "create SPI bus failed");
|
||||
|
||||
if (led_config->flags.invert_out == true) {
|
||||
esp_rom_gpio_connect_out_signal(led_config->strip_gpio_num, spi_periph_signal[spi_strip->spi_host].spid_out, true, false);
|
||||
}
|
||||
|
||||
spi_device_interface_config_t spi_dev_cfg = {
|
||||
.clock_source = clk_src,
|
||||
.command_bits = 0,
|
||||
.address_bits = 0,
|
||||
.dummy_bits = 0,
|
||||
.clock_speed_hz = LED_STRIP_SPI_DEFAULT_RESOLUTION,
|
||||
.mode = 0,
|
||||
//set -1 when CS is not used
|
||||
.spics_io_num = -1,
|
||||
.queue_size = LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE,
|
||||
};
|
||||
|
||||
ESP_GOTO_ON_ERROR(spi_bus_add_device(spi_strip->spi_host, &spi_dev_cfg, &spi_strip->spi_device), err, TAG, "Failed to add spi device");
|
||||
//ensure the reset time is enough
|
||||
esp_rom_delay_us(10);
|
||||
int clock_resolution_khz = 0;
|
||||
spi_device_get_actual_freq(spi_strip->spi_device, &clock_resolution_khz);
|
||||
// TODO: ideally we should decide the SPI_BYTES_PER_COLOR_BYTE by the real clock resolution
|
||||
// But now, let's fixed the resolution, the downside is, we don't support a clock source whose frequency is not multiple of LED_STRIP_SPI_DEFAULT_RESOLUTION
|
||||
// clock_resolution between 2.2MHz to 2.8MHz is supported
|
||||
ESP_GOTO_ON_FALSE((clock_resolution_khz < LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000 + 300) && (clock_resolution_khz > LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000 - 300), ESP_ERR_NOT_SUPPORTED, err,
|
||||
TAG, "unsupported clock resolution:%dKHz", clock_resolution_khz);
|
||||
|
||||
if (led_config->led_model != LED_MODEL_WS2812) {
|
||||
ESP_LOGW(TAG, "Only support WS2812. The timing requirements for other models may not be met");
|
||||
}
|
||||
|
||||
spi_strip->component_fmt = component_fmt;
|
||||
spi_strip->bytes_per_pixel = bytes_per_pixel;
|
||||
spi_strip->strip_len = led_config->max_leds;
|
||||
spi_strip->base.set_pixel = led_strip_spi_set_pixel;
|
||||
spi_strip->base.set_pixel_rgbw = led_strip_spi_set_pixel_rgbw;
|
||||
spi_strip->base.refresh = led_strip_spi_refresh;
|
||||
spi_strip->base.clear = led_strip_spi_clear;
|
||||
spi_strip->base.del = led_strip_spi_del;
|
||||
|
||||
*ret_strip = &spi_strip->base;
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (spi_strip) {
|
||||
if (spi_strip->spi_device) {
|
||||
spi_bus_remove_device(spi_strip->spi_device);
|
||||
}
|
||||
if (spi_strip->spi_host) {
|
||||
spi_bus_free(spi_strip->spi_host);
|
||||
}
|
||||
free(spi_strip);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
341
software/zig_main/docs/build-internals.md
Normal file
341
software/zig_main/docs/build-internals.md
Normal file
@@ -0,0 +1,341 @@
|
||||
## How does the mixin build-system work?
|
||||
|
||||
#### Intro
|
||||
|
||||
ESP-IDF uses the `idf.py` script as a wrapper around CMake. It's responsible for creating the build environment, running CMake to generate build files, and using Ninja to build the project.
|
||||
|
||||
The Zig build system (`build.zig`) is a wrapper around the Zig compiler and integrates with ESP-IDF's CMake infrastructure.
|
||||
|
||||
For more details about Zig commands, see [doc/zig-xtensa](zig-xtensa.md)
|
||||
|
||||
#### Building this project
|
||||
|
||||
After cloning this project, you need to install ESP-IDF and set up the environment:
|
||||
|
||||
1. **Install ESP-IDF** by following the official guide:
|
||||
- Clone ESP-IDF repository:
|
||||
```bash
|
||||
git clone --recursive https://github.com/espressif/esp-idf.git
|
||||
```
|
||||
- Run the installation script:
|
||||
- Windows: `install.bat` or `install.ps1`
|
||||
- POSIX: `./install.sh`
|
||||
|
||||
2. **Set up the ESP-IDF environment variables:**
|
||||
- Windows: run `export.bat` or `./export.ps1`
|
||||
- POSIX: `. ./export.sh`
|
||||
|
||||
Once the environment is set up, you can build the project using this scheme:
|
||||
|
||||

|
||||
|
||||
3. **Set the target ESP device** (if not already set):
|
||||
|
||||
```bash
|
||||
idf.py set-target <esp-device>
|
||||
```
|
||||
|
||||
**Supported targets:**
|
||||
- RISC-V: `esp32c2`, `esp32c3`, `esp32c5`, `esp32c6`, `esp32c61`, `esp32c61eco0`, `esp32h2`, `esp32h21`, `esp32h4`, `esp32s31`, `esp32p4`, `esp32p4eco4`
|
||||
- Xtensa: `esp32`, `esp32s2`, `esp32s3`
|
||||
|
||||
4. **Add managed components** (optional):
|
||||
```bash
|
||||
idf.py add-dependency espressif/led_strip
|
||||
idf.py add-dependency espressif/esp-dsp
|
||||
```
|
||||
|
||||
Managed components are automatically detected during build and their headers are included in the Zig bindings.
|
||||
|
||||
5. **Build the project:**
|
||||
```bash
|
||||
idf.py build
|
||||
```
|
||||
|
||||
This will:
|
||||
- Configure CMake and detect managed components
|
||||
- Download/use appropriate Zig toolchain (espressif or upstream)
|
||||
- Generate `idf-sys.zig` bindings via `translate-c`
|
||||
- Apply target-specific patches
|
||||
- Build Zig code into object files
|
||||
- Link everything with ESP-IDF components
|
||||
|
||||
6. **Flash the firmware to your device:**
|
||||
```bash
|
||||
idf.py -p PORT flash
|
||||
```
|
||||
Replace `PORT` with your device's serial port (e.g., `COM3` on Windows or `/dev/ttyUSB0` on Linux)
|
||||
|
||||
7. **Monitor the device output:**
|
||||
```bash
|
||||
idf.py monitor
|
||||
```
|
||||
|
||||
**Additional useful commands:**
|
||||
- Clean the project: `idf.py clean`
|
||||
- Full clean and rebuild: `idf.py fullclean`
|
||||
- Build and flash in one command: `idf.py -p PORT flash monitor`
|
||||
- Show all targets: `idf.py --list-targets`
|
||||
- Configure project: `idf.py menuconfig`
|
||||
- Reconfigure (refresh component detection): `idf.py reconfigure`
|
||||
|
||||
---
|
||||
|
||||
### Current role of `build.zig`
|
||||
|
||||
Since the latest refactors (post [#37](https://github.com/kassane/zig-esp-idf-sample/pull/37) and related CMake changes), `build.zig` is **focused exclusively on Zig code compilation**:
|
||||
|
||||
**What `build.zig` handles:**
|
||||
- Defines Zig modules:
|
||||
- `esp_idf` → High-level facade module that re-exports safe wrappers from `imports/*.zig` (gpio, wifi, heap, rtos, led, etc.)
|
||||
- `sys` → Low-level module that imports the generated `idf-sys.zig` (raw C bindings)
|
||||
- Collects and compiles Zig source files
|
||||
- Uses pre-generated includes and dependencies from CMake
|
||||
- Generates object files (`app_zig.o`) that CMake links with ESP-IDF
|
||||
|
||||
**What has moved to CMake:**
|
||||
- Searching and linking IDF object files and `.a` libraries → handled by ESP-IDF's CMake system
|
||||
- Collecting include paths from IDF components → automatic via `zig-config.cmake`
|
||||
- Running `translate-c` on `stubs.h` to generate `idf-sys.zig` → fully automated in `cmake/` scripts
|
||||
- Applying target-specific patches → handled by `cmake/patch.cmake`
|
||||
- Detecting and including managed components → automatic detection in `zig-config.cmake`
|
||||
- Toolchain selection (espressif vs upstream Zig) → automatic based on target architecture
|
||||
|
||||
**This separation makes the project cleaner:**
|
||||
- **CMake** handles the complex C/ESP-IDF world (toolchain, bindings generation, component management)
|
||||
- **Zig** handles only modern, safe, comptime-friendly code compilation
|
||||
|
||||
---
|
||||
|
||||
### Managed Components Integration
|
||||
|
||||
The build system automatically detects managed components installed via `idf.py add-dependency`:
|
||||
|
||||
**Add extra-components like:**
|
||||
- `espressif/led_strip` → `HAS_LED_STRIP` define
|
||||
- `espressif/esp-dsp` → `HAS_ESP_DSP` define
|
||||
- `espressif/esp_bsp_devkit` → `HAS_ESP_BSP_DEVKIT` define
|
||||
|
||||
**How it works:**
|
||||
1. CMake detects components in `managed_components/` directory
|
||||
2. Adds component include paths to `INCLUDE_DIRS`
|
||||
3. Generates preprocessor defines (e.g., `HAS_LED_STRIP=1`)
|
||||
4. Passes defines to `translate-c` for binding generation
|
||||
5. Headers are conditionally included in `include/stubs.h`:
|
||||
```c
|
||||
#if HAS_LED_STRIP
|
||||
#include "led_strip.h"
|
||||
#endif
|
||||
```
|
||||
|
||||
**To add a new managed component:**
|
||||
1. Run: `idf.py add-dependency vendor/component`
|
||||
2. Add detection in `extra-components.cmake`:
|
||||
```cmake
|
||||
check_managed_component("Component Name" "vendor" "component" "HAS_COMPONENT")
|
||||
```
|
||||
3. Add conditional include in `include/stubs.h`
|
||||
4. Use in Zig via the wrapped API in `imports/`
|
||||
|
||||
---
|
||||
|
||||
### Toolchain Selection
|
||||
|
||||
The build system intelligently selects the appropriate Zig toolchain:
|
||||
|
||||
**RISC-V targets (all variants including H4/P4):**
|
||||
- Works with both **upstream Zig** and **Espressif's Zig fork**
|
||||
- Upstream Zig: uses generic `riscv32-freestanding-none` with feature flags (e.g. `+m+a+c+zicsr+zifencei`)
|
||||
- Espressif fork: uses named CPU models (e.g. `esp32c6`, `esp32p4`) with exact feature sets
|
||||
- Build system auto-detects which toolchain is in use and selects the right CPU model
|
||||
|
||||
**Xtensa targets (ESP32, ESP32-S2, ESP32-S3):**
|
||||
- Always uses **Espressif's Zig fork** (Xtensa support not in upstream)
|
||||
|
||||
The toolchain is automatically downloaded and cached in `build/zig-relsafe-*` if not found.
|
||||
|
||||
---
|
||||
|
||||
### Advanced: Build System Internals
|
||||
|
||||
For advanced users who want to understand or modify the build system:
|
||||
|
||||
**Key CMake files:**
|
||||
- `cmake/zig-config.cmake` → Main configuration, target detection, component discovery
|
||||
- `cmake/zig-download.cmake` → Automatic Zig toolchain download
|
||||
- `cmake/zig-runner.cmake` → Helper functions for running Zig commands
|
||||
- `cmake/bindings.cmake` → get esp-rs/esp-idf-sys bindings header
|
||||
- `cmake/extra-components.cmake` → helper to add more components
|
||||
- `cmake/patch.cmake` → Post-processing patches for generated bindings
|
||||
|
||||
**Bindings generation flow:**
|
||||
1. CMake collects all IDF component include paths
|
||||
2. Detects managed components and adds their paths
|
||||
3. Runs `zig translate-c` on `include/stubs.h` with all includes
|
||||
4. Generates `imports/idf-sys.zig` with raw C bindings
|
||||
5. Applies target-specific patches (ESP32-H2, H4, P4)
|
||||
6. Zig code imports via `@import("sys")` or high-level `@import("esp_idf")`
|
||||
|
||||
**Zig module structure:**
|
||||
```
|
||||
imports/
|
||||
├── idf-sys.zig # Generated C bindings (don't edit manually)
|
||||
├── esp_idf.zig # Main facade module
|
||||
├── gpio.zig # GPIO wrapper
|
||||
├── wifi.zig # WiFi wrapper
|
||||
├── heap.zig # Heap allocators
|
||||
├── rtos.zig # FreeRTOS wrappers
|
||||
├── led-strip.zig # LED strip wrapper (requires HAS_LED_STRIP)
|
||||
└── ... # Other high-level wrappers
|
||||
```
|
||||
|
||||
**In your Zig code:**
|
||||
```zig
|
||||
const idf = @import("esp_idf");
|
||||
const sys = idf.sys; // Access raw C bindings if needed
|
||||
const led = idf.led; // Use wrapped APIs (recommended)
|
||||
```
|
||||
|
||||
This architecture allows safe, idiomatic Zig code while maintaining full access to ESP-IDF's C APIs when necessary.
|
||||
|
||||
### Build Scheme Graph
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ ESP-IDF Build System │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────┐
|
||||
│ idf.py │
|
||||
│ (Python) │
|
||||
└───────┬───────┘
|
||||
│
|
||||
┌───────────────┴───────────────┐
|
||||
▼ ▼
|
||||
┌───────────────┐ ┌──────────────────┐
|
||||
│ set-target │ │ add-dependency │
|
||||
│ (esp32c6) │ │ (managed_comps) │
|
||||
└───────┬───────┘ └────────┬─────────┘
|
||||
│ │
|
||||
└──────────────┬──────────────┘
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ CMake │
|
||||
│ Configure │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌─────────────────────────┼─────────────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────────┐ ┌─────────────────────┐ ┌──────────────────┐
|
||||
│ ESP-IDF │ │ zig-config.cmake │ │ Component │
|
||||
│ Components │ │ • Detect target │ │ Detection │
|
||||
│ (.a libs) │ │ • Find toolchain │ │ (managed_comps) │
|
||||
└──────┬───────┘ │ • Collect includes │ └────────┬─────────┘
|
||||
│ │ • Check components │ │
|
||||
│ └──────────┬──────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────▼──────────┐ │
|
||||
│ │ bindings.cmake │ │
|
||||
│ │ • Build INCLUDE │◄────────────┘
|
||||
│ │ list with │
|
||||
│ │ managed_comps │
|
||||
│ │ • Generate defines │
|
||||
│ │ (HAS_COMP_NAME) │
|
||||
│ └──────────┬──────────┘
|
||||
│ │
|
||||
│ ┌──────────▼──────────┐
|
||||
│ │ zig translate-c │
|
||||
│ │ stubs.h → │
|
||||
│ │ idf-sys.zig │
|
||||
│ └──────────┬──────────┘
|
||||
│ │
|
||||
│ ┌──────────▼──────────┐
|
||||
│ │ patch.cmake │
|
||||
│ │ • Fix bitfields │
|
||||
│ │ • Target patches │
|
||||
│ │ (H2, H4, P4) │
|
||||
│ └──────────┬──────────┘
|
||||
│ │
|
||||
│ ▼
|
||||
│ ┌─────────────────────┐
|
||||
│ │ build.zig │◄─────────┐
|
||||
│ │ • Import idf-sys │ │
|
||||
│ │ • Define modules: │ │
|
||||
│ │ - esp_idf │ │
|
||||
│ │ - sys │ │
|
||||
│ │ • Compile Zig │ │
|
||||
│ │ sources │ │
|
||||
│ └──────────┬──────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────┐ │
|
||||
│ │ Zig Compiler │ │
|
||||
│ │ (upstream or │ │
|
||||
│ │ espressif fork) │ │
|
||||
│ └──────────┬──────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────┐ │
|
||||
│ │ app_zig.o │ │
|
||||
│ │ (Zig object) │ │
|
||||
│ └──────────┬──────────┘ │
|
||||
│ │ │
|
||||
└───────────────────────┼─────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────┐
|
||||
│ Ninja / Make │
|
||||
│ Link all objects │
|
||||
│ + ESP-IDF libs │
|
||||
└──────────┬──────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────┐
|
||||
│ ELF Binary │
|
||||
│ ├─ bootloader.bin │
|
||||
│ ├─ partition.bin │
|
||||
│ └─ app.bin │
|
||||
└──────────┬──────────┘
|
||||
│
|
||||
┌──────────▼──────────┐
|
||||
│ idf.py flash │
|
||||
│ (esptool.py) │
|
||||
└──────────┬──────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────┐
|
||||
│ ESP32 │
|
||||
│ Device │
|
||||
└──────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ Key Data Flows │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ 1. Component Discovery: CMake → managed_components/ → HAS_* defines │
|
||||
│ 2. Binding Generation: stubs.h + includes → translate-c → idf-sys.zig │
|
||||
│ 3. Zig Compilation: build.zig → zig build-obj → app_zig.o │
|
||||
│ 4. Final Link: ESP-IDF .a libs + app_zig.o → firmware.elf │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
### Led-dtrip component e.g:
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ Managed Components Flow │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ idf.py add-dependency espressif/led_strip │
|
||||
│ ↓ │
|
||||
│ managed_components/espressif__led_strip/ │
|
||||
│ ↓ │
|
||||
│ zig-config.cmake detects component │
|
||||
│ ↓ │
|
||||
│ Adds: -DHAS_LED_STRIP=1 -I.../espressif__led_strip/include │
|
||||
│ ↓ │
|
||||
│ stubs.h: #if HAS_LED_STRIP → #include "led_strip.h" │
|
||||
│ ↓ │
|
||||
│ translate-c generates bindings │
|
||||
│ ↓ │
|
||||
│ Zig code: const led = @import("esp_idf").led; │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
BIN
software/zig_main/docs/build-scheme.png
Normal file
BIN
software/zig_main/docs/build-scheme.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
820
software/zig_main/docs/getting-started.md
Normal file
820
software/zig_main/docs/getting-started.md
Normal file
@@ -0,0 +1,820 @@
|
||||
# Getting Started with Zig ESP-IDF Sample
|
||||
|
||||
A complete guide to building ESP32 firmware using Zig and ESP-IDF.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Installation](#installation)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Project Structure](#project-structure)
|
||||
- [Building Your First Project](#building-your-first-project)
|
||||
- [Working with Components](#working-with-components)
|
||||
- [Examples](#examples)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Next Steps](#next-steps)
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Required Software
|
||||
|
||||
- **Python 3.12+** (for ESP-IDF tools)
|
||||
- **Git** (for cloning repositories)
|
||||
- **CMake 3.16+** (bundled with ESP-IDF)
|
||||
- **Ninja** or **Make** (bundled with ESP-IDF)
|
||||
- **Zig compiler** (optional - will be auto-downloaded if not found)
|
||||
|
||||
### Supported Operating Systems
|
||||
|
||||
- **Linux** (Ubuntu 20.04+, Debian, Fedora, Arch)
|
||||
- **macOS** (10.15+)
|
||||
- **Windows** (10/11 with PowerShell or WSL2)
|
||||
- **Nix/NixOS** (via `flake.nix`)
|
||||
|
||||
### Supported ESP32 Targets
|
||||
|
||||
| Architecture | Targets |
|
||||
|--------------|---------|
|
||||
| **RISC-V** | ESP32-C2, C3, C5, C6, C61, H2, H21, H4, P4 |
|
||||
| **Xtensa** | ESP32, ESP32-S2, ESP32-S3 |
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Step 1: Install ESP-IDF
|
||||
|
||||
**Option A: Standard Installation**
|
||||
|
||||
```bash
|
||||
# Clone ESP-IDF
|
||||
git clone --recursive https://github.com/espressif/esp-idf.git
|
||||
cd esp-idf
|
||||
|
||||
# Linux/macOS:
|
||||
./install.sh
|
||||
|
||||
# Windows (PowerShell):
|
||||
.\install.ps1
|
||||
|
||||
# Windows (Command Prompt):
|
||||
install.bat
|
||||
```
|
||||
|
||||
**Option B: Using Nix Flakes** (Linux/macOS)
|
||||
|
||||
```bash
|
||||
# Enter development environment with all dependencies
|
||||
nix develop
|
||||
|
||||
# Or use direnv for automatic activation
|
||||
echo "use flake" > .envrc
|
||||
direnv allow
|
||||
```
|
||||
|
||||
**Option C: ESP-IDF installer** (Windows/macOS)
|
||||
|
||||
Download from: https://dl.espressif.com/dl/esp-idf/
|
||||
|
||||
### Step 2: Set up ESP-IDF Environment
|
||||
|
||||
Every time you open a new terminal, activate ESP-IDF:
|
||||
|
||||
```bash
|
||||
# Linux/macOS:
|
||||
. $HOME/esp/esp-idf/export.sh
|
||||
|
||||
# Windows (PowerShell):
|
||||
. $HOME/esp/esp-idf/export.ps1
|
||||
|
||||
# Windows (Command Prompt):
|
||||
%USERPROFILE%\esp\esp-idf\export.bat
|
||||
```
|
||||
|
||||
### Step 3: Clone This Project
|
||||
|
||||
```bash
|
||||
git clone https://github.com/kassane/zig-esp-idf-sample.git
|
||||
cd zig-esp-idf-sample
|
||||
```
|
||||
|
||||
### Step 4: Verify Installation
|
||||
|
||||
```bash
|
||||
idf.py --version
|
||||
# Expected output: ESP-IDF v5.x.x or v6.x.x
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Set Your Target Device
|
||||
|
||||
```bash
|
||||
# For ESP32-C6 (RISC-V)
|
||||
idf.py set-target esp32c6
|
||||
|
||||
# For ESP32-S3 (Xtensa)
|
||||
idf.py set-target esp32s3
|
||||
|
||||
# For ESP32 (original, Xtensa)
|
||||
idf.py set-target esp32
|
||||
```
|
||||
|
||||
### 2. Build the Project
|
||||
|
||||
```bash
|
||||
idf.py build
|
||||
```
|
||||
|
||||
**What happens during build:**
|
||||
- ✅ CMake detects your target and configures the build
|
||||
- ✅ Zig toolchain is automatically downloaded (if needed)
|
||||
- ✅ C bindings are generated from ESP-IDF headers
|
||||
- ✅ Target-specific patches are applied
|
||||
- ✅ Zig code is compiled to object files
|
||||
- ✅ Everything is linked with ESP-IDF libraries
|
||||
- ✅ Firmware binaries are generated
|
||||
|
||||
### 3. Flash to Device
|
||||
|
||||
```bash
|
||||
# Connect your ESP32 via USB, then:
|
||||
idf.py -p PORT flash
|
||||
|
||||
# Find your port:
|
||||
# Linux: /dev/ttyUSB0 or /dev/ttyACM0
|
||||
# macOS: /dev/cu.usbserial-*
|
||||
# Windows: COM3, COM4, etc.
|
||||
|
||||
# Example:
|
||||
idf.py -p /dev/ttyUSB0 flash
|
||||
```
|
||||
|
||||
### 4. Monitor Output
|
||||
|
||||
```bash
|
||||
idf.py -p PORT monitor
|
||||
|
||||
# Or combine flash + monitor:
|
||||
idf.py -p PORT flash monitor
|
||||
|
||||
# Exit monitor: Ctrl+]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
zig-esp-idf-sample/
|
||||
├── build.zig # Zig build script (compiles Zig code)
|
||||
├── build.zig.zon # Zig package manifest
|
||||
├── CMakeLists.txt # Root CMake config
|
||||
├── sdkconfig # ESP-IDF configuration (generated)
|
||||
├── sdkconfig.defaults # Default SDK config
|
||||
├── sdkconfig.defaults.esp32 # ESP32-specific defaults
|
||||
├── partitions_matter.csv # Custom partition table for Matter (3 MB app, 4 MB flash)
|
||||
├── dependencies.lock # Component version lock
|
||||
├── wokwi.toml # Wokwi simulator config
|
||||
│
|
||||
├── main/
|
||||
│ ├── CMakeLists.txt # Main component config
|
||||
│ ├── placeholder.c # Minimal C entry point (required by CMake)
|
||||
│ ├── matter_wrappers.cpp # C++ shims for esp_matter C++ API (activated when component present)
|
||||
│ ├── app.zig # Main Zig application entry
|
||||
│ ├── idf_component.yml # Component dependencies
|
||||
│ ├── Kconfig.projbuild # Project configuration options
|
||||
│ └── examples/
|
||||
│ ├── gpio-blink.zig # Toggle LED on GPIO2
|
||||
│ ├── uart-echo.zig # UART echo
|
||||
│ ├── i2c-scan.zig # I2C bus scan
|
||||
│ ├── wifi-station.zig # WiFi station
|
||||
│ ├── http-server.zig # HTTP server
|
||||
│ ├── ble-gatt-server.zig # BLE GATT server
|
||||
│ ├── smartled-rgb.zig # WS2812B LED strip
|
||||
│ ├── dsp-math.zig # DSP/FFT operations
|
||||
│ └── matter-light.zig # Matter On/Off Light
|
||||
│
|
||||
├── imports/ # Zig API wrappers and bindings
|
||||
│ ├── idf.zig # Main ESP-IDF facade module
|
||||
│ ├── idf-sys.zig # Generated C bindings (auto-generated)
|
||||
│ ├── sys.zig # Re-exports idf-sys
|
||||
│ ├── error.zig # esp_err_t → Zig error mapping
|
||||
│ ├── logger.zig # std.log integration (espLogFn)
|
||||
│ ├── version.zig # ESP-IDF version info (ver)
|
||||
│ ├── heap.zig # HeapCapsAllocator, MultiHeapAllocator
|
||||
│ ├── bootloader.zig # Partition/bootloader control
|
||||
│ ├── gpio.zig # GPIO wrapper
|
||||
│ ├── wifi.zig # WiFi station/AP/scan
|
||||
│ ├── uart.zig # UART driver
|
||||
│ ├── i2c.zig # I2C master
|
||||
│ ├── spi.zig # SPI master (+ SDSPI)
|
||||
│ ├── i2s.zig # I2S audio (STD, PDM, TDM)
|
||||
│ ├── http.zig # HTTP server + client
|
||||
│ ├── mqtt.zig # MQTT client
|
||||
│ ├── lwip.zig # lwIP sockets, DNS, SNTP
|
||||
│ ├── crc.zig # ESP-ROM CRC-8/16/32
|
||||
│ ├── bluetooth.zig # Bluedroid BLE
|
||||
│ ├── nimble.zig # NimBLE BLE (compile-time guarded)
|
||||
│ ├── now.zig # ESP-NOW protocol
|
||||
│ ├── nvs.zig # NVS flash key-value storage
|
||||
│ ├── partition.zig # Partition table operations
|
||||
│ ├── sleep.zig # Deep/light sleep + wakeup
|
||||
│ ├── event.zig # ESP event loop
|
||||
│ ├── wdt.zig # Task watchdog timer
|
||||
│ ├── rtos.zig # FreeRTOS tasks/queues/semaphores/timers
|
||||
│ ├── pcnt.zig # Pulse counter (pulse)
|
||||
│ ├── phy.zig # Wireless PHY / RF calibration
|
||||
│ ├── segger.zig # Segger SystemView profiling
|
||||
│ ├── led-strip.zig # LED strip — requires espressif/led_strip
|
||||
│ ├── dsp.zig # DSP/FFT — requires espressif/esp-dsp
|
||||
│ ├── hosted.zig # ESP-Hosted coexistence — requires espressif/esp_hosted
|
||||
│ ├── wifi_remote.zig # WiFi remote — requires espressif/esp_wifi_remote
|
||||
│ ├── timer.zig # High-resolution esp_timer (one-shot + periodic)
|
||||
│ ├── ledc.zig # LED PWM controller (duty, fade)
|
||||
│ ├── twai.zig # TWAI/CAN bus driver
|
||||
│ ├── pm.zig # Power management locks
|
||||
│ ├── pthread.zig # POSIX threads (FreeRTOS-backed)
|
||||
│ └── panic.zig # Zig panic handler
|
||||
│
|
||||
├── include/ # C headers for binding generation
|
||||
│ ├── stubs.h # Core ESP-IDF headers (input to zig translate-c)
|
||||
│ ├── wifi_stubs.h # WiFi macro wrappers
|
||||
│ ├── bt_stubs.h # Bluetooth macro wrappers
|
||||
│ ├── matter_stubs.h # C wrapper interface for esp_matter (C++ component)
|
||||
│ ├── matter_closure_patch.h # GCC 14 C++23 fix for closure-control cluster
|
||||
│ └── bindings.h # Additional bindings
|
||||
│
|
||||
├── cmake/ # CMake build system scripts
|
||||
│ ├── zig-config.cmake # Main Zig configuration
|
||||
│ ├── zig-download.cmake # Auto-download Zig toolchain
|
||||
│ ├── zig-runner.cmake # Helper functions for Zig
|
||||
│ ├── bindings.cmake # Binding generation
|
||||
│ ├── extra-components.cmake # Managed component detection
|
||||
│ └── patch.cmake # Post-processing patches
|
||||
│
|
||||
├── patches/ # Binding fixes for translate-c issues
|
||||
│ ├── *.zig
|
||||
│
|
||||
├── docs/ # Documentation
|
||||
│ ├── build-internals.md # Build system details
|
||||
│ ├── build-scheme.png # Build flow diagram
|
||||
│ └── zig-xtensa.md # Xtensa toolchain info
|
||||
│
|
||||
└── flake.nix # Nix development environment
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Building Your First Project
|
||||
|
||||
### Example 1: Hello World
|
||||
|
||||
```zig
|
||||
const std = @import("std");
|
||||
const idf = @import("esp_idf");
|
||||
|
||||
comptime {
|
||||
@export(&main, .{ .name = "app_main" });
|
||||
}
|
||||
|
||||
fn main() callconv(.c) void {
|
||||
log.info("Hello from Zig on ESP32!", .{});
|
||||
log.info("Zig version: {s}", .{@import("builtin").zig_version_string});
|
||||
|
||||
// Show memory info
|
||||
var heap = idf.heap.HeapCapsAllocator.init(null); // default: MALLOC_CAP_DEFAULT
|
||||
log.info("Free heap: {} bytes", .{heap.freeSize()});
|
||||
|
||||
// Sleep forever
|
||||
while (true) {
|
||||
idf.rtos.Task.delayMs(1000);
|
||||
}
|
||||
}
|
||||
|
||||
// overwrite zig std_options config
|
||||
pub const std_options: std.Options = .{
|
||||
.logFn = idf.log.espLogFn,
|
||||
};
|
||||
// rename log instance
|
||||
const log = std.log.scoped(.@"esp-idf");
|
||||
// overwrite std panic-handler
|
||||
pub const panic = idf.esp_panic.panic;
|
||||
```
|
||||
|
||||
**Build and run:**
|
||||
```bash
|
||||
idf.py build flash monitor
|
||||
```
|
||||
|
||||
### Example 2: Blinking LED
|
||||
|
||||
**Create `main/examples/blink.zig`:**
|
||||
|
||||
```zig
|
||||
const std = @import("std");
|
||||
const idf = @import("esp_idf");
|
||||
|
||||
const LED_PIN = .@"18"; // Change to your LED pin
|
||||
|
||||
comptime {
|
||||
@export(&main, .{ .name = "app_main" });
|
||||
}
|
||||
|
||||
fn main() callconv(.c) void {
|
||||
// Configure GPIO as output
|
||||
idf.gpio.Direction.set(LED_PIN, .output) catch {
|
||||
log.err("Failed to configure GPIO", .{});
|
||||
return;
|
||||
};
|
||||
|
||||
log.info("Blinking LED on GPIO {d}", .{LED_PIN});
|
||||
|
||||
while (true) {
|
||||
// LED ON
|
||||
idf.gpio.Level.set(LED_PIN, 1) catch {};
|
||||
log.info("LED ON", .{});
|
||||
idf.rtos.Task.delayMs(1000);
|
||||
|
||||
// LED OFF
|
||||
idf.gpio.Level.set(LED_PIN, 0) catch {};
|
||||
log.info("LED OFF", .{});
|
||||
idf.rtos.Task.delayMs(1000);
|
||||
}
|
||||
}
|
||||
|
||||
// overwrite zig std_options config
|
||||
pub const std_options: std.Options = .{
|
||||
.logFn = idf.log.espLogFn,
|
||||
};
|
||||
// rename log instance
|
||||
const log = std.log.scoped(.blink);
|
||||
// overwrite std panic-handler
|
||||
pub const panic = idf.esp_panic.panic;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Working with Components
|
||||
|
||||
### Adding Managed Components
|
||||
|
||||
Components are specified in `main/idf_component.yml`:
|
||||
|
||||
**Add a component:**
|
||||
|
||||
```bash
|
||||
# Use idf.py command:
|
||||
idf.py add-dependency espressif/led_strip
|
||||
# then
|
||||
idf.py reconfigure
|
||||
```
|
||||
|
||||
### Using LED Strip Component
|
||||
|
||||
**The build system automatically:**
|
||||
1. ✅ Detects components in `dependencies.lock`
|
||||
2. ✅ Adds include paths to binding generation
|
||||
3. ✅ Generates `HAS_*` defines (e.g., `HAS_LED_STRIP=1`)
|
||||
4. ✅ Includes headers in `include/stubs.h` conditionally
|
||||
5. ✅ Makes APIs available in Zig via `idf.*` modules
|
||||
|
||||
**1. Ensure LED strip is in `main/idf_component.yml`:**
|
||||
```yaml
|
||||
dependencies:
|
||||
espressif/led_strip: "*"
|
||||
```
|
||||
|
||||
**2. Reconfigure:**
|
||||
```bash
|
||||
idf.py reconfigure
|
||||
```
|
||||
|
||||
**3. Check provided example:**
|
||||
|
||||
The example in [examples/smartled-rgb.zig](../main/examples/smartled-rgb.zig) demonstrates:
|
||||
- Configuring WS2812B LED strip on GPIO 2
|
||||
- Setting individual pixel colors
|
||||
- Refreshing the display
|
||||
- Blinking pattern
|
||||
|
||||
### DSP basics (FFT + math)
|
||||
|
||||
[examples/dsp-math.zig](../main/examples/dsp-math.zig)
|
||||
|
||||
- Generates a sine tone (freq = 0.2 normalized)
|
||||
- Applies Hann window
|
||||
- Does FFT (1024 points, float32, radix-2)
|
||||
- Computes power spectrum in dB
|
||||
- Prints ASCII plot of the spectrum (64×10 chars, -120 to +40 dB)
|
||||
|
||||
### Using WiFi
|
||||
|
||||
**Use the WiFi station example:**
|
||||
[examples/wifi-station.zig](../main/examples/wifi-station.zig)
|
||||
|
||||
**Configure WiFi credentials:**
|
||||
|
||||
```bash
|
||||
idf.py menuconfig
|
||||
# Navigate to: Example Configuration
|
||||
# Set WiFi SSID and Password
|
||||
```
|
||||
|
||||
Or edit [main/Kconfig.projbuild](../main/Kconfig.projbuild) to change default values.
|
||||
|
||||
### Available Wrapper APIs
|
||||
|
||||
The project provides comprehensive Zig wrappers in `imports/`:
|
||||
|
||||
| Module | Description | Notes |
|
||||
|--------|-------------|-------|
|
||||
| `idf.gpio` | GPIO control | Any target |
|
||||
| `idf.wifi` | WiFi station/AP/scan | Not on H2/H4/P4 |
|
||||
| `idf.bt` | Bluedroid BLE | Requires `CONFIG_BT_ENABLED` |
|
||||
| `idf.nimble` | NimBLE BLE | Requires `CONFIG_BT_NIMBLE_ENABLED` |
|
||||
| `idf.heap` | HeapCapsAllocator, MultiHeapAllocator | Any target |
|
||||
| `idf.rtos` | FreeRTOS tasks/queues/semaphores/timers | Any target |
|
||||
| `idf.nvs` | NVS flash key-value storage | Any target |
|
||||
| `idf.partition` | Partition table operations | Any target |
|
||||
| `idf.sleep` | Deep/light sleep + wakeup sources | Any target |
|
||||
| `idf.event` | ESP event loop | Any target |
|
||||
| `idf.wdt` | Task watchdog timer | Any target |
|
||||
| `idf.bl` | Bootloader/partition control | Any target |
|
||||
| `idf.i2c` | I2C master | Any target |
|
||||
| `idf.spi` | SPI master + SDSPI | Any target |
|
||||
| `idf.uart` | UART driver | Any target |
|
||||
| `idf.i2s` | I2S audio (STD, PDM, TDM) | Any target |
|
||||
| `idf.http` | HTTP server (httpd) + client | Any target |
|
||||
| `idf.mqtt` | MQTT client | Any target |
|
||||
| `idf.lwip` | lwIP sockets, DNS, SNTP | Any target |
|
||||
| `idf.crc` | ESP-ROM CRC-8/16/32 | Any target |
|
||||
| `idf.esp_now` | ESP-NOW protocol | Any target |
|
||||
| `idf.pulse` | Pulse counter (PCNT) | Any target |
|
||||
| `idf.phy` | Wireless PHY / RF calibration | Any target |
|
||||
| `idf.segger` | Segger SystemView profiling | Any target |
|
||||
| `idf.ver` | ESP-IDF version info | Any target |
|
||||
| `idf.led` | LED strip WS2812B | Requires `espressif/led_strip` |
|
||||
| `idf.dsp` | DSP/FFT operations | Requires `espressif/esp-dsp` |
|
||||
| `idf.esp_hosted` | ESP-Hosted coexistence | Requires `espressif/esp_hosted` |
|
||||
| `idf.wifi_remote` | WiFi via slave MCU | Requires `espressif/esp_wifi_remote` |
|
||||
| `idf.timer` | High-resolution esp_timer | Any target |
|
||||
| `idf.ledc` | LED PWM controller | Any target |
|
||||
| `idf.twai` | TWAI/CAN bus driver | Any target |
|
||||
| `idf.pm` | Power management locks | Any target |
|
||||
| `idf.pthread` | POSIX threads (FreeRTOS-backed) | Any target |
|
||||
| `idf.matter` | Matter/CHIP protocol | Requires `espressif/esp_matter` |
|
||||
| `idf.log` | std.log integration (`espLogFn`) | Any target |
|
||||
| `idf.err` | esp_err_t → Zig error mapping | Any target |
|
||||
| `idf.sys` | Raw C bindings | Direct ESP-IDF API access |
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### FreeRTOS Tasks
|
||||
|
||||
```zig
|
||||
const idf = @import("esp_idf");
|
||||
const std = @import("std");
|
||||
|
||||
// overwrite std panic-handler
|
||||
pub const panic = idf.esp_panic.panic;
|
||||
// overwrite std.log
|
||||
pub const std_options: std.Options = .{
|
||||
.logFn = idf.log.espLogFn,
|
||||
};
|
||||
|
||||
export fn myTask(_: ?*anyopaque) callconv(.c) void {
|
||||
const log = std.log.scoped(.task);
|
||||
while (true) {
|
||||
log.info("Task running!", .{});
|
||||
idf.rtos.Task.delayMs(1000);
|
||||
}
|
||||
}
|
||||
|
||||
export fn app_main() callconv(.c) void {
|
||||
_ = idf.rtos.Task.create(myTask, "my_task", 2048, null, 5) catch @panic("Failed to create task");
|
||||
|
||||
// Main task continues...
|
||||
while (true) {
|
||||
idf.rtos.Task.delayMs(1000);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using Custom Allocators
|
||||
|
||||
```zig
|
||||
const idf = @import("esp_idf");
|
||||
const std = @import("std");
|
||||
|
||||
export fn app_main() callconv(.c) void {
|
||||
var arena = std.heap.ArenaAllocator.init(std.heap.c_allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
const allocator = arena.allocator();
|
||||
|
||||
var list: std.ArrayList(u32) = .empty;
|
||||
defer list.deinit(allocator);
|
||||
|
||||
list.append(allocator, 10) catch {};
|
||||
list.append(allocator, 20) catch {};
|
||||
list.append(allocator, 30) catch {};
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Accessing Raw C APIs
|
||||
|
||||
When wrappers aren't available, use raw bindings:
|
||||
|
||||
```zig
|
||||
const idf = @import("esp_idf");
|
||||
const std = @import("std");
|
||||
const sys = idf.sys;
|
||||
const log = std.log.scoped(.@"esp-idf");
|
||||
|
||||
export fn app_main() callconv(.c) void {
|
||||
var mac_addr: [6]u8 = undefined;
|
||||
|
||||
// Direct ESP-IDF C API call
|
||||
const result = sys.esp_efuse_mac_get_default(&mac_addr);
|
||||
if (result != sys.ESP_OK) {
|
||||
std.log.err("Failed to get MAC address", .{});
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("MAC: {X:0>2}:{X:0>2}:{X:0>2}:{X:0>2}:{X:0>2}:{X:0>2}", .{
|
||||
mac_addr[0], mac_addr[1], mac_addr[2],
|
||||
mac_addr[3], mac_addr[4], mac_addr[5],
|
||||
});
|
||||
}
|
||||
|
||||
pub const std_options: std.Options = .{
|
||||
.logFn = idf.log.espLogFn,
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build Errors
|
||||
|
||||
**"zig: command not found"**
|
||||
- The Zig toolchain should auto-download. Check `build/zig-relsafe-*` directory
|
||||
- Or install manually: https://ziglang.org/download/
|
||||
- For Nix users: `nix develop`
|
||||
|
||||
**"Component not found: espressif__led_strip"**
|
||||
```bash
|
||||
# Check idf_component.yml
|
||||
cat main/idf_component.yml
|
||||
|
||||
# Reconfigure to download components
|
||||
idf.py reconfigure
|
||||
```
|
||||
|
||||
**"translate-c failed"**
|
||||
- Check `include/stubs.h` and `include/wifi_stubs.h` for syntax errors
|
||||
- Ensure managed components exist in `managed_components/`
|
||||
- Check that `HAS_*` defines match components in `cmake/extra-components.cmake`
|
||||
|
||||
**Binding generation errors**
|
||||
- The `patches/` directory contains fixes for common `translate-c` issues
|
||||
- If you see struct layout errors, a patch likely needs updating
|
||||
- Check `cmake/patch.cmake` for how patches are applied
|
||||
|
||||
### Flash Errors
|
||||
|
||||
**"Serial port not found"**
|
||||
```bash
|
||||
# Linux: Add user to dialout group
|
||||
sudo usermod -a -G dialout $USER
|
||||
# Log out and back in
|
||||
|
||||
# Check available ports
|
||||
ls /dev/tty*
|
||||
|
||||
# Windows: Check Device Manager for COM port
|
||||
```
|
||||
|
||||
**"Failed to connect to ESP32"**
|
||||
- Hold BOOT button while connecting
|
||||
- Try different USB cable/port (must be data cable, not power-only)
|
||||
- Verify target matches your device: `idf.py set-target esp32c6`
|
||||
- Check USB drivers are installed (CP210x or CH340)
|
||||
|
||||
### Runtime Errors
|
||||
|
||||
**"Guru Meditation Error: Core panic"**
|
||||
- Check stack size (increase in `xTaskCreate`, default 2048 often too small)
|
||||
- Verify GPIO pins match your hardware
|
||||
- Enable debug build: `idf.py menuconfig` → Compiler options → Debug (-Og)
|
||||
- Check `sdkconfig` for panic handler settings
|
||||
|
||||
**"Out of memory" / Heap errors**
|
||||
- Use arena allocators: `std.heap.ArenaAllocator`
|
||||
- Check available heap: `heap.freeSize()`
|
||||
- Reduce allocations in hot paths
|
||||
- Consider enabling PSRAM if available
|
||||
- Check `idf.py size-components` for memory usage
|
||||
|
||||
**WiFi not connecting**
|
||||
- Verify SSID and password in menuconfig
|
||||
- Check WiFi country code matches your region
|
||||
- Ensure 2.4GHz WiFi (5GHz not supported)
|
||||
- Check WiFi credentials don't contain special characters
|
||||
|
||||
**Matter example: "app partition is too small"**
|
||||
|
||||
The Matter binary is ~2.2 MB and requires a 4 MB flash chip and custom partition table:
|
||||
```bash
|
||||
# Ensure sdkconfig has 4 MB flash and custom partition:
|
||||
# CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
# CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
# CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_matter.csv"
|
||||
|
||||
# If sdkconfig doesn't reflect sdkconfig.defaults changes, update it:
|
||||
idf.py reconfigure
|
||||
idf.py build -DCONFIG_ZIG_EXAMPLE_MATTER_LIGHT=y
|
||||
```
|
||||
|
||||
**Matter example: IDF version incompatibility**
|
||||
- `espressif/esp_matter` 1.4.x requires IDF **v5.x** (tested with v5.5)
|
||||
- Not compatible with IDF v6.x (depends on `json`/`mqtt` components removed in v6)
|
||||
- Check your IDF version: `idf.py --version`
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
### Learn More
|
||||
|
||||
- **Zig Language:** https://ziglang.org/documentation/master/
|
||||
- **ESP-IDF Docs:** https://docs.espressif.com/projects/esp-idf/
|
||||
- **Project Wiki:**
|
||||
- [Build Internals](./build-internals.md)
|
||||
- [Xtensa Toolchain](./zig-xtensa.md)
|
||||
|
||||
### Explore Examples
|
||||
|
||||
All examples are in `main/examples/`:
|
||||
- `gpio-blink.zig` - Toggle an LED on GPIO2 (any target)
|
||||
- `uart-echo.zig` - Read UART1 and echo back
|
||||
- `i2c-scan.zig` - Scan I2C bus for devices
|
||||
- `wifi-station.zig` - Connect to a WiFi AP
|
||||
- `http-server.zig` - Serve a web page over WiFi
|
||||
- `ble-gatt-server.zig` - BLE peripheral with GATT notifications (requires `CONFIG_BT_ENABLED`)
|
||||
- `smartled-rgb.zig` - WS2812B LED strip control (requires `espressif/led_strip`)
|
||||
- `dsp-math.zig` - FFT + power spectrum via DSP (requires `espressif/esp-dsp`)
|
||||
- `matter-light.zig` - Matter On/Off Light device (requires `espressif/esp_matter`, IDF v5.x, 4 MB flash)
|
||||
|
||||
### Configuration
|
||||
|
||||
```bash
|
||||
# Interactive configuration menu
|
||||
idf.py menuconfig
|
||||
|
||||
# Key settings to explore:
|
||||
# - Component config → FreeRTOS → Tick rate (Hz)
|
||||
# - Component config → ESP System Settings → Panic handler behavior
|
||||
# - Partition Table → Choose partition scheme
|
||||
# - Example Configuration → WiFi credentials
|
||||
# - Compiler options → Optimization level
|
||||
```
|
||||
|
||||
### Testing with Wokwi Simulator
|
||||
|
||||
The project includes `wokwi.toml` for simulation:
|
||||
|
||||
1. Install Wokwi CLI: https://docs.wokwi.com/wokwi-ci/idf-wokwi-usage
|
||||
2. Edit `wokwi.toml` to match your project
|
||||
3. Run: `idf.py wokwi --timeout 30000`
|
||||
|
||||
or use VSCode Extension
|
||||
|
||||
* More info: https://docs.wokwi.com/vscode/getting-started
|
||||
|
||||
### Advanced Topics
|
||||
|
||||
- **Custom Components:** Create reusable Zig modules in `imports/`
|
||||
- **WiFi & Networking:** HTTP servers, WebSockets, mDNS
|
||||
- **OTA Updates:** Over-the-air firmware updates
|
||||
- **Bluetooth:** BLE advertising, GATT services
|
||||
- **Deep Sleep:** Ultra-low power modes
|
||||
- **File Systems:** SPIFFS, FAT, LittleFS
|
||||
- **Cryptography:** Hardware-accelerated crypto
|
||||
|
||||
### Development Tips
|
||||
|
||||
**Use `sdkconfig.defaults` for version control:**
|
||||
- Base config: `sdkconfig.defaults`
|
||||
- Target-specific: `sdkconfig.defaults.esp32`
|
||||
- Don't commit `sdkconfig` (it's generated)
|
||||
|
||||
**Debugging:**
|
||||
```bash
|
||||
# Monitor with timestamps
|
||||
idf.py monitor --timestamps
|
||||
|
||||
# Filter logs by tag
|
||||
idf.py monitor --print-filter "tag:app"
|
||||
|
||||
# Save logs to file
|
||||
idf.py monitor | tee output.log
|
||||
```
|
||||
|
||||
**Clean builds when needed:**
|
||||
```bash
|
||||
# Clean Zig cache
|
||||
rm -rf .zig-cache
|
||||
|
||||
# Full CMake clean
|
||||
idf.py fullclean
|
||||
|
||||
# Regenerate bindings
|
||||
idf.py reconfigure
|
||||
```
|
||||
|
||||
### Contributing
|
||||
|
||||
Found a bug or want to contribute?
|
||||
- **GitHub:** https://github.com/kassane/zig-esp-idf-sample
|
||||
- **Issues:** https://github.com/kassane/zig-esp-idf-sample/issues
|
||||
- **Pull Requests:** See [CONTRIBUTING.md](../CONTRIBUTING.md)
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Common Commands
|
||||
|
||||
```bash
|
||||
# Setup
|
||||
idf.py set-target esp32c6 # Set target device
|
||||
idf.py reconfigure # Regenerate build config / update deps
|
||||
|
||||
# Build
|
||||
idf.py build # Build project
|
||||
idf.py clean # Clean build files
|
||||
idf.py fullclean # Full clean (including config)
|
||||
|
||||
# Flash
|
||||
idf.py -p PORT flash # Flash firmware
|
||||
idf.py -p PORT monitor # Monitor serial output
|
||||
idf.py -p PORT flash monitor # Flash and monitor
|
||||
idf.py -p PORT app-flash # Flash app only (faster)
|
||||
|
||||
# Config
|
||||
idf.py menuconfig # Interactive configuration
|
||||
idf.py size # Show binary sizes
|
||||
idf.py size-components # Component size breakdown
|
||||
|
||||
# Dependencies
|
||||
idf.py add-dependency PKG # Add managed component
|
||||
|
||||
# Help
|
||||
idf.py --help # Show all commands
|
||||
idf.py --list-targets # List supported targets
|
||||
```
|
||||
|
||||
### File Locations
|
||||
|
||||
- **Main app:** `main/app.zig`
|
||||
- **Examples:** `main/examples/*.zig`
|
||||
- **Dependencies:** `main/idf_component.yml`
|
||||
- **Configuration:** `sdkconfig`, `sdkconfig.defaults*`
|
||||
- **Bindings:** `imports/idf-sys.zig` (auto-generated)
|
||||
- **Wrappers:** `imports/*.zig`
|
||||
- **Build scripts:** `cmake/*.cmake`
|
||||
|
||||
### Pin Configuration
|
||||
|
||||
Check your board's pinout:
|
||||
- **GPIO Pins:** Varies by model (ESP32: 0-39, ESP32-C6: 0-30)
|
||||
- **Built-in LED:** Often GPIO 2, 8, or 18
|
||||
- **UART:** Usually GPIO 1 (TX), GPIO 3 (RX)
|
||||
- **I2C:** SDA/SCL pins vary by board
|
||||
- **SPI:** MOSI/MISO/CLK pins vary by board
|
||||
|
||||
### Hardware Requirements
|
||||
|
||||
- **ESP32 Development Board** (any supported variant)
|
||||
- **USB Cable** (must be data cable, not power-only)
|
||||
- **LEDs/Components** (optional, for examples)
|
||||
- **LED Strip WS2812B** (optional, for smartled-rgb example)
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
- **Documentation:** [docs/](.)
|
||||
- **Issues:** https://github.com/kassane/zig-esp-idf-sample/issues
|
||||
- **Discussions:** https://github.com/kassane/zig-esp-idf-sample/discussions
|
||||
184
software/zig_main/docs/zig-xtensa.md
Normal file
184
software/zig_main/docs/zig-xtensa.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# Zig for Xtensa (Esp32, Esp8266 and Esp32-S series)
|
||||
|
||||
Like [esp-rs](https://github.com/espressif/rust-esp32-example/blob/main/docs/rust-on-xtensa.md), forked zig toolchain uses **LLVM codegen** for xtensa target.
|
||||
|
||||
**Current version:**
|
||||
|
||||
- **Zig**: v0.16.0 ([bootstrap fork](https://github.com/kassane/zig-espressif-bootstrap))
|
||||
- **LLVM**: v21.1.0 ([espressif-fork](https://github.com/espressif/llvm-project))
|
||||
|
||||
|
||||
### Commands
|
||||
|
||||
**Zig command line interface:**
|
||||
|
||||
- `build-lib`: build static-lib or shared-lib (add `-dynamic` flag);
|
||||
- `build-obj`: build object file, like `clang/gcc -c`.
|
||||
- `build-exe`: build executable
|
||||
- `build`: build-system mode, need `build.zig`.
|
||||
|
||||
**Clang command line interface:**
|
||||
|
||||
- `zig cc`: clang CLI
|
||||
- `zig c++`: clang++ CLI (uses `llvm-libc++` + `llvm-libunwind` by default)
|
||||
|
||||
**Note:** Zig toolchain does not change `libclang` codegen. However, the default config uses `-fsanitize=undefined`.
|
||||
|
||||
### Targets available
|
||||
|
||||
```bash
|
||||
$> zig build-lib --show-builtin -target xtensa-freestanding-none -mcpu=(empty or any text)
|
||||
|
||||
info: available CPUs for architecture 'xtensa':
|
||||
cnl
|
||||
esp32
|
||||
esp32s2
|
||||
esp32s3
|
||||
esp8266
|
||||
generic
|
||||
|
||||
error: unknown CPU: ''
|
||||
```
|
||||
|
||||
**Note:** Freestanding targets are not listed on `zig targets | jq .libc` (triple-targets)
|
||||
|
||||
#### CPU Features
|
||||
|
||||
Similar to [Targets available](#targets-available) command, add `-mcpu` or `-Dcpu=` (need `build.zig`).
|
||||
|
||||
- `+` add feature
|
||||
- `-` remove feature
|
||||
|
||||
**Note:** For show feature list add `+`/`-` without feature name
|
||||
|
||||
**e.g.:**
|
||||
```bash
|
||||
$> zig build-lib --show-builtin -target xtensa-freestanding-none -mcpu=esp32+
|
||||
|
||||
info: available CPU features for architecture 'xtensa':
|
||||
bool: Enable Xtensa Boolean extension
|
||||
clamps: Enable Xtensa CLAMPS option
|
||||
coprocessor: Enable Xtensa Coprocessor option
|
||||
dcache: Enable Xtensa Data Cache option
|
||||
debug: Enable Xtensa Debug option
|
||||
density: Enable Density instructions
|
||||
dfpaccel: Enable Xtensa Double Precision FP acceleration
|
||||
div32: Enable Xtensa Div32 option
|
||||
esp32s2ops: Support Xtensa esp32-s2 ISA extension
|
||||
esp32s3ops: Support Xtensa esp32-s3 ISA extension
|
||||
exception: Enable Xtensa Exception option
|
||||
extendedl32r: Enable Xtensa Extended L32R option
|
||||
forced_atomics: Assume that lock-free native-width atomics are available
|
||||
fp: Enable Xtensa Single FP instructions
|
||||
hifi3: Enable Xtensa HIFI3 instructions
|
||||
highpriinterrupts: Enable Xtensa HighPriInterrupts option
|
||||
highpriinterrupts_level3: Enable Xtensa HighPriInterrupts Level3
|
||||
highpriinterrupts_level4: Enable Xtensa HighPriInterrupts Level4
|
||||
highpriinterrupts_level5: Enable Xtensa HighPriInterrupts Level5
|
||||
highpriinterrupts_level6: Enable Xtensa HighPriInterrupts Level6
|
||||
highpriinterrupts_level7: Enable Xtensa HighPriInterrupts Level7
|
||||
interrupt: Enable Xtensa Interrupt option
|
||||
loop: Enable Xtensa Loop extension
|
||||
mac16: Enable Xtensa MAC16 instructions
|
||||
minmax: Enable Xtensa MINMAX option
|
||||
miscsr: Enable Xtensa Miscellaneous SR option
|
||||
mul16: Enable Xtensa Mul16 option
|
||||
mul32: Enable Xtensa Mul32 option
|
||||
mul32high: Enable Xtensa Mul32High option
|
||||
nsa: Enable Xtensa NSA option
|
||||
prid: Enable Xtensa Processor ID option
|
||||
regprotect: Enable Xtensa Region Protection option
|
||||
rvector: Enable Xtensa Relocatable Vector option
|
||||
s32c1i: Enable Xtensa S32C1I option
|
||||
sext: Enable Xtensa Sign Extend option
|
||||
threadptr: Enable Xtensa THREADPTR option
|
||||
timers1: Enable Xtensa Timers 1
|
||||
timers2: Enable Xtensa Timers 2
|
||||
timers3: Enable Xtensa Timers 3
|
||||
windowed: Enable Xtensa Windowed Register option
|
||||
|
||||
error: unknown CPU feature: ''
|
||||
```
|
||||
|
||||
|
||||
#### Target info (builtin)
|
||||
|
||||
**Note:** If like syntax-highlighting use `| bat -p -l zig` pipeline command or save this output as `builtin.zig` and open on your code editor.
|
||||
|
||||
```bash
|
||||
$> zig build-lib --show-builtin -target xtensa-freestanding-none -mcpu=esp32s3
|
||||
```
|
||||
```zig
|
||||
const std = @import("std");
|
||||
/// Zig version. When writing code that supports multiple versions of Zig, prefer
|
||||
/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
|
||||
pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;
|
||||
pub const zig_version_string = "0.16.0-xtensa-dev.2287+eb3f16db5";
|
||||
pub const zig_backend = std.builtin.CompilerBackend.stage2_llvm;
|
||||
|
||||
pub const output_mode: std.builtin.OutputMode = .Lib;
|
||||
pub const link_mode: std.builtin.LinkMode = .static;
|
||||
pub const unwind_tables: std.builtin.UnwindTables = .async;
|
||||
pub const is_test = false;
|
||||
pub const single_threaded = false;
|
||||
pub const abi: std.Target.Abi = .none;
|
||||
pub const cpu: std.Target.Cpu = .{
|
||||
.arch = .xtensa,
|
||||
.model = &std.Target.xtensa.cpu.esp32s3,
|
||||
.features = std.Target.xtensa.featureSet(&.{
|
||||
.bool,
|
||||
.clamps,
|
||||
.coprocessor,
|
||||
.dcache,
|
||||
.debug,
|
||||
.density,
|
||||
.div32,
|
||||
.esp32s3ops,
|
||||
.exception,
|
||||
.fp,
|
||||
.highpriinterrupts,
|
||||
.highpriinterrupts_level7,
|
||||
.interrupt,
|
||||
.loop,
|
||||
.mac16,
|
||||
.minmax,
|
||||
.miscsr,
|
||||
.mul16,
|
||||
.mul32,
|
||||
.mul32high,
|
||||
.nsa,
|
||||
.prid,
|
||||
.regprotect,
|
||||
.rvector,
|
||||
.s32c1i,
|
||||
.sext,
|
||||
.threadptr,
|
||||
.timers3,
|
||||
.windowed,
|
||||
}),
|
||||
};
|
||||
pub const os: std.Target.Os = .{
|
||||
.tag = .freestanding,
|
||||
.version_range = .{ .none = {} },
|
||||
};
|
||||
pub const target: std.Target = .{
|
||||
.cpu = cpu,
|
||||
.os = os,
|
||||
.abi = abi,
|
||||
.ofmt = object_format,
|
||||
.dynamic_linker = .none,
|
||||
};
|
||||
pub const object_format: std.Target.ObjectFormat = .elf;
|
||||
pub const mode: std.builtin.OptimizeMode = .Debug;
|
||||
pub const link_libc = false;
|
||||
pub const link_libcpp = false;
|
||||
pub const have_error_return_tracing = true;
|
||||
pub const valgrind_support = false;
|
||||
pub const sanitize_thread = false;
|
||||
pub const fuzz = false;
|
||||
pub const position_independent_code = false;
|
||||
pub const position_independent_executable = false;
|
||||
pub const strip_debug_info = false;
|
||||
pub const code_model: std.builtin.CodeModel = .default;
|
||||
pub const omit_frame_pointer = false;
|
||||
```
|
||||
520
software/zig_main/imports/bluetooth.zig
Normal file
520
software/zig_main/imports/bluetooth.zig
Normal file
@@ -0,0 +1,520 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shim — BT_CONTROLLER_INIT_CONFIG_DEFAULT() macro cannot be translated by
|
||||
// zig translate-c; placeholder.c exposes it as a regular C function.
|
||||
// ---------------------------------------------------------------------------
|
||||
extern fn zig_bt_controller_default_cfg() sys.esp_bt_controller_config_t;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// BT mode
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Mode = enum(sys.esp_bt_mode_t) {
|
||||
idle = sys.ESP_BT_MODE_IDLE,
|
||||
ble = sys.ESP_BT_MODE_BLE,
|
||||
classic = sys.ESP_BT_MODE_CLASSIC_BT,
|
||||
dual = sys.ESP_BT_MODE_BTDM,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// BLE power
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const PowerType = sys.esp_ble_power_type_t;
|
||||
pub const PowerLevel = sys.esp_power_level_t;
|
||||
|
||||
pub fn txPowerSet(power_type: PowerType, level: PowerLevel) !void {
|
||||
try errors.espCheckError(sys.esp_ble_tx_power_set(power_type, level));
|
||||
}
|
||||
|
||||
pub fn txPowerGet(power_type: PowerType) PowerLevel {
|
||||
return sys.esp_ble_tx_power_get(power_type);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Controller lifecycle
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Controller = struct {
|
||||
/// Return the default controller config.
|
||||
/// Wraps the BT_CONTROLLER_INIT_CONFIG_DEFAULT() macro via a static
|
||||
/// inline C function in include/bt_stubs.h (translated by translate-c).
|
||||
pub fn defaultConfig() sys.esp_bt_controller_config_t {
|
||||
return sys.zig_bt_controller_default_cfg();
|
||||
}
|
||||
|
||||
pub fn init(cfg: *sys.esp_bt_controller_config_t) !void {
|
||||
try errors.espCheckError(sys.esp_bt_controller_init(cfg));
|
||||
}
|
||||
|
||||
pub fn deinit() !void {
|
||||
try errors.espCheckError(sys.esp_bt_controller_deinit());
|
||||
}
|
||||
|
||||
pub fn enable(mode: Mode) !void {
|
||||
try errors.espCheckError(sys.esp_bt_controller_enable(@intFromEnum(mode)));
|
||||
}
|
||||
|
||||
pub fn disable() !void {
|
||||
try errors.espCheckError(sys.esp_bt_controller_disable());
|
||||
}
|
||||
|
||||
pub fn getStatus() sys.esp_bt_controller_status_t {
|
||||
return sys.esp_bt_controller_get_status();
|
||||
}
|
||||
|
||||
/// Release memory for a BT mode that won't be used (saves heap).
|
||||
/// Call before `init` when only BLE or only Classic BT is needed.
|
||||
pub fn memRelease(mode: Mode) !void {
|
||||
try errors.espCheckError(sys.esp_bt_controller_mem_release(@intFromEnum(mode)));
|
||||
}
|
||||
|
||||
/// Release both controller and host memory for unused BT mode.
|
||||
pub fn memReleaseAll(mode: Mode) !void {
|
||||
try errors.espCheckError(sys.esp_bt_mem_release(@intFromEnum(mode)));
|
||||
}
|
||||
|
||||
pub fn sleepEnable() !void {
|
||||
try errors.espCheckError(sys.esp_bt_sleep_enable());
|
||||
}
|
||||
|
||||
pub fn sleepDisable() !void {
|
||||
try errors.espCheckError(sys.esp_bt_sleep_disable());
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Bluedroid host stack lifecycle
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Bluedroid = struct {
|
||||
pub fn init() !void {
|
||||
try errors.espCheckError(sys.esp_bluedroid_init());
|
||||
}
|
||||
|
||||
pub fn initWithCfg(cfg: *sys.esp_bluedroid_config_t) !void {
|
||||
try errors.espCheckError(sys.esp_bluedroid_init_with_cfg(cfg));
|
||||
}
|
||||
|
||||
pub fn deinit() !void {
|
||||
try errors.espCheckError(sys.esp_bluedroid_deinit());
|
||||
}
|
||||
|
||||
pub fn enable() !void {
|
||||
try errors.espCheckError(sys.esp_bluedroid_enable());
|
||||
}
|
||||
|
||||
pub fn disable() !void {
|
||||
try errors.espCheckError(sys.esp_bluedroid_disable());
|
||||
}
|
||||
|
||||
pub fn getStatus() sys.esp_bluedroid_status_t {
|
||||
return sys.esp_bluedroid_get_status();
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Convenience: full BLE init / deinit sequence
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Initialise BT controller (BLE-only mode) + Bluedroid stack.
|
||||
/// Typical call sequence for BLE applications:
|
||||
/// 1. `idf.nvs.flashInitOrErase()`
|
||||
/// 2. `bluetooth.bleInit()`
|
||||
/// 3. Register GAP + GATT callbacks
|
||||
/// 4. `bluetooth.Gap.startAdvertising(¶ms)`
|
||||
pub fn bleInit() !void {
|
||||
var cfg = Controller.defaultConfig();
|
||||
// Release Classic BT memory — we only need BLE.
|
||||
try Controller.memRelease(.classic);
|
||||
try Controller.init(&cfg);
|
||||
try Controller.enable(.ble);
|
||||
try Bluedroid.init();
|
||||
try Bluedroid.enable();
|
||||
}
|
||||
|
||||
/// Graceful BLE teardown (reverse of `bleInit`).
|
||||
pub fn bleDeinit() !void {
|
||||
try Bluedroid.disable();
|
||||
try Bluedroid.deinit();
|
||||
try Controller.disable();
|
||||
try Controller.deinit();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Device
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Device = struct {
|
||||
/// Register a callback for generic BT device events.
|
||||
pub fn registerCallback(cb: sys.esp_bt_dev_cb_t) !void {
|
||||
try errors.espCheckError(sys.esp_bt_dev_register_callback(cb));
|
||||
}
|
||||
|
||||
/// Get the local BT/BLE MAC address (6 bytes, big-endian).
|
||||
pub fn getAddress() [*]const u8 {
|
||||
return sys.esp_bt_dev_get_address();
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GAP BLE
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Gap = struct {
|
||||
pub const CbT = sys.esp_gap_ble_cb_t;
|
||||
pub const AdvData = sys.esp_ble_adv_data_t;
|
||||
pub const AdvParams = sys.esp_ble_adv_params_t;
|
||||
pub const ScanParams = sys.esp_ble_scan_params_t;
|
||||
pub const ConnUpdateParams = sys.esp_ble_conn_update_params_t;
|
||||
pub const AddrType = sys.esp_ble_addr_type_t;
|
||||
pub const SmParam = sys.esp_ble_sm_param_t;
|
||||
pub const BondDev = sys.esp_ble_bond_dev_t;
|
||||
|
||||
pub fn registerCallback(cb: CbT) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_register_callback(cb));
|
||||
}
|
||||
|
||||
// -- Advertising ---------------------------------------------------------
|
||||
|
||||
pub fn configAdvData(adv_data: *AdvData) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_config_adv_data(adv_data));
|
||||
}
|
||||
|
||||
pub fn configAdvDataRaw(raw_data: []u8) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_config_adv_data_raw(raw_data.ptr, @intCast(raw_data.len)));
|
||||
}
|
||||
|
||||
pub fn configScanRspDataRaw(raw_data: []u8) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_config_scan_rsp_data_raw(raw_data.ptr, @intCast(raw_data.len)));
|
||||
}
|
||||
|
||||
pub fn startAdvertising(params: *AdvParams) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_start_advertising(params));
|
||||
}
|
||||
|
||||
pub fn stopAdvertising() !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_stop_advertising());
|
||||
}
|
||||
|
||||
// -- Scanning ------------------------------------------------------------
|
||||
|
||||
pub fn setScanParams(scan_params: *ScanParams) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_set_scan_params(scan_params));
|
||||
}
|
||||
|
||||
/// Start scanning for `duration` seconds (0 = indefinite).
|
||||
pub fn startScanning(duration: u32) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_start_scanning(duration));
|
||||
}
|
||||
|
||||
pub fn stopScanning() !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_stop_scanning());
|
||||
}
|
||||
|
||||
// -- Connection ----------------------------------------------------------
|
||||
|
||||
pub fn updateConnParams(params: *ConnUpdateParams) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_update_conn_params(params));
|
||||
}
|
||||
|
||||
pub fn disconnect(remote_bda: []u8) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_disconnect(remote_bda.ptr));
|
||||
}
|
||||
|
||||
pub fn readRssi(remote_addr: []u8) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_read_rssi(remote_addr.ptr));
|
||||
}
|
||||
|
||||
// -- Local identity ------------------------------------------------------
|
||||
|
||||
pub fn setDeviceName(name: [*:0]const u8) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_set_device_name(name));
|
||||
}
|
||||
|
||||
pub fn configLocalPrivacy(enable: bool) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_config_local_privacy(enable));
|
||||
}
|
||||
|
||||
// -- Security ------------------------------------------------------------
|
||||
|
||||
pub fn setSecurityParam(param: SmParam, value: *anyopaque, len: u8) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_set_security_param(param, value, len));
|
||||
}
|
||||
|
||||
pub fn securityReply(bd_addr: []u8, accept: bool) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_security_rsp(bd_addr.ptr, accept));
|
||||
}
|
||||
|
||||
pub fn passkeyReply(bd_addr: []u8, accept: bool, passkey: u32) !void {
|
||||
try errors.espCheckError(sys.esp_ble_passkey_reply(bd_addr.ptr, accept, passkey));
|
||||
}
|
||||
|
||||
pub fn confirmReply(bd_addr: []u8, accept: bool) !void {
|
||||
try errors.espCheckError(sys.esp_ble_confirm_reply(bd_addr.ptr, accept));
|
||||
}
|
||||
|
||||
// -- Bond management -----------------------------------------------------
|
||||
|
||||
pub fn removeBondDevice(bd_addr: []u8) !void {
|
||||
try errors.espCheckError(sys.esp_ble_remove_bond_device(bd_addr.ptr));
|
||||
}
|
||||
|
||||
pub fn getBondDeviceNum() c_int {
|
||||
return sys.esp_ble_get_bond_device_num();
|
||||
}
|
||||
|
||||
pub fn getBondDeviceList(dev_num: *c_int, dev_list: [*]BondDev) !void {
|
||||
try errors.espCheckError(sys.esp_ble_get_bond_device_list(dev_num, dev_list));
|
||||
}
|
||||
|
||||
// -- Whitelist -----------------------------------------------------------
|
||||
|
||||
pub fn updateWhitelist(add: bool, remote_bda: []u8, addr_type: sys.esp_ble_wl_addr_type_t) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_update_whitelist(add, remote_bda.ptr, addr_type));
|
||||
}
|
||||
|
||||
pub fn clearWhitelist() !void {
|
||||
try errors.espCheckError(sys.esp_ble_gap_clear_whitelist());
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GATT common
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const GattIf = sys.esp_gatt_if_t;
|
||||
pub const Uuid = sys.esp_bt_uuid_t;
|
||||
pub const GattStatus = sys.esp_gatt_status_t;
|
||||
pub const GattPerm = sys.esp_gatt_perm_t;
|
||||
pub const GattProp = sys.esp_gatt_char_prop_t;
|
||||
pub const AttrValue = sys.esp_attr_value_t;
|
||||
pub const AttrControl = sys.esp_attr_control_t;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GATT Server (GATTS)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const GattServer = struct {
|
||||
pub const CbT = sys.esp_gatts_cb_t;
|
||||
pub const SrvcId = sys.esp_gatt_srvc_id_t;
|
||||
pub const AttrDb = sys.esp_gatts_attr_db_t;
|
||||
pub const Rsp = sys.esp_gatt_rsp_t;
|
||||
|
||||
pub fn registerCallback(cb: CbT) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_register_callback(cb));
|
||||
}
|
||||
|
||||
pub fn appRegister(app_id: u16) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_app_register(app_id));
|
||||
}
|
||||
|
||||
pub fn appUnregister(gatts_if: GattIf) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_app_unregister(gatts_if));
|
||||
}
|
||||
|
||||
pub fn createService(gatts_if: GattIf, service_id: *SrvcId, num_handle: u16) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_create_service(gatts_if, service_id, num_handle));
|
||||
}
|
||||
|
||||
/// Create all attributes in one call using an attribute table.
|
||||
pub fn createAttrTab(attr_db: []const AttrDb, gatts_if: GattIf, srvc_inst_id: u8) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_create_attr_tab(attr_db.ptr, gatts_if, @intCast(attr_db.len), srvc_inst_id));
|
||||
}
|
||||
|
||||
pub fn addChar(
|
||||
service_handle: u16,
|
||||
char_uuid: *Uuid,
|
||||
perm: GattPerm,
|
||||
property: GattProp,
|
||||
char_val: ?*AttrValue,
|
||||
control: ?*AttrControl,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_add_char(service_handle, char_uuid, perm, property, char_val, control));
|
||||
}
|
||||
|
||||
pub fn addCharDescr(
|
||||
service_handle: u16,
|
||||
descr_uuid: *Uuid,
|
||||
perm: GattPerm,
|
||||
val: ?*AttrValue,
|
||||
control: ?*AttrControl,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_add_char_descr(service_handle, descr_uuid, perm, val, control));
|
||||
}
|
||||
|
||||
pub fn deleteService(service_handle: u16) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_delete_service(service_handle));
|
||||
}
|
||||
|
||||
pub fn startService(service_handle: u16) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_start_service(service_handle));
|
||||
}
|
||||
|
||||
pub fn stopService(service_handle: u16) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_stop_service(service_handle));
|
||||
}
|
||||
|
||||
/// Send a notification or indication to a connected client.
|
||||
pub fn sendIndicate(
|
||||
gatts_if: GattIf,
|
||||
conn_id: u16,
|
||||
attr_handle: u16,
|
||||
value: []u8,
|
||||
need_confirm: bool,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_send_indicate(
|
||||
gatts_if,
|
||||
conn_id,
|
||||
attr_handle,
|
||||
@intCast(value.len),
|
||||
value.ptr,
|
||||
need_confirm,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn sendResponse(
|
||||
gatts_if: GattIf,
|
||||
conn_id: u16,
|
||||
trans_id: u32,
|
||||
status: GattStatus,
|
||||
rsp: *Rsp,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_send_response(gatts_if, conn_id, trans_id, status, rsp));
|
||||
}
|
||||
|
||||
pub fn setAttrValue(attr_handle: u16, value: []const u8) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_set_attr_value(attr_handle, @intCast(value.len), value.ptr));
|
||||
}
|
||||
|
||||
pub fn getAttrValue(attr_handle: u16, length: *u16, value: *[*]const u8) GattStatus {
|
||||
return sys.esp_ble_gatts_get_attr_value(attr_handle, length, value);
|
||||
}
|
||||
|
||||
pub fn open(gatts_if: GattIf, remote_bda: []u8, is_direct: bool) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_open(gatts_if, remote_bda.ptr, is_direct));
|
||||
}
|
||||
|
||||
pub fn close(gatts_if: GattIf, conn_id: u16) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_close(gatts_if, conn_id));
|
||||
}
|
||||
|
||||
pub fn showLocalDatabase() !void {
|
||||
try errors.espCheckError(sys.esp_ble_gatts_show_local_database());
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GATT Client (GATTC)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const GattClient = struct {
|
||||
pub const CbT = sys.esp_gattc_cb_t;
|
||||
pub const SvcElem = sys.esp_gattc_service_elem_t;
|
||||
pub const CharElem = sys.esp_gattc_char_elem_t;
|
||||
pub const DescrElem = sys.esp_gattc_descr_elem_t;
|
||||
pub const WriteType = sys.esp_gatt_write_type_t;
|
||||
pub const AuthReq = sys.esp_gatt_auth_req_t;
|
||||
|
||||
pub fn registerCallback(cb: CbT) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gattc_register_callback(cb));
|
||||
}
|
||||
|
||||
pub fn appRegister(app_id: u16) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gattc_app_register(app_id));
|
||||
}
|
||||
|
||||
pub fn appUnregister(gattc_if: GattIf) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gattc_app_unregister(gattc_if));
|
||||
}
|
||||
|
||||
pub fn open(
|
||||
gattc_if: GattIf,
|
||||
remote_bda: []u8,
|
||||
addr_type: sys.esp_ble_addr_type_t,
|
||||
is_direct: bool,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gattc_open(gattc_if, remote_bda.ptr, addr_type, is_direct));
|
||||
}
|
||||
|
||||
pub fn close(gattc_if: GattIf, conn_id: u16) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gattc_close(gattc_if, conn_id));
|
||||
}
|
||||
|
||||
pub fn sendMtuReq(gattc_if: GattIf, conn_id: u16) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gattc_send_mtu_req(gattc_if, conn_id));
|
||||
}
|
||||
|
||||
pub fn searchService(gattc_if: GattIf, conn_id: u16, filter_uuid: ?*Uuid) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gattc_search_service(gattc_if, conn_id, filter_uuid));
|
||||
}
|
||||
|
||||
pub fn readChar(gattc_if: GattIf, conn_id: u16, handle: u16, auth_req: AuthReq) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gattc_read_char(gattc_if, conn_id, handle, auth_req));
|
||||
}
|
||||
|
||||
pub fn writeChar(
|
||||
gattc_if: GattIf,
|
||||
conn_id: u16,
|
||||
handle: u16,
|
||||
value: []u8,
|
||||
write_type: WriteType,
|
||||
auth_req: AuthReq,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gattc_write_char(
|
||||
gattc_if,
|
||||
conn_id,
|
||||
handle,
|
||||
@intCast(value.len),
|
||||
value.ptr,
|
||||
write_type,
|
||||
auth_req,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn writeCharDescr(
|
||||
gattc_if: GattIf,
|
||||
conn_id: u16,
|
||||
handle: u16,
|
||||
value: []u8,
|
||||
write_type: WriteType,
|
||||
auth_req: AuthReq,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gattc_write_char_descr(
|
||||
gattc_if,
|
||||
conn_id,
|
||||
handle,
|
||||
@intCast(value.len),
|
||||
value.ptr,
|
||||
write_type,
|
||||
auth_req,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn registerForNotify(gattc_if: GattIf, server_bda: []u8, handle: u16) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gattc_register_for_notify(gattc_if, server_bda.ptr, handle));
|
||||
}
|
||||
|
||||
pub fn unregisterForNotify(gattc_if: GattIf, server_bda: []u8, handle: u16) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gattc_unregister_for_notify(gattc_if, server_bda.ptr, handle));
|
||||
}
|
||||
|
||||
pub fn cacheRefresh(remote_bda: []u8) !void {
|
||||
try errors.espCheckError(sys.esp_ble_gattc_cache_refresh(remote_bda.ptr));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// WiFi/BT power domain (shared with wifi.zig)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const PowerDomain = struct {
|
||||
pub fn on() void {
|
||||
sys.esp_wifi_bt_power_domain_on();
|
||||
}
|
||||
pub fn off() void {
|
||||
sys.esp_wifi_bt_power_domain_off();
|
||||
}
|
||||
};
|
||||
631
software/zig_main/imports/bootloader.zig
Normal file
631
software/zig_main/imports/bootloader.zig
Normal file
@@ -0,0 +1,631 @@
|
||||
const sys = @import("sys");
|
||||
const std = @import("std");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Re-export register/device types directly from sys — they are opaque bitfield
|
||||
// unions that zig translate-c demoted; there is no benefit in redeclaring them.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const EfuseDev = sys.efuse_dev_t;
|
||||
pub const EfuseBlock = sys.ets_efuse_block_t;
|
||||
pub const EfusePurpose = sys.ets_efuse_purpose_t;
|
||||
pub const EtsStatus = sys.ets_status_t;
|
||||
pub const SecureBootStatus = sys.ets_secure_boot_status_t;
|
||||
pub const SecureBootSigBlock = sys.ets_secure_boot_sig_block_t;
|
||||
pub const SecureBootSignature = sys.ets_secure_boot_signature_t;
|
||||
pub const SecureBootKeyDigests = sys.ets_secure_boot_key_digests_t;
|
||||
pub const SecureBootSigBlockV1 = sys.esp_secure_boot_sig_block_t;
|
||||
pub const SecureBootIvDigest = sys.esp_secure_boot_iv_digest_t;
|
||||
pub const ImageSigPubKeyDigests = sys.esp_image_sig_public_key_digests_t;
|
||||
pub const RsaPubkey = sys.ets_rsa_pubkey_t;
|
||||
|
||||
pub const OtaSelectEntry = sys.esp_ota_select_entry_t;
|
||||
pub const PartitionPos = sys.esp_partition_pos_t;
|
||||
pub const PartitionInfo = sys.esp_partition_info_t;
|
||||
pub const BootloaderState = sys.bootloader_state_t;
|
||||
|
||||
pub const ImageHeader = sys.esp_image_header_t; // opaque
|
||||
pub const ImageSegmentHeader = sys.esp_image_segment_header_t;
|
||||
pub const ImageMetadata = sys.esp_image_metadata_t;
|
||||
pub const ImageFlashMapping = sys.esp_image_flash_mapping_t;
|
||||
pub const RtcRetainMem = sys.rtc_retain_mem_t;
|
||||
|
||||
pub const ChipId = sys.esp_chip_id_t;
|
||||
pub const SpiMode = sys.esp_image_spi_mode_t;
|
||||
pub const SpiFreq = sys.esp_image_spi_freq_t;
|
||||
pub const FlashSize = sys.esp_image_flash_size_t;
|
||||
pub const ImageLoadMode = sys.esp_image_load_mode_t;
|
||||
pub const ImageType = sys.esp_image_type;
|
||||
pub const GpioHold = sys.esp_comm_gpio_hold_t;
|
||||
|
||||
pub const EtsEvent = sys.ETSEvent;
|
||||
pub const EtsTask = sys.ETSTask;
|
||||
pub const EtsTimer = sys.ETSTimer;
|
||||
pub const EtsTimerFunc = sys.ETSTimerFunc;
|
||||
pub const EtsIsrFn = sys.ets_isr_t;
|
||||
pub const EtsIdleCb = sys.ets_idle_cb_t;
|
||||
|
||||
// The EFUSE peripheral register bank — memory-mapped, volatile.
|
||||
pub extern var EFUSE: EfuseDev;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Efuse — high-level ESP-IDF API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Efuse = struct {
|
||||
pub fn setTiming(clock: u32) !void {
|
||||
if (sys.ets_efuse_set_timing(clock) != 0) return error.EfuseTimingFailed;
|
||||
}
|
||||
|
||||
pub fn read() !void {
|
||||
if (sys.ets_efuse_read() != 0) return error.EfuseReadFailed;
|
||||
}
|
||||
|
||||
pub fn program(block: EfuseBlock) !void {
|
||||
if (sys.ets_efuse_program(block) != 0) return error.EfuseProgramFailed;
|
||||
}
|
||||
|
||||
pub fn clearProgramRegisters() void {
|
||||
sys.ets_efuse_clear_program_registers();
|
||||
}
|
||||
|
||||
pub fn writeKey(
|
||||
key_block: EfuseBlock,
|
||||
purpose: EfusePurpose,
|
||||
data: []const u8,
|
||||
) !void {
|
||||
if (sys.ets_efuse_write_key(key_block, purpose, data.ptr, data.len) != 0)
|
||||
return error.EfuseWriteKeyFailed;
|
||||
}
|
||||
|
||||
pub fn getReadRegisterAddress(block: EfuseBlock) u32 {
|
||||
return sys.ets_efuse_get_read_register_address(block);
|
||||
}
|
||||
|
||||
pub fn getKeyPurpose(block: EfuseBlock) EfusePurpose {
|
||||
return sys.ets_efuse_get_key_purpose(block);
|
||||
}
|
||||
|
||||
pub fn findPurpose(purpose: EfusePurpose, out_block: *EfuseBlock) bool {
|
||||
return sys.ets_efuse_find_purpose(purpose, out_block);
|
||||
}
|
||||
|
||||
pub fn keyBlockUnused(block: EfuseBlock) bool {
|
||||
return sys.ets_efuse_key_block_unused(block);
|
||||
}
|
||||
|
||||
pub fn findUnusedKeyBlock() EfuseBlock {
|
||||
return sys.ets_efuse_find_unused_key_block();
|
||||
}
|
||||
|
||||
pub fn countUnusedKeyBlocks() c_uint {
|
||||
return sys.ets_efuse_count_unused_key_blocks();
|
||||
}
|
||||
|
||||
pub fn rsCalculate(data: []const u8, rs_values: []u8) void {
|
||||
sys.ets_efuse_rs_calculate(data.ptr, rs_values.ptr);
|
||||
}
|
||||
|
||||
pub fn getSpiConfig() u32 {
|
||||
return sys.ets_efuse_get_spiconfig();
|
||||
}
|
||||
pub fn getWpPad() u32 {
|
||||
return sys.ets_efuse_get_wp_pad();
|
||||
}
|
||||
|
||||
pub fn downloadModesDisabled() bool {
|
||||
return sys.ets_efuse_download_modes_disabled();
|
||||
}
|
||||
pub fn legacySpiBootModeDisabled() bool {
|
||||
return sys.ets_efuse_legacy_spi_boot_mode_disabled();
|
||||
}
|
||||
pub fn getUartPrintControl() u32 {
|
||||
return sys.ets_efuse_get_uart_print_control();
|
||||
}
|
||||
pub fn usbSerialJtagPrintDisabled() u32 {
|
||||
return sys.ets_efuse_usb_serial_jtag_print_is_disabled();
|
||||
}
|
||||
pub fn usbDownloadModeDisabled() bool {
|
||||
return sys.ets_efuse_usb_download_mode_disabled();
|
||||
}
|
||||
pub fn usbModuleDisabled() bool {
|
||||
return sys.ets_efuse_usb_module_disabled();
|
||||
}
|
||||
pub fn securityDownloadModesEnabled() bool {
|
||||
return sys.ets_efuse_security_download_modes_enabled();
|
||||
}
|
||||
pub fn secureBootEnabled() bool {
|
||||
return sys.ets_efuse_secure_boot_enabled();
|
||||
}
|
||||
pub fn secureBootAggressiveRevokeEnabled() bool {
|
||||
return sys.ets_efuse_secure_boot_aggressive_revoke_enabled();
|
||||
}
|
||||
pub fn cacheEncryptionEnabled() bool {
|
||||
return sys.ets_efuse_cache_encryption_enabled();
|
||||
}
|
||||
pub fn flashOpi5padsPowerSelVddspi() bool {
|
||||
return sys.ets_efuse_flash_opi_5pads_power_sel_vddspi();
|
||||
}
|
||||
pub fn forceSendResume() bool {
|
||||
return sys.ets_efuse_force_send_resume();
|
||||
}
|
||||
pub fn getFlashDelayUs() u32 {
|
||||
return sys.ets_efuse_get_flash_delay_us();
|
||||
}
|
||||
|
||||
pub fn enableJtagTemporarily(hmac_key: []const u8, block: EfuseBlock) !void {
|
||||
if (sys.ets_jtag_enable_temporarily(hmac_key.ptr, block) != 0)
|
||||
return error.JtagEnableFailed;
|
||||
}
|
||||
|
||||
pub fn romMacAddressCrc8(data: []const u8) u8 {
|
||||
return sys.esp_rom_efuse_mac_address_crc8(data.ptr, @intCast(data.len));
|
||||
}
|
||||
pub fn romGetFlashGpioInfo() u32 {
|
||||
return sys.esp_rom_efuse_get_flash_gpio_info();
|
||||
}
|
||||
pub fn romGetFlashWpGpio() u32 {
|
||||
return sys.esp_rom_efuse_get_flash_wp_gpio();
|
||||
}
|
||||
pub fn romIsSecureBootEnabled() bool {
|
||||
return sys.esp_rom_efuse_is_secure_boot_enabled();
|
||||
}
|
||||
|
||||
/// CRC-8 over arbitrary data (ESP ROM implementation).
|
||||
pub fn crc8(data: []const u8) u8 {
|
||||
return sys.esp_crc8(data.ptr, @intCast(data.len));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// EFUSE register-level inline accessors (mirror efuse_ll_* from esp-idf).
|
||||
// These read directly from the memory-mapped EFUSE peripheral.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const EfuseLl = struct {
|
||||
pub fn getFlashCryptCnt() u32 {
|
||||
return sys.efuse_ll_get_flash_crypt_cnt();
|
||||
}
|
||||
pub fn getWdtDelaySel() u32 {
|
||||
return sys.efuse_ll_get_wdt_delay_sel();
|
||||
}
|
||||
pub fn getMac0() u32 {
|
||||
return sys.efuse_ll_get_mac0();
|
||||
}
|
||||
pub fn getMac1() u32 {
|
||||
return sys.efuse_ll_get_mac1();
|
||||
}
|
||||
pub fn getSecureBootV2En() bool {
|
||||
return sys.efuse_ll_get_secure_boot_v2_en();
|
||||
}
|
||||
pub fn getErrRstEnable() bool {
|
||||
return sys.efuse_ll_get_err_rst_enable();
|
||||
}
|
||||
pub fn getWaferVersionMajor() u32 {
|
||||
return sys.efuse_ll_get_chip_wafer_version_major();
|
||||
}
|
||||
pub fn getWaferVersionMinor() u32 {
|
||||
return sys.efuse_ll_get_chip_wafer_version_minor();
|
||||
}
|
||||
pub fn getDisableWaferVersionMajor() bool {
|
||||
return sys.efuse_ll_get_disable_wafer_version_major();
|
||||
}
|
||||
pub fn getBlkVersionMajor() u32 {
|
||||
return sys.efuse_ll_get_blk_version_major();
|
||||
}
|
||||
pub fn getBlkVersionMinor() u32 {
|
||||
return sys.efuse_ll_get_blk_version_minor();
|
||||
}
|
||||
pub fn getDisableBlkVersionMajor() bool {
|
||||
return sys.efuse_ll_get_disable_blk_version_major();
|
||||
}
|
||||
pub fn getChipVerPkg() u32 {
|
||||
return sys.efuse_ll_get_chip_ver_pkg();
|
||||
}
|
||||
pub fn getOcode() u32 {
|
||||
return sys.efuse_ll_get_ocode();
|
||||
}
|
||||
pub fn getKRtcLdo() u32 {
|
||||
return sys.efuse_ll_get_k_rtc_ldo();
|
||||
}
|
||||
pub fn getKDigLdo() u32 {
|
||||
return sys.efuse_ll_get_k_dig_ldo();
|
||||
}
|
||||
pub fn getVRtcDbias20() u32 {
|
||||
return sys.efuse_ll_get_v_rtc_dbias20();
|
||||
}
|
||||
pub fn getVDigDbias20() u32 {
|
||||
return sys.efuse_ll_get_v_dig_dbias20();
|
||||
}
|
||||
pub fn getDigDbias_hvt() u32 {
|
||||
return sys.efuse_ll_get_dig_dbias_hvt();
|
||||
}
|
||||
|
||||
pub fn isReadCmdPending() bool {
|
||||
return sys.efuse_ll_get_read_cmd();
|
||||
}
|
||||
pub fn isPgmCmdPending() bool {
|
||||
return sys.efuse_ll_get_pgm_cmd();
|
||||
}
|
||||
pub fn triggerReadCmd() void {
|
||||
sys.efuse_ll_set_read_cmd();
|
||||
}
|
||||
pub fn triggerPgmCmd(block: u32) void {
|
||||
sys.efuse_ll_set_pgm_cmd(block);
|
||||
}
|
||||
pub fn setConfReadOpCode() void {
|
||||
sys.efuse_ll_set_conf_read_op_code();
|
||||
}
|
||||
pub fn setConfWriteOpCode() void {
|
||||
sys.efuse_ll_set_conf_write_op_code();
|
||||
}
|
||||
pub fn setDacNum(val: u8) void {
|
||||
sys.efuse_ll_set_dac_num(val);
|
||||
}
|
||||
pub fn setDacClkDiv(val: u8) void {
|
||||
sys.efuse_ll_set_dac_clk_div(val);
|
||||
}
|
||||
pub fn setPwrOnNum(val: u16) void {
|
||||
sys.efuse_ll_set_pwr_on_num(val);
|
||||
}
|
||||
pub fn setPwrOffNum(val: u16) void {
|
||||
sys.efuse_ll_set_pwr_off_num(val);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Partition table
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Partition = struct {
|
||||
pub fn tableVerify(
|
||||
table: [*]const PartitionInfo,
|
||||
log_errors: bool,
|
||||
num_partitions: *c_int,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_partition_table_verify(table, log_errors, num_partitions));
|
||||
}
|
||||
|
||||
pub fn isFlashRegionWritable(addr: usize, size: usize) bool {
|
||||
return sys.esp_partition_is_flash_region_writable(addr, size);
|
||||
}
|
||||
|
||||
pub fn mainFlashRegionSafe(addr: usize, size: usize) bool {
|
||||
return sys.esp_partition_main_flash_region_safe(addr, size);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Image verification / loading
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Image = struct {
|
||||
pub fn verify(
|
||||
mode: ImageLoadMode,
|
||||
part: *const PartitionPos,
|
||||
data: ?*ImageMetadata,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_image_verify(mode, part, data));
|
||||
}
|
||||
|
||||
pub fn getMetadata(part: *const PartitionPos, metadata: *ImageMetadata) !void {
|
||||
try errors.espCheckError(sys.esp_image_get_metadata(part, metadata));
|
||||
}
|
||||
|
||||
pub fn getFlashSize(app_flash_size: FlashSize) c_int {
|
||||
return sys.esp_image_get_flash_size(app_flash_size);
|
||||
}
|
||||
|
||||
pub fn verifyBootloader(out_length: *u32) !void {
|
||||
try errors.espCheckError(sys.esp_image_verify_bootloader(out_length));
|
||||
}
|
||||
|
||||
pub fn verifyBootloaderData(data: *ImageMetadata) !void {
|
||||
try errors.espCheckError(sys.esp_image_verify_bootloader_data(data));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Bootloader
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Bootloader = struct {
|
||||
pub fn loadImage(part: *const PartitionPos, data: *ImageMetadata) !void {
|
||||
try errors.espCheckError(sys.bootloader_load_image(part, data));
|
||||
}
|
||||
|
||||
pub fn loadImageNoVerify(part: *const PartitionPos, data: *ImageMetadata) !void {
|
||||
try errors.espCheckError(sys.bootloader_load_image_no_verify(part, data));
|
||||
}
|
||||
|
||||
pub fn readOtadata(
|
||||
ota_info: *const PartitionPos,
|
||||
two_otadata: *[2]OtaSelectEntry,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.bootloader_common_read_otadata(ota_info, two_otadata));
|
||||
}
|
||||
|
||||
pub fn otaSelectCrc(s: *const OtaSelectEntry) u32 {
|
||||
return sys.bootloader_common_ota_select_crc(s);
|
||||
}
|
||||
|
||||
pub fn otaSelectValid(s: *const OtaSelectEntry) bool {
|
||||
return sys.bootloader_common_ota_select_valid(s);
|
||||
}
|
||||
|
||||
pub fn otaSelectInvalid(s: *const OtaSelectEntry) bool {
|
||||
return sys.bootloader_common_ota_select_invalid(s);
|
||||
}
|
||||
|
||||
pub fn checkLongHoldGpio(pin: u32, delay_sec: u32) GpioHold {
|
||||
return sys.bootloader_common_check_long_hold_gpio(pin, delay_sec);
|
||||
}
|
||||
|
||||
pub fn checkLongHoldGpioLevel(pin: u32, delay_sec: u32, level: bool) GpioHold {
|
||||
return sys.bootloader_common_check_long_hold_gpio_level(pin, delay_sec, level);
|
||||
}
|
||||
|
||||
pub fn erasePartTypeData(list_erase: [*:0]const u8, ota_data_erase: bool) bool {
|
||||
return sys.bootloader_common_erase_part_type_data(list_erase, ota_data_erase);
|
||||
}
|
||||
|
||||
pub fn labelSearch(list: [*:0]const u8, label: [*:0]u8) bool {
|
||||
return sys.bootloader_common_label_search(list, label);
|
||||
}
|
||||
|
||||
pub fn configureSpiPins(drv: c_int) void {
|
||||
sys.bootloader_configure_spi_pins(drv);
|
||||
}
|
||||
|
||||
pub fn getSha256OfPartition(
|
||||
address: u32,
|
||||
size: u32,
|
||||
part_type: c_int,
|
||||
out_sha_256: *[32]u8,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.bootloader_common_get_sha256_of_partition(address, size, part_type, out_sha_256));
|
||||
}
|
||||
|
||||
pub fn getActiveOtadata(two_otadata: *[2]OtaSelectEntry) c_int {
|
||||
return sys.bootloader_common_get_active_otadata(two_otadata);
|
||||
}
|
||||
|
||||
pub fn selectOtadata(
|
||||
two_otadata: *const [2]OtaSelectEntry,
|
||||
valid_two_otadata: *[2]bool,
|
||||
max: bool,
|
||||
) c_int {
|
||||
return sys.bootloader_common_select_otadata(two_otadata, valid_two_otadata, max);
|
||||
}
|
||||
|
||||
pub fn getChipVerPkg() u32 {
|
||||
return sys.bootloader_common_get_chip_ver_pkg();
|
||||
}
|
||||
|
||||
pub fn checkChipValidity(img_hdr: *const ImageHeader, kind: ImageType) !void {
|
||||
try errors.espCheckError(sys.bootloader_common_check_chip_validity(img_hdr, kind));
|
||||
}
|
||||
|
||||
pub fn vddsdioConf() void {
|
||||
sys.bootloader_common_vddsdio_configure();
|
||||
}
|
||||
|
||||
pub fn randomEnable() void {
|
||||
sys.bootloader_random_enable();
|
||||
}
|
||||
pub fn randomDisable() void {
|
||||
sys.bootloader_random_disable();
|
||||
}
|
||||
pub fn fillRandom(buf: []u8) void {
|
||||
sys.bootloader_fill_random(buf.ptr, buf.len);
|
||||
}
|
||||
|
||||
pub fn readBootloaderHeader() !void {
|
||||
try errors.espCheckError(sys.bootloader_read_bootloader_header());
|
||||
}
|
||||
|
||||
pub fn checkBootloaderValidity() !void {
|
||||
try errors.espCheckError(sys.bootloader_check_bootloader_validity());
|
||||
}
|
||||
|
||||
pub fn clearBssSection() void {
|
||||
sys.bootloader_clear_bss_section();
|
||||
}
|
||||
pub fn configWdt() void {
|
||||
sys.bootloader_config_wdt();
|
||||
}
|
||||
pub fn enableRandom() void {
|
||||
sys.bootloader_enable_random();
|
||||
}
|
||||
pub fn printBanner() void {
|
||||
sys.bootloader_print_banner();
|
||||
}
|
||||
|
||||
pub fn init() !void {
|
||||
try errors.espCheckError(sys.bootloader_init());
|
||||
}
|
||||
|
||||
pub fn flashEncrypt(bs: *BootloaderState) bool {
|
||||
return sys.flash_encrypt(bs);
|
||||
}
|
||||
|
||||
/// True if two flash regions [start1,end1) and [start2,end2) overlap.
|
||||
pub fn regionsOverlap(start1: isize, end1: isize, start2: isize, end2: isize) bool {
|
||||
return (end1 > start2) and (end2 > start1);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Secure boot
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const SecureBoot = struct {
|
||||
pub fn enabled() bool {
|
||||
// Reads directly from the efuse secure_boot_v2 bit — no ESP-IDF call needed.
|
||||
return EfuseLl.getSecureBootV2En();
|
||||
}
|
||||
|
||||
pub fn generateDigest() !void {
|
||||
try errors.espCheckError(sys.esp_secure_boot_generate_digest());
|
||||
}
|
||||
|
||||
pub fn permanentlyEnable() !void {
|
||||
try errors.espCheckError(sys.esp_secure_boot_permanently_enable());
|
||||
}
|
||||
|
||||
pub fn v2PermanentlyEnable(image_data: *const ImageMetadata) !void {
|
||||
try errors.espCheckError(sys.esp_secure_boot_v2_permanently_enable(image_data));
|
||||
}
|
||||
|
||||
pub fn verifySignature(src_addr: u32, length: u32) !void {
|
||||
try errors.espCheckError(sys.esp_secure_boot_verify_signature(src_addr, length));
|
||||
}
|
||||
|
||||
pub fn verifyEcdsaSignatureBlock(
|
||||
sig_block: *const SecureBootSigBlockV1,
|
||||
image_digest: [*:0]const u8,
|
||||
verified_digest: *u8,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_secure_boot_verify_ecdsa_signature_block(sig_block, image_digest, verified_digest));
|
||||
}
|
||||
|
||||
pub fn initChecks() void {
|
||||
sys.esp_secure_boot_init_checks();
|
||||
}
|
||||
|
||||
pub fn enableSecureFeatures() !void {
|
||||
try errors.espCheckError(sys.esp_secure_boot_enable_secure_features());
|
||||
}
|
||||
|
||||
pub fn cfgVerifyReleaseMode() bool {
|
||||
return sys.esp_secure_boot_cfg_verify_release_mode();
|
||||
}
|
||||
|
||||
// ROM-level secure boot ---------------------------------------------------
|
||||
|
||||
pub fn romVerifyBootloaderWithKeys(
|
||||
verified_hash: *u8,
|
||||
trusted_keys: *const SecureBootKeyDigests,
|
||||
stage_load: bool,
|
||||
) SecureBootStatus {
|
||||
return sys.ets_secure_boot_verify_bootloader_with_keys(verified_hash, trusted_keys, stage_load);
|
||||
}
|
||||
|
||||
pub fn romReadKeyDigests(trusted_keys: *SecureBootKeyDigests) EtsStatus {
|
||||
return sys.ets_secure_boot_read_key_digests(trusted_keys);
|
||||
}
|
||||
|
||||
pub fn romVerifySignature(
|
||||
sig: *const SecureBootSignature,
|
||||
image_digest: [*:0]const u8,
|
||||
trusted_keys: *const SecureBootKeyDigests,
|
||||
verified_digest: *u8,
|
||||
) SecureBootStatus {
|
||||
return sys.ets_secure_boot_verify_signature(sig, image_digest, trusted_keys, verified_digest);
|
||||
}
|
||||
|
||||
pub fn romRevokePublicKeyDigest(index: c_int) void {
|
||||
sys.ets_secure_boot_revoke_public_key_digest(index);
|
||||
}
|
||||
|
||||
pub fn romRsaPssVerify(
|
||||
key: *const RsaPubkey,
|
||||
sig: [*:0]const u8,
|
||||
digest: [*:0]const u8,
|
||||
verified_digest: *u8,
|
||||
) bool {
|
||||
return sys.ets_rsa_pss_verify(key, sig, digest, verified_digest);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ETS (ROM system services)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Ets = struct {
|
||||
pub fn printf(fmt: [*:0]const u8, args: anytype) c_int {
|
||||
return @call(.auto, sys.ets_printf, .{fmt} ++ args);
|
||||
}
|
||||
|
||||
pub fn delayUs(us: u32) void {
|
||||
sys.ets_delay_us(us);
|
||||
}
|
||||
pub fn getCpuFrequency() u32 {
|
||||
return sys.ets_get_cpu_frequency();
|
||||
}
|
||||
pub fn updateCpuFrequency(ticks_per_us: u32) void {
|
||||
sys.ets_update_cpu_frequency(ticks_per_us);
|
||||
}
|
||||
pub fn getXtalFreq() u32 {
|
||||
return sys.ets_get_xtal_freq();
|
||||
}
|
||||
pub fn getXtalDiv() u32 {
|
||||
return sys.ets_get_xtal_div();
|
||||
}
|
||||
pub fn getApbFreq() u32 {
|
||||
return sys.ets_get_apb_freq();
|
||||
}
|
||||
|
||||
pub fn isrAttach(irq: c_int, func: EtsIsrFn, arg: ?*anyopaque) void {
|
||||
sys.ets_isr_attach(irq, func, arg);
|
||||
}
|
||||
pub fn isrMask(mask: u32) void {
|
||||
sys.ets_isr_mask(mask);
|
||||
}
|
||||
pub fn isrUnmask(mask: u32) void {
|
||||
sys.ets_isr_unmask(mask);
|
||||
}
|
||||
pub fn intrLock() void {
|
||||
sys.ets_intr_lock();
|
||||
}
|
||||
pub fn intrUnlock() void {
|
||||
sys.ets_intr_unlock();
|
||||
}
|
||||
|
||||
pub fn setUserStart(start: u32) void {
|
||||
sys.ets_set_user_start(start);
|
||||
}
|
||||
pub fn installPutc1(p: ?*const fn (u8) callconv(.c) void) void {
|
||||
sys.ets_install_putc1(p);
|
||||
}
|
||||
pub fn installPutc2(p: ?*const fn (u8) callconv(.c) void) void {
|
||||
sys.ets_install_putc2(p);
|
||||
}
|
||||
pub fn installUartPrintf() void {
|
||||
sys.ets_install_uart_printf();
|
||||
}
|
||||
|
||||
pub fn intr_matrix_set(cpu_no: c_int, model_num: u32, intr_num: u32) void {
|
||||
sys.intr_matrix_set(cpu_no, model_num, intr_num);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ETS Timer
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const EtsTimerApi = struct {
|
||||
pub fn init() void {
|
||||
sys.ets_timer_init();
|
||||
}
|
||||
pub fn deinit() void {
|
||||
sys.ets_timer_deinit();
|
||||
}
|
||||
|
||||
pub fn arm(timer: *EtsTimer, timeout_ms: u32, repeat: bool) void {
|
||||
sys.ets_timer_arm(timer, timeout_ms, repeat);
|
||||
}
|
||||
pub fn armUs(timer: *EtsTimer, us: u32, repeat: bool) void {
|
||||
sys.ets_timer_arm_us(timer, us, repeat);
|
||||
}
|
||||
pub fn disarm(timer: *EtsTimer) void {
|
||||
sys.ets_timer_disarm(timer);
|
||||
}
|
||||
pub fn setFn(timer: *EtsTimer, func: ?*const EtsTimerFunc, arg: ?*anyopaque) void {
|
||||
sys.ets_timer_setfn(timer, func, arg);
|
||||
}
|
||||
pub fn done(timer: *EtsTimer) void {
|
||||
sys.ets_timer_done(timer);
|
||||
}
|
||||
};
|
||||
51
software/zig_main/imports/crc.zig
Normal file
51
software/zig_main/imports/crc.zig
Normal file
@@ -0,0 +1,51 @@
|
||||
const sys = @import("sys");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CRC helpers — thin wrappers around the ESP-ROM implementations.
|
||||
//
|
||||
// All functions take a running `crc` (pass 0 for the first block) and a
|
||||
// data slice, and return the updated CRC value.
|
||||
//
|
||||
// Le = reflected polynomial (little-endian / standard CRC convention).
|
||||
// Be = non-reflected polynomial (big-endian convention).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// ── CRC-8 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// CRC-8 with reflected (little-endian) polynomial.
|
||||
pub fn crc8Le(crc: u8, buf: []const u8) u8 {
|
||||
return sys.esp_rom_crc8_le(crc, buf.ptr, buf.len);
|
||||
}
|
||||
|
||||
/// CRC-8 with non-reflected (big-endian) polynomial.
|
||||
pub fn crc8Be(crc: u8, buf: []const u8) u8 {
|
||||
return sys.esp_rom_crc8_be(crc, buf.ptr, buf.len);
|
||||
}
|
||||
|
||||
// ── CRC-16 ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// CRC-16 with reflected (little-endian) polynomial.
|
||||
pub fn crc16Le(crc: u16, buf: []const u8) u16 {
|
||||
return sys.esp_rom_crc16_le(crc, buf.ptr, buf.len);
|
||||
}
|
||||
|
||||
/// CRC-16 with non-reflected (big-endian) polynomial.
|
||||
pub fn crc16Be(crc: u16, buf: []const u8) u16 {
|
||||
return sys.esp_rom_crc16_be(crc, buf.ptr, buf.len);
|
||||
}
|
||||
|
||||
// ── CRC-32 ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// CRC-32 with reflected (little-endian) polynomial — compatible with
|
||||
/// zlib / Ethernet / standard CRC-32.
|
||||
pub fn crc32Le(crc: u32, buf: []const u8) u32 {
|
||||
return sys.esp_rom_crc32_le(crc, buf.ptr, buf.len);
|
||||
}
|
||||
|
||||
/// CRC-32 with non-reflected (big-endian) polynomial.
|
||||
pub fn crc32Be(crc: u32, buf: []const u8) u32 {
|
||||
return sys.esp_rom_crc32_be(crc, buf.ptr, buf.len);
|
||||
}
|
||||
|
||||
// ── Zig stdlib CRC (kept for pure-Zig use without ROM calls) ──────────────
|
||||
pub const zigCRC32 = @import("std").hash.crc;
|
||||
440
software/zig_main/imports/dsp.zig
Normal file
440
software/zig_main/imports/dsp.zig
Normal file
@@ -0,0 +1,440 @@
|
||||
// ESP-DSP (Digital Signal Processing) Library Wrapper
|
||||
// requires: idf.py add-dependency espressif/esp-dsp
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
const std = @import("std");
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Complex number types
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// Complex number with 16-bit integer components (short complex)
|
||||
pub const Complex16 = extern struct {
|
||||
re: i16,
|
||||
im: i16,
|
||||
|
||||
pub fn init(re: i16, im: i16) Complex16 {
|
||||
return .{ .re = re, .im = im };
|
||||
}
|
||||
|
||||
pub fn fromUnion(u: sys.sc16_t) Complex16 {
|
||||
return .{ .re = u.unnamed_0.re, .im = u.unnamed_0.im };
|
||||
}
|
||||
|
||||
pub fn toUnion(self: Complex16) sys.sc16_t {
|
||||
return .{ .unnamed_0 = .{ .re = self.re, .im = self.im } };
|
||||
}
|
||||
};
|
||||
|
||||
/// Complex number with 32-bit float components (float complex)
|
||||
pub const Complex32 = extern struct {
|
||||
re: f32,
|
||||
im: f32,
|
||||
|
||||
pub fn init(re: f32, im: f32) Complex32 {
|
||||
return .{ .re = re, .im = im };
|
||||
}
|
||||
|
||||
pub fn fromUnion(u: sys.fc32_t) Complex32 {
|
||||
return .{ .re = u.unnamed_0.re, .im = u.unnamed_0.im };
|
||||
}
|
||||
|
||||
pub fn toUnion(self: Complex32) sys.fc32_t {
|
||||
return .{ .unnamed_0 = .{ .re = self.re, .im = self.im } };
|
||||
}
|
||||
|
||||
pub fn magnitude(self: Complex32) f32 {
|
||||
return @sqrt(self.re * self.re + self.im * self.im);
|
||||
}
|
||||
|
||||
pub fn phase(self: Complex32) f32 {
|
||||
return std.math.atan2(self.im, self.re);
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Image/2D data structure
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const Image2D = extern struct {
|
||||
data: ?*anyopaque = null,
|
||||
step_x: c_int = 0,
|
||||
step_y: c_int = 0,
|
||||
stride_x: c_int = 0,
|
||||
stride_y: c_int = 0,
|
||||
|
||||
pub fn init(data: *anyopaque, step_x: i32, step_y: i32, stride_x: i32, stride_y: i32) Image2D {
|
||||
return .{ .data = data, .step_x = step_x, .step_y = step_y, .stride_x = stride_x, .stride_y = stride_y };
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Filter structures
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// FIR filter with float32 coefficients
|
||||
pub const FirF32 = struct {
|
||||
inner: sys.fir_f32_t,
|
||||
|
||||
pub fn init(coeffs: []f32, delay: []f32) !FirF32 {
|
||||
if (coeffs.len != delay.len) return error.LengthMismatch;
|
||||
|
||||
var fir: sys.fir_f32_t = undefined;
|
||||
try errors.espCheckError(sys.dsps_fir_init_f32(&fir, coeffs.ptr, delay.ptr, @intCast(coeffs.len)));
|
||||
|
||||
return .{ .inner = fir };
|
||||
}
|
||||
|
||||
pub fn initDecimated(coeffs: []f32, delay: []f32, decimation: u32) !FirF32 {
|
||||
if (coeffs.len != delay.len) return error.LengthMismatch;
|
||||
|
||||
var fir: sys.fir_f32_t = undefined;
|
||||
try errors.espCheckError(sys.dsps_fird_init_f32(&fir, coeffs.ptr, delay.ptr, @intCast(coeffs.len), @intCast(decimation)));
|
||||
|
||||
return .{ .inner = fir };
|
||||
}
|
||||
|
||||
pub fn process(self: *FirF32, input: []const f32, output: []f32) !void {
|
||||
if (input.len != output.len) return error.LengthMismatch;
|
||||
try errors.espCheckError(sys.dsps_fir_f32_ae32(&self.inner, input.ptr, output.ptr, @intCast(input.len)));
|
||||
}
|
||||
|
||||
pub fn deinit(self: *FirF32) !void {
|
||||
try errors.espCheckError(sys.dsps_fir_f32_free(&self.inner));
|
||||
}
|
||||
};
|
||||
|
||||
/// FIR filter with int16 coefficients
|
||||
pub const FirS16 = struct {
|
||||
inner: sys.fir_s16_t,
|
||||
|
||||
pub fn initDecimated(coeffs: []i16, delay: []i16, decimation: u16, start_pos: u16, shift: u16) !FirS16 {
|
||||
if (coeffs.len != delay.len) return error.LengthMismatch;
|
||||
|
||||
var fir: sys.fir_s16_t = undefined;
|
||||
try errors.espCheckError(sys.dsps_fird_init_s16(&fir, coeffs.ptr, delay.ptr, @intCast(coeffs.len), @intCast(decimation), @intCast(start_pos), @intCast(shift)));
|
||||
|
||||
return .{ .inner = fir };
|
||||
}
|
||||
|
||||
pub fn process(self: *FirS16, input: []const i16, output: []i16) !u32 {
|
||||
if (input.len != output.len) return error.LengthMismatch;
|
||||
const result = sys.dsps_fird_s16_aes3(&self.inner, input.ptr, output.ptr, @intCast(input.len));
|
||||
return @intCast(result);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *FirS16) !void {
|
||||
try errors.espCheckError(sys.dsps_fird_s16_aexx_free(&self.inner));
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// FFT operations
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const FFT = struct {
|
||||
/// Initialize FFT with radix-2 (power of 2 sizes)
|
||||
pub fn initRadix2F32(table_buffer: []f32, table_size: u32) !void {
|
||||
try errors.espCheckError(sys.dsps_fft2r_init_fc32(table_buffer.ptr, @intCast(table_size)));
|
||||
}
|
||||
|
||||
pub fn deinitRadix2F32() void {
|
||||
sys.dsps_fft2r_deinit_fc32();
|
||||
}
|
||||
|
||||
/// Perform radix-2 FFT on complex data (in-place)
|
||||
/// data must be [re0, im0, re1, im1, ...] format
|
||||
pub fn fft2rF32(data: []f32, fft_size: u32, w_table: []f32) !void {
|
||||
try errors.espCheckError(sys.dsps_fft2r_fc32_aes3_(data.ptr, @intCast(fft_size), w_table.ptr));
|
||||
}
|
||||
|
||||
/// Bit-reverse reordering for FFT
|
||||
pub fn bitReverseF32(data: []f32, fft_size: u32) !void {
|
||||
try errors.espCheckError(sys.dsps_bit_rev_fc32_ansi(data.ptr, @intCast(fft_size)));
|
||||
}
|
||||
|
||||
/// Initialize FFT with radix-4 (optimized for power of 4 sizes)
|
||||
pub fn initRadix4F32(table_buffer: []f32, max_fft_size: u32) !void {
|
||||
try errors.espCheckError(sys.dsps_fft4r_init_fc32(table_buffer.ptr, @intCast(max_fft_size)));
|
||||
}
|
||||
|
||||
pub fn deinitRadix4F32() void {
|
||||
sys.dsps_fft4r_deinit_fc32();
|
||||
}
|
||||
|
||||
/// Perform radix-4 FFT
|
||||
pub fn fft4rF32(data: []f32, fft_size: u32, w_table: []f32) !void {
|
||||
try errors.espCheckError(sys.dsps_fft4r_fc32_ae32_(data.ptr, @intCast(fft_size), w_table.ptr, @intCast(w_table.len)));
|
||||
}
|
||||
|
||||
/// Convert complex FFT output to real signal
|
||||
pub fn complex2RealF32(data: []f32, fft_size: u32, w_table: []f32) !void {
|
||||
try errors.espCheckError(sys.dsps_cplx2real_fc32_ae32_(data.ptr, @intCast(fft_size), w_table.ptr, @intCast(w_table.len)));
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Basic math operations
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const Math = struct {
|
||||
/// Vector addition: output = input1 + input2
|
||||
pub fn addF32(input1: []const f32, input2: []const f32, output: []f32) !void {
|
||||
if (input1.len != input2.len or input1.len != output.len) {
|
||||
return error.LengthMismatch;
|
||||
}
|
||||
try errors.espCheckError(sys.dsps_add_f32_ae32(input1.ptr, input2.ptr, output.ptr, @intCast(input1.len), 1, 1, 1));
|
||||
}
|
||||
|
||||
/// Vector addition (16-bit): output = input1 + input2
|
||||
pub fn addS16(input1: []const i16, input2: []const i16, output: []i16, shift: i32) !void {
|
||||
if (input1.len != input2.len or input1.len != output.len) {
|
||||
return error.LengthMismatch;
|
||||
}
|
||||
try errors.espCheckError(sys.dsps_add_s16_aes3(input1.ptr, input2.ptr, output.ptr, @intCast(input1.len), 1, 1, 1, shift));
|
||||
}
|
||||
|
||||
/// Vector subtraction: output = input1 - input2
|
||||
pub fn subF32(input1: []const f32, input2: []const f32, output: []f32) !void {
|
||||
if (input1.len != input2.len or input1.len != output.len) {
|
||||
return error.LengthMismatch;
|
||||
}
|
||||
try errors.espCheckError(sys.dsps_sub_f32_ae32(input1.ptr, input2.ptr, output.ptr, @intCast(input1.len), 1, 1, 1));
|
||||
}
|
||||
|
||||
/// Vector multiplication: output = input1 * input2
|
||||
pub fn mulF32(input1: []const f32, input2: []const f32, output: []f32) !void {
|
||||
if (input1.len != input2.len or input1.len != output.len) {
|
||||
return error.LengthMismatch;
|
||||
}
|
||||
try errors.espCheckError(sys.dsps_mul_f32_ae32(input1.ptr, input2.ptr, output.ptr, @intCast(input1.len), 1, 1, 1));
|
||||
}
|
||||
|
||||
/// Scalar addition: output = input + C
|
||||
pub fn addConstF32(input: []const f32, output: []f32, constant: f32) !void {
|
||||
if (input.len != output.len) return error.LengthMismatch;
|
||||
try errors.espCheckError(sys.dsps_addc_f32_ae32(input.ptr, output.ptr, @intCast(input.len), constant, 1, 1));
|
||||
}
|
||||
|
||||
/// Scalar multiplication: output = input * C
|
||||
pub fn mulConstF32(input: []const f32, output: []f32, constant: f32) !void {
|
||||
if (input.len != output.len) return error.LengthMismatch;
|
||||
try errors.espCheckError(sys.dsps_mulc_f32_ae32(input.ptr, output.ptr, @intCast(input.len), constant, 1, 1));
|
||||
}
|
||||
|
||||
/// Element-wise square root
|
||||
pub fn sqrtF32(input: []const f32, output: []f32) !void {
|
||||
if (input.len != output.len) return error.LengthMismatch;
|
||||
try errors.espCheckError(sys.dsps_sqrt_f32_ansi(input.ptr, output.ptr, @intCast(input.len)));
|
||||
}
|
||||
|
||||
/// Dot product: result = sum(src1[i] * src2[i])
|
||||
pub fn dotProductF32(src1: []const f32, src2: []const f32) !f32 {
|
||||
if (src1.len != src2.len) return error.LengthMismatch;
|
||||
var result: f32 = 0;
|
||||
try errors.espCheckError(sys.dsps_dotprod_f32_aes3(src1.ptr, src2.ptr, &result, @intCast(src1.len)));
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Dot product (16-bit)
|
||||
pub fn dotProductS16(src1: []const i16, src2: []const i16, shift: i8) !i16 {
|
||||
if (src1.len != src2.len) return error.LengthMismatch;
|
||||
var result: i16 = 0;
|
||||
try errors.espCheckError(sys.dsps_dotprod_s16_ae32(src1.ptr, src2.ptr, &result, @intCast(src1.len), shift));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Matrix operations
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const Matrix = struct {
|
||||
/// Matrix multiplication: C = A * B
|
||||
/// A: m x k, B: k x n, C: m x n
|
||||
pub fn multiplyF32(A: []const f32, B: []const f32, C: []f32, m: u32, n: u32, k: u32) !void {
|
||||
try errors.espCheckError(sys.dspm_mult_f32_aes3(A.ptr, B.ptr, C.ptr, @intCast(m), @intCast(n), @intCast(k)));
|
||||
}
|
||||
|
||||
/// Matrix addition: output = input1 + input2
|
||||
pub fn addF32(input1: []const f32, input2: []const f32, output: []f32, rows: u32, cols: u32) !void {
|
||||
try errors.espCheckError(sys.dspm_add_f32_ae32(input1.ptr, input2.ptr, output.ptr, @intCast(rows), @intCast(cols), 0, 0, 0, 1, 1, 1));
|
||||
}
|
||||
|
||||
/// Matrix subtraction: output = input1 - input2
|
||||
pub fn subF32(input1: []const f32, input2: []const f32, output: []f32, rows: u32, cols: u32) !void {
|
||||
try errors.espCheckError(sys.dspm_sub_f32_ae32(input1.ptr, input2.ptr, output.ptr, @intCast(rows), @intCast(cols), 0, 0, 0, 1, 1, 1));
|
||||
}
|
||||
|
||||
/// Scalar matrix multiplication: output = input * C
|
||||
pub fn mulConstF32(input: []const f32, output: []f32, constant: f32, rows: u32, cols: u32) !void {
|
||||
try errors.espCheckError(sys.dspm_mulc_f32_ae32(input.ptr, output.ptr, constant, @intCast(rows), @intCast(cols), 0, 0, 1, 1));
|
||||
}
|
||||
|
||||
/// Optimized 3x3 matrix multiplication
|
||||
pub fn multiply3x3F32(A: []const f32, B: []const f32, C: []f32) !void {
|
||||
if (A.len < 9 or B.len < 9 or C.len < 9) return error.InvalidSize;
|
||||
try errors.espCheckError(sys.dspm_mult_3x3x3_f32_ae32(A.ptr, B.ptr, C.ptr));
|
||||
}
|
||||
|
||||
/// Optimized 4x4 matrix multiplication
|
||||
pub fn multiply4x4F32(A: []const f32, B: []const f32, C: []f32) !void {
|
||||
if (A.len < 16 or B.len < 16 or C.len < 16) return error.InvalidSize;
|
||||
try errors.espCheckError(sys.dspm_mult_4x4x4_f32_ae32(A.ptr, B.ptr, C.ptr));
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Window functions
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const Window = struct {
|
||||
pub fn hann(window: []f32) void {
|
||||
sys.dsps_wind_hann_f32(window.ptr, @intCast(window.len));
|
||||
}
|
||||
|
||||
pub fn blackman(window: []f32) void {
|
||||
sys.dsps_wind_blackman_f32(window.ptr, @intCast(window.len));
|
||||
}
|
||||
|
||||
pub fn blackmanHarris(window: []f32) void {
|
||||
sys.dsps_wind_blackman_harris_f32(window.ptr, @intCast(window.len));
|
||||
}
|
||||
|
||||
pub fn blackmanNuttall(window: []f32) void {
|
||||
sys.dsps_wind_blackman_nuttall_f32(window.ptr, @intCast(window.len));
|
||||
}
|
||||
|
||||
pub fn nuttall(window: []f32) void {
|
||||
sys.dsps_wind_nuttall_f32(window.ptr, @intCast(window.len));
|
||||
}
|
||||
|
||||
pub fn flatTop(window: []f32) void {
|
||||
sys.dsps_wind_flat_top_f32(window.ptr, @intCast(window.len));
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Biquad IIR filter
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const BiquadFilterType = enum {
|
||||
lowpass,
|
||||
highpass,
|
||||
bandpass,
|
||||
bandpass_0db,
|
||||
notch,
|
||||
allpass_360,
|
||||
allpass_180,
|
||||
peaking_eq,
|
||||
low_shelf,
|
||||
high_shelf,
|
||||
};
|
||||
|
||||
pub const BiquadFilter = struct {
|
||||
coeffs: [5]f32,
|
||||
state: [2]f32,
|
||||
|
||||
pub fn init(filter_type: BiquadFilterType, freq: f32, q_factor: f32, gain: f32) !BiquadFilter {
|
||||
var coeffs: [5]f32 = undefined;
|
||||
|
||||
const result = switch (filter_type) {
|
||||
.lowpass => sys.dsps_biquad_gen_lpf_f32(&coeffs, freq, q_factor),
|
||||
.highpass => sys.dsps_biquad_gen_hpf_f32(&coeffs, freq, q_factor),
|
||||
.bandpass => sys.dsps_biquad_gen_bpf_f32(&coeffs, freq, q_factor),
|
||||
.bandpass_0db => sys.dsps_biquad_gen_bpf0db_f32(&coeffs, freq, q_factor),
|
||||
.notch => sys.dsps_biquad_gen_notch_f32(&coeffs, freq, gain, q_factor),
|
||||
.allpass_360 => sys.dsps_biquad_gen_allpass360_f32(&coeffs, freq, q_factor),
|
||||
.allpass_180 => sys.dsps_biquad_gen_allpass180_f32(&coeffs, freq, q_factor),
|
||||
.peaking_eq => sys.dsps_biquad_gen_peakingEQ_f32(&coeffs, freq, q_factor),
|
||||
.low_shelf => sys.dsps_biquad_gen_lowShelf_f32(&coeffs, freq, gain, q_factor),
|
||||
.high_shelf => sys.dsps_biquad_gen_highShelf_f32(&coeffs, freq, gain, q_factor),
|
||||
};
|
||||
|
||||
try errors.espCheckError(result);
|
||||
|
||||
return .{ .coeffs = coeffs, .state = std.mem.zeroes([2]f32) };
|
||||
}
|
||||
|
||||
pub fn process(self: *BiquadFilter, input: []const f32, output: []f32) !void {
|
||||
if (input.len != output.len) return error.LengthMismatch;
|
||||
try errors.espCheckError(sys.dsps_biquad_f32_aes3(input.ptr, output.ptr, @intCast(input.len), &self.coeffs, &self.state));
|
||||
}
|
||||
|
||||
pub fn reset(self: *BiquadFilter) void {
|
||||
self.state = std.mem.zeroes([2]f32);
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Convolution and correlation
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const Signal = struct {
|
||||
/// Convolution: convout = Signal ⊛ Kernel
|
||||
pub fn convolveF32(signal: []const f32, kernel: []const f32, output: []f32) !void {
|
||||
try errors.espCheckError(sys.dsps_conv_f32_ae32(signal.ptr, @intCast(signal.len), kernel.ptr, @intCast(kernel.len), output.ptr));
|
||||
}
|
||||
|
||||
/// Cross-correlation: dest = Signal ⋆ Pattern
|
||||
pub fn correlateF32(signal: []const f32, pattern: []const f32, output: []f32) !void {
|
||||
try errors.espCheckError(sys.dsps_corr_f32_ae32(signal.ptr, @intCast(signal.len), pattern.ptr, @intCast(pattern.len), output.ptr));
|
||||
}
|
||||
|
||||
/// Generate tone: output = Ampl * sin(2π * freq * t + phase)
|
||||
pub fn generateToneF32(output: []f32, amplitude: f32, frequency: f32, phase: f32) !void {
|
||||
try errors.espCheckError(sys.dsps_tone_gen_f32(output.ptr, @intCast(output.len), amplitude, frequency, phase));
|
||||
}
|
||||
|
||||
/// Calculate Signal-to-Noise Ratio
|
||||
pub fn snrF32(input: []const f32, use_dc: bool) f32 {
|
||||
return sys.dsps_snr_f32(input.ptr, @intCast(input.len), if (use_dc) 1 else 0);
|
||||
}
|
||||
|
||||
/// Calculate Spurious-Free Dynamic Range
|
||||
pub fn sfdrF32(input: []const f32, use_dc: bool) f32 {
|
||||
return sys.dsps_sfdr_f32(input.ptr, @intCast(input.len), if (use_dc) 1 else 0);
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// DCT (Discrete Cosine Transform)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const DCT = struct {
|
||||
/// Forward DCT (in-place)
|
||||
pub fn forwardF32(data: []f32) !void {
|
||||
try errors.espCheckError(sys.dsps_dct_f32(data.ptr, @intCast(data.len)));
|
||||
}
|
||||
|
||||
/// Inverse DCT (in-place)
|
||||
pub fn inverseF32(data: []f32) !void {
|
||||
try errors.espCheckError(sys.dsps_dct_inv_f32(data.ptr, @intCast(data.len)));
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Utility functions
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const Utils = struct {
|
||||
/// Check if a number is a power of two
|
||||
pub fn isPowerOfTwo(x: i32) bool {
|
||||
return sys.dsp_is_power_of_two(x);
|
||||
}
|
||||
|
||||
/// Get the power of two value (e.g., 3 → 8, 4 → 16)
|
||||
pub fn powerOfTwo(x: i32) i32 {
|
||||
return sys.dsp_power_of_two(x);
|
||||
}
|
||||
|
||||
/// Visualize signal data (for debugging)
|
||||
pub fn viewSignalF32(data: []const f32, width: u32, height: u32, min: f32, max: f32) void {
|
||||
sys.dsps_view(data.ptr, @intCast(data.len), @intCast(width), @intCast(height), min, max, '*');
|
||||
}
|
||||
|
||||
/// Visualize spectrum data (for debugging)
|
||||
pub fn viewSpectrumF32(data: []const f32, min: f32, max: f32) void {
|
||||
sys.dsps_view_spectrum(data.ptr, @intCast(data.len), min, max);
|
||||
}
|
||||
};
|
||||
58
software/zig_main/imports/error.zig
Normal file
58
software/zig_main/imports/error.zig
Normal file
@@ -0,0 +1,58 @@
|
||||
const sys = @import("sys");
|
||||
const std = @import("std");
|
||||
|
||||
// Zig error
|
||||
const esp_error = error{
|
||||
Fail,
|
||||
ErrorNoMem,
|
||||
ErrorInvalidArg,
|
||||
ErrorInvalidState,
|
||||
ErrorInvalidSize,
|
||||
ErrorNotFound,
|
||||
ErrorNotSupported,
|
||||
ErrorTimeout,
|
||||
ErrorInvalidResponse,
|
||||
ErrorInvalidCRC,
|
||||
ErrorInvalidVersion,
|
||||
ErrorInvalidMAC,
|
||||
ErrorNotFinished,
|
||||
ErrorNotAllowed,
|
||||
ErrorWiFiBase,
|
||||
ErrorMeshBase,
|
||||
ErrorFlashBase,
|
||||
ErrorHWCryptoBase,
|
||||
ErrorMemProtectBase,
|
||||
};
|
||||
|
||||
// C to Zig error
|
||||
pub fn espError(err: sys.esp_err_t) esp_error!sys.esp_err_t {
|
||||
return switch (@as(sys.esp_err_t, err)) {
|
||||
@as(sys.esp_err_t, sys.ESP_FAIL) => esp_error.Fail,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_NO_MEM) => esp_error.ErrorNoMem,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_INVALID_ARG) => esp_error.ErrorInvalidArg,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_INVALID_STATE) => esp_error.ErrorInvalidState,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_INVALID_SIZE) => esp_error.ErrorInvalidSize,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_NOT_FOUND) => esp_error.ErrorNotFound,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_NOT_SUPPORTED) => esp_error.ErrorNotSupported,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_TIMEOUT) => esp_error.ErrorTimeout,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_INVALID_RESPONSE) => esp_error.ErrorInvalidResponse,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_INVALID_CRC) => esp_error.ErrorInvalidCRC,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_INVALID_VERSION) => esp_error.ErrorInvalidVersion,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_INVALID_MAC) => esp_error.ErrorInvalidMAC,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_NOT_FINISHED) => esp_error.ErrorNotFinished,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_NOT_ALLOWED) => esp_error.ErrorNotAllowed,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_WIFI_BASE) => esp_error.ErrorWiFiBase,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_MESH_BASE) => esp_error.ErrorMeshBase,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_FLASH_BASE) => esp_error.ErrorFlashBase,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_HW_CRYPTO_BASE) => esp_error.ErrorHWCryptoBase,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_MEMPROT_BASE) => esp_error.ErrorMemProtectBase,
|
||||
else => err, // Return the original `sys.esp_err_t` if it's not mapped
|
||||
};
|
||||
}
|
||||
|
||||
pub fn espCheckError(errc: sys.esp_err_t) esp_error!void {
|
||||
if (errc == @as(sys.esp_err_t, sys.ESP_OK)) return;
|
||||
// Try to surface a specific error variant; unknown non-zero codes become Fail.
|
||||
_ = try espError(errc);
|
||||
return error.Fail;
|
||||
}
|
||||
202
software/zig_main/imports/event.zig
Normal file
202
software/zig_main/imports/event.zig
Normal file
@@ -0,0 +1,202 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type aliases
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Base = sys.esp_event_base_t;
|
||||
pub const LoopHandle = sys.esp_event_loop_handle_t;
|
||||
pub const HandlerT = sys.esp_event_handler_t;
|
||||
pub const HandlerInstance = sys.esp_event_handler_instance_t;
|
||||
pub const LoopArgs = sys.esp_event_loop_args_t;
|
||||
|
||||
/// Match any event base when registering handlers.
|
||||
pub const ANY_BASE: Base = sys.ESP_EVENT_ANY_BASE;
|
||||
/// Match any event ID when registering handlers.
|
||||
pub const ANY_ID: i32 = sys.ESP_EVENT_ANY_ID;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Default event loop
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Create the default event loop. Must be called once before posting events
|
||||
/// or registering handlers on the default loop.
|
||||
pub fn loopCreateDefault() !void {
|
||||
try errors.espCheckError(sys.esp_event_loop_create_default());
|
||||
}
|
||||
|
||||
/// Delete the default event loop.
|
||||
pub fn loopDeleteDefault() !void {
|
||||
try errors.espCheckError(sys.esp_event_loop_delete_default());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Custom event loop
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Create a custom event loop with the given configuration.
|
||||
pub fn loopCreate(args: *const LoopArgs) !LoopHandle {
|
||||
var loop: LoopHandle = null;
|
||||
try errors.espCheckError(sys.esp_event_loop_create(args, &loop));
|
||||
return loop;
|
||||
}
|
||||
|
||||
/// Delete a custom event loop.
|
||||
pub fn loopDelete(loop: LoopHandle) !void {
|
||||
try errors.espCheckError(sys.esp_event_loop_delete(loop));
|
||||
}
|
||||
|
||||
/// Run a custom event loop for up to `ticks_to_run` ticks.
|
||||
pub fn loopRun(loop: LoopHandle, ticks_to_run: sys.TickType_t) !void {
|
||||
try errors.espCheckError(sys.esp_event_loop_run(loop, ticks_to_run));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handler registration — default loop
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Register an event handler on the default event loop.
|
||||
///
|
||||
/// Pass `ANY_BASE` / `ANY_ID` as wildcards.
|
||||
pub fn handlerRegister(
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
handler: HandlerT,
|
||||
arg: ?*anyopaque,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_handler_register(event_base, event_id, handler, arg));
|
||||
}
|
||||
|
||||
/// Unregister a previously registered handler from the default loop.
|
||||
pub fn handlerUnregister(
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
handler: HandlerT,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_handler_unregister(event_base, event_id, handler));
|
||||
}
|
||||
|
||||
/// Register an instance-based handler on the default loop.
|
||||
/// Returns an `HandlerInstance` token usable with `handlerInstanceUnregister`.
|
||||
pub fn handlerInstanceRegister(
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
handler: HandlerT,
|
||||
arg: ?*anyopaque,
|
||||
) !HandlerInstance {
|
||||
var instance: HandlerInstance = null;
|
||||
try errors.espCheckError(sys.esp_event_handler_instance_register(event_base, event_id, handler, arg, &instance));
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// Unregister an instance-based handler from the default loop.
|
||||
pub fn handlerInstanceUnregister(
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
instance: HandlerInstance,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_handler_instance_unregister(event_base, event_id, instance));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handler registration — custom loop
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Register a handler on a custom event loop.
|
||||
pub fn handlerRegisterWith(
|
||||
loop: LoopHandle,
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
handler: HandlerT,
|
||||
arg: ?*anyopaque,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_handler_register_with(loop, event_base, event_id, handler, arg));
|
||||
}
|
||||
|
||||
/// Unregister a handler from a custom event loop.
|
||||
pub fn handlerUnregisterWith(
|
||||
loop: LoopHandle,
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
handler: HandlerT,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_handler_unregister_with(loop, event_base, event_id, handler));
|
||||
}
|
||||
|
||||
/// Register an instance-based handler on a custom event loop.
|
||||
pub fn handlerInstanceRegisterWith(
|
||||
loop: LoopHandle,
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
handler: HandlerT,
|
||||
arg: ?*anyopaque,
|
||||
) !HandlerInstance {
|
||||
var instance: HandlerInstance = null;
|
||||
try errors.espCheckError(sys.esp_event_handler_instance_register_with(loop, event_base, event_id, handler, arg, &instance));
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// Unregister an instance-based handler from a custom event loop.
|
||||
pub fn handlerInstanceUnregisterWith(
|
||||
loop: LoopHandle,
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
instance: HandlerInstance,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_handler_instance_unregister_with(loop, event_base, event_id, instance));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Event posting
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Post an event to the default event loop.
|
||||
///
|
||||
/// `event_data` may be null when `event_data_size` is 0.
|
||||
/// `ticks_to_wait` is how long to block if the queue is full.
|
||||
pub fn post(
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
event_data: ?*const anyopaque,
|
||||
event_data_size: usize,
|
||||
ticks_to_wait: sys.TickType_t,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_post(event_base, event_id, event_data, event_data_size, ticks_to_wait));
|
||||
}
|
||||
|
||||
/// Post an event to a custom event loop.
|
||||
pub fn postTo(
|
||||
loop: LoopHandle,
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
event_data: ?*const anyopaque,
|
||||
event_data_size: usize,
|
||||
ticks_to_wait: sys.TickType_t,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_post_to(loop, event_base, event_id, event_data, event_data_size, ticks_to_wait));
|
||||
}
|
||||
|
||||
/// Post an event from an ISR to the default event loop.
|
||||
/// Sets `*task_unblocked` to true if a higher-priority task was unblocked.
|
||||
pub fn isrPost(
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
event_data: ?*const anyopaque,
|
||||
event_data_size: usize,
|
||||
task_unblocked: *sys.BaseType_t,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_isr_post(event_base, event_id, event_data, event_data_size, task_unblocked));
|
||||
}
|
||||
|
||||
/// Post an event from an ISR to a custom event loop.
|
||||
pub fn isrPostTo(
|
||||
loop: LoopHandle,
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
event_data: ?*const anyopaque,
|
||||
event_data_size: usize,
|
||||
task_unblocked: *sys.BaseType_t,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_isr_post_to(loop, event_base, event_id, event_data, event_data_size, task_unblocked));
|
||||
}
|
||||
383
software/zig_main/imports/gpio.zig
Normal file
383
software/zig_main/imports/gpio.zig
Normal file
@@ -0,0 +1,383 @@
|
||||
const sys = @import("sys");
|
||||
const std = @import("std");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Num — generated enum containing only pins that exist on the current target.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn Num() type {
|
||||
comptime var names: []const []const u8 = &.{};
|
||||
comptime var values: []const sys.gpio_num_t = &.{};
|
||||
|
||||
// Always-present sentinels.
|
||||
names = names ++ &[_][]const u8{ "NC", "MAX" };
|
||||
values = values ++ &[_]sys.gpio_num_t{
|
||||
@intCast(sys.GPIO_NUM_NC),
|
||||
@intCast(sys.GPIO_NUM_MAX),
|
||||
};
|
||||
|
||||
// Add only the pins the current target actually declares in sys.
|
||||
inline for (0..49) |n| {
|
||||
@setEvalBranchQuota(200000);
|
||||
const decl = std.fmt.comptimePrint("GPIO_NUM_{d}", .{n});
|
||||
if (@hasDecl(sys, decl)) {
|
||||
names = names ++ &[_][]const u8{std.fmt.comptimePrint("{d}", .{n})};
|
||||
values = values ++ &[_]sys.gpio_num_t{@intCast(@field(sys, decl))};
|
||||
}
|
||||
}
|
||||
|
||||
// @Enum(TagInt, mode, field_names, field_values)
|
||||
return @Enum(
|
||||
sys.gpio_num_t,
|
||||
.exhaustive,
|
||||
names,
|
||||
values[0..],
|
||||
);
|
||||
}
|
||||
|
||||
/// The GPIO pin enum for the current target.
|
||||
/// Only pins declared by the BSP/sys module are present as fields.
|
||||
/// Referencing a missing pin (e.g. `.@"22"` on ESP32-C3) is a compile error.
|
||||
pub const GpioNum = Num();
|
||||
|
||||
/// Convert a GpioNum to the raw C type expected by esp-idf APIs.
|
||||
pub inline fn numToC(gpio_num: GpioNum) sys.gpio_num_t {
|
||||
return @intFromEnum(gpio_num);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Other enumerations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Port = enum(sys.gpio_port_t) {
|
||||
GPIO_PORT_0 = sys.GPIO_PORT_0,
|
||||
GPIO_PORT_MAX = sys.GPIO_PORT_MAX,
|
||||
};
|
||||
|
||||
pub const IntType = enum(sys.gpio_int_type_t) {
|
||||
disable = sys.GPIO_INTR_DISABLE,
|
||||
posedge = sys.GPIO_INTR_POSEDGE,
|
||||
negedge = sys.GPIO_INTR_NEGEDGE,
|
||||
anyedge = sys.GPIO_INTR_ANYEDGE,
|
||||
low_level = sys.GPIO_INTR_LOW_LEVEL,
|
||||
high_level = sys.GPIO_INTR_HIGH_LEVEL,
|
||||
max = sys.GPIO_INTR_MAX,
|
||||
};
|
||||
|
||||
pub const Mode = enum(sys.gpio_mode_t) {
|
||||
disable = sys.GPIO_MODE_DISABLE,
|
||||
input = sys.GPIO_MODE_INPUT,
|
||||
output = sys.GPIO_MODE_OUTPUT,
|
||||
output_od = sys.GPIO_MODE_OUTPUT_OD,
|
||||
input_output_od = sys.GPIO_MODE_INPUT_OUTPUT_OD,
|
||||
input_output = sys.GPIO_MODE_INPUT_OUTPUT,
|
||||
};
|
||||
|
||||
pub const Pullup = enum(sys.gpio_pullup_t) {
|
||||
disable = sys.GPIO_PULLUP_DISABLE,
|
||||
enable = sys.GPIO_PULLUP_ENABLE,
|
||||
};
|
||||
|
||||
pub const Pulldown = enum(sys.gpio_pulldown_t) {
|
||||
disable = sys.GPIO_PULLDOWN_DISABLE,
|
||||
enable = sys.GPIO_PULLDOWN_ENABLE,
|
||||
};
|
||||
|
||||
pub const PullMode = enum(sys.gpio_pull_mode_t) {
|
||||
pullup_only = sys.GPIO_PULLUP_ONLY,
|
||||
pulldown_only = sys.GPIO_PULLDOWN_ONLY,
|
||||
pullup_pulldown = sys.GPIO_PULLUP_PULLDOWN,
|
||||
floating = sys.GPIO_FLOATING,
|
||||
};
|
||||
|
||||
pub const DriveCap = enum(sys.gpio_drive_cap_t) {
|
||||
cap_0 = sys.GPIO_DRIVE_CAP_0,
|
||||
cap_1 = sys.GPIO_DRIVE_CAP_1,
|
||||
cap_2 = sys.GPIO_DRIVE_CAP_2,
|
||||
default = sys.GPIO_DRIVE_CAP_DEFAULT,
|
||||
cap_3 = sys.GPIO_DRIVE_CAP_3,
|
||||
max = sys.GPIO_DRIVE_CAP_MAX,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Configuration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn config(cfg: [*c]const sys.gpio_config_t) !void {
|
||||
try errors.espCheckError(sys.gpio_config(cfg));
|
||||
}
|
||||
|
||||
pub fn resetPin(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_reset_pin(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Interrupts
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn setIntrType(gpio_num: GpioNum, intr_type: IntType) !void {
|
||||
try errors.espCheckError(sys.gpio_set_intr_type(numToC(gpio_num), @intFromEnum(intr_type)));
|
||||
}
|
||||
|
||||
pub fn intrEnable(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_intr_enable(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
pub fn intrDisable(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_intr_disable(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
pub fn installISRService(intr_alloc_flags: c_int) !void {
|
||||
try errors.espCheckError(sys.gpio_install_isr_service(intr_alloc_flags));
|
||||
}
|
||||
|
||||
pub fn uninstallISRService() void {
|
||||
sys.gpio_uninstall_isr_service();
|
||||
}
|
||||
|
||||
pub fn isrRegister(
|
||||
handler: ?*const fn (?*anyopaque) callconv(.c) void,
|
||||
arg: ?*anyopaque,
|
||||
intr_alloc_flags: c_int,
|
||||
handle: [*c]sys.gpio_isr_handle_t,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.gpio_isr_register(handler, arg, intr_alloc_flags, handle));
|
||||
}
|
||||
|
||||
pub fn isrHandlerAdd(gpio_num: GpioNum, isr_handler: sys.gpio_isr_t, args: ?*anyopaque) !void {
|
||||
try errors.espCheckError(sys.gpio_isr_handler_add(numToC(gpio_num), isr_handler, args));
|
||||
}
|
||||
|
||||
pub fn isrHandlerRemove(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_isr_handler_remove(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Level / Direction
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Level = struct {
|
||||
pub fn set(gpio_num: GpioNum, level: u32) !void {
|
||||
try errors.espCheckError(sys.gpio_set_level(numToC(gpio_num), level));
|
||||
}
|
||||
/// Returns true if the pin is high, false if low.
|
||||
pub fn get(gpio_num: GpioNum) bool {
|
||||
return sys.gpio_get_level(numToC(gpio_num)) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Direction = struct {
|
||||
pub fn set(gpio_num: GpioNum, mode: Mode) !void {
|
||||
try errors.espCheckError(sys.gpio_set_direction(numToC(gpio_num), @intFromEnum(mode)));
|
||||
}
|
||||
pub fn sleepSet(gpio_num: GpioNum, mode: Mode) !void {
|
||||
try errors.espCheckError(sys.gpio_sleep_set_direction(numToC(gpio_num), @intFromEnum(mode)));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Pull resistors
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn setPullMode(gpio_num: GpioNum, pull: PullMode) !void {
|
||||
try errors.espCheckError(sys.gpio_set_pull_mode(numToC(gpio_num), @intFromEnum(pull)));
|
||||
}
|
||||
|
||||
pub fn sleepSetPullMode(gpio_num: GpioNum, pull: PullMode) !void {
|
||||
try errors.espCheckError(sys.gpio_sleep_set_pull_mode(numToC(gpio_num), @intFromEnum(pull)));
|
||||
}
|
||||
|
||||
pub const PULL = struct {
|
||||
pub fn upEn(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_pullup_en(numToC(gpio_num)));
|
||||
}
|
||||
pub fn upDis(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_pullup_dis(numToC(gpio_num)));
|
||||
}
|
||||
pub fn downEn(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_pulldown_en(numToC(gpio_num)));
|
||||
}
|
||||
pub fn downDis(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_pulldown_dis(numToC(gpio_num)));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Drive strength
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn setDriveCapability(gpio_num: GpioNum, strength: DriveCap) !void {
|
||||
try errors.espCheckError(sys.gpio_set_drive_capability(numToC(gpio_num), @intFromEnum(strength)));
|
||||
}
|
||||
|
||||
/// Returns the drive capability of the given pin.
|
||||
pub fn getDriveCapability(gpio_num: GpioNum) !DriveCap {
|
||||
var raw: sys.gpio_drive_cap_t = undefined;
|
||||
try errors.espCheckError(sys.gpio_get_drive_capability(numToC(gpio_num), &raw));
|
||||
return @enumFromInt(raw);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Hold / deep-sleep
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn holdEn(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_hold_en(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
pub fn holdDis(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_hold_dis(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
pub fn forceHoldAll() !void {
|
||||
try errors.espCheckError(sys.gpio_force_hold_all());
|
||||
}
|
||||
|
||||
pub fn forceUnholdAll() !void {
|
||||
try errors.espCheckError(sys.gpio_force_unhold_all());
|
||||
}
|
||||
|
||||
pub fn deepSleepHoldEn() void {
|
||||
sys.gpio_deep_sleep_hold_en();
|
||||
}
|
||||
|
||||
pub fn deepSleepHoldDis() void {
|
||||
sys.gpio_deep_sleep_hold_dis();
|
||||
}
|
||||
|
||||
pub fn deepSleepWakeupEnable(gpio_num: GpioNum, intr_type: IntType) !void {
|
||||
try errors.espCheckError(sys.gpio_deep_sleep_wakeup_enable(numToC(gpio_num), @intFromEnum(intr_type)));
|
||||
}
|
||||
|
||||
pub fn deepSleepWakeupDisable(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_deep_sleep_wakeup_disable(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Sleep select
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn sleepSelEn(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_sleep_sel_en(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
pub fn sleepSelDis(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_sleep_sel_dis(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Wakeup
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn wakeupEnable(gpio_num: GpioNum, intr_type: IntType) !void {
|
||||
try errors.espCheckError(sys.gpio_wakeup_enable(numToC(gpio_num), @intFromEnum(intr_type)));
|
||||
}
|
||||
|
||||
pub fn wakeupDisable(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_wakeup_disable(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IOMUX
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn iomuxIn(gpio_num: GpioNum, signal_idx: u32) void {
|
||||
sys.gpio_iomux_in(numToC(gpio_num), signal_idx);
|
||||
}
|
||||
|
||||
pub fn iomuxOut(gpio_num: GpioNum, func: c_int, oen_inv: bool) void {
|
||||
sys.gpio_iomux_out(numToC(gpio_num), func, oen_inv);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Debug
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn dumpIOConfiguration(out_stream: ?*std.c.FILE, io_bit_mask: u64) !void {
|
||||
try errors.espCheckError(sys.gpio_dump_io_configuration(out_stream, io_bit_mask));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ROM helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const ROM = struct {
|
||||
pub fn padSelectGPIO(iopad_num: u32) void {
|
||||
sys.esp_rom_gpio_pad_select_gpio(iopad_num);
|
||||
}
|
||||
pub fn padPullupOnly(iopad_num: u32) void {
|
||||
sys.esp_rom_gpio_pad_pullup_only(iopad_num);
|
||||
}
|
||||
pub fn padUnhold(gpio_num: GpioNum) void {
|
||||
sys.esp_rom_gpio_pad_unhold(numToC(gpio_num));
|
||||
}
|
||||
pub fn padSetDrive(iopad_num: u32, drv: u32) void {
|
||||
sys.esp_rom_gpio_pad_set_drv(iopad_num, drv);
|
||||
}
|
||||
pub fn connectInSignal(gpio_num: GpioNum, signal_idx: u32, inv: bool) void {
|
||||
sys.esp_rom_gpio_connect_in_signal(numToC(gpio_num), signal_idx, inv);
|
||||
}
|
||||
pub fn connectOutSignal(gpio_num: GpioNum, signal_idx: u32, out_inv: bool, oen_inv: bool) void {
|
||||
sys.esp_rom_gpio_connect_out_signal(numToC(gpio_num), signal_idx, out_inv, oen_inv);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ETM (Event Task Matrix)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const ETM = struct {
|
||||
pub fn newChannel(cfg: [*c]const sys.esp_etm_channel_config_t, ret_chan: [*c]sys.esp_etm_channel_handle_t) !void {
|
||||
try errors.espCheckError(sys.esp_etm_new_channel(cfg, ret_chan));
|
||||
}
|
||||
pub fn delChannel(chan: sys.esp_etm_channel_handle_t) !void {
|
||||
try errors.espCheckError(sys.esp_etm_del_channel(chan));
|
||||
}
|
||||
pub fn channelEnable(chan: sys.esp_etm_channel_handle_t) !void {
|
||||
try errors.espCheckError(sys.esp_etm_channel_enable(chan));
|
||||
}
|
||||
pub fn channelDisable(chan: sys.esp_etm_channel_handle_t) !void {
|
||||
try errors.espCheckError(sys.esp_etm_channel_disable(chan));
|
||||
}
|
||||
pub fn channelConnect(
|
||||
chan: sys.esp_etm_channel_handle_t,
|
||||
event: sys.esp_etm_event_handle_t,
|
||||
task: sys.esp_etm_task_handle_t,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_etm_channel_connect(chan, event, task));
|
||||
}
|
||||
pub fn delEvent(event: sys.esp_etm_event_handle_t) !void {
|
||||
try errors.espCheckError(sys.esp_etm_del_event(event));
|
||||
}
|
||||
pub fn delTask(task: sys.esp_etm_task_handle_t) !void {
|
||||
try errors.espCheckError(sys.esp_etm_del_task(task));
|
||||
}
|
||||
pub fn dump(out_stream: ?*std.c.FILE) !void {
|
||||
try errors.espCheckError(sys.esp_etm_dump(out_stream));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GPIO ETM event/task binding
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn newEtmEvent(cfg: [*c]const sys.gpio_etm_event_config_t, ret_event: [*c]sys.esp_etm_event_handle_t) !void {
|
||||
try errors.espCheckError(sys.gpio_new_etm_event(cfg, ret_event));
|
||||
}
|
||||
|
||||
pub fn etmEventBindGPIO(event: sys.esp_etm_event_handle_t, gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_etm_event_bind_gpio(event, numToC(gpio_num)));
|
||||
}
|
||||
|
||||
pub fn newEtmTask(cfg: [*c]const sys.gpio_etm_task_config_t, ret_task: [*c]sys.esp_etm_task_handle_t) !void {
|
||||
try errors.espCheckError(sys.gpio_new_etm_task(cfg, ret_task));
|
||||
}
|
||||
|
||||
pub fn etmTaskAddGPIO(task: sys.esp_etm_task_handle_t, gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_etm_task_add_gpio(task, numToC(gpio_num)));
|
||||
}
|
||||
|
||||
pub fn etmTaskRemoveGPIO(task: sys.esp_etm_task_handle_t, gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_etm_task_rm_gpio(task, numToC(gpio_num)));
|
||||
}
|
||||
326
software/zig_main/imports/heap.zig
Normal file
326
software/zig_main/imports/heap.zig
Normal file
@@ -0,0 +1,326 @@
|
||||
const sys = @import("sys");
|
||||
const std = @import("std");
|
||||
const errors = @import("error");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
// read: https://github.com/espressif/esp-idf/blob/97d95853572ab74f4769597496af9d5fe8b6bdea/components/heap/include/esp_heap_caps.h#L29-L53
|
||||
// ---------------------------------------------------------------------------
|
||||
// Caps — packed struct matching esp_heap_caps.h bit positions exactly.
|
||||
//
|
||||
// Bit layout (matches MALLOC_CAP_* defines):
|
||||
// 0 exec (only when CONFIG_HEAP_HAS_EXEC_HEAP)
|
||||
// 1 32bit
|
||||
// 2 8bit
|
||||
// 3 dma
|
||||
// 4- 9 pid2..pid7
|
||||
// 10 spiram
|
||||
// 11 internal
|
||||
// 12 default
|
||||
// 13 iram_8bit
|
||||
// 14 retention
|
||||
// 15 rtcram
|
||||
// 16 tcm
|
||||
// 17 dma_desc_ahb
|
||||
// 18 dma_desc_axi
|
||||
// 19 cache_aligned
|
||||
// 20 simd
|
||||
// 21-30 (reserved)
|
||||
// 31 invalid
|
||||
// ---------------------------------------------------------------------------
|
||||
pub const Caps = packed struct(u32) {
|
||||
exec: bool = false, // bit 0 — requires CONFIG_HEAP_HAS_EXEC_HEAP
|
||||
@"32bit": bool = false, // bit 1
|
||||
@"8bit": bool = false, // bit 2
|
||||
dma: bool = false, // bit 3
|
||||
pid2: bool = false, // bit 4
|
||||
pid3: bool = false, // bit 5
|
||||
pid4: bool = false, // bit 6
|
||||
pid5: bool = false, // bit 7
|
||||
pid6: bool = false, // bit 8
|
||||
pid7: bool = false, // bit 9
|
||||
spiram: bool = false, // bit 10
|
||||
internal: bool = false, // bit 11
|
||||
default: bool = false, // bit 12
|
||||
iram_8bit: bool = false, // bit 13
|
||||
retention: bool = false, // bit 14
|
||||
rtcram: bool = false, // bit 15
|
||||
tcm: bool = false, // bit 16
|
||||
dma_desc_ahb: bool = false, // bit 17
|
||||
dma_desc_axi: bool = false, // bit 18
|
||||
cache_aligned: bool = false, // bit 19
|
||||
simd: bool = false, // bit 20
|
||||
_reserved: u10 = 0, // bits 21-30
|
||||
invalid: bool = false, // bit 31
|
||||
|
||||
/// Cast to the raw u32 value the heap_caps_* C functions expect.
|
||||
pub fn toRaw(self: Caps) u32 {
|
||||
return @bitCast(self);
|
||||
}
|
||||
|
||||
/// Re-hydrate from a raw C bitmask (e.g. value returned by a C API).
|
||||
pub fn fromRaw(raw: u32) Caps {
|
||||
return @bitCast(raw);
|
||||
}
|
||||
|
||||
// -- Named presets matching common ESP-IDF usage patterns ---------------
|
||||
|
||||
/// General-purpose heap (equivalent to malloc/calloc).
|
||||
pub const default_caps: Caps = .{ .default = true };
|
||||
/// Internal RAM, byte-addressable.
|
||||
pub const internal_caps: Caps = .{ .internal = true, .@"8bit" = true };
|
||||
/// DMA-capable internal RAM.
|
||||
pub const dma_caps: Caps = .{ .dma = true, .@"8bit" = true, .internal = true };
|
||||
/// External SPI RAM, byte-addressable.
|
||||
pub const spiram_caps: Caps = .{ .spiram = true, .@"8bit" = true };
|
||||
/// RTC fast memory (survives deep sleep).
|
||||
pub const rtcram_caps: Caps = .{ .rtcram = true };
|
||||
/// Tightly-coupled memory.
|
||||
pub const tcm_caps: Caps = .{ .tcm = true };
|
||||
/// Executable memory (requires CONFIG_HEAP_HAS_EXEC_HEAP).
|
||||
pub const exec_caps: Caps = .{ .exec = true };
|
||||
/// Cache-line aligned memory.
|
||||
pub const cache_aligned_caps: Caps = .{ .cache_aligned = true, .default = true };
|
||||
};
|
||||
|
||||
// Verify the bit layout matches the C header at compile time.
|
||||
comptime {
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .exec = true })) == (1 << 0));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .@"32bit" = true })) == (1 << 1));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .@"8bit" = true })) == (1 << 2));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .dma = true })) == (1 << 3));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .spiram = true })) == (1 << 10));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .internal = true })) == (1 << 11));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .default = true })) == (1 << 12));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .simd = true })) == (1 << 20));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .invalid = true })) == (1 << 31));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HeapCapsAllocator
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const HeapCapsAllocator = struct {
|
||||
caps: Caps = Caps.default_caps,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(caps: ?Caps) Self {
|
||||
return .{ .caps = caps orelse Caps.default_caps };
|
||||
}
|
||||
|
||||
pub fn allocator(self: *Self) std.mem.Allocator {
|
||||
return .{
|
||||
.ptr = self,
|
||||
.vtable = &.{
|
||||
.alloc = alloc,
|
||||
.resize = resize,
|
||||
.remap = remap,
|
||||
.free = free,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dump(self: Self) void {
|
||||
sys.heap_caps_dump(self.caps.toRaw());
|
||||
}
|
||||
pub fn allocatedSize(_: Self, ptr: ?*anyopaque) usize {
|
||||
return sys.heap_caps_get_allocated_size(ptr);
|
||||
}
|
||||
pub fn largestFreeBlock(self: Self) usize {
|
||||
return sys.heap_caps_get_largest_free_block(self.caps.toRaw());
|
||||
}
|
||||
pub fn totalSize(self: Self) usize {
|
||||
return sys.heap_caps_get_total_size(self.caps.toRaw());
|
||||
}
|
||||
pub fn freeSize(self: Self) usize {
|
||||
return sys.heap_caps_get_free_size(self.caps.toRaw());
|
||||
}
|
||||
pub fn minimumFreeSize(self: Self) usize {
|
||||
return sys.heap_caps_get_minimum_free_size(self.caps.toRaw());
|
||||
}
|
||||
pub fn internalFreeSize(_: Self) usize {
|
||||
return sys.esp_get_free_internal_heap_size();
|
||||
}
|
||||
|
||||
fn alloc(ctx: *anyopaque, len: usize, alignment: std.mem.Alignment, _: usize) ?[*]u8 {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
return @ptrCast(sys.heap_caps_aligned_alloc(
|
||||
alignment.toByteUnits(),
|
||||
len,
|
||||
self.caps.toRaw(),
|
||||
));
|
||||
}
|
||||
|
||||
fn resize(_: *anyopaque, buf: []u8, _: std.mem.Alignment, new_len: usize, _: usize) bool {
|
||||
if (new_len <= buf.len) return true;
|
||||
if (@TypeOf(sys.heap_caps_get_allocated_size) != void) {
|
||||
if (new_len <= sys.heap_caps_get_allocated_size(buf.ptr)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn remap(ctx: *anyopaque, memory: []u8, _: std.mem.Alignment, new_len: usize, _: usize) ?[*]u8 {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
return @ptrCast(sys.heap_caps_realloc(memory.ptr, new_len, self.caps.toRaw()));
|
||||
}
|
||||
|
||||
fn free(_: *anyopaque, buf: []u8, _: std.mem.Alignment, _: usize) void {
|
||||
sys.heap_caps_free(buf.ptr);
|
||||
if (builtin.mode == .Debug) {
|
||||
if (!sys.heap_caps_check_integrity_all(true))
|
||||
@panic("heap_caps: integrity check failed after free");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MultiHeapAllocator
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const MultiHeapAllocator = struct {
|
||||
handle: sys.multi_heap_handle_t = null,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(handle: sys.multi_heap_handle_t) Self {
|
||||
return .{ .handle = handle };
|
||||
}
|
||||
|
||||
pub fn allocator(self: *Self) std.mem.Allocator {
|
||||
return .{
|
||||
.ptr = self,
|
||||
.vtable = &.{
|
||||
.alloc = alloc,
|
||||
.resize = resize,
|
||||
.remap = remap,
|
||||
.free = free,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn allocatedSize(self: Self, p: ?*anyopaque) usize {
|
||||
return sys.multi_heap_get_allocated_size(self.handle, p);
|
||||
}
|
||||
pub fn freeSize(self: Self) usize {
|
||||
return sys.multi_heap_free_size(self.handle);
|
||||
}
|
||||
pub fn minimumFreeSize(self: Self) usize {
|
||||
return sys.multi_heap_minimum_free_size(self.handle);
|
||||
}
|
||||
|
||||
fn alloc(ctx: *anyopaque, len: usize, _: std.mem.Alignment, _: usize) ?[*]u8 {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
return @ptrCast(sys.multi_heap_malloc(self.handle, len));
|
||||
}
|
||||
|
||||
fn resize(ctx: *anyopaque, buf: []u8, _: std.mem.Alignment, new_len: usize, _: usize) bool {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
if (new_len <= buf.len) return true;
|
||||
if (@TypeOf(sys.multi_heap_get_allocated_size) != void) {
|
||||
if (new_len <= sys.multi_heap_get_allocated_size(self.handle, buf.ptr))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn remap(ctx: *anyopaque, memory: []u8, _: std.mem.Alignment, new_len: usize, _: usize) ?[*]u8 {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
return @ptrCast(sys.multi_heap_realloc(self.handle, memory.ptr, new_len));
|
||||
}
|
||||
|
||||
fn free(ctx: *anyopaque, buf: []u8, _: std.mem.Alignment, _: usize) void {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
sys.multi_heap_free(self.handle, buf.ptr);
|
||||
if (builtin.mode == .Debug) {
|
||||
if (!sys.multi_heap_check(self.handle, true))
|
||||
@panic("multi_heap: integrity check failed after free");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// VPortAllocator
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const VPortAllocator = struct {
|
||||
const Self = @This();
|
||||
|
||||
pub fn init() Self {
|
||||
return .{};
|
||||
}
|
||||
|
||||
pub fn allocator(self: *Self) std.mem.Allocator {
|
||||
return .{
|
||||
.ptr = self,
|
||||
.vtable = &.{
|
||||
.alloc = alloc,
|
||||
.resize = resize,
|
||||
.remap = remap,
|
||||
.free = free,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn freeSize(_: Self) usize {
|
||||
return sys.xPortGetFreeHeapSize();
|
||||
}
|
||||
pub fn minimumFreeSize(_: Self) usize {
|
||||
return sys.xPortGetMinimumEverFreeHeapSize();
|
||||
}
|
||||
|
||||
fn alloc(_: *anyopaque, len: usize, _: std.mem.Alignment, _: usize) ?[*]u8 {
|
||||
return @ptrCast(sys.pvPortMalloc(len));
|
||||
}
|
||||
|
||||
fn resize(_: *anyopaque, buf: []u8, _: std.mem.Alignment, new_len: usize, _: usize) bool {
|
||||
return new_len <= buf.len;
|
||||
}
|
||||
|
||||
fn remap(_: *anyopaque, memory: []u8, _: std.mem.Alignment, new_len: usize, _: usize) ?[*]u8 {
|
||||
const new_ptr = sys.pvPortMalloc(new_len) orelse return null;
|
||||
@memcpy(@as([*]u8, @ptrCast(new_ptr))[0..@min(memory.len, new_len)], memory[0..@min(memory.len, new_len)]);
|
||||
sys.vPortFree(memory.ptr);
|
||||
return @ptrCast(new_ptr);
|
||||
}
|
||||
|
||||
fn free(_: *anyopaque, buf: []u8, _: std.mem.Alignment, _: usize) void {
|
||||
sys.vPortFree(buf.ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// TRACE
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const TRACE = struct {
|
||||
pub fn initStandalone(record_buffer: [*c]sys.heap_trace_record_t, num_records: usize) !void {
|
||||
try errors.espCheckError(sys.heap_trace_init_standalone(record_buffer, num_records));
|
||||
}
|
||||
pub fn initTohost() !void {
|
||||
try errors.espCheckError(sys.heap_trace_init_tohost());
|
||||
}
|
||||
pub fn start(mode: sys.heap_trace_mode_t) !void {
|
||||
try errors.espCheckError(sys.heap_trace_start(mode));
|
||||
}
|
||||
pub fn stop() !void {
|
||||
try errors.espCheckError(sys.heap_trace_stop());
|
||||
}
|
||||
pub fn @"resume"() !void {
|
||||
try errors.espCheckError(sys.heap_trace_resume());
|
||||
}
|
||||
pub fn getCount() usize {
|
||||
return sys.heap_trace_get_count();
|
||||
}
|
||||
pub fn get(index: usize, record: [*c]sys.heap_trace_record_t) !void {
|
||||
try errors.espCheckError(sys.heap_trace_get(index, record));
|
||||
}
|
||||
pub fn dump() void {
|
||||
sys.heap_trace_dump();
|
||||
}
|
||||
pub fn dumpCaps(caps: Caps) void {
|
||||
sys.heap_trace_dump_caps(caps.toRaw());
|
||||
}
|
||||
pub fn summary(sum: [*c]sys.heap_trace_summary_t) !void {
|
||||
try errors.espCheckError(sys.heap_trace_summary(sum));
|
||||
}
|
||||
};
|
||||
191
software/zig_main/imports/hosted.zig
Normal file
191
software/zig_main/imports/hosted.zig
Normal file
@@ -0,0 +1,191 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
pub const MAC_LEN = 6;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Re-exported / renamed types from sys
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const EspMacType = sys.esp_mac_type_t;
|
||||
pub const EspResetReason = sys.esp_reset_reason_t;
|
||||
pub const EspEventBase = sys.esp_event_base_t;
|
||||
pub const EspHostedAppDesc = sys.esp_hosted_app_desc_t;
|
||||
pub const EspHostedMemInfo = sys.esp_hosted_mem_info_t;
|
||||
pub const EspHostedCapInfo = sys.esp_hosted_cap_info_t;
|
||||
pub const EspHostedCurrMemInfo = sys.esp_hosted_curr_mem_info_t;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Constants & enums
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const EVENT_BASE = sys.ESP_HOSTED_EVENT;
|
||||
|
||||
pub const EventId = enum(c_int) {
|
||||
cp_init = sys.ESP_HOSTED_EVENT_CP_INIT,
|
||||
cp_heartbeat = sys.ESP_HOSTED_EVENT_CP_HEARTBEAT,
|
||||
transport_failure = sys.ESP_HOSTED_EVENT_TRANSPORT_FAILURE,
|
||||
transport_up = sys.ESP_HOSTED_EVENT_TRANSPORT_UP,
|
||||
transport_down = sys.ESP_HOSTED_EVENT_TRANSPORT_DOWN,
|
||||
mem_monitor = sys.ESP_HOSTED_EVENT_MEM_MONITOR,
|
||||
_,
|
||||
};
|
||||
|
||||
pub const SlaveOtaStatus = enum(c_int) {
|
||||
activated = sys.ESP_HOSTED_SLAVE_OTA_ACTIVATED,
|
||||
completed = sys.ESP_HOSTED_SLAVE_OTA_COMPLETED,
|
||||
not_required = sys.ESP_HOSTED_SLAVE_OTA_NOT_REQUIRED,
|
||||
not_started = sys.ESP_HOSTED_SLAVE_OTA_NOT_STARTED,
|
||||
in_progress = sys.ESP_HOSTED_SLAVE_OTA_IN_PROGRESS,
|
||||
failed = sys.ESP_HOSTED_SLAVE_OTA_FAILED,
|
||||
_,
|
||||
};
|
||||
|
||||
pub const MemMonitorConfig = enum(c_uint) {
|
||||
no_change = sys.ESP_HOSTED_MEMMONITOR_NO_CHANGE,
|
||||
disable = sys.ESP_HOSTED_MEMMONITOR_DISABLE,
|
||||
enable = sys.ESP_HOSTED_MEMMONITOR_ENABLE,
|
||||
_,
|
||||
};
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Callback types (Zig-friendly)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const CustomDataCallback = *const fn (
|
||||
msg_id: u32,
|
||||
data: []const u8,
|
||||
) void;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Internal adapter state
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
var custom_data_cb: ?CustomDataCallback = null;
|
||||
|
||||
fn customDataCbAdapter(
|
||||
msg_id: u32,
|
||||
data: [*c]const u8,
|
||||
data_len: usize,
|
||||
) callconv(.C) void {
|
||||
if (custom_data_cb) |cb| {
|
||||
cb(msg_id, data[0..data_len]);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Hosted API
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const Hosted = struct {
|
||||
|
||||
// ─── Lifecycle ──────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn btControllerInit() !void {
|
||||
try errors.espCheckError(sys.esp_hosted_bt_controller_init());
|
||||
}
|
||||
|
||||
pub fn btControllerDeinit(release_mem: bool) !void {
|
||||
try errors.espCheckError(sys.esp_hosted_bt_controller_deinit(release_mem));
|
||||
}
|
||||
|
||||
pub fn btControllerEnable() !void {
|
||||
try errors.espCheckError(sys.esp_hosted_bt_controller_enable());
|
||||
}
|
||||
|
||||
pub fn btControllerDisable() !void {
|
||||
try errors.espCheckError(sys.esp_hosted_bt_controller_disable());
|
||||
}
|
||||
|
||||
// ─── MAC address management ─────────────────────────────────────────────────
|
||||
|
||||
pub fn setInterfaceMac(typ: EspMacType, mac: []const u8) !void {
|
||||
if (mac.len != MAC_LEN) return error.InvalidMacLength;
|
||||
try errors.espCheckError(sys.esp_hosted_iface_mac_addr_set(@constCast(mac.ptr), mac.len, typ));
|
||||
}
|
||||
|
||||
pub fn getInterfaceMac(typ: EspMacType) ![MAC_LEN]u8 {
|
||||
var mac: [MAC_LEN]u8 = undefined;
|
||||
try errors.espCheckError(sys.esp_hosted_iface_mac_addr_get(&mac, mac.len, typ));
|
||||
return mac;
|
||||
}
|
||||
|
||||
pub fn getInterfaceMacLen(typ: EspMacType) usize {
|
||||
return sys.esp_hosted_iface_mac_addr_len_get(typ);
|
||||
}
|
||||
|
||||
// ─── Coprocessor information ────────────────────────────────────────────────
|
||||
|
||||
pub fn getCoprocessorAppDesc() !EspHostedAppDesc {
|
||||
var desc: EspHostedAppDesc = undefined;
|
||||
try errors.espCheckError(sys.esp_hosted_get_coprocessor_app_desc(&desc));
|
||||
return desc;
|
||||
}
|
||||
|
||||
pub fn getCoprocessorFwVersion() ![3]u32 {
|
||||
var ver: [3]u32 = undefined;
|
||||
try errors.espCheckError(sys.esp_hosted_get_coprocessor_fwversion(&ver));
|
||||
return ver;
|
||||
}
|
||||
|
||||
// ─── Custom data channel ────────────────────────────────────────────────────
|
||||
|
||||
pub fn registerCustomDataCallback(cb: CustomDataCallback) !void {
|
||||
custom_data_cb = cb;
|
||||
try errors.espCheckError(sys.esp_hosted_register_custom_callback(0, customDataCbAdapter)); // msg_id currently ignored by most impls
|
||||
}
|
||||
|
||||
pub fn sendCustomData(msg_id: u32, data: []const u8) !void {
|
||||
try errors.espCheckError(sys.esp_hosted_send_custom_data(msg_id, data.ptr, data.len));
|
||||
}
|
||||
|
||||
// ─── Heartbeat ──────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn configureHeartbeat(enable: bool, duration_seconds: i32) !void {
|
||||
try errors.espCheckError(sys.esp_hosted_configure_heartbeat(enable, duration_seconds));
|
||||
}
|
||||
|
||||
// ─── Memory monitor ─────────────────────────────────────────────────────────
|
||||
|
||||
pub fn setMemoryMonitor(
|
||||
cfg: MemMonitorConfig,
|
||||
report_always: bool,
|
||||
interval_sec: u32,
|
||||
internal_thresholds: sys.esp_hosted_mem_monitor_threshold_t,
|
||||
external_thresholds: sys.esp_hosted_mem_monitor_threshold_t,
|
||||
) !EspHostedCurrMemInfo {
|
||||
const config = sys.esp_hosted_config_mem_monitor_t{
|
||||
.config = @intFromEnum(cfg),
|
||||
.report_always = report_always,
|
||||
.interval_sec = interval_sec,
|
||||
.internal_mem = internal_thresholds,
|
||||
.external_mem = external_thresholds,
|
||||
};
|
||||
|
||||
var curr: EspHostedCurrMemInfo = undefined;
|
||||
try errors.espCheckError(sys.esp_hosted_set_mem_monitor(&config, &curr));
|
||||
return curr;
|
||||
}
|
||||
|
||||
// ─── Slave OTA (very basic blocking style) ──────────────────────────────────
|
||||
|
||||
pub fn slaveOtaBegin() !void {
|
||||
try errors.espCheckError(sys.esp_hosted_slave_ota_begin());
|
||||
}
|
||||
|
||||
pub fn slaveOtaWrite(data: []const u8) !void {
|
||||
try errors.espCheckError(sys.esp_hosted_slave_ota_write(@constCast(data.ptr), @intCast(data.len)));
|
||||
}
|
||||
|
||||
pub fn slaveOtaEnd() !void {
|
||||
try errors.espCheckError(sys.esp_hosted_slave_ota_end());
|
||||
}
|
||||
|
||||
pub fn slaveOtaActivate() !void {
|
||||
try errors.espCheckError(sys.esp_hosted_slave_ota_activate());
|
||||
}
|
||||
|
||||
pub fn slaveOtaFromUrl(url: []const u8) !void {
|
||||
try errors.espCheckError(sys.esp_hosted_slave_ota(url.ptr));
|
||||
}
|
||||
};
|
||||
302
software/zig_main/imports/http.zig
Normal file
302
software/zig_main/imports/http.zig
Normal file
@@ -0,0 +1,302 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// http_parser (third-party parser bundled with ESP-IDF)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Parser = struct {
|
||||
pub const version = sys.http_parser_version;
|
||||
pub const init = sys.http_parser_init;
|
||||
pub const settingsInit = sys.http_parser_settings_init;
|
||||
pub const execute = sys.http_parser_execute;
|
||||
pub const urlInit = sys.http_parser_url_init;
|
||||
pub const parseUrl = sys.http_parser_parse_url;
|
||||
pub const pause = sys.http_parser_pause;
|
||||
};
|
||||
pub const shouldKeepAlive = sys.http_should_keep_alive;
|
||||
pub const methodStr = sys.http_method_str;
|
||||
pub const errnoName = sys.http_errno_name;
|
||||
pub const errnoDescription = sys.http_errno_description;
|
||||
pub const bodyIsFinal = sys.http_body_is_final;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HTTP server (httpd)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Server = struct {
|
||||
/// Start the HTTP server. Returns a handle on success.
|
||||
pub fn start(config: *const sys.httpd_config_t) !sys.httpd_handle_t {
|
||||
var handle: sys.httpd_handle_t = null;
|
||||
try errors.espCheckError(sys.httpd_start(&handle, config));
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// Stop and clean up the HTTP server.
|
||||
pub fn stop(handle: sys.httpd_handle_t) !void {
|
||||
return errors.espCheckError(sys.httpd_stop(handle));
|
||||
}
|
||||
|
||||
/// Register a URI handler.
|
||||
pub fn registerUri(handle: sys.httpd_handle_t, uri_handler: *const sys.httpd_uri_t) !void {
|
||||
return errors.espCheckError(sys.httpd_register_uri_handler(handle, uri_handler));
|
||||
}
|
||||
|
||||
/// Unregister a URI handler by URI string and method.
|
||||
pub fn unregisterUri(handle: sys.httpd_handle_t, uri: [*:0]const u8, method: sys.httpd_method_t) !void {
|
||||
return errors.espCheckError(sys.httpd_unregister_uri_handler(handle, uri, method));
|
||||
}
|
||||
|
||||
/// Unregister all URI handlers for a given URI string.
|
||||
pub fn unregisterAllUris(handle: sys.httpd_handle_t, uri: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_unregister_uri(handle, uri));
|
||||
}
|
||||
|
||||
pub const Session = struct {
|
||||
pub fn setReceiveOverride(hd: sys.httpd_handle_t, sockfd: c_int, recv_func: sys.httpd_recv_func_t) !void {
|
||||
return errors.espCheckError(sys.httpd_sess_set_recv_override(hd, sockfd, recv_func));
|
||||
}
|
||||
pub fn setSendOverride(hd: sys.httpd_handle_t, sockfd: c_int, send_func: sys.httpd_send_func_t) !void {
|
||||
return errors.espCheckError(sys.httpd_sess_set_send_override(hd, sockfd, send_func));
|
||||
}
|
||||
pub fn setPendingOverride(hd: sys.httpd_handle_t, sockfd: c_int, pending_func: sys.httpd_pending_func_t) !void {
|
||||
return errors.espCheckError(sys.httpd_sess_set_pending_override(hd, sockfd, pending_func));
|
||||
}
|
||||
pub fn triggerClose(handle: sys.httpd_handle_t, sockfd: c_int) !void {
|
||||
return errors.espCheckError(sys.httpd_sess_trigger_close(handle, sockfd));
|
||||
}
|
||||
pub fn updateLRUCounter(handle: sys.httpd_handle_t, sockfd: c_int) !void {
|
||||
return errors.espCheckError(sys.httpd_sess_update_lru_counter(handle, sockfd));
|
||||
}
|
||||
pub const getContext = sys.httpd_sess_get_ctx;
|
||||
pub const setContext = sys.httpd_sess_set_ctx;
|
||||
pub const getTransportContext = sys.httpd_sess_get_transport_ctx;
|
||||
pub const setTransportContext = sys.httpd_sess_set_transport_ctx;
|
||||
};
|
||||
|
||||
pub const Request = struct {
|
||||
pub fn asyncHandlerBegin(r: [*c]sys.httpd_req_t, out: [*c][*c]sys.httpd_req_t) !void {
|
||||
return errors.espCheckError(sys.httpd_req_async_handler_begin(r, out));
|
||||
}
|
||||
pub fn asyncHandlerComplete(r: [*c]sys.httpd_req_t) !void {
|
||||
return errors.espCheckError(sys.httpd_req_async_handler_complete(r));
|
||||
}
|
||||
pub const toSockfd = sys.httpd_req_to_sockfd;
|
||||
pub const receiver = sys.httpd_req_recv;
|
||||
pub const getHDRValueLen = sys.httpd_req_get_hdr_value_len;
|
||||
pub fn getHDRValueStr(r: [*c]sys.httpd_req_t, field: [*:0]const u8, val: [*:0]u8, val_size: usize) !void {
|
||||
return errors.espCheckError(sys.httpd_req_get_hdr_value_str(r, field, val, val_size));
|
||||
}
|
||||
pub const getUrlQueryLen = sys.httpd_req_get_url_query_len;
|
||||
pub fn getUrlQueryStr(r: [*c]sys.httpd_req_t, buf: [*:0]u8, buf_len: usize) !void {
|
||||
return errors.espCheckError(sys.httpd_req_get_url_query_str(r, buf, buf_len));
|
||||
}
|
||||
pub fn getCookieValue(req: [*c]sys.httpd_req_t, cookie_name: [*:0]const u8, val: [*:0]u8, val_size: [*c]usize) !void {
|
||||
return errors.espCheckError(sys.httpd_req_get_cookie_val(req, cookie_name, val, val_size));
|
||||
}
|
||||
};
|
||||
|
||||
pub fn queryKeyValue(qry: [*:0]const u8, key: [*:0]const u8, val: [*:0]u8, val_size: usize) !void {
|
||||
return errors.espCheckError(sys.httpd_query_key_value(qry, key, val, val_size));
|
||||
}
|
||||
|
||||
pub const uri_MatchWildcard = sys.httpd_uri_match_wildcard;
|
||||
|
||||
pub const Response = struct {
|
||||
/// Send a complete response with a byte-slice body.
|
||||
pub fn send(r: [*c]sys.httpd_req_t, buf: []const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send(r, buf.ptr, @intCast(buf.len)));
|
||||
}
|
||||
/// Send a response chunk with a byte-slice body.
|
||||
pub fn sendChunk(r: [*c]sys.httpd_req_t, buf: []const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send_chunk(r, buf.ptr, @intCast(buf.len)));
|
||||
}
|
||||
/// Send a null-terminated string as the complete response.
|
||||
/// Passes -1 (HTTPD_RESP_USE_STRLEN) so ESP-IDF calls strlen.
|
||||
pub fn sendStr(r: [*c]sys.httpd_req_t, str: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send(r, str, -1));
|
||||
}
|
||||
/// Send a null-terminated string as a response chunk.
|
||||
pub fn sendStrChunk(r: [*c]sys.httpd_req_t, str: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send_chunk(r, str, -1));
|
||||
}
|
||||
pub fn setStatus(r: [*c]sys.httpd_req_t, status: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_set_status(r, status));
|
||||
}
|
||||
pub fn setType(r: [*c]sys.httpd_req_t, @"type": [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_set_type(r, @"type"));
|
||||
}
|
||||
pub fn setHDR(r: [*c]sys.httpd_req_t, field: [*:0]const u8, value: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_set_hdr(r, field, value));
|
||||
}
|
||||
pub fn sendError(req: [*c]sys.httpd_req_t, @"error": sys.httpd_err_code_t, msg: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send_err(req, @"error", msg));
|
||||
}
|
||||
pub fn send404(r: [*c]sys.httpd_req_t) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send_err(r, .HTTPD_404_NOT_FOUND, null));
|
||||
}
|
||||
pub fn send408(r: [*c]sys.httpd_req_t) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send_err(r, .HTTPD_408_REQ_TIMEOUT, null));
|
||||
}
|
||||
pub fn send500(r: [*c]sys.httpd_req_t) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send_err(r, .HTTPD_500_INTERNAL_SERVER_ERROR, null));
|
||||
}
|
||||
};
|
||||
|
||||
pub const rawSend = sys.httpd_send;
|
||||
|
||||
pub const Socket = struct {
|
||||
pub const send = sys.httpd_socket_send;
|
||||
pub const receive = sys.httpd_socket_recv;
|
||||
};
|
||||
|
||||
pub const getGlobalUserContext = sys.httpd_get_global_user_ctx;
|
||||
pub const getGlobalTransportContext = sys.httpd_get_global_transport_ctx;
|
||||
pub fn getClientList(handle: sys.httpd_handle_t, fds: [*c]usize, client_fds: [*c]c_int) !void {
|
||||
return errors.espCheckError(sys.httpd_get_client_list(handle, fds, client_fds));
|
||||
}
|
||||
pub const httpd_work_fn_t = sys.httpd_work_fn_t;
|
||||
pub fn queueWork(handle: sys.httpd_handle_t, work: httpd_work_fn_t, arg: ?*anyopaque) !void {
|
||||
return errors.espCheckError(sys.httpd_queue_work(handle, work, arg));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HTTP client (esp_http_client)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Client = struct {
|
||||
handle: sys.esp_http_client_handle_t = null,
|
||||
|
||||
/// Initialise the HTTP client from a config struct. Call `deinit()` when done.
|
||||
pub fn init(config: *const sys.esp_http_client_config_t) Client {
|
||||
return .{ .handle = sys.esp_http_client_init(config) };
|
||||
}
|
||||
|
||||
/// Clean up the client handle. Must be called after use.
|
||||
pub fn deinit(self: Client) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_cleanup(self.handle));
|
||||
}
|
||||
|
||||
/// Perform a complete blocking request/response cycle.
|
||||
pub fn perform(self: Client) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_perform(self.handle));
|
||||
}
|
||||
|
||||
/// Open the connection (for streaming writes).
|
||||
/// `write_len` is Content-Length; pass 0 for requests without a body.
|
||||
pub fn open(self: Client, write_len: c_int) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_open(self.handle, write_len));
|
||||
}
|
||||
|
||||
/// Close the connection (without cleaning up the handle).
|
||||
pub fn close(self: Client) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_close(self.handle));
|
||||
}
|
||||
|
||||
/// Write request body data (call after `open()`).
|
||||
pub fn write(self: Client, data: []const u8) !c_int {
|
||||
const n = sys.esp_http_client_write(self.handle, data.ptr, @intCast(data.len));
|
||||
if (n < 0) return error.HttpWriteFailed;
|
||||
return n;
|
||||
}
|
||||
|
||||
/// Fetch (read) response headers after `open()`.
|
||||
/// Returns Content-Length, or negative on error / chunked encoding.
|
||||
pub fn fetchHeaders(self: Client) i64 {
|
||||
return sys.esp_http_client_fetch_headers(self.handle);
|
||||
}
|
||||
|
||||
/// Read response body into `buf`. Returns number of bytes read (0 = done).
|
||||
pub fn read(self: Client, buf: []u8) !c_int {
|
||||
const n = sys.esp_http_client_read(self.handle, buf.ptr, @intCast(buf.len));
|
||||
if (n < 0) return error.HttpReadFailed;
|
||||
return n;
|
||||
}
|
||||
|
||||
/// Read the entire response into `buf` (convenience wrapper).
|
||||
pub fn readResponse(self: Client, buf: []u8) !c_int {
|
||||
const n = sys.esp_http_client_read_response(self.handle, buf.ptr, @intCast(buf.len));
|
||||
if (n < 0) return error.HttpReadFailed;
|
||||
return n;
|
||||
}
|
||||
|
||||
/// HTTP status code of the last response.
|
||||
pub fn getStatusCode(self: Client) c_int {
|
||||
return sys.esp_http_client_get_status_code(self.handle);
|
||||
}
|
||||
|
||||
/// Content-Length of the last response (-1 if unknown / chunked).
|
||||
pub fn getContentLength(self: Client) i64 {
|
||||
return sys.esp_http_client_get_content_length(self.handle);
|
||||
}
|
||||
|
||||
/// Cancel the current in-progress request.
|
||||
pub fn cancelRequest(self: Client) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_cancel_request(self.handle));
|
||||
}
|
||||
|
||||
/// Setters — configure the client before calling `perform()` or `open()`.
|
||||
pub const Set = struct {
|
||||
pub fn url(client: sys.esp_http_client_handle_t, _url: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_url(client, _url));
|
||||
}
|
||||
pub fn postField(client: sys.esp_http_client_handle_t, data: []const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_post_field(client, data.ptr, @intCast(data.len)));
|
||||
}
|
||||
pub fn header(client: sys.esp_http_client_handle_t, field: [*:0]const u8, value: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_header(client, field, value));
|
||||
}
|
||||
pub fn username(client: sys.esp_http_client_handle_t, _username: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_username(client, _username));
|
||||
}
|
||||
pub fn password(client: sys.esp_http_client_handle_t, _password: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_password(client, _password));
|
||||
}
|
||||
pub fn method(client: sys.esp_http_client_handle_t, _method: sys.esp_http_client_method_t) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_method(client, _method));
|
||||
}
|
||||
pub fn timeout(client: sys.esp_http_client_handle_t, timeout_ms: u32) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_timeout_ms(client, timeout_ms));
|
||||
}
|
||||
pub fn userData(client: sys.esp_http_client_handle_t, user_data: ?*anyopaque) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_user_data(client, user_data));
|
||||
}
|
||||
pub fn authData(client: sys.esp_http_client_handle_t, auth_data: []const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_auth_data(client, auth_data.ptr, @intCast(auth_data.len)));
|
||||
}
|
||||
pub fn authType(client: sys.esp_http_client_handle_t, auth_type: sys.esp_http_client_auth_type_t) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_authtype(client, auth_type));
|
||||
}
|
||||
pub fn redirection(client: sys.esp_http_client_handle_t) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_redirection(client));
|
||||
}
|
||||
};
|
||||
|
||||
/// Getters — retrieve configuration or response state.
|
||||
pub const Get = struct {
|
||||
/// Copy the current URL into a buffer.
|
||||
pub fn url(client: sys.esp_http_client_handle_t, buf: []u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_get_url(client, buf.ptr, @intCast(buf.len)));
|
||||
}
|
||||
/// Get a pointer to the POST field data (no copy).
|
||||
pub fn postField(client: sys.esp_http_client_handle_t, data: *[*c]const u8, len: *c_int) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_get_post_field(client, data, len));
|
||||
}
|
||||
/// Get the value of a request header by key (returns pointer via `value`).
|
||||
pub fn header(client: sys.esp_http_client_handle_t, key: [*:0]const u8, value: *[*c]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_get_header(client, key, value));
|
||||
}
|
||||
/// Get the configured username (returns pointer via `value`).
|
||||
pub fn username(client: sys.esp_http_client_handle_t, value: *[*c]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_get_username(client, value));
|
||||
}
|
||||
/// Get the configured password (returns pointer via `value`).
|
||||
pub fn password(client: sys.esp_http_client_handle_t, value: *[*c]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_get_password(client, value));
|
||||
}
|
||||
pub fn userData(client: sys.esp_http_client_handle_t, user_data: *?*anyopaque) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_get_user_data(client, user_data));
|
||||
}
|
||||
};
|
||||
};
|
||||
44
software/zig_main/imports/i2c.zig
Normal file
44
software/zig_main/imports/i2c.zig
Normal file
@@ -0,0 +1,44 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
pub const BUS = struct {
|
||||
pub fn add(bus_config: ?*const sys.i2c_master_bus_config_t, ret_bus_handle: [*c]sys.i2c_master_bus_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2c_new_master_bus(bus_config, ret_bus_handle));
|
||||
}
|
||||
pub fn addDevice(bus_handle: sys.i2c_master_bus_handle_t, dev_config: [*c]const sys.i2c_device_config_t, ret_handle: [*c]sys.i2c_master_dev_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2c_master_bus_add_device(bus_handle, dev_config, ret_handle));
|
||||
}
|
||||
pub fn del(bus_handle: sys.i2c_master_bus_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2c_del_master_bus(bus_handle));
|
||||
}
|
||||
pub fn removeDevice(handle: sys.i2c_master_dev_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2c_master_bus_rm_device(handle));
|
||||
}
|
||||
pub fn reset(handle: sys.i2c_master_bus_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2c_master_bus_reset(handle));
|
||||
}
|
||||
};
|
||||
|
||||
/// Write bytes to an I2C device. Buffer length is taken from the slice.
|
||||
pub fn transmit(i2c_dev: sys.i2c_master_dev_handle_t, write_buffer: []const u8, xfer_timeout_ms: c_int) !void {
|
||||
return errors.espCheckError(sys.i2c_master_transmit(i2c_dev, write_buffer.ptr, write_buffer.len, xfer_timeout_ms));
|
||||
}
|
||||
|
||||
/// Write then read in a single I2C transaction (repeated-start).
|
||||
pub fn transmitReceive(i2c_dev: sys.i2c_master_dev_handle_t, write_buffer: []const u8, read_buffer: []u8, xfer_timeout_ms: c_int) !void {
|
||||
return errors.espCheckError(sys.i2c_master_transmit_receive(i2c_dev, write_buffer.ptr, write_buffer.len, read_buffer.ptr, read_buffer.len, xfer_timeout_ms));
|
||||
}
|
||||
|
||||
/// Read bytes from an I2C device. Buffer length is taken from the slice.
|
||||
pub fn receive(i2c_dev: sys.i2c_master_dev_handle_t, read_buffer: []u8, xfer_timeout_ms: c_int) !void {
|
||||
return errors.espCheckError(sys.i2c_master_receive(i2c_dev, read_buffer.ptr, read_buffer.len, xfer_timeout_ms));
|
||||
}
|
||||
pub fn probe(i2c_master: sys.i2c_master_bus_handle_t, address: u16, xfer_timeout_ms: c_int) !void {
|
||||
return try errors.espCheckError(sys.i2c_master_probe(i2c_master, address, xfer_timeout_ms));
|
||||
}
|
||||
pub fn registerEventCallbacks(i2c_dev: sys.i2c_master_dev_handle_t, cbs: [*c]const sys.i2c_master_event_callbacks_t, user_data: ?*anyopaque) !void {
|
||||
return try errors.espCheckError(sys.i2c_master_register_event_callbacks(i2c_dev, cbs, user_data));
|
||||
}
|
||||
pub fn waitAllDone(i2c_master: sys.i2c_master_bus_handle_t, timeout_ms: c_int) !void {
|
||||
return try errors.espCheckError(sys.i2c_master_wait_all_done(i2c_master, timeout_ms));
|
||||
}
|
||||
102
software/zig_main/imports/i2s.zig
Normal file
102
software/zig_main/imports/i2s.zig
Normal file
@@ -0,0 +1,102 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
pub fn newChannel(chan_cfg: [*c]const sys.i2s_chan_config_t, ret_tx_handle: [*c]sys.i2s_chan_handle_t, ret_rx_handle: [*c]sys.i2s_chan_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_new_channel(chan_cfg, ret_tx_handle, ret_rx_handle));
|
||||
}
|
||||
pub fn delChannel(handle: sys.i2s_chan_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_del_channel(handle));
|
||||
}
|
||||
pub fn channelGetInfo(handle: sys.i2s_chan_handle_t, chan_info: [*c]sys.i2s_chan_info_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_get_info(handle, chan_info));
|
||||
}
|
||||
pub fn channelEnable(handle: sys.i2s_chan_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_enable(handle));
|
||||
}
|
||||
pub fn channelDisable(handle: sys.i2s_chan_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_disable(handle));
|
||||
}
|
||||
pub fn channelPreloadData(tx_handle: sys.i2s_chan_handle_t, src: ?*const anyopaque, size: usize, bytes_loaded: [*c]usize) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_preload_data(tx_handle, src, size, bytes_loaded));
|
||||
}
|
||||
pub fn channelWrite(handle: sys.i2s_chan_handle_t, src: ?*const anyopaque, size: usize, bytes_written: [*c]usize, timeout_ms: u32) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_write(handle, src, size, bytes_written, timeout_ms));
|
||||
}
|
||||
pub fn channelRead(handle: sys.i2s_chan_handle_t, dest: ?*anyopaque, size: usize, bytes_read: [*c]usize, timeout_ms: u32) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_read(handle, dest, size, bytes_read, timeout_ms));
|
||||
}
|
||||
pub fn channelRegisterEventCallback(handle: sys.i2s_chan_handle_t, callbacks: [*c]const sys.i2s_event_callbacks_t, user_data: ?*anyopaque) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_register_event_callback(handle, callbacks, user_data));
|
||||
}
|
||||
pub fn channelInitPdmRXMode(handle: sys.i2s_chan_handle_t, pdm_rx_cfg: ?*const sys.i2s_pdm_rx_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_init_pdm_rx_mode(handle, pdm_rx_cfg));
|
||||
}
|
||||
pub fn channelReconfigPdmRXClock(handle: sys.i2s_chan_handle_t, clk_cfg: [*c]const sys.i2s_pdm_rx_clk_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_reconfig_pdm_rx_clock(handle, clk_cfg));
|
||||
}
|
||||
pub fn channelReconfigPdmRXSlot(handle: sys.i2s_chan_handle_t, slot_cfg: [*c]const sys.i2s_pdm_rx_slot_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_reconfig_pdm_rx_slot(handle, slot_cfg));
|
||||
}
|
||||
pub fn channelReconfigPdmRXGPIO(handle: sys.i2s_chan_handle_t, gpio_cfg: ?*const sys.i2s_pdm_rx_gpio_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_reconfig_pdm_rx_gpio(handle, gpio_cfg));
|
||||
}
|
||||
pub fn channelInitPdmTXMode(handle: sys.i2s_chan_handle_t, pdm_tx_cfg: ?*const sys.i2s_pdm_tx_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_init_pdm_tx_mode(handle, pdm_tx_cfg));
|
||||
}
|
||||
pub fn channelReconfigPdmTXClock(handle: sys.i2s_chan_handle_t, clk_cfg: [*c]const sys.i2s_pdm_tx_clk_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_reconfig_pdm_tx_clock(handle, clk_cfg));
|
||||
}
|
||||
pub fn channelReconfigPdmTXSlot(handle: sys.i2s_chan_handle_t, slot_cfg: [*c]const sys.i2s_pdm_tx_slot_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_reconfig_pdm_tx_slot(handle, slot_cfg));
|
||||
}
|
||||
pub fn channelReconfigPdmTXGPIO(handle: sys.i2s_chan_handle_t, gpio_cfg: ?*const sys.i2s_pdm_tx_gpio_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_reconfig_pdm_tx_gpio(handle, gpio_cfg));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// STD mode (standard I2S / PCM — most common)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Initialise a channel in standard I2S mode (I2S, MSB-justified, LSB-justified, PCM).
|
||||
pub fn channelInitStdMode(handle: sys.i2s_chan_handle_t, std_cfg: *const sys.i2s_std_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_init_std_mode(handle, std_cfg));
|
||||
}
|
||||
|
||||
/// Reconfigure clock parameters of a standard-mode channel (must be disabled first).
|
||||
pub fn channelReconfigStdClock(handle: sys.i2s_chan_handle_t, clk_cfg: *const sys.i2s_std_clk_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_reconfig_std_clock(handle, clk_cfg));
|
||||
}
|
||||
|
||||
/// Reconfigure slot parameters of a standard-mode channel (must be disabled first).
|
||||
pub fn channelReconfigStdSlot(handle: sys.i2s_chan_handle_t, slot_cfg: *const sys.i2s_std_slot_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_reconfig_std_slot(handle, slot_cfg));
|
||||
}
|
||||
|
||||
/// Reconfigure GPIO pins of a standard-mode channel (must be disabled first).
|
||||
pub fn channelReconfigStdGPIO(handle: sys.i2s_chan_handle_t, gpio_cfg: *const sys.i2s_std_gpio_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_reconfig_std_gpio(handle, gpio_cfg));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// TDM mode (Time-Division Multiplexed — multiple slots per frame)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Initialise a channel in TDM mode.
|
||||
pub fn channelInitTdmMode(handle: sys.i2s_chan_handle_t, tdm_cfg: *const sys.i2s_tdm_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_init_tdm_mode(handle, tdm_cfg));
|
||||
}
|
||||
|
||||
/// Reconfigure clock parameters of a TDM channel (must be disabled first).
|
||||
pub fn channelReconfigTdmClock(handle: sys.i2s_chan_handle_t, clk_cfg: *const sys.i2s_tdm_clk_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_reconfig_tdm_clock(handle, clk_cfg));
|
||||
}
|
||||
|
||||
/// Reconfigure slot parameters of a TDM channel (must be disabled first).
|
||||
pub fn channelReconfigTdmSlot(handle: sys.i2s_chan_handle_t, slot_cfg: *const sys.i2s_tdm_slot_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_reconfig_tdm_slot(handle, slot_cfg));
|
||||
}
|
||||
|
||||
/// Reconfigure GPIO pins of a TDM channel (must be disabled first).
|
||||
pub fn channelReconfigTdmGPIO(handle: sys.i2s_chan_handle_t, gpio_cfg: *const sys.i2s_tdm_gpio_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_reconfig_tdm_gpio(handle, gpio_cfg));
|
||||
}
|
||||
134
software/zig_main/imports/idf.zig
Normal file
134
software/zig_main/imports/idf.zig
Normal file
@@ -0,0 +1,134 @@
|
||||
pub const bl = @import("bootloader");
|
||||
pub const bt = switch (@hasDecl(sys, "CONFIG_BT_ENABLED")) {
|
||||
true => @import("bluetooth"),
|
||||
false => @compileError("bluetooth requires CONFIG_BT_ENABLED in sdkconfig"),
|
||||
};
|
||||
pub const nimble = if (@hasDecl(sys, "CONFIG_BT_NIMBLE_ENABLED"))
|
||||
@import("nimble")
|
||||
else
|
||||
@compileError(
|
||||
\\NimBLE not enabled. Enable via:
|
||||
\\ idf.py menuconfig → Component config → Bluetooth → Host → NimBLE
|
||||
\\Then run: idf.py reconfigure
|
||||
);
|
||||
pub const crc = @import("crc");
|
||||
pub const dsp = switch (@hasDecl(sys, "HAS_ESP_DSP")) {
|
||||
true => @import("dsp"),
|
||||
false => @compileError("requires: idf.py add-dependency espressif/esp-dsp"),
|
||||
};
|
||||
pub const err = @import("error");
|
||||
pub const gpio = @import("gpio");
|
||||
pub const heap = @import("heap");
|
||||
pub const http = @import("http");
|
||||
pub const i2c = @import("i2c");
|
||||
pub const i2s = @import("i2s");
|
||||
pub const led = switch (@hasDecl(sys, "HAS_LED_STRIP")) {
|
||||
true => @import("led"),
|
||||
false => @compileError("requires: idf.py add-dependency espressif/led_strip"),
|
||||
};
|
||||
pub const log = @import("log");
|
||||
pub const lwip = @import("lwip");
|
||||
pub const mqtt = @import("mqtt");
|
||||
pub const esp_now = @import("now");
|
||||
pub const phy = @import("phy");
|
||||
pub const pulse = @import("pulse");
|
||||
pub const esp_panic = @import("panic");
|
||||
pub const rtos = @import("rtos");
|
||||
pub const nvs = @import("nvs");
|
||||
pub const partition = @import("partition");
|
||||
pub const sleep = @import("sleep");
|
||||
pub const event = @import("event");
|
||||
pub const wdt = @import("wdt");
|
||||
pub const segger = @import("segger");
|
||||
pub const spi = @import("spi");
|
||||
pub const uart = @import("uart");
|
||||
pub const ver = @import("ver");
|
||||
pub const esp_hosted = switch (@hasDecl(sys, "HAS_ESP_HOSTED")) {
|
||||
true => @import("hosted"),
|
||||
false => @compileError("requires: idf.py add-dependency espressif/esp_hosted"),
|
||||
};
|
||||
pub const wifi_remote = switch (@hasDecl(sys, "HAS_ESP_WIFI_REMOTE")) {
|
||||
true => @import("wifi_remote"),
|
||||
false => @compileError("requires: idf.py add-dependency espressif/esp_wifi_remote"),
|
||||
};
|
||||
pub const timer = @import("timer");
|
||||
pub const ledc = @import("ledc");
|
||||
pub const twai = @import("twai");
|
||||
pub const pm = @import("pm");
|
||||
pub const pthread = @import("pthread");
|
||||
pub const matter = switch (@hasDecl(sys, "HAS_ESP_MATTER")) {
|
||||
true => @import("matter"),
|
||||
false => @compileError("requires: idf.py add-dependency espressif/esp_matter"),
|
||||
};
|
||||
pub const wifi = switch (currentTarget) {
|
||||
.esp32h2, .esp32h21, .esp32h4, .esp32p4 => @compileError("Wifi requires CONFIG_ESP_WIFI_ENABLED in sdkconfig"),
|
||||
else => @import("wifi"),
|
||||
};
|
||||
pub const sys = @import("sys");
|
||||
|
||||
const Device = enum {
|
||||
esp32,
|
||||
esp32s2,
|
||||
esp32s3,
|
||||
esp32s31,
|
||||
esp32c2,
|
||||
esp32c3,
|
||||
esp32c5,
|
||||
esp32c6,
|
||||
esp32c61,
|
||||
esp32h2,
|
||||
esp32h21,
|
||||
esp32h4,
|
||||
esp32p4,
|
||||
};
|
||||
|
||||
// Convert compile-time target string to enum
|
||||
pub const currentTarget = blk: {
|
||||
const target_str = sys.CONFIG_IDF_TARGET;
|
||||
break :blk @import("std").meta.stringToEnum(Device, target_str) orelse {
|
||||
@compileError("Unknown ESP32 device target: " ++ target_str);
|
||||
};
|
||||
};
|
||||
|
||||
// Check all imports
|
||||
comptime {
|
||||
_ = sys;
|
||||
_ = bl;
|
||||
if (@hasDecl(sys, "CONFIG_BT_ENABLED")) _ = bt;
|
||||
_ = crc;
|
||||
_ = err;
|
||||
_ = gpio;
|
||||
_ = heap;
|
||||
_ = http;
|
||||
_ = i2c;
|
||||
_ = i2s;
|
||||
_ = log;
|
||||
_ = lwip;
|
||||
_ = mqtt;
|
||||
if (@hasDecl(sys, "HAS_ESP_HOSTED")) _ = esp_hosted;
|
||||
_ = esp_now;
|
||||
_ = phy;
|
||||
_ = pulse;
|
||||
_ = esp_panic;
|
||||
_ = rtos;
|
||||
if (@hasDecl(sys, "CONFIG_BT_NIMBLE_ENABLED")) _ = nimble;
|
||||
_ = nvs;
|
||||
_ = partition;
|
||||
_ = sleep;
|
||||
_ = event;
|
||||
_ = wdt;
|
||||
_ = segger;
|
||||
_ = spi;
|
||||
_ = uart;
|
||||
_ = ver;
|
||||
if (@hasDecl(sys, "CONFIG_ESP_WIFI_ENABLED")) _ = wifi;
|
||||
if (@hasDecl(sys, "HAS_ESP_WIFI_REMOTE")) _ = wifi_remote;
|
||||
if (@hasDecl(sys, "HAS_ESP_DSP")) _ = dsp;
|
||||
if (@hasDecl(sys, "HAS_LED_STRIP")) _ = led;
|
||||
_ = timer;
|
||||
_ = ledc;
|
||||
_ = twai;
|
||||
_ = pm;
|
||||
_ = pthread;
|
||||
if (@hasDecl(sys, "HAS_ESP_MATTER")) _ = matter;
|
||||
}
|
||||
119
software/zig_main/imports/led-strip.zig
Normal file
119
software/zig_main/imports/led-strip.zig
Normal file
@@ -0,0 +1,119 @@
|
||||
// requires: idf.py add-dependency espressif/led_strip
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
pub const LedModel = enum(sys.led_model_t) {
|
||||
ws2812 = sys.LED_MODEL_WS2812,
|
||||
sk6812 = sys.LED_MODEL_SK6812,
|
||||
ws2811 = sys.LED_MODEL_WS2811,
|
||||
ws2816 = sys.LED_MODEL_WS2816,
|
||||
invalid = sys.LED_MODEL_INVALID,
|
||||
};
|
||||
pub const LedStripHandle = sys.led_strip_handle_t;
|
||||
|
||||
pub const ColorComponentFormat = extern union {
|
||||
format: packed struct {
|
||||
r_pos: u2, // Position of the red channel in the color order: 0~3
|
||||
g_pos: u2, // Position of the green channel in the color order: 0~3
|
||||
b_pos: u2, // Position of the blue channel in the color order: 0~3
|
||||
w_pos: u2, // Position of the white channel in the color order: 0~3
|
||||
reserved: u19, // Reserved
|
||||
bytes_per_color: u2, // Bytes per color component: 1 or 2. If set to 0, it will fallback to 1
|
||||
num_components: u3, // Number of color components per pixel: 3 or 4. If set to 0, it will fallback to 3
|
||||
},
|
||||
format_id: u32,
|
||||
};
|
||||
pub const PixelFormat = enum(u32) {
|
||||
grb = 0x00, // default WS2812
|
||||
rgb = 0x01,
|
||||
grbw = 0x10,
|
||||
rgbw = 0x11,
|
||||
};
|
||||
pub const LedStripConfig = extern struct {
|
||||
strip_gpio_num: i32,
|
||||
max_leds: u32,
|
||||
led_model: LedModel,
|
||||
color_component_format: ColorComponentFormat,
|
||||
// flags: sys.struct_led_strip_extra_flags_29, // opaque
|
||||
flags: u32 = 0, // most users leave = 0; see component for bits
|
||||
|
||||
pub fn init(gpio: u32, count: u32, model: LedModel, format_id: u32) LedStripConfig {
|
||||
return .{
|
||||
.strip_gpio_num = @intCast(gpio),
|
||||
.max_leds = count,
|
||||
.led_model = model,
|
||||
.color_component_format = .{ .format_id = format_id },
|
||||
.flags = 0,
|
||||
};
|
||||
}
|
||||
pub fn ws2812(gpio: u32, count: u32) LedStripConfig {
|
||||
return init(gpio, count, .ws2812, @intFromEnum(PixelFormat.grb));
|
||||
}
|
||||
};
|
||||
pub const LedStripRmtConfig = struct {
|
||||
clk_src: sys.rmt_clock_source_t,
|
||||
resolution_hz: u32,
|
||||
mem_block_symbols: usize = 0, // 0 = default/auto
|
||||
// flags: sys.struct_led_strip_rmt_extra_config_33, // opaque
|
||||
flags: u32 = 0,
|
||||
|
||||
pub fn init_10mhz() LedStripRmtConfig {
|
||||
return .{
|
||||
.clk_src = sys.RMT_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = 10_000_000,
|
||||
.mem_block_symbols = 0,
|
||||
.flags = 0,
|
||||
};
|
||||
}
|
||||
|
||||
// Very common setting in examples
|
||||
pub const default = init_10mhz();
|
||||
};
|
||||
pub const LedStripSpiConfig = struct {
|
||||
clk_src: sys.spi_clock_source_t,
|
||||
spi_bus: sys.spi_host_device_t,
|
||||
// flags: sys.struct_unnamed_34, // opaque
|
||||
flags: u32 = 0,
|
||||
};
|
||||
|
||||
// ───────────────────────────────────────────────
|
||||
// API functions
|
||||
|
||||
/// Creates RMT-backed LED strip
|
||||
pub fn newRmtDevice(led_config: *const LedStripConfig, rmt_config: *const LedStripRmtConfig, handle: *LedStripHandle) !LedStripHandle {
|
||||
const result = sys.led_strip_new_rmt_device(@ptrCast(led_config), @ptrCast(rmt_config), @ptrCast(handle));
|
||||
try errors.espCheckError(result);
|
||||
return handle.*;
|
||||
}
|
||||
|
||||
/// Creates SPI-backed LED strip
|
||||
pub fn newSpiDevice(led_config: *const LedStripConfig, spi_config: *const LedStripSpiConfig, handle: *LedStripHandle) !LedStripHandle {
|
||||
const result = sys.led_strip_new_spi_device(@ptrCast(led_config), @ptrCast(spi_config), @ptrCast(handle));
|
||||
try errors.espCheckError(result);
|
||||
return handle.*;
|
||||
}
|
||||
/// Set one RGB pixel
|
||||
pub fn setPixel(strip: LedStripHandle, index: u32, r: u8, g: u8, b: u8) !void {
|
||||
// The C API takes u32 — we clamp/convert for safety
|
||||
return try errors.espCheckError(sys.led_strip_set_pixel(strip, index, @as(u32, r), @as(u32, g), @as(u32, b)));
|
||||
}
|
||||
/// Variant that also sets white channel (SK6812 RGBW etc.)
|
||||
pub fn setPixelRgbw(strip: LedStripHandle, index: u32, r: u8, g: u8, b: u8, w: u8) !void {
|
||||
return try errors.espCheckError(sys.led_strip_set_pixel_rgbw(strip, index, @as(u32, r), @as(u32, g), @as(u32, b), @as(u32, w)));
|
||||
}
|
||||
/// HSV variant (convenience — internally converts to RGB)
|
||||
pub fn setPixelHsv(strip: LedStripHandle, index: u32, hue: u16, sat: u8, val: u8) !void {
|
||||
return try errors.espCheckError(sys.led_strip_set_pixel_hsv(strip, index, hue, sat, val));
|
||||
}
|
||||
/// Push all buffered pixel data to the strip
|
||||
pub fn refresh(strip: LedStripHandle) !void {
|
||||
return try errors.espCheckError(sys.led_strip_refresh(strip));
|
||||
}
|
||||
/// Turn all LEDs off
|
||||
pub fn clear(strip: LedStripHandle) !void {
|
||||
return try errors.espCheckError(sys.led_strip_clear(strip));
|
||||
}
|
||||
/// Free resources
|
||||
pub fn deinit(strip: LedStripHandle) !void {
|
||||
return try errors.espCheckError(sys.led_strip_del(strip));
|
||||
}
|
||||
218
software/zig_main/imports/ledc.zig
Normal file
218
software/zig_main/imports/ledc.zig
Normal file
@@ -0,0 +1,218 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type aliases & enums
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const ChannelConfig = sys.ledc_channel_config_t;
|
||||
pub const TimerConfig = sys.ledc_timer_config_t;
|
||||
pub const IsrHandle = sys.ledc_isr_handle_t;
|
||||
pub const CbParam = sys.ledc_cb_param_t;
|
||||
pub const Cbs = sys.ledc_cbs_t;
|
||||
|
||||
/// Speed mode. Most modern ESP32 variants only support LOW_SPEED_MODE.
|
||||
pub const SpeedMode = enum(sys.ledc_mode_t) {
|
||||
low = sys.LEDC_LOW_SPEED_MODE,
|
||||
};
|
||||
|
||||
pub const IntrType = enum(sys.ledc_intr_type_t) {
|
||||
disable = sys.LEDC_INTR_DISABLE,
|
||||
fade_end = sys.LEDC_INTR_FADE_END,
|
||||
};
|
||||
|
||||
pub const DutyDir = enum(sys.ledc_duty_direction_t) {
|
||||
decrease = sys.LEDC_DUTY_DIR_DECREASE,
|
||||
increase = sys.LEDC_DUTY_DIR_INCREASE,
|
||||
};
|
||||
|
||||
pub const Timer = enum(sys.ledc_timer_t) {
|
||||
@"0" = sys.LEDC_TIMER_0,
|
||||
@"1" = sys.LEDC_TIMER_1,
|
||||
@"2" = sys.LEDC_TIMER_2,
|
||||
@"3" = sys.LEDC_TIMER_3,
|
||||
};
|
||||
|
||||
pub const Channel = enum(sys.ledc_channel_t) {
|
||||
@"0" = sys.LEDC_CHANNEL_0,
|
||||
@"1" = sys.LEDC_CHANNEL_1,
|
||||
@"2" = sys.LEDC_CHANNEL_2,
|
||||
@"3" = sys.LEDC_CHANNEL_3,
|
||||
@"4" = sys.LEDC_CHANNEL_4,
|
||||
@"5" = sys.LEDC_CHANNEL_5,
|
||||
};
|
||||
|
||||
pub const TimerBit = enum(sys.ledc_timer_bit_t) {
|
||||
@"1" = sys.LEDC_TIMER_1_BIT,
|
||||
@"2" = sys.LEDC_TIMER_2_BIT,
|
||||
@"3" = sys.LEDC_TIMER_3_BIT,
|
||||
@"4" = sys.LEDC_TIMER_4_BIT,
|
||||
@"5" = sys.LEDC_TIMER_5_BIT,
|
||||
@"6" = sys.LEDC_TIMER_6_BIT,
|
||||
@"7" = sys.LEDC_TIMER_7_BIT,
|
||||
@"8" = sys.LEDC_TIMER_8_BIT,
|
||||
@"9" = sys.LEDC_TIMER_9_BIT,
|
||||
@"10" = sys.LEDC_TIMER_10_BIT,
|
||||
@"11" = sys.LEDC_TIMER_11_BIT,
|
||||
@"12" = sys.LEDC_TIMER_12_BIT,
|
||||
@"13" = sys.LEDC_TIMER_13_BIT,
|
||||
@"14" = sys.LEDC_TIMER_14_BIT,
|
||||
};
|
||||
|
||||
pub const FadeMode = enum(sys.ledc_fade_mode_t) {
|
||||
no_wait = sys.LEDC_FADE_NO_WAIT,
|
||||
wait_done = sys.LEDC_FADE_WAIT_DONE,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Timer configuration and control
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const TimerCtrl = struct {
|
||||
/// Configure a LEDC timer (frequency + duty resolution).
|
||||
pub fn config(cfg: *const TimerConfig) !void {
|
||||
try errors.espCheckError(sys.ledc_timer_config(cfg));
|
||||
}
|
||||
|
||||
/// Reset a LEDC timer.
|
||||
pub fn reset(mode: SpeedMode, timer: Timer) !void {
|
||||
try errors.espCheckError(sys.ledc_timer_rst(@intFromEnum(mode), @intFromEnum(timer)));
|
||||
}
|
||||
|
||||
/// Pause a LEDC timer.
|
||||
pub fn pause(mode: SpeedMode, timer: Timer) !void {
|
||||
try errors.espCheckError(sys.ledc_timer_pause(@intFromEnum(mode), @intFromEnum(timer)));
|
||||
}
|
||||
|
||||
/// Resume a paused LEDC timer.
|
||||
pub fn @"resume"(mode: SpeedMode, timer: Timer) !void {
|
||||
try errors.espCheckError(sys.ledc_timer_resume(@intFromEnum(mode), @intFromEnum(timer)));
|
||||
}
|
||||
|
||||
/// Set the frequency (Hz) of a running timer.
|
||||
pub fn setFreq(mode: SpeedMode, timer: Timer, freq_hz: u32) !void {
|
||||
try errors.espCheckError(sys.ledc_set_freq(@intFromEnum(mode), @intFromEnum(timer), freq_hz));
|
||||
}
|
||||
|
||||
/// Get the current frequency (Hz) of a timer.
|
||||
pub fn getFreq(mode: SpeedMode, timer: Timer) u32 {
|
||||
return sys.ledc_get_freq(@intFromEnum(mode), @intFromEnum(timer));
|
||||
}
|
||||
|
||||
/// Return the best duty resolution for a given source clock and target frequency.
|
||||
pub fn findSuitableDutyResolution(src_clk_freq: u32, timer_freq: u32) u32 {
|
||||
return sys.ledc_find_suitable_duty_resolution(src_clk_freq, timer_freq);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Channel configuration and control
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const ChannelCtrl = struct {
|
||||
/// Configure a LEDC output channel.
|
||||
pub fn config(cfg: *const ChannelConfig) !void {
|
||||
try errors.espCheckError(sys.ledc_channel_config(cfg));
|
||||
}
|
||||
|
||||
/// Re-assign a GPIO to an existing channel (without full reconfiguration).
|
||||
pub fn setPin(gpio_num: c_int, mode: SpeedMode, ch: Channel) !void {
|
||||
try errors.espCheckError(sys.ledc_set_pin(gpio_num, @intFromEnum(mode), @intFromEnum(ch)));
|
||||
}
|
||||
|
||||
/// Stop the channel output and set the GPIO to `idle_level` (0 or 1).
|
||||
pub fn stop(mode: SpeedMode, ch: Channel, idle_level: u32) !void {
|
||||
try errors.espCheckError(sys.ledc_stop(@intFromEnum(mode), @intFromEnum(ch), idle_level));
|
||||
}
|
||||
|
||||
/// Bind a channel to a different timer.
|
||||
pub fn bindTimer(mode: SpeedMode, ch: Channel, timer: Timer) !void {
|
||||
try errors.espCheckError(sys.ledc_bind_channel_timer(@intFromEnum(mode), @intFromEnum(ch), @intFromEnum(timer)));
|
||||
}
|
||||
|
||||
// ── Duty ──────────────────────────────────────────────────────────────
|
||||
|
||||
/// Set the duty cycle (without updating the hardware — call `updateDuty` after).
|
||||
pub fn setDuty(mode: SpeedMode, ch: Channel, duty: u32) !void {
|
||||
try errors.espCheckError(sys.ledc_set_duty(@intFromEnum(mode), @intFromEnum(ch), duty));
|
||||
}
|
||||
|
||||
/// Set duty + hpoint (without updating hardware).
|
||||
pub fn setDutyWithHpoint(mode: SpeedMode, ch: Channel, duty: u32, hpoint: u32) !void {
|
||||
try errors.espCheckError(sys.ledc_set_duty_with_hpoint(@intFromEnum(mode), @intFromEnum(ch), duty, hpoint));
|
||||
}
|
||||
|
||||
/// Latch the duty value set by `setDuty`/`setDutyWithHpoint` into hardware.
|
||||
pub fn updateDuty(mode: SpeedMode, ch: Channel) !void {
|
||||
try errors.espCheckError(sys.ledc_update_duty(@intFromEnum(mode), @intFromEnum(ch)));
|
||||
}
|
||||
|
||||
/// Set duty and immediately apply it to hardware (combines set + update).
|
||||
pub fn setDutyAndUpdate(mode: SpeedMode, ch: Channel, duty: u32, hpoint: u32) !void {
|
||||
try errors.espCheckError(sys.ledc_set_duty_and_update(@intFromEnum(mode), @intFromEnum(ch), duty, hpoint));
|
||||
}
|
||||
|
||||
/// Get the current duty cycle value.
|
||||
pub fn getDuty(mode: SpeedMode, ch: Channel) u32 {
|
||||
return sys.ledc_get_duty(@intFromEnum(mode), @intFromEnum(ch));
|
||||
}
|
||||
|
||||
/// Get the current hpoint value.
|
||||
pub fn getHpoint(mode: SpeedMode, ch: Channel) c_int {
|
||||
return sys.ledc_get_hpoint(@intFromEnum(mode), @intFromEnum(ch));
|
||||
}
|
||||
|
||||
// ── Callback ──────────────────────────────────────────────────────────
|
||||
|
||||
/// Register a callback for fade-end events on a channel.
|
||||
pub fn registerCallback(mode: SpeedMode, ch: Channel, cbs: *Cbs, user_arg: ?*anyopaque) !void {
|
||||
try errors.espCheckError(sys.ledc_cb_register(@intFromEnum(mode), @intFromEnum(ch), cbs, user_arg));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fade functions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Fade = struct {
|
||||
/// Install the fade ISR service. Call once before using any fade functions.
|
||||
/// `intr_alloc_flags`: interrupt allocation flags (e.g. ESP_INTR_FLAG_IRAM).
|
||||
pub fn install(intr_alloc_flags: c_int) !void {
|
||||
try errors.espCheckError(sys.ledc_fade_func_install(intr_alloc_flags));
|
||||
}
|
||||
|
||||
/// Uninstall the fade ISR service.
|
||||
pub fn uninstall() void {
|
||||
sys.ledc_fade_func_uninstall();
|
||||
}
|
||||
|
||||
/// Start a fade previously configured with `setFadeStep` or `setFadeTime`.
|
||||
pub fn start(mode: SpeedMode, ch: Channel, fade_mode: FadeMode) !void {
|
||||
try errors.espCheckError(sys.ledc_fade_start(@intFromEnum(mode), @intFromEnum(ch), @intFromEnum(fade_mode)));
|
||||
}
|
||||
|
||||
/// Stop an in-progress fade.
|
||||
pub fn stop(mode: SpeedMode, ch: Channel) !void {
|
||||
try errors.espCheckError(sys.ledc_fade_stop(@intFromEnum(mode), @intFromEnum(ch)));
|
||||
}
|
||||
|
||||
/// Configure a fade by time: ramp from current duty to `target_duty` in `fade_time_ms`.
|
||||
pub fn setFadeTime(mode: SpeedMode, ch: Channel, target_duty: u32, fade_time_ms: c_int) !void {
|
||||
try errors.espCheckError(sys.ledc_set_fade_with_time(@intFromEnum(mode), @intFromEnum(ch), target_duty, fade_time_ms));
|
||||
}
|
||||
|
||||
/// Configure a fade by step: ramp with a fixed `scale` increment every `cycle_num` PWM cycles.
|
||||
pub fn setFadeStep(mode: SpeedMode, ch: Channel, target_duty: u32, scale: u32, cycle_num: u32) !void {
|
||||
try errors.espCheckError(sys.ledc_set_fade_with_step(@intFromEnum(mode), @intFromEnum(ch), target_duty, scale, cycle_num));
|
||||
}
|
||||
|
||||
/// Set fade time and immediately start it.
|
||||
pub fn setFadeTimeAndStart(mode: SpeedMode, ch: Channel, target_duty: u32, fade_time_ms: u32, fade_mode: FadeMode) !void {
|
||||
try errors.espCheckError(sys.ledc_set_fade_time_and_start(@intFromEnum(mode), @intFromEnum(ch), target_duty, fade_time_ms, @intFromEnum(fade_mode)));
|
||||
}
|
||||
|
||||
/// Set fade step and immediately start it.
|
||||
pub fn setFadeStepAndStart(mode: SpeedMode, ch: Channel, target_duty: u32, scale: u32, cycle_num: u32, fade_mode: FadeMode) !void {
|
||||
try errors.espCheckError(sys.ledc_set_fade_step_and_start(@intFromEnum(mode), @intFromEnum(ch), target_duty, scale, cycle_num, @intFromEnum(fade_mode)));
|
||||
}
|
||||
};
|
||||
102
software/zig_main/imports/logger.zig
Normal file
102
software/zig_main/imports/logger.zig
Normal file
@@ -0,0 +1,102 @@
|
||||
const std = @import("std");
|
||||
const sys = @import("sys");
|
||||
|
||||
pub const default_log_scope = .espressif;
|
||||
|
||||
pub fn espLogFn(
|
||||
comptime level: std.log.Level,
|
||||
comptime scope: @TypeOf(.EnumLiteral),
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) void {
|
||||
const esp_level = comptime levelToEsp(level);
|
||||
const color = comptime levelColor(level);
|
||||
const prefix = color ++ "[" ++ comptime level.asText() ++ "] (" ++ @tagName(scope) ++ "): ";
|
||||
|
||||
var heap = std.heap.ArenaAllocator.init(std.heap.c_allocator);
|
||||
defer heap.deinit();
|
||||
|
||||
ESP_LOG(heap.allocator(), esp_level, "logging", prefix ++ format ++ LOG_RESET_COLOR ++ "\n", args);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Level mapping
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const default_level: sys.esp_log_level_t = switch (@import("builtin").mode) {
|
||||
.Debug => sys.ESP_LOG_DEBUG,
|
||||
.ReleaseSafe => sys.ESP_LOG_INFO,
|
||||
.ReleaseFast, .ReleaseSmall => sys.ESP_LOG_ERROR,
|
||||
};
|
||||
|
||||
/// Converts a Zig log level to its ESP-IDF equivalent.
|
||||
pub fn levelToEsp(comptime level: std.log.Level) sys.esp_log_level_t {
|
||||
return switch (level) {
|
||||
.err => sys.ESP_LOG_ERROR,
|
||||
.warn => sys.ESP_LOG_WARN,
|
||||
.info => sys.ESP_LOG_INFO,
|
||||
.debug => sys.ESP_LOG_DEBUG,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the ANSI color escape for a given Zig log level.
|
||||
pub fn levelColor(comptime level: std.log.Level) []const u8 {
|
||||
return switch (level) {
|
||||
.err => LOG_COLOR_E,
|
||||
.warn => LOG_COLOR_W,
|
||||
.info => LOG_COLOR_I,
|
||||
.debug => LOG_COLOR(LOG_COLOR_BLUE),
|
||||
};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Core log primitive
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn ESP_LOG(
|
||||
allocator: std.mem.Allocator,
|
||||
level: sys.esp_log_level_t,
|
||||
comptime tag: [*:0]const u8,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) void {
|
||||
if (isComptime(args)) {
|
||||
const buffer: [:0]const u8 = std.fmt.comptimePrint(fmt, args);
|
||||
sys.esp_log_write(level, tag, "%s", buffer.ptr);
|
||||
} else {
|
||||
const buffer: [:0]u8 = std.fmt.allocPrintSentinel(allocator, fmt, args, 0) catch return;
|
||||
sys.esp_log_write(level, tag, "%s", buffer.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ANSI color helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const LOG_COLOR_BLACK = "30";
|
||||
pub const LOG_COLOR_RED = "31";
|
||||
pub const LOG_COLOR_GREEN = "32";
|
||||
pub const LOG_COLOR_BROWN = "33";
|
||||
pub const LOG_COLOR_BLUE = "34";
|
||||
pub const LOG_COLOR_PURPLE = "35";
|
||||
pub const LOG_COLOR_CYAN = "36";
|
||||
|
||||
pub inline fn LOG_COLOR(comptime COLOR: []const u8) []const u8 {
|
||||
return "\x1b[0;" ++ COLOR ++ "m";
|
||||
}
|
||||
pub inline fn LOG_BOLD(comptime COLOR: []const u8) []const u8 {
|
||||
return "\x1b[1;" ++ COLOR ++ "m";
|
||||
}
|
||||
|
||||
pub const LOG_RESET_COLOR = "\x1b[0m";
|
||||
pub const LOG_COLOR_E = LOG_COLOR(LOG_COLOR_RED);
|
||||
pub const LOG_COLOR_W = LOG_COLOR(LOG_COLOR_BROWN);
|
||||
pub const LOG_COLOR_I = LOG_COLOR(LOG_COLOR_GREEN);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Internal helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
inline fn isComptime(val: anytype) bool {
|
||||
return @typeInfo(@TypeOf(.{val})).@"struct".fields[0].is_comptime;
|
||||
}
|
||||
771
software/zig_main/imports/lwip.zig
Normal file
771
software/zig_main/imports/lwip.zig
Normal file
@@ -0,0 +1,771 @@
|
||||
const std = @import("std");
|
||||
const sys = @import("sys");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// lwIP error type — maps err_t (i8) to Zig errors.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const LwipError = error{
|
||||
OutOfMemory,
|
||||
BufferError,
|
||||
Timeout,
|
||||
RoutingError,
|
||||
InProgress,
|
||||
IllegalValue,
|
||||
WouldBlock,
|
||||
AddressInUse,
|
||||
AlreadyConnecting,
|
||||
AlreadyConnected,
|
||||
NotConnected,
|
||||
InterfaceError,
|
||||
Aborted,
|
||||
Reset,
|
||||
Closed,
|
||||
IllegalArgument,
|
||||
};
|
||||
|
||||
pub fn errFromC(e: sys.err_t) LwipError!void {
|
||||
return switch (@as(sys.err_enum_t, @enumFromInt(e))) {
|
||||
.ERR_OK => {},
|
||||
.ERR_MEM => LwipError.OutOfMemory,
|
||||
.ERR_BUF => LwipError.BufferError,
|
||||
.ERR_TIMEOUT => LwipError.Timeout,
|
||||
.ERR_RTE => LwipError.RoutingError,
|
||||
.ERR_INPROGRESS => LwipError.InProgress,
|
||||
.ERR_VAL => LwipError.IllegalValue,
|
||||
.ERR_WOULDBLOCK => LwipError.WouldBlock,
|
||||
.ERR_USE => LwipError.AddressInUse,
|
||||
.ERR_ALREADY => LwipError.AlreadyConnecting,
|
||||
.ERR_ISCONN => LwipError.AlreadyConnected,
|
||||
.ERR_CONN => LwipError.NotConnected,
|
||||
.ERR_IF => LwipError.InterfaceError,
|
||||
.ERR_ABRT => LwipError.Aborted,
|
||||
.ERR_RST => LwipError.Reset,
|
||||
.ERR_CLSD => LwipError.Closed,
|
||||
.ERR_ARG => LwipError.IllegalArgument,
|
||||
};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Re-export raw types callers may need.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const RawNetif = sys.netif;
|
||||
pub const RawPbuf = sys.pbuf;
|
||||
pub const RawIp4Addr = sys.ip4_addr_t;
|
||||
pub const RawIp6Addr = sys.ip6_addr_t;
|
||||
pub const RawIpAddr = sys.ip_addr_t;
|
||||
pub const RawSockAddr = sys.sockaddr;
|
||||
pub const SockLen = sys.socklen_t;
|
||||
pub const AddrFamily = sys.sa_family_t;
|
||||
pub const MsgHdr = sys.msghdr;
|
||||
pub const AddrInfo = sys.addrinfo;
|
||||
pub const HostEnt = sys.hostent;
|
||||
pub const PollFd = sys.pollfd;
|
||||
pub const NFds = sys.nfds_t;
|
||||
pub const IoVec = sys.iovec;
|
||||
pub const InAddr = sys.in_addr;
|
||||
pub const In6Addr = sys.in6_addr;
|
||||
pub const SockAddrIn = sys.sockaddr_in;
|
||||
pub const SockAddrIn6 = sys.sockaddr_in6;
|
||||
pub const SockAddrStore = sys.sockaddr_storage;
|
||||
pub const IpMreq = sys.ip_mreq;
|
||||
pub const Ipv6Mreq = sys.ipv6_mreq;
|
||||
pub const Linger = sys.linger;
|
||||
pub const InPktInfo = sys.in_pktinfo;
|
||||
pub const IpAddrType = sys.enum_lwip_ip_addr_type;
|
||||
pub const NetifMacFilterAction = sys.enum_netif_mac_filter_action;
|
||||
pub const PbufLayer = sys.pbuf_layer;
|
||||
pub const PbufType = sys.pbuf_type;
|
||||
pub const MempType = sys.memp_t;
|
||||
pub const DnsCb = sys.dns_found_callback;
|
||||
pub const NetifExtCbFn = sys.netif_ext_callback_fn;
|
||||
pub const NetifNscReason = sys.netif_nsc_reason_t;
|
||||
pub const SysThreadCoreLock = sys.sys_thread_core_lock_t;
|
||||
pub const LwipSock = sys.lwip_sock;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IP address helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Ip4Addr = struct {
|
||||
raw: RawIp4Addr,
|
||||
|
||||
pub fn fromString(s: [*:0]const u8) ?Ip4Addr {
|
||||
var a: RawIp4Addr = undefined;
|
||||
if (sys.ip4addr_aton(s, &a) == 0) return null;
|
||||
return .{ .raw = a };
|
||||
}
|
||||
|
||||
pub fn toString(self: *const Ip4Addr, buf: []u8) ?[]const u8 {
|
||||
const p = sys.ip4addr_ntoa_r(&self.raw, buf.ptr, @intCast(buf.len));
|
||||
if (p == null) return null;
|
||||
return std.mem.sliceTo(p, 0);
|
||||
}
|
||||
|
||||
pub fn isbroadcast(self: Ip4Addr, netif: *const RawNetif) bool {
|
||||
return sys.ip4_addr_isbroadcast_u32(self.raw.addr, netif) != 0;
|
||||
}
|
||||
|
||||
pub fn netmaskValid(self: Ip4Addr) bool {
|
||||
return sys.ip4_addr_netmask_valid(self.raw.addr) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Ip6Addr = struct {
|
||||
raw: RawIp6Addr,
|
||||
|
||||
pub fn fromString(s: [*:0]const u8) ?Ip6Addr {
|
||||
var a: RawIp6Addr = undefined;
|
||||
if (sys.ip6addr_aton(s, &a) == 0) return null;
|
||||
return .{ .raw = a };
|
||||
}
|
||||
|
||||
pub fn toString(self: *const Ip6Addr, buf: []u8) ?[]const u8 {
|
||||
const p = sys.ip6addr_ntoa_r(&self.raw, buf.ptr, @intCast(buf.len));
|
||||
if (p == null) return null;
|
||||
return std.mem.sliceTo(p, 0);
|
||||
}
|
||||
};
|
||||
|
||||
pub const IpAddr = struct {
|
||||
raw: RawIpAddr,
|
||||
|
||||
pub fn fromString(s: [*:0]const u8) ?IpAddr {
|
||||
var a: RawIpAddr = undefined;
|
||||
if (sys.ipaddr_aton(s, &a) == 0) return null;
|
||||
return .{ .raw = a };
|
||||
}
|
||||
|
||||
pub fn toString(self: *const IpAddr, buf: []u8) ?[]const u8 {
|
||||
const p = sys.ipaddr_ntoa_r(&self.raw, buf.ptr, @intCast(buf.len));
|
||||
if (p == null) return null;
|
||||
return std.mem.sliceTo(p, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Socket API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// A file descriptor returned by socket(). Negative values are invalid.
|
||||
pub const Socket = struct {
|
||||
fd: c_int,
|
||||
|
||||
pub fn isValid(self: Socket) bool {
|
||||
return self.fd >= 0;
|
||||
}
|
||||
|
||||
// -- Lifecycle ----------------------------------------------------------
|
||||
|
||||
pub fn create(domain: c_int, kind: c_int, protocol: c_int) !Socket {
|
||||
const fd = sys.lwip_socket(domain, kind, protocol);
|
||||
if (fd < 0) return LwipError.InterfaceError;
|
||||
return .{ .fd = fd };
|
||||
}
|
||||
|
||||
pub fn close(self: Socket) !void {
|
||||
if (sys.lwip_close(self.fd) != 0) return LwipError.InterfaceError;
|
||||
}
|
||||
|
||||
pub fn shutdown(self: Socket, how: c_int) !void {
|
||||
if (sys.lwip_shutdown(self.fd, how) != 0) return LwipError.InterfaceError;
|
||||
}
|
||||
|
||||
// -- Address binding / connection ---------------------------------------
|
||||
|
||||
pub fn bind(self: Socket, addr: *const RawSockAddr, len: SockLen) !void {
|
||||
if (sys.lwip_bind(self.fd, addr, len) != 0) return LwipError.AddressInUse;
|
||||
}
|
||||
|
||||
pub fn listen(self: Socket, backlog: c_int) !void {
|
||||
if (sys.lwip_listen(self.fd, backlog) != 0) return LwipError.InterfaceError;
|
||||
}
|
||||
|
||||
pub fn accept(self: Socket, addr: ?*RawSockAddr, len: ?*SockLen) !Socket {
|
||||
const fd = sys.lwip_accept(self.fd, addr, len);
|
||||
if (fd < 0) return LwipError.NotConnected;
|
||||
return .{ .fd = fd };
|
||||
}
|
||||
|
||||
pub fn connect(self: Socket, addr: *const RawSockAddr, len: SockLen) !void {
|
||||
if (sys.lwip_connect(self.fd, addr, len) != 0) return LwipError.NotConnected;
|
||||
}
|
||||
|
||||
// -- Send / Receive -----------------------------------------------------
|
||||
|
||||
pub fn send(self: Socket, data: []const u8, flags: c_int) !usize {
|
||||
const n = sys.lwip_send(self.fd, data.ptr, data.len, flags);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn sendTo(
|
||||
self: Socket,
|
||||
data: []const u8,
|
||||
flags: c_int,
|
||||
to: *const RawSockAddr,
|
||||
tolen: SockLen,
|
||||
) !usize {
|
||||
const n = sys.lwip_sendto(self.fd, data.ptr, data.len, flags, to, tolen);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn sendMsg(self: Socket, msg: *const MsgHdr, flags: c_int) !usize {
|
||||
const n = sys.lwip_sendmsg(self.fd, msg, flags);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn recv(self: Socket, buf: []u8, flags: c_int) !usize {
|
||||
const n = sys.lwip_recv(self.fd, buf.ptr, buf.len, flags);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn recvFrom(
|
||||
self: Socket,
|
||||
buf: []u8,
|
||||
flags: c_int,
|
||||
from: ?*RawSockAddr,
|
||||
fromlen: ?*SockLen,
|
||||
) !usize {
|
||||
const n = sys.lwip_recvfrom(self.fd, buf.ptr, buf.len, flags, from, fromlen);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn recvMsg(self: Socket, msg: *MsgHdr, flags: c_int) !usize {
|
||||
const n = sys.lwip_recvmsg(self.fd, msg, flags);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn read(self: Socket, buf: []u8) !usize {
|
||||
const n = sys.lwip_read(self.fd, buf.ptr, buf.len);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn write(self: Socket, data: []const u8) !usize {
|
||||
const n = sys.lwip_write(self.fd, data.ptr, data.len);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn readv(self: Socket, iov: []const IoVec) !usize {
|
||||
const n = sys.lwip_readv(self.fd, iov.ptr, @intCast(iov.len));
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn writev(self: Socket, iov: []const IoVec) !usize {
|
||||
const n = sys.lwip_writev(self.fd, iov.ptr, @intCast(iov.len));
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
// -- Socket options -----------------------------------------------------
|
||||
|
||||
pub fn setOpt(
|
||||
self: Socket,
|
||||
level: c_int,
|
||||
optname: c_int,
|
||||
optval: *const anyopaque,
|
||||
optlen: SockLen,
|
||||
) !void {
|
||||
if (sys.lwip_setsockopt(self.fd, level, optname, optval, optlen) != 0)
|
||||
return LwipError.IllegalArgument;
|
||||
}
|
||||
|
||||
pub fn getOpt(
|
||||
self: Socket,
|
||||
level: c_int,
|
||||
optname: c_int,
|
||||
optval: *anyopaque,
|
||||
optlen: *SockLen,
|
||||
) !void {
|
||||
if (sys.lwip_getsockopt(self.fd, level, optname, optval, optlen) != 0)
|
||||
return LwipError.IllegalArgument;
|
||||
}
|
||||
|
||||
pub fn setOptExt(
|
||||
self: *LwipSock,
|
||||
level: c_int,
|
||||
optname: c_int,
|
||||
optval: *const anyopaque,
|
||||
optlen: u32,
|
||||
) !void {
|
||||
var err_code: c_int = 0;
|
||||
if (!sys.lwip_setsockopt_impl_ext(self, level, optname, optval, optlen, &err_code))
|
||||
return LwipError.IllegalArgument;
|
||||
}
|
||||
|
||||
pub fn getOptExt(
|
||||
self: *LwipSock,
|
||||
level: c_int,
|
||||
optname: c_int,
|
||||
optval: *anyopaque,
|
||||
optlen: *u32,
|
||||
) !void {
|
||||
var err_code: c_int = 0;
|
||||
if (!sys.lwip_getsockopt_impl_ext(self, level, optname, optval, optlen, &err_code))
|
||||
return LwipError.IllegalArgument;
|
||||
}
|
||||
|
||||
// -- Name queries -------------------------------------------------------
|
||||
|
||||
pub fn getPeerName(self: Socket, addr: *RawSockAddr, len: *SockLen) !void {
|
||||
if (sys.lwip_getpeername(self.fd, addr, len) != 0) return LwipError.NotConnected;
|
||||
}
|
||||
|
||||
pub fn getSockName(self: Socket, addr: *RawSockAddr, len: *SockLen) !void {
|
||||
if (sys.lwip_getsockname(self.fd, addr, len) != 0) return LwipError.InterfaceError;
|
||||
}
|
||||
|
||||
// -- Misc ---------------------------------------------------------------
|
||||
|
||||
pub fn ioctl(self: Socket, cmd: c_long, argp: ?*anyopaque) !void {
|
||||
if (sys.lwip_ioctl(self.fd, cmd, argp) != 0) return LwipError.IllegalArgument;
|
||||
}
|
||||
|
||||
pub fn fcntl(self: Socket, cmd: c_int, val: c_int) !c_int {
|
||||
const r = sys.lwip_fcntl(self.fd, cmd, val);
|
||||
if (r < 0) return LwipError.IllegalArgument;
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
/// poll() over a set of sockets.
|
||||
pub fn poll(fds: []PollFd, timeout_ms: c_int) !c_int {
|
||||
const r = sys.lwip_poll(fds.ptr, @intCast(fds.len), timeout_ms);
|
||||
if (r < 0) return LwipError.InterfaceError;
|
||||
return r;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Address conversion
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn inetNtop(af: c_int, src: *const anyopaque, dst: []u8) ?[*:0]const u8 {
|
||||
return sys.lwip_inet_ntop(af, src, dst.ptr, @intCast(dst.len));
|
||||
}
|
||||
|
||||
pub fn inetPton(af: c_int, src: [*:0]const u8, dst: *anyopaque) bool {
|
||||
return sys.lwip_inet_pton(af, src, dst) == 1;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DNS
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Dns = struct {
|
||||
pub fn init() void {
|
||||
sys.dns_init();
|
||||
}
|
||||
|
||||
pub fn setServer(index: u8, server: *const RawIpAddr) void {
|
||||
sys.dns_setserver(index, server);
|
||||
}
|
||||
|
||||
pub fn getServer(index: u8) *const RawIpAddr {
|
||||
return sys.dns_getserver(index);
|
||||
}
|
||||
|
||||
pub fn getHostByName(
|
||||
hostname: [*:0]const u8,
|
||||
addr: *RawIpAddr,
|
||||
callback: DnsCb,
|
||||
callback_arg: ?*anyopaque,
|
||||
) !void {
|
||||
try errFromC(sys.dns_gethostbyname(hostname, addr, callback, callback_arg));
|
||||
}
|
||||
|
||||
pub fn getHostByNameAddrType(
|
||||
hostname: [*:0]const u8,
|
||||
addr: *RawIpAddr,
|
||||
callback: DnsCb,
|
||||
callback_arg: ?*anyopaque,
|
||||
addr_type: u8,
|
||||
) !void {
|
||||
try errFromC(sys.dns_gethostbyname_addrtype(hostname, addr, callback, callback_arg, addr_type));
|
||||
}
|
||||
|
||||
pub fn clearCache() void {
|
||||
sys.dns_clear_cache();
|
||||
}
|
||||
|
||||
/// Blocking getaddrinfo (uses lwip_getaddrinfo).
|
||||
pub fn getAddrInfo(
|
||||
nodename: [*:0]const u8,
|
||||
servname: [*:0]const u8,
|
||||
hints: ?*const AddrInfo,
|
||||
res: *[*c]AddrInfo,
|
||||
) !void {
|
||||
if (sys.lwip_getaddrinfo(nodename, servname, hints, res) != 0)
|
||||
return LwipError.NotConnected;
|
||||
}
|
||||
|
||||
pub fn freeAddrInfo(ai: *AddrInfo) void {
|
||||
sys.lwip_freeaddrinfo(ai);
|
||||
}
|
||||
|
||||
pub fn getHostByNameR(
|
||||
name: [*:0]const u8,
|
||||
ret: *HostEnt,
|
||||
buf: []u8,
|
||||
result: *[*c]HostEnt,
|
||||
h_errnop: *c_int,
|
||||
) !void {
|
||||
if (sys.lwip_gethostbyname_r(name, ret, buf.ptr, buf.len, result, h_errnop) != 0)
|
||||
return LwipError.NotConnected;
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Network interface (netif)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Netif = struct {
|
||||
pub fn init() void {
|
||||
sys.netif_init();
|
||||
}
|
||||
|
||||
pub fn add(
|
||||
netif_ptr: *RawNetif,
|
||||
ipaddr: ?*const RawIp4Addr,
|
||||
netmask: ?*const RawIp4Addr,
|
||||
gw: ?*const RawIp4Addr,
|
||||
state: ?*anyopaque,
|
||||
init_fn: sys.netif_init_fn,
|
||||
input_fn: sys.netif_input_fn,
|
||||
) ?*RawNetif {
|
||||
return sys.netif_add(netif_ptr, ipaddr, netmask, gw, state, init_fn, input_fn);
|
||||
}
|
||||
|
||||
pub fn addNoAddr(
|
||||
netif_ptr: *RawNetif,
|
||||
state: ?*anyopaque,
|
||||
init_fn: sys.netif_init_fn,
|
||||
input_fn: sys.netif_input_fn,
|
||||
) ?*RawNetif {
|
||||
return sys.netif_add_noaddr(netif_ptr, state, init_fn, input_fn);
|
||||
}
|
||||
|
||||
pub fn remove(netif_ptr: *RawNetif) void {
|
||||
sys.netif_remove(netif_ptr);
|
||||
}
|
||||
|
||||
pub fn find(name: [*:0]const u8) ?*RawNetif {
|
||||
return sys.netif_find(name);
|
||||
}
|
||||
|
||||
pub fn setDefault(netif_ptr: *RawNetif) void {
|
||||
sys.netif_set_default(netif_ptr);
|
||||
}
|
||||
|
||||
pub fn setAddr(
|
||||
netif_ptr: *RawNetif,
|
||||
ipaddr: *const RawIp4Addr,
|
||||
netmask: *const RawIp4Addr,
|
||||
gw: *const RawIp4Addr,
|
||||
) void {
|
||||
sys.netif_set_addr(netif_ptr, ipaddr, netmask, gw);
|
||||
}
|
||||
|
||||
pub fn setUp(netif_ptr: *RawNetif) void {
|
||||
sys.netif_set_up(netif_ptr);
|
||||
}
|
||||
pub fn setDown(netif_ptr: *RawNetif) void {
|
||||
sys.netif_set_down(netif_ptr);
|
||||
}
|
||||
pub fn setLinkUp(netif_ptr: *RawNetif) void {
|
||||
sys.netif_set_link_up(netif_ptr);
|
||||
}
|
||||
pub fn setLinkDown(netif_ptr: *RawNetif) void {
|
||||
sys.netif_set_link_down(netif_ptr);
|
||||
}
|
||||
|
||||
pub fn ip6AddrSet(netif_ptr: *RawNetif, idx: i8, addr: *const RawIp6Addr) void {
|
||||
sys.netif_ip6_addr_set(netif_ptr, idx, addr);
|
||||
}
|
||||
|
||||
pub fn ip6AddrSetState(netif_ptr: *RawNetif, idx: i8, state: u8) void {
|
||||
sys.netif_ip6_addr_set_state(netif_ptr, idx, state);
|
||||
}
|
||||
|
||||
pub fn createIp6Linklocal(netif_ptr: *RawNetif, from_mac_48bit: bool) void {
|
||||
sys.netif_create_ip6_linklocal_address(netif_ptr, @intFromBool(from_mac_48bit));
|
||||
}
|
||||
|
||||
pub fn addIp6Address(netif_ptr: *RawNetif, addr: *const RawIp6Addr, chosen_idx: *i8) !void {
|
||||
try errFromC(sys.netif_add_ip6_address(netif_ptr, addr, chosen_idx));
|
||||
}
|
||||
|
||||
pub fn nameToIndex(name: [*:0]const u8) u8 {
|
||||
return sys.netif_name_to_index(name);
|
||||
}
|
||||
|
||||
pub fn getByIndex(idx: u8) ?*RawNetif {
|
||||
return sys.netif_get_by_index(idx);
|
||||
}
|
||||
|
||||
pub fn addExtCallback(cb: *sys.netif_ext_callback_t, func: NetifExtCbFn) void {
|
||||
sys.netif_add_ext_callback(cb, func);
|
||||
}
|
||||
|
||||
pub fn removeExtCallback(cb: *sys.netif_ext_callback_t) void {
|
||||
sys.netif_remove_ext_callback(cb);
|
||||
}
|
||||
|
||||
pub fn input(p: *RawPbuf, inp: *RawNetif) !void {
|
||||
try errFromC(sys.netif_input(p, inp));
|
||||
}
|
||||
|
||||
pub fn loopOutput(netif_ptr: *RawNetif, p: *RawPbuf) !void {
|
||||
try errFromC(sys.netif_loop_output(netif_ptr, p));
|
||||
}
|
||||
|
||||
pub fn poll(netif_ptr: *RawNetif) void {
|
||||
sys.netif_poll(netif_ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Packet buffer (pbuf)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Pbuf = struct {
|
||||
pub fn alloc(layer: PbufLayer, length: u16, kind: PbufType) ?*RawPbuf {
|
||||
return sys.pbuf_alloc(layer, length, kind);
|
||||
}
|
||||
|
||||
pub fn free(p: *RawPbuf) u8 {
|
||||
return sys.pbuf_free(p);
|
||||
}
|
||||
|
||||
pub fn ref(p: *RawPbuf) void {
|
||||
sys.pbuf_ref(p);
|
||||
}
|
||||
|
||||
pub fn chain(head: *RawPbuf, tail: *RawPbuf) void {
|
||||
sys.pbuf_chain(head, tail);
|
||||
}
|
||||
|
||||
pub fn cat(head: *RawPbuf, tail: *RawPbuf) void {
|
||||
sys.pbuf_cat(head, tail);
|
||||
}
|
||||
|
||||
pub fn dechain(p: *RawPbuf) ?*RawPbuf {
|
||||
return sys.pbuf_dechain(p);
|
||||
}
|
||||
|
||||
pub fn copy(dst: *RawPbuf, src: *const RawPbuf) !void {
|
||||
try errFromC(sys.pbuf_copy(dst, src));
|
||||
}
|
||||
|
||||
pub fn copyPartial(p: *const RawPbuf, dst: []u8, offset: u16) u16 {
|
||||
return sys.pbuf_copy_partial(p, dst.ptr, @intCast(dst.len), offset);
|
||||
}
|
||||
|
||||
pub fn take(buf: *RawPbuf, data: []const u8) !void {
|
||||
try errFromC(sys.pbuf_take(buf, data.ptr, @intCast(data.len)));
|
||||
}
|
||||
|
||||
pub fn coalesce(p: *RawPbuf, layer: PbufLayer) ?*RawPbuf {
|
||||
return sys.pbuf_coalesce(p, layer);
|
||||
}
|
||||
|
||||
pub fn getAt(p: *const RawPbuf, offset: u16) u8 {
|
||||
return sys.pbuf_get_at(p, offset);
|
||||
}
|
||||
|
||||
pub fn putAt(p: *RawPbuf, offset: u16, data: u8) void {
|
||||
sys.pbuf_put_at(p, offset, data);
|
||||
}
|
||||
|
||||
pub fn chainLength(p: *const RawPbuf) u16 {
|
||||
return sys.pbuf_clen(p);
|
||||
}
|
||||
|
||||
pub fn realloc(p: *RawPbuf, size: u16) void {
|
||||
sys.pbuf_realloc(p, size);
|
||||
}
|
||||
|
||||
pub fn addHeader(p: *RawPbuf, increment: usize) bool {
|
||||
return sys.pbuf_add_header(p, increment) == 0;
|
||||
}
|
||||
|
||||
pub fn removeHeader(p: *RawPbuf, size: usize) bool {
|
||||
return sys.pbuf_remove_header(p, size) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SNTP helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const SNTP = struct {
|
||||
pub fn getSyncInterval() u32 {
|
||||
return sys.sntp_get_sync_interval();
|
||||
}
|
||||
pub fn setSystemTime(sec: u32, us: u32) void {
|
||||
sys.sntp_set_system_time(sec, us);
|
||||
}
|
||||
pub fn getSystemTime(sec: *u32, us: *u32) void {
|
||||
sys.sntp_get_system_time(sec, us);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// lwIP OS abstraction (sys_arch_*)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Sys = struct {
|
||||
pub const Sem = sys.sys_sem_t;
|
||||
pub const Mutex = sys.sys_mutex_t;
|
||||
pub const Thread = sys.sys_thread_t;
|
||||
pub const Mbox = sys.sys_mbox_t;
|
||||
|
||||
pub fn init() void {
|
||||
sys.sys_init();
|
||||
}
|
||||
pub fn now() u32 {
|
||||
return sys.sys_now();
|
||||
}
|
||||
pub fn jiffies() u32 {
|
||||
return sys.sys_jiffies();
|
||||
}
|
||||
pub fn delayMs(ms: u32) void {
|
||||
sys.sys_delay_ms(ms);
|
||||
}
|
||||
|
||||
pub fn semNew(sem: *Sem, count: u8) !void {
|
||||
try errFromC(sys.sys_sem_new(sem, count));
|
||||
}
|
||||
pub fn semSignal(sem: *Sem) void {
|
||||
sys.sys_sem_signal(sem);
|
||||
}
|
||||
pub fn semWait(sem: *Sem, timeout_ms: u32) u32 {
|
||||
return sys.sys_arch_sem_wait(sem, timeout_ms);
|
||||
}
|
||||
pub fn semFree(sem: *Sem) void {
|
||||
sys.sys_sem_free(sem);
|
||||
}
|
||||
|
||||
pub fn mutexNew(m: *Mutex) !void {
|
||||
try errFromC(sys.sys_mutex_new(m));
|
||||
}
|
||||
pub fn mutexLock(m: *Mutex) void {
|
||||
sys.sys_mutex_lock(m);
|
||||
}
|
||||
pub fn mutexUnlock(m: *Mutex) void {
|
||||
sys.sys_mutex_unlock(m);
|
||||
}
|
||||
pub fn mutexFree(m: *Mutex) void {
|
||||
sys.sys_mutex_free(m);
|
||||
}
|
||||
|
||||
pub fn mboxNew(mbox: *Mbox, size: c_int) !void {
|
||||
try errFromC(sys.sys_mbox_new(mbox, size));
|
||||
}
|
||||
pub fn mboxPost(mbox: *Mbox, msg: ?*anyopaque) void {
|
||||
sys.sys_mbox_post(mbox, msg);
|
||||
}
|
||||
pub fn mboxTryPost(mbox: *Mbox, msg: ?*anyopaque) !void {
|
||||
try errFromC(sys.sys_mbox_trypost(mbox, msg));
|
||||
}
|
||||
pub fn mboxTryPostFromISR(mbox: *Mbox, msg: ?*anyopaque) !void {
|
||||
try errFromC(sys.sys_mbox_trypost_fromisr(mbox, msg));
|
||||
}
|
||||
pub fn mboxFetch(mbox: *Mbox, msg: *?*anyopaque, timeout_ms: u32) u32 {
|
||||
return sys.sys_arch_mbox_fetch(mbox, msg, timeout_ms);
|
||||
}
|
||||
pub fn mboxTryFetch(mbox: *Mbox, msg: *?*anyopaque) u32 {
|
||||
return sys.sys_arch_mbox_tryfetch(mbox, msg);
|
||||
}
|
||||
pub fn mboxFree(mbox: *Mbox) void {
|
||||
sys.sys_mbox_free(mbox);
|
||||
}
|
||||
|
||||
pub fn threadNew(
|
||||
name: [*:0]const u8,
|
||||
thread_fn: sys.lwip_thread_fn,
|
||||
arg: ?*anyopaque,
|
||||
stacksize: c_int,
|
||||
prio: c_int,
|
||||
) Thread {
|
||||
return sys.sys_thread_new(name, thread_fn, arg, stacksize, prio);
|
||||
}
|
||||
|
||||
pub fn threadSemInit() [*c]Sem {
|
||||
return sys.sys_thread_sem_init();
|
||||
}
|
||||
pub fn threadSemDeinit() void {
|
||||
sys.sys_thread_sem_deinit();
|
||||
}
|
||||
pub fn threadSemGet() [*c]Sem {
|
||||
return sys.sys_thread_sem_get();
|
||||
}
|
||||
pub fn threadTcpip(kind: SysThreadCoreLock) bool {
|
||||
return sys.sys_thread_tcpip(kind);
|
||||
}
|
||||
|
||||
pub fn archProtect() c_int {
|
||||
return sys.sys_arch_protect();
|
||||
}
|
||||
pub fn archUnprotect(pval: c_int) void {
|
||||
sys.sys_arch_unprotect(pval);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// lwIP memory pool helpers (low-level, rarely used directly)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Memp = struct {
|
||||
pub fn init() void {
|
||||
sys.memp_init();
|
||||
}
|
||||
pub fn malloc(kind: MempType) ?*anyopaque {
|
||||
return sys.memp_malloc(kind);
|
||||
}
|
||||
pub fn free(kind: MempType, mem: ?*anyopaque) void {
|
||||
sys.memp_free(kind, mem);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Byte order helpers (htons/htonl)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub inline fn htons(x: u16) u16 {
|
||||
return sys.lwip_htons(x);
|
||||
}
|
||||
pub inline fn htonl(x: u32) u32 {
|
||||
return sys.lwip_htonl(x);
|
||||
}
|
||||
pub inline fn ntohs(x: u16) u16 {
|
||||
return sys.lwip_htons(x);
|
||||
} // symmetric on all archs
|
||||
pub inline fn ntohl(x: u32) u32 {
|
||||
return sys.lwip_htonl(x);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// String helpers (lwip_str*)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn strnicmp(a: [*:0]const u8, b: [*:0]const u8, n: usize) c_int {
|
||||
return sys.lwip_strnicmp(a, b, n);
|
||||
}
|
||||
pub fn stricmp(a: [*:0]const u8, b: [*:0]const u8) c_int {
|
||||
return sys.lwip_stricmp(a, b);
|
||||
}
|
||||
pub fn strnstr(haystack: [*:0]const u8, needle: [*:0]const u8, n: usize) ?[*:0]u8 {
|
||||
return sys.lwip_strnstr(haystack, needle, n);
|
||||
}
|
||||
pub fn itoa(buf: []u8, number: c_int) void {
|
||||
sys.lwip_itoa(buf.ptr, buf.len, number);
|
||||
}
|
||||
252
software/zig_main/imports/matter.zig
Normal file
252
software/zig_main/imports/matter.zig
Normal file
@@ -0,0 +1,252 @@
|
||||
//! ESP Matter wrapper — Zig interface to the CHIP/Matter C++ SDK via matter_stubs.h.
|
||||
//!
|
||||
//! Requires the `espressif/esp_matter` managed component.
|
||||
//! To add it: `idf.py add-dependency espressif/esp_matter`
|
||||
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ── Re-export core types ─────────────────────────────────────────────────────
|
||||
|
||||
pub const Node = sys.esp_matter_node_t;
|
||||
pub const Endpoint = sys.esp_matter_endpoint_t;
|
||||
pub const Cluster = sys.esp_matter_cluster_t;
|
||||
pub const Attribute = sys.esp_matter_attribute_t;
|
||||
|
||||
/// Endpoint creation flags.
|
||||
pub const EpFlags = enum(u8) {
|
||||
none = sys.ESP_MATTER_EP_FLAG_NONE,
|
||||
destroyable = sys.ESP_MATTER_EP_FLAG_DESTROYABLE,
|
||||
bridge = sys.ESP_MATTER_EP_FLAG_BRIDGE,
|
||||
};
|
||||
|
||||
/// Attribute value types (Zig enum wrapping the C enum constants).
|
||||
pub const ValType = enum(c_uint) {
|
||||
invalid = sys.ESP_MATTER_VAL_TYPE_INVALID,
|
||||
boolean = sys.ESP_MATTER_VAL_TYPE_BOOLEAN,
|
||||
integer = sys.ESP_MATTER_VAL_TYPE_INTEGER,
|
||||
float_ = sys.ESP_MATTER_VAL_TYPE_FLOAT,
|
||||
array = sys.ESP_MATTER_VAL_TYPE_ARRAY,
|
||||
char_string = sys.ESP_MATTER_VAL_TYPE_CHAR_STRING,
|
||||
octet_string = sys.ESP_MATTER_VAL_TYPE_OCTET_STRING,
|
||||
int8 = sys.ESP_MATTER_VAL_TYPE_INT8,
|
||||
uint8 = sys.ESP_MATTER_VAL_TYPE_UINT8,
|
||||
int16 = sys.ESP_MATTER_VAL_TYPE_INT16,
|
||||
uint16 = sys.ESP_MATTER_VAL_TYPE_UINT16,
|
||||
int32 = sys.ESP_MATTER_VAL_TYPE_INT32,
|
||||
uint32 = sys.ESP_MATTER_VAL_TYPE_UINT32,
|
||||
int64 = sys.ESP_MATTER_VAL_TYPE_INT64,
|
||||
uint64 = sys.ESP_MATTER_VAL_TYPE_UINT64,
|
||||
enum8 = sys.ESP_MATTER_VAL_TYPE_ENUM8,
|
||||
bitmap8 = sys.ESP_MATTER_VAL_TYPE_BITMAP8,
|
||||
bitmap16 = sys.ESP_MATTER_VAL_TYPE_BITMAP16,
|
||||
bitmap32 = sys.ESP_MATTER_VAL_TYPE_BITMAP32,
|
||||
enum16 = sys.ESP_MATTER_VAL_TYPE_ENUM16,
|
||||
long_char_string = sys.ESP_MATTER_VAL_TYPE_LONG_CHAR_STRING,
|
||||
long_octet_string = sys.ESP_MATTER_VAL_TYPE_LONG_OCTET_STRING,
|
||||
_,
|
||||
};
|
||||
|
||||
/// Attribute value (union mirroring esp_matter_val_t).
|
||||
pub const Val = sys.esp_matter_val_t;
|
||||
|
||||
/// Attribute value with type tag.
|
||||
pub const AttrVal = sys.esp_matter_attr_val_t;
|
||||
|
||||
/// Attribute update callback type (Zig enum with named members).
|
||||
/// Use e.g. `matter.AttrCbType.ESP_MATTER_ATTR_CB_POST_UPDATE`.
|
||||
pub const AttrCbType = enum(c_uint) {
|
||||
ESP_MATTER_ATTR_CB_PRE_UPDATE = sys.ESP_MATTER_ATTR_CB_PRE_UPDATE,
|
||||
ESP_MATTER_ATTR_CB_POST_UPDATE = sys.ESP_MATTER_ATTR_CB_POST_UPDATE,
|
||||
ESP_MATTER_ATTR_CB_READ = sys.ESP_MATTER_ATTR_CB_READ,
|
||||
ESP_MATTER_ATTR_CB_WRITE = sys.ESP_MATTER_ATTR_CB_WRITE,
|
||||
_,
|
||||
};
|
||||
|
||||
/// Attribute update callback function pointer type.
|
||||
/// The callback receives a `?*AttrVal` which at the C ABI boundary is `[*c]AttrVal`.
|
||||
/// Return 0 (ESP_OK) to allow, non-zero from PRE_UPDATE to block.
|
||||
pub const AttrCallback = ?*const fn (
|
||||
AttrCbType,
|
||||
u16,
|
||||
u32,
|
||||
u32,
|
||||
?*AttrVal,
|
||||
?*anyopaque,
|
||||
) callconv(.c) c_int;
|
||||
|
||||
/// Identify cluster callback type.
|
||||
pub const IdentifyCbType = enum(c_uint) {
|
||||
ESP_MATTER_IDENTIFY_CB_START = sys.ESP_MATTER_IDENTIFY_CB_START,
|
||||
ESP_MATTER_IDENTIFY_CB_STOP = sys.ESP_MATTER_IDENTIFY_CB_STOP,
|
||||
ESP_MATTER_IDENTIFY_CB_EFFECT = sys.ESP_MATTER_IDENTIFY_CB_EFFECT,
|
||||
_,
|
||||
};
|
||||
|
||||
/// Identify cluster callback function pointer type.
|
||||
pub const IdentifyCallback = ?*const fn (IdentifyCbType, u16, u8, u8, ?*anyopaque) callconv(.c) c_int;
|
||||
|
||||
// ── Attribute value helpers ──────────────────────────────────────────────────
|
||||
|
||||
/// Convenience wrappers matching esp_matter_val_bool(), esp_matter_uint8(), etc.
|
||||
pub const val = struct {
|
||||
pub fn boolean(b: bool) AttrVal {
|
||||
return sys.esp_matter_val_bool(b);
|
||||
}
|
||||
pub fn uint8(v: u8) AttrVal {
|
||||
return sys.esp_matter_val_uint8(v);
|
||||
}
|
||||
pub fn uint16(v: u16) AttrVal {
|
||||
return sys.esp_matter_val_uint16(v);
|
||||
}
|
||||
pub fn uint32(v: u32) AttrVal {
|
||||
return sys.esp_matter_val_uint32(v);
|
||||
}
|
||||
pub fn int16(v: i16) AttrVal {
|
||||
return sys.esp_matter_val_int16(v);
|
||||
}
|
||||
pub fn nullable() AttrVal {
|
||||
return sys.esp_matter_val_nullable();
|
||||
}
|
||||
};
|
||||
|
||||
// ── Core ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Start the Matter stack (non-blocking; spawns Matter OS task).
|
||||
/// Call after building the node + endpoint + cluster data model.
|
||||
pub fn start(attr_cb: AttrCallback, identify_cb: IdentifyCallback) !void {
|
||||
// @ptrCast converts our typed function pointer to the raw sys type.
|
||||
// The types are ABI-compatible: enum(c_uint)↔c_uint, ?*T↔[*c]T.
|
||||
try errors.espCheckError(sys.esp_matter_wrapper_start(@ptrCast(attr_cb), @ptrCast(identify_cb)));
|
||||
}
|
||||
|
||||
/// Erase Matter NVS data and reboot. Non-recoverable.
|
||||
pub fn factoryReset() !void {
|
||||
try errors.espCheckError(sys.esp_matter_wrapper_factory_reset());
|
||||
}
|
||||
|
||||
/// Return true if Matter has been started.
|
||||
pub fn isStarted() bool {
|
||||
return sys.esp_matter_wrapper_is_started();
|
||||
}
|
||||
|
||||
// ── Node ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Create the root Matter node (endpoint 0, Root Node device type).
|
||||
/// attr_cb fires on every attribute read/write; identify_cb fires on Identify requests.
|
||||
/// priv_data is forwarded to both callbacks.
|
||||
pub fn nodeCreate(attr_cb: AttrCallback, identify_cb: IdentifyCallback, priv_data: ?*anyopaque) !*Node {
|
||||
const n = sys.esp_matter_wrapper_node_create(@ptrCast(attr_cb), @ptrCast(identify_cb), priv_data);
|
||||
if (n == null) return error.MatterNodeCreateFailed;
|
||||
return @ptrCast(n);
|
||||
}
|
||||
|
||||
// ── Endpoint ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Endpoint operations.
|
||||
pub const endpoint = struct {
|
||||
/// Create a generic endpoint on the node.
|
||||
pub fn create(node: *Node, flags: EpFlags, priv_data: ?*anyopaque) !*Endpoint {
|
||||
const ep = sys.esp_matter_wrapper_endpoint_create(node, @intFromEnum(flags), priv_data);
|
||||
if (ep == null) return error.MatterEndpointCreateFailed;
|
||||
return @ptrCast(ep);
|
||||
}
|
||||
|
||||
/// Destroy a destroyable endpoint.
|
||||
pub fn destroy(node: *Node, ep: *Endpoint) !void {
|
||||
try errors.espCheckError(sys.esp_matter_wrapper_endpoint_destroy(node, ep));
|
||||
}
|
||||
|
||||
/// Return the endpoint's numeric ID.
|
||||
pub fn getId(ep: *Endpoint) u16 {
|
||||
return sys.esp_matter_wrapper_endpoint_get_id(ep);
|
||||
}
|
||||
|
||||
/// Associate a Matter device type with this endpoint.
|
||||
pub fn addDeviceType(ep: *Endpoint, device_type_id: u32, version: u8) !void {
|
||||
try errors.espCheckError(
|
||||
sys.esp_matter_wrapper_endpoint_add_device_type(ep, device_type_id, version),
|
||||
);
|
||||
}
|
||||
|
||||
/// Enable a dynamically-created endpoint (only after Matter is started).
|
||||
pub fn enable(ep: *Endpoint) !void {
|
||||
try errors.espCheckError(sys.esp_matter_wrapper_endpoint_enable(ep));
|
||||
}
|
||||
|
||||
// ── Pre-built device-type helpers ─────────────────────────────────────
|
||||
|
||||
/// Add an On/Off Light endpoint (device type 0x0100).
|
||||
pub fn addOnOffLight(node: *Node, flags: EpFlags, priv_data: ?*anyopaque) !*Endpoint {
|
||||
const ep = sys.esp_matter_wrapper_add_on_off_light(node, @intFromEnum(flags), priv_data);
|
||||
if (ep == null) return error.MatterEndpointCreateFailed;
|
||||
return @ptrCast(ep);
|
||||
}
|
||||
|
||||
/// Add an On/Off Switch endpoint (device type 0x0103).
|
||||
pub fn addOnOffSwitch(node: *Node, flags: EpFlags, priv_data: ?*anyopaque) !*Endpoint {
|
||||
const ep = sys.esp_matter_wrapper_add_on_off_switch(node, @intFromEnum(flags), priv_data);
|
||||
if (ep == null) return error.MatterEndpointCreateFailed;
|
||||
return @ptrCast(ep);
|
||||
}
|
||||
|
||||
/// Add a Dimmable Light endpoint (device type 0x0101).
|
||||
pub fn addDimmableLight(node: *Node, flags: EpFlags, priv_data: ?*anyopaque) !*Endpoint {
|
||||
const ep = sys.esp_matter_wrapper_add_dimmable_light(node, @intFromEnum(flags), priv_data);
|
||||
if (ep == null) return error.MatterEndpointCreateFailed;
|
||||
return @ptrCast(ep);
|
||||
}
|
||||
|
||||
/// Add a Color Temperature Light endpoint (device type 0x010C).
|
||||
pub fn addColorTemperatureLight(node: *Node, flags: EpFlags, priv_data: ?*anyopaque) !*Endpoint {
|
||||
const ep = sys.esp_matter_wrapper_add_color_temperature_light(node, @intFromEnum(flags), priv_data);
|
||||
if (ep == null) return error.MatterEndpointCreateFailed;
|
||||
return @ptrCast(ep);
|
||||
}
|
||||
};
|
||||
|
||||
// ── Cluster ───────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Cluster operations.
|
||||
pub const cluster = struct {
|
||||
/// Cluster flag: server cluster.
|
||||
pub const FLAG_SERVER: u8 = 0x40;
|
||||
/// Cluster flag: client cluster.
|
||||
pub const FLAG_CLIENT: u8 = 0x80;
|
||||
|
||||
/// Create a cluster on an endpoint.
|
||||
pub fn create(ep: *Endpoint, cluster_id: u32, flags: u8) !*Cluster {
|
||||
const cl = sys.esp_matter_wrapper_cluster_create(ep, cluster_id, flags);
|
||||
if (cl == null) return error.MatterClusterCreateFailed;
|
||||
return @ptrCast(cl);
|
||||
}
|
||||
};
|
||||
|
||||
// ── Attribute ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Attribute operations.
|
||||
pub const attribute = struct {
|
||||
/// Create an attribute on a cluster with a default value.
|
||||
pub fn create(cl: *Cluster, attribute_id: u32, flags: u16, default_val: AttrVal) !*Attribute {
|
||||
const attr = sys.esp_matter_wrapper_attribute_create(cl, attribute_id, flags, default_val);
|
||||
if (attr == null) return error.MatterAttributeCreateFailed;
|
||||
return @ptrCast(attr);
|
||||
}
|
||||
|
||||
/// Update an attribute value (use after Matter is started).
|
||||
pub fn update(endpoint_id: u16, cluster_id: u32, attribute_id: u32, v: *AttrVal) !void {
|
||||
try errors.espCheckError(
|
||||
sys.esp_matter_wrapper_attribute_update(endpoint_id, cluster_id, attribute_id, v),
|
||||
);
|
||||
}
|
||||
|
||||
/// Get the current value of an attribute.
|
||||
pub fn getVal(attr: *Attribute, v: *AttrVal) !void {
|
||||
try errors.espCheckError(sys.esp_matter_wrapper_attribute_get_val(attr, v));
|
||||
}
|
||||
|
||||
/// Set an attribute value (use before Matter is started).
|
||||
pub fn setVal(attr: *Attribute, v: *AttrVal) !void {
|
||||
try errors.espCheckError(sys.esp_matter_wrapper_attribute_set_val(attr, v));
|
||||
}
|
||||
};
|
||||
236
software/zig_main/imports/mqtt.zig
Normal file
236
software/zig_main/imports/mqtt.zig
Normal file
@@ -0,0 +1,236 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Re-exported types
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
pub const Handle = sys.esp_mqtt_client_handle_t;
|
||||
pub const EventId = sys.esp_mqtt_event_id_t;
|
||||
pub const Event = sys.esp_mqtt_event_t;
|
||||
pub const EventHandle = sys.esp_mqtt_event_handle_t;
|
||||
pub const EventHandler = sys.esp_event_handler_t;
|
||||
pub const ClientConfig = sys.esp_mqtt_client_config_t;
|
||||
pub const Topic = sys.esp_mqtt_topic_t;
|
||||
pub const ErrorCodes = sys.esp_mqtt_error_codes_t;
|
||||
pub const ErrorType = sys.esp_mqtt_error_type_t;
|
||||
pub const Transport = sys.esp_mqtt_transport_t;
|
||||
pub const ProtocolVersion = sys.esp_mqtt_protocol_ver_t;
|
||||
pub const ConnectReturnCode = sys.esp_mqtt_connect_return_code_t;
|
||||
|
||||
// MQTT v5
|
||||
pub const Handle5 = sys.esp_mqtt5_client_handle_t;
|
||||
pub const ErrorReasonCode5 = sys.mqtt5_error_reason_code;
|
||||
pub const UserPropertyHandle5 = sys.mqtt5_user_property_handle_t;
|
||||
pub const UserPropertyItem5 = sys.esp_mqtt5_user_property_item_t;
|
||||
pub const ConnectionProperty5 = sys.esp_mqtt5_connection_property_config_t;
|
||||
pub const PublishProperty5 = sys.esp_mqtt5_publish_property_config_t;
|
||||
pub const SubscribeProperty5 = sys.esp_mqtt5_subscribe_property_config_t;
|
||||
pub const UnsubscribeProperty5 = sys.esp_mqtt5_unsubscribe_property_config_t;
|
||||
pub const DisconnectProperty5 = sys.esp_mqtt5_disconnect_property_config_t;
|
||||
pub const EventProperty5 = sys.esp_mqtt5_event_property_t;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Errors
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
pub const Error = error{
|
||||
/// publish / subscribe / enqueue returned -1 (queuing failed)
|
||||
Failed,
|
||||
/// publish returned -2 (outbox full)
|
||||
OutboxFull,
|
||||
};
|
||||
|
||||
/// Converts a message-id return value (≥0 = ok, -1 = failed, -2 = outbox full)
|
||||
/// into `!u32`.
|
||||
fn msgIdResult(rc: c_int) Error!u32 {
|
||||
return switch (rc) {
|
||||
-2 => error.OutboxFull,
|
||||
-1 => error.Failed,
|
||||
else => @intCast(rc),
|
||||
};
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Client lifecycle
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Initialise a new MQTT client. Returns `null` if allocation fails.
|
||||
pub fn init(cfg: *const ClientConfig) ?Handle {
|
||||
return sys.esp_mqtt_client_init(cfg);
|
||||
}
|
||||
|
||||
pub fn deinit(client: Handle) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_destroy(client));
|
||||
}
|
||||
|
||||
pub fn setConfig(client: Handle, cfg: *const ClientConfig) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_set_config(client, cfg));
|
||||
}
|
||||
|
||||
pub fn setUri(client: Handle, uri: [:0]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_set_uri(client, uri.ptr));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Connection control
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn start(client: Handle) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_start(client));
|
||||
}
|
||||
|
||||
pub fn stop(client: Handle) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_stop(client));
|
||||
}
|
||||
|
||||
pub fn reconnect(client: Handle) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_reconnect(client));
|
||||
}
|
||||
|
||||
pub fn disconnect(client: Handle) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_disconnect(client));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Publish
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const PublishConfig = struct {
|
||||
qos: u2 = 0,
|
||||
retain: bool = false,
|
||||
};
|
||||
|
||||
/// Publish `data` to `topic`. Returns the message-id assigned by the broker.
|
||||
pub fn publish(client: Handle, topic: [:0]const u8, data: []const u8, cfg: PublishConfig) Error!u32 {
|
||||
return msgIdResult(sys.esp_mqtt_client_publish(
|
||||
client,
|
||||
topic.ptr,
|
||||
data.ptr,
|
||||
@intCast(data.len),
|
||||
@intCast(cfg.qos),
|
||||
@intFromBool(cfg.retain),
|
||||
));
|
||||
}
|
||||
|
||||
pub const EnqueueConfig = struct {
|
||||
qos: u2 = 0,
|
||||
retain: bool = false,
|
||||
/// Store in outbox even when disconnected.
|
||||
store: bool = true,
|
||||
};
|
||||
|
||||
/// Enqueue a publish for delivery (stores in outbox when disconnected).
|
||||
pub fn enqueue(client: Handle, topic: [:0]const u8, data: []const u8, cfg: EnqueueConfig) Error!u32 {
|
||||
return msgIdResult(sys.esp_mqtt_client_enqueue(
|
||||
client,
|
||||
topic.ptr,
|
||||
data.ptr,
|
||||
@intCast(data.len),
|
||||
@intCast(cfg.qos),
|
||||
@intFromBool(cfg.retain),
|
||||
cfg.store,
|
||||
));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Subscribe / Unsubscribe
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Subscribe to a single topic filter. Returns the message-id.
|
||||
pub fn subscribe(client: Handle, topic: [:0]const u8, qos: u2) Error!u32 {
|
||||
return msgIdResult(sys.esp_mqtt_client_subscribe_single(client, topic.ptr, @intCast(qos)));
|
||||
}
|
||||
|
||||
/// Subscribe to multiple topic filters in one call.
|
||||
pub fn subscribeMultiple(client: Handle, topics: []const Topic) Error!u32 {
|
||||
return msgIdResult(sys.esp_mqtt_client_subscribe_multiple(
|
||||
client,
|
||||
topics.ptr,
|
||||
@intCast(topics.len),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn unsubscribe(client: Handle, topic: [:0]const u8) Error!u32 {
|
||||
return msgIdResult(sys.esp_mqtt_client_unsubscribe(client, topic.ptr));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Event registration
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn registerEvent(client: Handle, event: EventId, handler: EventHandler, arg: ?*anyopaque) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_register_event(client, event, handler, arg));
|
||||
}
|
||||
|
||||
pub fn unregisterEvent(client: Handle, event: EventId, handler: EventHandler) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_unregister_event(client, event, handler));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Diagnostics
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Returns the current outbox size in bytes.
|
||||
pub fn getOutboxSize(client: Handle) usize {
|
||||
return @intCast(sys.esp_mqtt_client_get_outbox_size(client));
|
||||
}
|
||||
|
||||
/// Dispatch a custom application event through the MQTT event loop.
|
||||
pub fn dispatchCustomEvent(client: Handle, event: *Event) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_dispatch_custom_event(client, event));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// MQTT v5 extensions
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const v5 = struct {
|
||||
pub fn setConnectProperty(client: Handle5, prop: *const ConnectionProperty5) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt5_client_set_connect_property(client, prop));
|
||||
}
|
||||
|
||||
pub fn setPublishProperty(client: Handle5, prop: *const PublishProperty5) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt5_client_set_publish_property(client, prop));
|
||||
}
|
||||
|
||||
pub fn setSubscribeProperty(client: Handle5, prop: *const SubscribeProperty5) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt5_client_set_subscribe_property(client, prop));
|
||||
}
|
||||
|
||||
pub fn setUnsubscribeProperty(client: Handle5, prop: *const UnsubscribeProperty5) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt5_client_set_unsubscribe_property(client, prop));
|
||||
}
|
||||
|
||||
pub fn setDisconnectProperty(client: Handle5, prop: *const DisconnectProperty5) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt5_client_set_disconnect_property(client, prop));
|
||||
}
|
||||
|
||||
// ── User properties ───────────────────────────────────────────────────
|
||||
|
||||
pub fn setUserProperty(handle: *UserPropertyHandle5, items: []UserPropertyItem5) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt5_client_set_user_property(
|
||||
handle,
|
||||
items.ptr,
|
||||
@intCast(items.len),
|
||||
));
|
||||
}
|
||||
|
||||
/// Returns how many user-property items are present.
|
||||
pub fn getUserPropertyCount(handle: UserPropertyHandle5) u8 {
|
||||
return sys.esp_mqtt5_client_get_user_property_count(handle);
|
||||
}
|
||||
|
||||
/// Read user-property items into the caller-supplied slice.
|
||||
/// The slice length must be ≥ `getUserPropertyCount()`.
|
||||
pub fn getUserProperty(handle: UserPropertyHandle5, out: []UserPropertyItem5) !void {
|
||||
var count: u8 = @intCast(out.len);
|
||||
return errors.espCheckError(sys.esp_mqtt5_client_get_user_property(
|
||||
handle,
|
||||
out.ptr,
|
||||
&count,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn deleteUserProperty(handle: UserPropertyHandle5) void {
|
||||
sys.esp_mqtt5_client_delete_user_property(handle);
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user