5050
5151ALL_PLATFORMS = {
5252 # classic Arduino AVR
53- "uno" : "arduino:avr:uno" ,
54- "leonardo" : "arduino:avr:leonardo" ,
55- "mega2560" : "arduino:avr:mega:cpu=atmega2560" ,
53+ "uno" : [ "arduino:avr:uno" , None ] ,
54+ "leonardo" : [ "arduino:avr:leonardo" , None ] ,
55+ "mega2560" : [ "arduino:avr:mega:cpu=atmega2560" , None ] ,
5656 # Arduino SAMD
57- "zero" : "arduino:samd:arduino_zero_native" ,
58- "cpx" : "arduino:samd:adafruit_circuitplayground_m0" ,
57+ "zero" : [ "arduino:samd:arduino_zero_native" , "0x68ed2b88" ] ,
58+ "cpx" : [ "arduino:samd:adafruit_circuitplayground_m0" , "0x68ed2b88" ] ,
5959 # Espressif
60- "esp8266" : "esp8266:esp8266:huzzah:eesz=4M3M,xtal=80" ,
61- "esp32" : "esp32:esp32:featheresp32:FlashFreq=80" ,
62- "magtag" : "esp32:esp32:adafruit_magtag29_esp32s2" ,
63- "funhouse" : "esp32:esp32:adafruit_funhouse_esp32s2" ,
64- "metroesp32s2" : "esp32:esp32:adafruit_metro_esp32s2" ,
60+ "esp8266" : [ "esp8266:esp8266:huzzah:eesz=4M3M,xtal=80" , "0x7eab61ed" ] ,
61+ "esp32" : [ "esp32:esp32:featheresp32:FlashFreq=80" , "0x1c5f21b0" ] ,
62+ "magtag" : [ "esp32:esp32:adafruit_magtag29_esp32s2" , "0xbfdd4eee" ] ,
63+ "funhouse" : [ "esp32:esp32:adafruit_funhouse_esp32s2" , "0xbfdd4eee" ] ,
64+ "metroesp32s2" : [ "esp32:esp32:adafruit_metro_esp32s2" , "0xbfdd4eee" ] ,
6565 # Adafruit AVR
66- "trinket_3v" : "adafruit:avr:trinket3" ,
67- "trinket_5v" : "adafruit:avr:trinket5" ,
68- "protrinket_3v" : "adafruit:avr:protrinket3" ,
69- "protrinket_5v" : "adafruit:avr:protrinket5" ,
70- "gemma" : "adafruit:avr:gemma" ,
71- "flora" : "adafruit:avr:flora8" ,
72- "feather32u4" : "adafruit:avr:feather32u4" ,
73- "cpc" : "arduino:avr:circuitplay32u4cat" ,
66+ "trinket_3v" : [ "adafruit:avr:trinket3" , None ] ,
67+ "trinket_5v" : [ "adafruit:avr:trinket5" , None ] ,
68+ "protrinket_3v" : [ "adafruit:avr:protrinket3" , None ] ,
69+ "protrinket_5v" : [ "adafruit:avr:protrinket5" , None ] ,
70+ "gemma" : [ "adafruit:avr:gemma" , None ] ,
71+ "flora" : [ "adafruit:avr:flora8" , None ] ,
72+ "feather32u4" : [ "adafruit:avr:feather32u4" , None ] ,
73+ "cpc" : [ "arduino:avr:circuitplay32u4cat" , None ] ,
7474 # Adafruit SAMD
75- "gemma_m0" : "adafruit:samd:adafruit_gemma_m0" ,
76- "trinket_m0" : "adafruit:samd:adafruit_trinket_m0" ,
77- "feather_m0_express" : "adafruit:samd:adafruit_feather_m0_express" ,
78- "feather_m4_can" : "adafruit:samd:adafruit_feather_m4_can:speed=120" ,
79- "feather_m4_can_tinyusb" : "adafruit:samd:adafruit_feather_m4_can:speed=120,usbstack=tinyusb" ,
80- "metro_m0" : "adafruit:samd:adafruit_metro_m0" ,
81- "metro_m0_tinyusb" : "adafruit:samd:adafruit_metro_m0:usbstack=tinyusb" ,
82- "metro_m4" : "adafruit:samd:adafruit_metro_m4:speed=120" ,
83- "metro_m4_tinyusb" : "adafruit:samd:adafruit_metro_m4:speed=120,usbstack=tinyusb" ,
84- "metro_m4_airliftlite" : "adafruit:samd:adafruit_metro_m4_airliftlite:speed=120" ,
85- "metro_m4_airliftlite_tinyusb" : "adafruit:samd:adafruit_metro_m4_airliftlite:speed=120,usbstack=tinyusb" ,
86- "pybadge" : "adafruit:samd:adafruit_pybadge_m4:speed=120" ,
87- "pybadge_tinyusb" : "adafruit:samd:adafruit_pybadge_m4:speed=120,usbstack=tinyusb" ,
88- "pygamer" : "adafruit:samd:adafruit_pygamer_m4:speed=120" ,
75+ "gemma_m0" : [ "adafruit:samd:adafruit_gemma_m0" , "0x68ed2b88" ] ,
76+ "trinket_m0" : [ "adafruit:samd:adafruit_trinket_m0" , "0x68ed2b88" ] ,
77+ "feather_m0_express" : [ "adafruit:samd:adafruit_feather_m0_express" , "0x68ed2b88" ] ,
78+ "feather_m4_can" : [ "adafruit:samd:adafruit_feather_m4_can:speed=120" , "0x68ed2b88" ] ,
79+ "feather_m4_can_tinyusb" : [ "adafruit:samd:adafruit_feather_m4_can:speed=120,usbstack=tinyusb" , "0x68ed2b88" ] ,
80+ "metro_m0" : [ "adafruit:samd:adafruit_metro_m0" , "0x68ed2b88" ] ,
81+ "metro_m0_tinyusb" : [ "adafruit:samd:adafruit_metro_m0:usbstack=tinyusb" , "0x68ed2b88" ] ,
82+ "metro_m4" : [ "adafruit:samd:adafruit_metro_m4:speed=120" , "0x55114460" ] ,
83+ "metro_m4_tinyusb" : [ "adafruit:samd:adafruit_metro_m4:speed=120,usbstack=tinyusb" , "0x55114460" ] ,
84+ "metro_m4_airliftlite" : [ "adafruit:samd:adafruit_metro_m4_airliftlite:speed=120" , "0x55114460" ] ,
85+ "metro_m4_airliftlite_tinyusb" : [ "adafruit:samd:adafruit_metro_m4_airliftlite:speed=120,usbstack=tinyusb" , "0x55114460" ] ,
86+ "pybadge" : [ "adafruit:samd:adafruit_pybadge_m4:speed=120" , "0x55114460" ] ,
87+ "pybadge_tinyusb" : [ "adafruit:samd:adafruit_pybadge_m4:speed=120,usbstack=tinyusb" , "0x55114460" ] ,
88+ "pygamer" : [ "adafruit:samd:adafruit_pygamer_m4:speed=120" , "0x55114460" ] ,
8989 "hallowing_m0" : "adafruit:samd:adafruit_hallowing" ,
90- "hallowing_m4" : "adafruit:samd:adafruit_hallowing_m4:speed=120" ,
91- "hallowing_m4_tinyusb" : "adafruit:samd:adafruit_hallowing_m4:speed=120,usbstack=tinyusb" ,
92- "neotrellis_m4" : "adafruit:samd:adafruit_trellis_m4:speed=120" ,
93- "monster_m4sk" : "adafruit:samd:adafruit_monster_m4sk:speed=120" ,
94- "monster_m4sk_tinyusb" : "adafruit:samd:adafruit_monster_m4sk:speed=120,usbstack=tinyusb" ,
95- "pyportal" : "adafruit:samd:adafruit_pyportal_m4:speed=120" ,
96- "pyportal_tinyusb" : "adafruit:samd:adafruit_pyportal_m4:speed=120,usbstack=tinyusb" ,
97- "pyportal_titano" : "adafruit:samd:adafruit_pyportal_m4_titano:speed=120" ,
98- "pyportal_titano_tinyusb" : "adafruit:samd:adafruit_pyportal_m4_titano:speed=120,usbstack=tinyusb" ,
99- "cpx_ada" : "adafruit:samd:adafruit_circuitplayground_m0" ,
100- "grand_central" : "adafruit:samd:adafruit_grandcentral_m4:speed=120" ,
101- "grand_central_tinyusb" : "adafruit:samd:adafruit_grandcentral_m4:speed=120,usbstack=tinyusb" ,
102- "matrixportal" : "adafruit:samd:adafruit_matrixportal_m4:speed=120" ,
103- "matrixportal_tinyusb" : "adafruit:samd:adafruit_matrixportal_m4:speed=120,usbstack=tinyusb" ,
104- "neotrinkey_m0" : "adafruit:samd:adafruit_neotrinkey_m0" ,
105- "rotarytrinkey_m0" : "adafruit:samd:adafruit_rotarytrinkey_m0" ,
106- "neokeytrinkey_m0" : "adafruit:samd:adafruit_neokeytrinkey_m0" ,
107- "slidetrinkey_m0" : "adafruit:samd:adafruit_slidetrinkey_m0" ,
108- "proxlighttrinkey_m0" : "adafruit:samd:adafruit_proxlighttrinkey_m0" ,
90+ "hallowing_m4" : [ "adafruit:samd:adafruit_hallowing_m4:speed=120" , "0x55114460" ] ,
91+ "hallowing_m4_tinyusb" : [ "adafruit:samd:adafruit_hallowing_m4:speed=120,usbstack=tinyusb" , "0x55114460" ] ,
92+ "neotrellis_m4" : [ "adafruit:samd:adafruit_trellis_m4:speed=120" , "0x55114460" ] ,
93+ "monster_m4sk" : [ "adafruit:samd:adafruit_monster_m4sk:speed=120" , "0x55114460" ] ,
94+ "monster_m4sk_tinyusb" : [ "adafruit:samd:adafruit_monster_m4sk:speed=120,usbstack=tinyusb" , "0x55114460" ] ,
95+ "pyportal" : [ "adafruit:samd:adafruit_pyportal_m4:speed=120" , "0x55114460" ] ,
96+ "pyportal_tinyusb" : [ "adafruit:samd:adafruit_pyportal_m4:speed=120,usbstack=tinyusb" , "0x55114460" ] ,
97+ "pyportal_titano" : [ "adafruit:samd:adafruit_pyportal_m4_titano:speed=120" , "0x55114460" ] ,
98+ "pyportal_titano_tinyusb" : [ "adafruit:samd:adafruit_pyportal_m4_titano:speed=120,usbstack=tinyusb" , "0x55114460" ] ,
99+ "cpx_ada" : [ "adafruit:samd:adafruit_circuitplayground_m0" , "0x68ed2b88" ] ,
100+ "grand_central" : [ "adafruit:samd:adafruit_grandcentral_m4:speed=120" , "0x55114460" ] ,
101+ "grand_central_tinyusb" : [ "adafruit:samd:adafruit_grandcentral_m4:speed=120,usbstack=tinyusb" , "0x55114460" ] ,
102+ "matrixportal" : [ "adafruit:samd:adafruit_matrixportal_m4:speed=120" , "0x55114460" ] ,
103+ "matrixportal_tinyusb" : [ "adafruit:samd:adafruit_matrixportal_m4:speed=120,usbstack=tinyusb" , "0x55114460" ] ,
104+ "neotrinkey_m0" : [ "adafruit:samd:adafruit_neotrinkey_m0" , "0x68ed2b88" ] ,
105+ "rotarytrinkey_m0" : [ "adafruit:samd:adafruit_rotarytrinkey_m0" , "0x68ed2b88" ] ,
106+ "neokeytrinkey_m0" : [ "adafruit:samd:adafruit_neokeytrinkey_m0" , "0x68ed2b88" ] ,
107+ "slidetrinkey_m0" : [ "adafruit:samd:adafruit_slidetrinkey_m0" , "0x68ed2b88" ] ,
108+ "proxlighttrinkey_m0" : [ "adafruit:samd:adafruit_proxlighttrinkey_m0" , "0x68ed2b88" ] ,
109109 # Arduino nRF
110- "microbit" : "sandeepmistry:nRF5:BBCmicrobit:softdevice=s110" ,
110+ "microbit" : [ "sandeepmistry:nRF5:BBCmicrobit:softdevice=s110" , None ] ,
111111 # Adafruit nRF
112- "nrf52832" : "adafruit:nrf52:feather52832:softdevice=s132v6,debug=l0" ,
113- "nrf52840" : "adafruit:nrf52:feather52840:softdevice=s140v6,debug=l0" ,
114- "cpb" : "adafruit:nrf52:cplaynrf52840:softdevice=s140v6,debug=l0" ,
115- "clue" : "adafruit:nrf52:cluenrf52840:softdevice=s140v6,debug=l0" ,
112+ "nrf52832" : [ "adafruit:nrf52:feather52832:softdevice=s132v6,debug=l0" , None ] ,
113+ "nrf52840" : [ "adafruit:nrf52:feather52840:softdevice=s140v6,debug=l0" , "0xada52840" ] ,
114+ "cpb" : [ "adafruit:nrf52:cplaynrf52840:softdevice=s140v6,debug=l0" , "0xada52840" ] ,
115+ "clue" : [ "adafruit:nrf52:cluenrf52840:softdevice=s140v6,debug=l0" , "0xada52840" ] ,
116116 # RP2040 (Philhower)
117- "pico_rp2040" : "rp2040:rp2040:rpipico:freq=125,flash=2097152_0" ,
118- "pico_rp2040_tinyusb" : "rp2040:rp2040:rpipico:flash=2097152_0,freq=125,dbgport=Disabled,dbglvl=None,usbstack=tinyusb" ,
119- "feather_rp2040" : "rp2040:rp2040:adafruitfeather:freq=125,flash=8388608_0" ,
120- "feather_rp2040_tinyusb" : "rp2040:rp2040:adafruit_feather:flash=8388608_0,freq=125,dbgport=Disabled,dbglvl=None,usbstack=tinyusb" ,
121- "qt2040_trinkey" : "rp2040:rp2040:adafruit_trinkeyrp2040qt:freq=125,flash=8388608_0" ,
122- "qt2040_trinkey_tinyusb" : "rp2040:rp2040:adafruit_trinkeyrp2040qt:flash=8388608_0,freq=125,dbgport=Disabled,dbglvl=None,usbstack=tinyusb" ,
117+ "pico_rp2040" : [ "rp2040:rp2040:rpipico:freq=125,flash=2097152_0" , "0xe48bff56" ] ,
118+ "pico_rp2040_tinyusb" : [ "rp2040:rp2040:rpipico:flash=2097152_0,freq=125,dbgport=Disabled,dbglvl=None,usbstack=tinyusb" , "0xe48bff56" ] ,
119+ "feather_rp2040" : [ "rp2040:rp2040:adafruitfeather:freq=125,flash=8388608_0" , "0xe48bff56" ] ,
120+ "feather_rp2040_tinyusb" : [ "rp2040:rp2040:adafruit_feather:flash=8388608_0,freq=125,dbgport=Disabled,dbglvl=None,usbstack=tinyusb" , "0xe48bff56" ] ,
121+ "qt2040_trinkey" : [ "rp2040:rp2040:adafruit_trinkeyrp2040qt:freq=125,flash=8388608_0" , "0xe48bff56" ] ,
122+ "qt2040_trinkey_tinyusb" : [ "rp2040:rp2040:adafruit_trinkeyrp2040qt:flash=8388608_0,freq=125,dbgport=Disabled,dbglvl=None,usbstack=tinyusb" , "0xe48bff56" ] ,
123123 # Attiny8xy (SpenceKonde)
124- "attiny817" : "megaTinyCore:megaavr:atxy7:chip=817" ,
125- "attiny816" : "megaTinyCore:megaavr:atxy6:chip=816" ,
126- "attiny807" : "megaTinyCore:megaavr:atxy7:chip=807" ,
127- "attiny806" : "megaTinyCore:megaavr:atxy6:chip=806" ,
124+ "attiny817" : [ "megaTinyCore:megaavr:atxy7:chip=817" , None ] ,
125+ "attiny816" : [ "megaTinyCore:megaavr:atxy6:chip=816" , None ] ,
126+ "attiny807" : [ "megaTinyCore:megaavr:atxy7:chip=807" , None ] ,
127+ "attiny806" : [ "megaTinyCore:megaavr:atxy6:chip=806" , None ] ,
128128 # groupings
129129 "main_platforms" : ("uno" , "leonardo" , "mega2560" , "zero" ,
130130 "esp8266" , "esp32" , "metro_m4" , "trinket_m0" ),
131131 "arcada_platforms" : ("pybadge" , "pygamer" , "hallowing_m4" ,
132132 "cpb" , "cpx_ada" ),
133+ "wippersnapper_platforms" : ("metro_m4_airliftlite_tinyusb" , "pyportal_tinyusb" ),
133134 "rp2040_platforms" : ("pico_rp2040" , "feather_rp2040" )
134135}
135136
@@ -211,12 +212,38 @@ def run_or_die(cmd, error):
211212
212213print ("Libraries installed: " , glob .glob (os .environ ['HOME' ]+ '/Arduino/libraries/*' ))
213214
214- # link our library folder to the arduino libraries folder
215- if not IS_LEARNING_SYS :
216- try :
217- os .symlink (BUILD_DIR , os .environ ['HOME' ]+ '/Arduino/libraries/' + os .path .basename (BUILD_DIR ))
218- except FileExistsError :
219- pass
215+ ################################ UF2 Utils.
216+
217+ def glob1 (pattern ):
218+ result = glob .glob (pattern )
219+ if len (result ) != 1 :
220+ raise RuntimeError (f"Required pattern { pattern } to match exactly 1 file, got { result } " )
221+ return result [0 ]
222+
223+ def generate_uf2 (example_path ):
224+ """Generates a .uf2 file from a .bin or .hex file.
225+ :param str example_path: A path to the compiled .bin or .hex file.
226+ """
227+ if ALL_PLATFORMS [platform ][1 ] == None :
228+ return False
229+ # Convert .hex to .uf2
230+ family_id = ALL_PLATFORMS [platform ][1 ]
231+ cli_build_path = "build/*.*." + fqbn .split (':' )[2 ] + "/*.hex"
232+ input_file = glob1 (os .path .join (example_path , cli_build_path ))
233+ output_file = os .path .splitext (input_file )[0 ] + ".uf2"
234+ cmd = ['python3' , 'uf2conv.py' , input_file , '-c' , '-f' , family_id , '-o' , output_file ]
235+ proc = subprocess .Popen (cmd , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
236+ r = proc .wait (timeout = 60 )
237+ out = proc .stdout .read ()
238+ err = proc .stderr .read ()
239+ if r == 0 and not err :
240+ ColorPrint .print_pass (CHECK )
241+ else :
242+ ColorPrint .print_fail (CROSS )
243+ ColorPrint .print_fail (out .decode ("utf-8" ))
244+ ColorPrint .print_fail (err .decode ("utf-8" ))
245+ return False
246+ return True
220247
221248################################ Test platforms
222249platforms = []
@@ -225,9 +252,9 @@ def run_or_die(cmd, error):
225252# expand groups:
226253for arg in sys .argv [1 :]:
227254 platform = ALL_PLATFORMS .get (arg , None )
228- if isinstance (platform , str ):
255+ if isinstance (platform , list ):
229256 platforms .append (arg )
230- elif isinstance (platform , collections . abc . Iterable ):
257+ elif isinstance (platform , tuple ):
231258 for p in platform :
232259 platforms .append (p )
233260 else :
@@ -248,15 +275,35 @@ def test_examples_in_folder(folderpath):
248275 # check if we should SKIP
249276 skipfilename = folderpath + "/." + platform + ".test.skip"
250277 onlyfilename = folderpath + "/." + platform + ".test.only"
278+ # check if we should GENERATE UF2
279+ gen_file_name = folderpath + "/." + platform + ".generate"
251280 if os .path .exists (skipfilename ):
252281 ColorPrint .print_warn ("skipping" )
253282 continue
254283 if glob .glob (folderpath + "/.*.test.only" ) and not os .path .exists (onlyfilename ):
255284 ColorPrint .print_warn ("skipping" )
256285 continue
286+ if os .path .exists (gen_file_name ):
287+ ColorPrint .print_info ("Generating UF2 after compile." )
288+ # Download uf2conv.py and dependency if we don't already have it
289+ cmd = "wget -nc --no-check-certificate https://github.com/microsoft/uf2/master/utils/uf2families.json https://github.com/microsoft/uf2/master/utils/uf2conv.py"
290+ proc = subprocess .Popen (cmd , stdout = subprocess .PIPE , stderr = subprocess .PIPE , shell = True )
291+ r = proc .wait (timeout = 60 )
292+ out = proc .stdout .read ()
293+ if r != 0 :
294+ ColorPrint .print_fail ("Failed to download UF2 Utils!" )
295+ ColorPrint .print_fail (out .decode ("utf-8" ))
296+ ColorPrint .print_fail (err .decode ("utf-8" ))
297+ continue
298+ # Create a uf2 directory if doesn't exist
299+ if not os .path .isdir ("uf2" ):
300+ os .mkdir ("uf2" )
257301
258302 if BUILD_WARN :
259- cmd = ['arduino-cli' , 'compile' , '--warnings' , 'all' , '--fqbn' , fqbn , examplepath ]
303+ if os .path .exists (gen_file_name ):
304+ cmd = ['arduino-cli' , 'compile' , '--warnings' , 'all' , '--fqbn' , fqbn , '-e' , examplepath ]
305+ else :
306+ cmd = ['arduino-cli' , 'compile' , '--warnings' , 'all' , '--fqbn' , fqbn , examplepath ]
260307 else :
261308 cmd = ['arduino-cli' , 'compile' , '--warnings' , 'none' , '--export-binaries' , '--fqbn' , fqbn , examplepath ]
262309 proc = subprocess .Popen (cmd , stdout = subprocess .PIPE ,
@@ -269,6 +316,9 @@ def test_examples_in_folder(folderpath):
269316 if err :
270317 # also print out warning message
271318 ColorPrint .print_fail (err .decode ("utf-8" ))
319+ if os .path .exists (gen_file_name ):
320+ ColorPrint .print_info ("Generating UF2..." )
321+ success = generate_uf2 (folderpath )
272322 else :
273323 ColorPrint .print_fail (CROSS )
274324 ColorPrint .print_fail (out .decode ("utf-8" ))
@@ -315,7 +365,7 @@ def test_examples_in_learningrepo(folderpath):
315365
316366
317367for platform in platforms :
318- fqbn = ALL_PLATFORMS [platform ]
368+ fqbn = ALL_PLATFORMS [platform ][ 0 ]
319369 print ('#' * 80 )
320370 ColorPrint .print_info ("SWITCHING TO " + fqbn )
321371 install_platform (":" .join (fqbn .split (':' , 2 )[0 :2 ])) # take only first two elements
0 commit comments