4 # Copyright (c) 2020-2023 Raspberry Pi (Trading) Ltd.
6 # SPDX-License-Identifier: BSD-3-Clause
12 from pyexpat import features
14 from pathlib import Path
26 from tkinter import messagebox as mb
27 from tkinter import filedialog as fd
28 from tkinter import simpledialog as sd
29 from tkinter import ttk
31 CMAKELIST_FILENAME = 'CMakeLists.txt'
32 CMAKECACHE_FILENAME = 'CMakeCache.txt'
34 COMPILER_NAME = 'arm-none-eabi-gcc'
36 VSCODE_LAUNCH_FILENAME = 'launch.json'
37 VSCODE_C_PROPERTIES_FILENAME = 'c_cpp_properties.json'
38 VSCODE_SETTINGS_FILENAME ='settings.json'
39 VSCODE_EXTENSIONS_FILENAME ='extensions.json'
40 VSCODE_TASKS_FILENAME ='tasks.json'
41 VSCODE_FOLDER='.vscode'
43 CONFIG_UNSET="Not set"
45 # Standard libraries for all builds
46 # And any more to string below, space separator
47 STANDARD_LIBRARIES = 'pico_stdlib'
49 # Indexed on feature name, tuple contains the C file, the H file and the CMake project name for the feature.
50 # Some lists may contain an extra/ancillary file needed for that feature
58 'spi' : ("SPI", "spi.c", "hardware/spi.h", "hardware_spi"),
59 'i2c' : ("I2C interface", "i2c.c", "hardware/i2c.h", "hardware_i2c"),
60 'dma' : ("DMA support", "dma.c", "hardware/dma.h", "hardware_dma"),
61 'pio' : ("PIO interface", "pio.c", "hardware/pio.h", "hardware_pio"),
62 'interp' : ("HW interpolation", "interp.c", "hardware/interp.h", "hardware_interp"),
63 'timer' : ("HW timer", "timer.c", "hardware/timer.h", "hardware_timer"),
64 'watchdog' : ("HW watchdog", "watch.c", "hardware/watchdog.h", "hardware_watchdog"),
65 'clocks' : ("HW clocks", "clocks.c", "hardware/clocks.h", "hardware_clocks"),
68 picow_options_list = {
69 'picow_none' : ("None", "", "", "", ""),
70 'picow_led' : ("PicoW onboard LED", "", "pico/cyw43_arch.h", "pico_cyw43_arch_none", ""),
71 'picow_poll' : ("Polled lwIP", "", "pico/cyw43_arch.h", "pico_cyw43_arch_lwip_poll", "lwipopts.h"),
72 'picow_background' :("Background lwIP", "", "pico/cyw43_arch.h", "pico_cyw43_arch_lwip_threadsafe_background", "lwipopts.h"),
73 # 'picow_freertos' : ("Full lwIP (FreeRTOS)", "", "pico/cyw43_arch.h", "pico_cyw43_arch_lwip_sys_freertos", "lwipopts.h"),
76 stdlib_examples_list = {
77 'uart': ("UART", "uart.c", "hardware/uart.h", "hardware_uart"),
78 'gpio' : ("GPIO interface", "gpio.c", "hardware/gpio.h", "hardware_gpio"),
79 'div' : ("Low level HW Divider", "divider.c", "hardware/divider.h", "hardware_divider")
82 debugger_list = ["DebugProbe (CMSIS-DAP)", "SWD (Pi host)"]
83 debugger_config_list = ["cmsis-dap.cfg", "raspberrypi-swd.cfg"]
87 # Could add an extra item that shows how to use some of the available functions for the feature
90 # This also contains example code for the standard library (see stdlib_examples_list)
91 code_fragments_per_feature = {
94 "// By default the stdout UART is `uart0`, so we will use the second one",
95 "#define UART_ID uart1",
96 "#define BAUD_RATE 9600", "",
97 "// Use pins 4 and 5 for UART1",
98 "// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
99 "#define UART_TX_PIN 4",
100 "#define UART_RX_PIN 5" ),
102 ( "// Set up our UART",
103 "uart_init(UART_ID, BAUD_RATE);",
104 "// Set the TX and RX pins by using the function select on the GPIO",
105 "// Set datasheet for more information on function select",
106 "gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);",
107 "gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);", "" )
111 "// We are going to use SPI 0, and allocate it to the following GPIO pins",
112 "// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
113 "#define SPI_PORT spi0",
114 "#define PIN_MISO 16",
116 "#define PIN_SCK 18",
117 "#define PIN_MOSI 19" ),
119 ( "// SPI initialisation. This example will use SPI at 1MHz.",
120 "spi_init(SPI_PORT, 1000*1000);",
121 "gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);",
122 "gpio_set_function(PIN_CS, GPIO_FUNC_SIO);",
123 "gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);",
124 "gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);", "",
125 "// Chip select is active-low, so we'll initialise it to a driven-high state",
126 "gpio_set_dir(PIN_CS, GPIO_OUT);",
127 "gpio_put(PIN_CS, 1);", "")
132 "// This example will use I2C0 on GPIO8 (SDA) and GPIO9 (SCL) running at 400KHz.",
133 "// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
134 "#define I2C_PORT i2c0",
139 "// I2C Initialisation. Using it at 400Khz.",
140 "i2c_init(I2C_PORT, 400*1000);","",
141 "gpio_set_function(I2C_SDA, GPIO_FUNC_I2C);",
142 "gpio_set_function(I2C_SCL, GPIO_FUNC_I2C);",
143 "gpio_pull_up(I2C_SDA);",
144 "gpio_pull_up(I2C_SCL);"
150 "// Example uses GPIO 2",
154 "// GPIO initialisation.",
155 "// We will make this GPIO an input, and pull it up by default",
157 "gpio_set_dir(GPIO, GPIO_IN);",
158 "gpio_pull_up(GPIO);","",
164 "// Interpolator example code",
165 "interp_config cfg = interp_default_config();",
166 "// Now use the various interpolator library functions for your use case",
167 "// e.g. interp_config_clamp(&cfg, true);",
168 "// interp_config_shift(&cfg, 2);",
169 "// Then set the config ",
170 "interp_set_config(interp0, 0, &cfg);",
176 "int64_t alarm_callback(alarm_id_t id, void *user_data) {",
177 " // Put your timeout handler code in here",
182 "// Timer example code - This example fires off the callback after 2000ms",
183 "add_alarm_in_ms(2000, alarm_callback, NULL, false);"
189 "// Watchdog example code",
190 "if (watchdog_caused_reboot()) {",
191 " // Whatever action you may take if a watchdog caused a reboot",
193 "// Enable the watchdog, requiring the watchdog to be updated every 100ms or the chip will reboot",
194 "// second arg is pause on debug which means the watchdog will pause when stepping through code",
195 "watchdog_enable(100, 1);","",
196 "// You need to call this function at least more often than the 100ms in the enable call to prevent a reboot"
197 "watchdog_update();",
203 "// Example of using the HW divider. The pico_divider library provides a more user friendly set of APIs ",
204 "// over the divider (and support for 64 bit divides), and of course by default regular C language integer",
205 "// divisions are redirected thru that library, meaning you can just use C level `/` and `%` operators and",
206 "// gain the benefits of the fast hardware divider.",
207 "int32_t dividend = 123456;",
208 "int32_t divisor = -321;",
209 "// This is the recommended signed fast divider for general use.",
210 "divmod_result_t result = hw_divider_divmod_s32(dividend, divisor);",
211 "printf(\"%d/%d = %d remainder %d\\n\", dividend, divisor, to_quotient_s32(result), to_remainder_s32(result));",
212 "// This is the recommended unsigned fast divider for general use.",
213 "int32_t udividend = 123456;",
214 "int32_t udivisor = 321;",
215 "divmod_result_t uresult = hw_divider_divmod_u32(udividend, udivisor);",
216 "printf(\"%d/%d = %d remainder %d\\n\", udividend, udivisor, to_quotient_u32(uresult), to_remainder_u32(uresult));"
221 configuration_dictionary = list(dict())
225 compilerPath = Path("/usr/bin/arm-none-eabi-gcc")
227 def relativeSDKPath(sdkVersion):
228 return f"/.pico-sdk/sdk/{sdkVersion}"
230 def relativeToolchainPath(toolchainVersion):
231 return f"/.pico-sdk/toolchain/{toolchainVersion}"
233 def cmakeSdkPath(sdkVersion):
234 return f"${{USERHOME}}{relativeSDKPath(sdkVersion)}"
236 def cmakeToolchainPath(toolchainVersion):
237 return f"${{USERHOME}}{relativeToolchainPath(toolchainVersion)}"
239 def codeSdkPath(sdkVersion):
241 return f"${{env:USERPROFILE}}{relativeSDKPath(sdkVersion)}"
243 return f"${{env:HOME}}{relativeSDKPath(sdkVersion)}"
245 def codeToolchainPath(toolchainVersion):
247 return f"${{env:USERPROFILE}}{relativeToolchainPath(toolchainVersion)}"
249 return f"${{env:HOME}}{relativeToolchainPath(toolchainVersion)}"
254 def GetButtonBackground():
260 def GetButtonTextColour():
264 def RunGUI(sdkpath, args):
266 style = ttk.Style(root)
267 style.theme_use('default')
269 style.configure("TButton", padding=6, relief="groove", border=2, foreground=GetButtonTextColour(), background=GetButtonBackground())
270 style.configure("TLabel", foreground=GetTextColour(), background=GetBackground() )
271 style.configure("TCheckbutton", foreground=GetTextColour(), background=GetBackground())
272 style.configure("TRadiobutton", foreground=GetTextColour(), background=GetBackground() )
273 style.configure("TLabelframe", foreground=GetTextColour(), background=GetBackground() )
274 style.configure("TLabelframe.Label", foreground=GetTextColour(), background=GetBackground() )
275 style.configure("TCombobox", foreground=GetTextColour(), background=GetBackground() )
276 style.configure("TListbox", foreground=GetTextColour(), background=GetBackground() )
278 style.map("TCheckbutton", background = [('disabled', GetBackground())])
279 style.map("TRadiobutton", background = [('disabled', GetBackground())])
280 style.map("TButton", background = [('disabled', GetBackground())])
281 style.map("TLabel", background = [('background', GetBackground())])
282 style.map("TComboBox", background = [('readonly', GetBackground())])
284 app = ProjectWindow(root, sdkpath, args)
286 app.configure(background=GetBackground())
292 def RunWarning(message):
293 mb.showwarning('Raspberry Pi Pico Project Generator', message)
299 def thread_function(text, command, ok):
300 l = shlex.split(command)
301 proc = subprocess.Popen(l, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
302 for line in iter(proc.stdout.readline,''):
305 ok["state"] = tk.NORMAL
307 text.insert(tk.END, line)
310 # Function to run an OS command and display the output in a new modal window
312 class DisplayWindow(tk.Toplevel):
313 def __init__(self, parent, title):
314 tk.Toplevel.__init__(self, parent)
316 self.init_window(title)
318 def init_window(self, title):
321 frame = tk.Frame(self, borderwidth=5, relief=tk.RIDGE)
322 frame.pack(fill=tk.X, expand=True, side=tk.TOP)
324 scrollbar = tk.Scrollbar(frame)
325 self.text = tk.Text(frame, bg='gray14', fg='gray99')
326 scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
327 self.text.pack(side=tk.LEFT, fill=tk.Y)
328 scrollbar.config(command=self.text.yview)
329 self.text.config(yscrollcommand=scrollbar.set)
331 frame1 = tk.Frame(self, borderwidth=1)
332 frame1.pack(fill=tk.X, expand=True, side=tk.BOTTOM)
333 self.OKButton = ttk.Button(frame1, text="OK", command=self.OK)
334 self.OKButton["state"] = tk.DISABLED
338 self.transient(self.parent)
345 def RunCommandInWindow(parent, command):
346 w = DisplayWindow(parent, command)
347 x = threading.Thread(target=thread_function, args=(w.text, command, w.OKButton))
349 parent.wait_window(w)
351 class EditBoolWindow(sd.Dialog):
353 def __init__(self, parent, configitem, current):
355 self.config_item = configitem
356 self.current = current
357 sd.Dialog.__init__(self, parent, "Edit boolean configuration")
360 def body(self, master):
361 self.configure(background=GetBackground())
362 ttk.Label(self, text=self.config_item['name']).pack()
363 self.result = tk.StringVar()
364 self.result.set(self.current)
365 ttk.Radiobutton(master, text="True", variable=self.result, value="True").pack(anchor=tk.W)
366 ttk.Radiobutton(master, text="False", variable=self.result, value="False").pack(anchor=tk.W)
367 ttk.Radiobutton(master, text=CONFIG_UNSET, variable=self.result, value=CONFIG_UNSET).pack(anchor=tk.W)
370 return self.result.get()
372 class EditIntWindow(sd.Dialog):
374 def __init__(self, parent, configitem, current):
376 self.config_item = configitem
377 self.current = current
378 sd.Dialog.__init__(self, parent, "Edit integer configuration")
380 def body(self, master):
381 self.configure(background=GetBackground())
382 str = self.config_item['name'] + " Max = " + self.config_item['max'] + " Min = " + self.config_item['min']
383 ttk.Label(self, text=str).pack()
384 self.input = tk.Entry(self)
385 self.input.pack(pady=4)
386 self.input.insert(0, self.current)
387 ttk.Button(self, text=CONFIG_UNSET, command=self.unset).pack(pady=5)
390 self.result = self.input.get()
391 # Check for numeric entry
395 self.result = CONFIG_UNSET
401 class EditEnumWindow(sd.Dialog):
402 def __init__(self, parent, configitem, current):
404 self.config_item = configitem
405 self.current = current
406 sd.Dialog.__init__(self, parent, "Edit Enumeration configuration")
408 def body(self, master):
409 #self.configure(background=GetBackground())
410 values = self.config_item['enumvalues'].split('|')
411 values.insert(0,'Not set')
412 self.input = ttk.Combobox(self, values=values, state='readonly')
413 self.input.set(self.current)
414 self.input.pack(pady=12)
417 self.result = self.input.get()
424 class ConfigurationWindow(tk.Toplevel):
426 def __init__(self, parent, initial_config):
427 tk.Toplevel.__init__(self, parent)
429 self.results = initial_config
430 self.init_window(self)
432 def init_window(self, args):
433 self.configure(background=GetBackground())
434 self.title("Advanced Configuration")
435 ttk.Label(self, text="Select the advanced options you wish to enable or change. Note that you really should understand the implications of changing these items before using them!").grid(row=0, column=0, columnspan=5)
436 ttk.Label(self, text="Name").grid(row=1, column=0, sticky=tk.W)
437 ttk.Label(self, text="Type").grid(row=1, column=1, sticky=tk.W)
438 ttk.Label(self, text="Min").grid(row=1, column=2, sticky=tk.W)
439 ttk.Label(self, text="Max").grid(row=1, column=3, sticky=tk.W)
440 ttk.Label(self, text="Default").grid(row=1, column=4, sticky=tk.W)
441 ttk.Label(self, text="User").grid(row=1, column=5, sticky=tk.W)
443 okButton = ttk.Button(self, text="OK", command=self.ok)
444 cancelButton = ttk.Button(self, text="Cancel", command=self.cancel)
446 self.namelist = tk.Listbox(self, selectmode=tk.SINGLE)
447 self.typelist = tk.Listbox(self, selectmode=tk.SINGLE)
448 self.minlist = tk.Listbox(self, selectmode=tk.SINGLE)
449 self.maxlist = tk.Listbox(self, selectmode=tk.SINGLE)
450 self.defaultlist = tk.Listbox(self, selectmode=tk.SINGLE)
451 self.valuelist = tk.Listbox(self, selectmode=tk.SINGLE)
453 self.descriptionText = tk.Text(self, state=tk.DISABLED, height=2)
455 ## Make a list of our list boxes to make it all easier to handle
456 self.listlist = [self.namelist, self.typelist, self.minlist, self.maxlist, self.defaultlist, self.valuelist]
458 scroll = tk.Scrollbar(self, orient=tk.VERTICAL, command=self.yview)
460 for box in self.listlist:
462 box.config(yscrollcommand=scroll.set)
463 box.bind("<MouseWheel>", self.mousewheel)
464 box.bind("<Button-4>", self.mousewheel)
465 box.bind("<Button-5>", self.mousewheel)
466 box.bind("<<ListboxSelect>>", self.changeSelection)
467 box.bind("<Double-Button>", self.doubleClick)
468 box.config(exportselection=False)
469 box.bind("<Down>", self.OnEntryUpDown)
470 box.bind("<Up>", self.OnEntryUpDown)
472 scroll.grid(column=7, sticky=tk.N + tk.S)
475 for box in self.listlist:
476 box.grid(row=2, column=i, padx=0, sticky=tk.W + tk.E)
479 self.descriptionText.grid(row = 3, column=0, columnspan=4, sticky=tk.W + tk.E)
480 cancelButton.grid(column=5, row = 3, padx=5)
481 okButton.grid(column=4, row = 3, sticky=tk.E, padx=5)
483 # populate the list box with our config options
484 for conf in configuration_dictionary:
485 self.namelist.insert(tk.END, conf['name'])
489 self.typelist.insert(tk.END, s)
490 self.maxlist.insert(tk.END, conf['max'])
491 self.minlist.insert(tk.END, conf['min'])
492 self.defaultlist.insert(tk.END, conf['default'])
494 # see if this config has a setting, our results member has this predefined from init
495 val = self.results.get(conf['name'], CONFIG_UNSET)
496 self.valuelist.insert(tk.END, val)
497 if val != CONFIG_UNSET:
498 self.valuelist.itemconfig(self.valuelist.size() - 1, {'bg':'green'})
500 def yview(self, *args):
501 for box in self.listlist:
504 def mousewheel(self, event):
505 if (event.num == 4): # Linux encodes wheel as 'buttons' 4 and 5
507 elif (event.num == 5):
509 else: # Windows & OSX
512 for box in self.listlist:
513 box.yview("scroll", delta, "units")
516 def changeSelection(self, evt):
518 sellist = box.curselection()
521 index = int(sellist[0])
522 config = self.namelist.get(index)
523 # Now find the description for that config in the dictionary
524 for conf in configuration_dictionary:
525 if conf['name'] == config:
526 self.descriptionText.config(state=tk.NORMAL)
527 self.descriptionText.delete(1.0,tk.END)
528 str = config + "\n" + conf['description']
529 self.descriptionText.insert(1.0, str)
530 self.descriptionText.config(state=tk.DISABLED)
532 # Set all the other list boxes to the same index
533 for b in self.listlist:
535 b.selection_clear(0, tk.END)
536 b.selection_set(index)
538 def OnEntryUpDown(self, event):
540 selection = box.curselection()
543 index = int(selection[0])
544 if event.keysym == 'Up':
546 elif event.keysym == 'Down':
549 if 0 <= index < box.size():
550 for b in self.listlist:
551 b.selection_clear(0, tk.END)
552 b.selection_set(index)
555 def doubleClick(self, evt):
557 index = int(box.curselection()[0])
558 config = self.namelist.get(index)
559 # Get the associated dict entry from our list of configs
560 for conf in configuration_dictionary:
561 if conf['name'] == config:
562 if (conf['type'] == 'bool'):
563 result = EditBoolWindow(self, conf, self.valuelist.get(index)).get()
564 elif (conf['type'] == 'int' or conf['type'] == ""): # "" defaults to int
565 result = EditIntWindow(self, conf, self.valuelist.get(index)).get()
566 elif conf['type'] == 'enum':
567 result = EditEnumWindow(self, conf, self.valuelist.get(index)).get()
569 # Update the valuelist with our new item
570 self.valuelist.delete(index)
571 self.valuelist.insert(index, result)
572 if result != CONFIG_UNSET:
573 self.valuelist.itemconfig(index, {'bg':'green'})
577 # Get the selections, and create a list of them
578 for i, val in enumerate(self.valuelist.get(0, tk.END)):
579 if val != CONFIG_UNSET:
580 self.results[self.namelist.get(i)] = val
582 self.results.pop(self.namelist.get(i), None)
592 class WirelessSettingsWindow(sd.Dialog):
594 def __init__(self, parent):
595 sd.Dialog.__init__(self, parent, "Wireless settings")
598 def body(self, master):
599 self.configure(background=GetBackground())
600 master.configure(background=GetBackground())
601 self.ssid = tk.StringVar()
602 self.password = tk.StringVar()
604 a = ttk.Label(master, text='SSID :', background=GetBackground())
605 a.grid(row=0, column=0, sticky=tk.E)
606 a.configure(background=GetBackground())
607 ttk.Entry(master, textvariable=self.ssid).grid(row=0, column=1, sticky=tk.W+tk.E, padx=5)
609 ttk.Label(master, text='Password :').grid(row=1, column=0, sticky=tk.E)
610 ttk.Entry(master, textvariable=self.password).grid(row=1, column=1, sticky=tk.W+tk.E, padx=5)
612 self.transient(self.parent)
623 return (self.ssid.get(), self.password.get())
626 class ProjectWindow(tk.Frame):
628 def __init__(self, parent, sdkpath, args):
629 tk.Frame.__init__(self, parent)
631 self.sdkpath = sdkpath
632 self.init_window(args)
633 self.configs = dict()
635 self.password = str()
637 def setState(self, thing, state):
638 for child in thing.winfo_children():
639 child.configure(state=state)
641 def boardtype_change_callback(self, event):
642 boardtype = self.boardtype.get()
643 if boardtype == "pico_w":
644 self.setState(self.picowSubframe, "enabled")
646 self.setState(self.picowSubframe, "disabled")
648 def wirelessSettings(self):
649 result = WirelessSettingsWindow(self)
650 self.ssid, self.password = result.get()
652 def init_window(self, args):
653 self.master.title("Raspberry Pi Pico Project Generator")
654 self.master.configure(bg=GetBackground())
658 mainFrame = tk.Frame(self, bg=GetBackground()).grid(row=optionsRow, column=0, columnspan=6, rowspan=12)
660 # Need to keep a reference to the image or it will not appear.
661 self.logo = tk.PhotoImage(file=GetFilePath("logo_alpha.gif"))
662 logowidget = ttk.Label(mainFrame, image=self.logo, borderwidth=0, relief="solid").grid(row=0,column=0, columnspan=5, pady=10)
666 namelbl = ttk.Label(mainFrame, text='Project Name :').grid(row=optionsRow, column=0, sticky=tk.E)
667 self.projectName = tk.StringVar()
669 if args.name != None:
670 self.projectName.set(args.name)
672 self.projectName.set('ProjectName')
674 nameEntry = ttk.Entry(mainFrame, textvariable=self.projectName).grid(row=optionsRow, column=1, sticky=tk.W+tk.E, padx=5)
678 locationlbl = ttk.Label(mainFrame, text='Location :').grid(row=optionsRow, column=0, sticky=tk.E)
679 self.locationName = tk.StringVar()
680 self.locationName.set(os.getcwd())
681 locationEntry = ttk.Entry(mainFrame, textvariable=self.locationName).grid(row=optionsRow, column=1, columnspan=3, sticky=tk.W+tk.E, padx=5)
682 locationBrowse = ttk.Button(mainFrame, text='Browse', command=self.browse).grid(row=3, column=4)
686 ttk.Label(mainFrame, text = "Board Type :").grid(row=optionsRow, column=0, padx=4, sticky=tk.E)
687 self.boardtype = ttk.Combobox(mainFrame, values=boardtype_list, )
688 self.boardtype.grid(row=4, column=1, padx=4, sticky=tk.W+tk.E)
689 self.boardtype.set('pico')
690 self.boardtype.bind('<<ComboboxSelected>>',self.boardtype_change_callback)
694 featuresframe = ttk.LabelFrame(mainFrame, text="Library Options", relief=tk.RIDGE, borderwidth=2)
695 featuresframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=5, ipadx=5, padx=5, pady=5, sticky=tk.E+tk.W)
697 s = (len(features_list)/3)
699 self.feature_checkbox_vars = []
702 for i in features_list:
703 var = tk.StringVar(value='') # Off by default for the moment
704 c = features_list[i][GUI_TEXT]
705 cb = ttk.Checkbutton(featuresframe, text = c, var=var, onvalue=i, offvalue='')
706 cb.grid(row=row, column=col, padx=15, pady=2, ipadx=1, ipady=1, sticky=tk.E+tk.W)
707 self.feature_checkbox_vars.append(var)
715 # PicoW options section
716 self.picowSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Pico Wireless Options")
717 self.picowSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
718 self.pico_wireless = tk.StringVar()
722 for i in picow_options_list:
723 rb = ttk.Radiobutton(self.picowSubframe, text=picow_options_list[i][GUI_TEXT], variable=self.pico_wireless, val=i)
724 rb.grid(row=row, column=col, padx=15, pady=1, sticky=tk.E+tk.W)
730 # DOnt actually need any settings at the moment.
731 # ttk.Button(self.picowSubframe, text='Settings', command=self.wirelessSettings).grid(row=0, column=4, padx=5, pady=2, sticky=tk.E)
733 self.setState(self.picowSubframe, "disabled")
737 # output options section
738 ooptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Console Options")
739 ooptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
741 self.wantUART = tk.IntVar()
742 self.wantUART.set(args.uart)
743 ttk.Checkbutton(ooptionsSubframe, text="Console over UART", variable=self.wantUART).grid(row=0, column=0, padx=4, sticky=tk.W)
745 self.wantUSB = tk.IntVar()
746 self.wantUSB.set(args.usb)
747 ttk.Checkbutton(ooptionsSubframe, text="Console over USB (Disables other USB use)", variable=self.wantUSB).grid(row=0, column=1, padx=4, sticky=tk.W)
751 # Code options section
752 coptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Code Options")
753 coptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=3, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
755 self.wantExamples = tk.IntVar()
756 self.wantExamples.set(args.examples)
757 ttk.Checkbutton(coptionsSubframe, text="Add examples for Pico library", variable=self.wantExamples).grid(row=0, column=0, padx=4, sticky=tk.W)
759 self.wantRunFromRAM = tk.IntVar()
760 self.wantRunFromRAM.set(args.runFromRAM)
761 ttk.Checkbutton(coptionsSubframe, text="Run from RAM", variable=self.wantRunFromRAM).grid(row=0, column=1, padx=4, sticky=tk.W)
763 self.wantCPP = tk.IntVar()
764 self.wantCPP.set(args.cpp)
765 ttk.Checkbutton(coptionsSubframe, text="Generate C++", variable=self.wantCPP).grid(row=0, column=3, padx=4, sticky=tk.W)
767 ttk.Button(coptionsSubframe, text="Advanced...", command=self.config).grid(row=0, column=4, sticky=tk.E)
769 self.wantCPPExceptions = tk.IntVar()
770 self.wantCPPExceptions.set(args.cppexceptions)
771 ttk.Checkbutton(coptionsSubframe, text="Enable C++ exceptions", variable=self.wantCPPExceptions).grid(row=1, column=0, padx=4, sticky=tk.W)
773 self.wantCPPRTTI = tk.IntVar()
774 self.wantCPPRTTI.set(args.cpprtti)
775 ttk.Checkbutton(coptionsSubframe, text="Enable C++ RTTI", variable=self.wantCPPRTTI).grid(row=1, column=1, padx=4, sticky=tk.W)
779 # Build Options section
781 boptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Build Options")
782 boptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
784 self.wantBuild = tk.IntVar()
785 self.wantBuild.set(args.build)
786 ttk.Checkbutton(boptionsSubframe, text="Run build after generation", variable=self.wantBuild).grid(row=0, column=0, padx=4, sticky=tk.W)
788 self.wantOverwrite = tk.IntVar()
789 self.wantOverwrite.set(args.overwrite)
790 ttk.Checkbutton(boptionsSubframe, text="Overwrite existing projects", variable=self.wantOverwrite).grid(row=0, column=1, padx=4, sticky=tk.W)
794 # IDE Options section
796 vscodeoptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="IDE Options")
797 vscodeoptionsSubframe.grid_columnconfigure(2, weight=1)
798 vscodeoptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
800 self.wantVSCode = tk.IntVar()
801 if args.project is None:
802 self.wantVSCode.set(False)
804 self.wantVSCode.set('vscode' in args.project)
805 ttk.Checkbutton(vscodeoptionsSubframe, text="Create VSCode project", variable=self.wantVSCode).grid(row=0, column=0, padx=4, sticky=tk.W)
807 ttk.Label(vscodeoptionsSubframe, text = " Debugger:").grid(row=0, column=1, padx=4, sticky=tk.W)
809 self.debugger = ttk.Combobox(vscodeoptionsSubframe, values=debugger_list, state="readonly")
810 self.debugger.grid(row=0, column=2, padx=4, sticky=tk.EW)
811 self.debugger.current(args.debugger)
815 # OK, Cancel, Help section
817 QuitButton = ttk.Button(mainFrame, text="Quit", command=self.quit).grid(row=optionsRow, column=4, stick=tk.E, padx=10, pady=5)
818 OKButton = ttk.Button(mainFrame, text="OK", command=self.OK).grid(row=optionsRow, column=3, padx=4, pady=5, sticky=tk.E)
820 # TODO help not implemented yet
821 # HelpButton = ttk.Button(mainFrame, text="Help", command=self.help).grid(row=optionsRow, column=0, pady=5)
823 # You can set a default path here, replace the string with whereever you want.
824 # self.locationName.set('/home/pi/pico_projects')
826 def GetFeatures(self):
830 for cb in self.feature_checkbox_vars:
835 picow_extra = self.pico_wireless.get()
837 if picow_extra != 'picow_none':
838 features.append(picow_extra)
843 # TODO Check if we want to exit here
847 # OK, grab all the settings from the page, then call the generators
848 projectPath = self.locationName.get()
849 features = self.GetFeatures()
851 if (self.wantVSCode.get()):
852 projects.append("vscode")
855 'sdkPath' : self.sdkpath,
856 'projectRoot' : Path(projectPath),
857 'projectName' : self.projectName.get(),
859 'wantOverwrite' : self.wantOverwrite.get(),
860 'wantBuild' : self.wantBuild.get(),
861 'boardtype' : self.boardtype.get(),
862 'features' : features,
863 'projects' : projects,
864 'configs' : self.configs,
865 'wantRunFromRAM': self.wantRunFromRAM.get(),
866 'wantExamples' : self.wantExamples.get(),
867 'wantUART' : self.wantUART.get(),
868 'wantUSB' : self.wantUSB.get(),
869 'wantCPP' : self.wantCPP.get(),
870 'debugger' : self.debugger.current(),
871 'exceptions' : self.wantCPPExceptions.get(),
872 'rtti' : self.wantCPPRTTI.get(),
874 'password' : self.password,
877 DoEverything(self, params)
880 name = fd.askdirectory()
881 self.locationName.set(name)
887 # Run the configuration window
888 self.configs = ConfigurationWindow(self, self.configs).get()
890 def CheckPrerequisites():
891 global isMac, isWindows
892 isMac = (platform.system() == 'Darwin')
893 isWindows = (platform.system() == 'Windows')
895 # Do we have a compiler?
896 return shutil.which(COMPILER_NAME, 1, os.environ["Path" if isWindows else "PATH"])
899 def CheckSDKPath(gui):
900 sdkPath = os.getenv('PICO_SDK_PATH')
903 m = 'Unable to locate the Raspberry Pi Pico SDK, PICO_SDK_PATH is not set'
904 if gui and ENABLE_TK_GUI:
908 elif not os.path.isdir(sdkPath):
909 m = 'Unable to locate the Raspberry Pi Pico SDK, PICO_SDK_PATH does not point to a directory'
910 if gui and ENABLE_TK_GUI:
918 def GetFilePath(filename):
919 if os.path.islink(__file__):
920 script_file = os.readlink(__file__)
922 script_file = __file__
923 return os.path.join(os.path.dirname(script_file), filename)
925 def ParseCommandLine():
926 debugger_flags = ', '.join('{} = {}'.format(i, v) for i, v in enumerate(debugger_list))
927 parser = argparse.ArgumentParser(description='Pico Project generator')
928 parser.add_argument("name", nargs="?", help="Name of the project")
929 parser.add_argument("-t", "--tsv", help="Select an alternative pico_configs.tsv file", default=GetFilePath("pico_configs.tsv"))
930 parser.add_argument("-o", "--output", help="Set an alternative CMakeList.txt filename", default="CMakeLists.txt")
931 parser.add_argument("-x", "--examples", action='store_true', help="Add example code for the Pico standard library")
932 parser.add_argument("-l", "--list", action='store_true', help="List available features")
933 parser.add_argument("-c", "--configs", action='store_true', help="List available project configuration items")
934 parser.add_argument("-f", "--feature", action='append', help="Add feature to generated project")
935 parser.add_argument("-over", "--overwrite", action='store_true', help="Overwrite any existing project AND files")
936 parser.add_argument("-b", "--build", action='store_true', help="Build after project created")
937 parser.add_argument("-g", "--gui", action='store_true', help="Run a GUI version of the project generator")
938 parser.add_argument("-p", "--project", action='append', help="Generate projects files for IDE. Options are: vscode")
939 parser.add_argument("-r", "--runFromRAM", action='store_true', help="Run the program from RAM rather than flash")
940 parser.add_argument("-uart", "--uart", action='store_true', default=1, help="Console output to UART (default)")
941 parser.add_argument("-nouart", "--nouart", action='store_true', default=0, help="Disable console output to UART")
942 parser.add_argument("-usb", "--usb", action='store_true', help="Console output to USB (disables other USB functionality")
943 parser.add_argument("-cpp", "--cpp", action='store_true', default=0, help="Generate C++ code")
944 parser.add_argument("-cpprtti", "--cpprtti", action='store_true', default=0, help="Enable C++ RTTI (Uses more memory)")
945 parser.add_argument("-cppex", "--cppexceptions", action='store_true', default=0, help="Enable C++ exceptions (Uses more memory)")
946 parser.add_argument("-d", "--debugger", type=int, help="Select debugger ({})".format(debugger_flags), default=0)
947 parser.add_argument("-board", "--boardtype", action="store", default='pico', help="Select board type (see --boardlist for available boards)")
948 parser.add_argument("-bl", "--boardlist", action="store_true", help="List available board types")
949 parser.add_argument("-cp", "--cpath", help="Override default VSCode compiler path")
950 parser.add_argument("-root", "--projectRoot", help="Override default project root where the new project will be created")
951 parser.add_argument("-sdkVersion", "--sdkVersion", help="Pico SDK version to use (required)")
952 parser.add_argument("-tcVersion", "--toolchainVersion", help="ARM Embeded Toolchain version to use (required)")
954 return parser.parse_args()
957 def GenerateMain(folder, projectName, features, cpp):
960 filename = Path(folder) / (projectName + '.cpp')
962 filename = Path(folder) / (projectName + '.c')
964 file = open(filename, 'w')
966 main = ('#include <stdio.h>\n'
967 '#include "pico/stdlib.h"\n'
974 for feat in features:
975 if (feat in features_list):
976 o = f'#include "{features_list[feat][H_FILE]}"\n'
978 if (feat in stdlib_examples_list):
979 o = f'#include "{stdlib_examples_list[feat][H_FILE]}"\n'
981 if (feat in picow_options_list):
982 o = f'#include "{picow_options_list[feat][H_FILE]}"\n'
988 for feat in features:
989 if (feat in code_fragments_per_feature):
990 for s in code_fragments_per_feature[feat][DEFINES]:
998 ' stdio_init_all();\n\n'
1002 # Add any initialisers
1004 for feat in features:
1005 if (feat in code_fragments_per_feature):
1006 for s in code_fragments_per_feature[feat][INITIALISERS]:
1007 main += (" " * indent)
1012 main += (' puts("Hello, world!");\n\n'
1022 def GenerateCMake(folder, params):
1024 filename = Path(folder) / CMAKELIST_FILENAME
1025 projectName = params['projectName']
1026 board_type = params['boardtype']
1028 # OK, for the path, CMake will accept forward slashes on Windows, and thats
1029 # seemingly a bit easier to handle than the backslashes
1030 p = str(params['sdkPath']).replace('\\','/')
1032 cmake_header1 = (f"# Generated Cmake Pico project file\n\n"
1033 "cmake_minimum_required(VERSION 3.13)\n\n"
1034 "set(CMAKE_C_STANDARD 11)\n"
1035 "set(CMAKE_CXX_STANDARD 17)\n\n"
1036 "# Initialise pico_sdk from installed location\n"
1037 "# (note this can come from environment, CMake cache etc)\n\n"
1038 "# == DO NEVER EDIT THE NEXT LINES for RaspberryPiPico VS Code Extension to work == \n"
1040 " set(USERHOME $ENV{USERPROFILE})\n"
1042 " set(USERHOME $ENV{HOME})\n"
1044 f"set(PICO_SDK_PATH {cmakeSdkPath(params['sdkVersion'])})\n"
1045 f"set(PICO_TOOLCHAIN_PATH {cmakeToolchainPath(params['toolchainVersion'])})\n"
1046 "# ====================================================================================\n"
1047 f"set(PICO_BOARD {board_type} CACHE STRING \"Board type\")\n\n"
1048 "# Pull in Raspberry Pi Pico SDK (must be before project)\n"
1049 "include(pico_sdk_import.cmake)\n\n"
1050 "if (PICO_SDK_VERSION_STRING VERSION_LESS \"1.4.0\")\n"
1051 " message(FATAL_ERROR \"Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}\")\n"
1053 f"project({projectName} C CXX ASM)\n"
1057 "\n# Initialise the Raspberry Pi Pico SDK\n"
1058 "pico_sdk_init()\n\n"
1059 "# Add executable. Default name is the project name, version 0.1\n\n"
1063 file = open(filename, 'w')
1065 file.write(cmake_header1)
1067 if params['exceptions']:
1068 file.write("\nset(PICO_CXX_ENABLE_EXCEPTIONS 1)\n")
1071 file.write("\nset(PICO_CXX_ENABLE_RTTI 1)\n")
1073 file.write(cmake_header3)
1075 # add the preprocessor defines for overall configuration
1076 if params['configs']:
1077 file.write('# Add any PICO_CONFIG entries specified in the Advanced settings\n')
1078 for c, v in params['configs'].items():
1083 file.write(f'add_compile_definitions({c} = {v})\n')
1086 # No GUI/command line to set a different executable name at this stage
1087 executableName = projectName
1089 if params['wantCPP']:
1090 file.write(f'add_executable({projectName} {projectName}.cpp )\n\n')
1092 file.write(f'add_executable({projectName} {projectName}.c )\n\n')
1094 file.write(f'pico_set_program_name({projectName} "{executableName}")\n')
1095 file.write(f'pico_set_program_version({projectName} "0.1")\n\n')
1097 if params['wantRunFromRAM']:
1098 file.write(f'# no_flash means the target is to run from RAM\n')
1099 file.write(f'pico_set_binary_type({projectName} no_flash)\n\n')
1101 # Console output destinations
1102 if params['wantUART']:
1103 file.write(f'pico_enable_stdio_uart({projectName} 1)\n')
1105 file.write(f'pico_enable_stdio_uart({projectName} 0)\n')
1107 if params['wantUSB']:
1108 file.write(f'pico_enable_stdio_usb({projectName} 1)\n\n')
1110 file.write(f'pico_enable_stdio_usb({projectName} 0)\n\n')
1112 # If we need wireless, check for SSID and password
1113 # removed for the moment as these settings are currently only needed for the pico-examples
1114 # but may be required in here at a later date.
1116 if 'ssid' in params or 'password' in params:
1117 file.write('# Add any wireless access point information\n')
1118 file.write(f'target_compile_definitions({projectName} PRIVATE\n')
1119 if 'ssid' in params:
1120 file.write(f'WIFI_SSID=\" {params["ssid"]} \"\n')
1122 file.write(f'WIFI_SSID=\"${WIFI_SSID}\"')
1124 if 'password' in params:
1125 file.write(f'WIFI_PASSWORD=\"{params["password"]}\"\n')
1127 file.write(f'WIFI_PASSWORD=\"${WIFI_PASSWORD}\"')
1130 # Standard libraries
1131 file.write('# Add the standard library to the build\n')
1132 file.write(f'target_link_libraries({projectName}\n')
1133 file.write(" " + STANDARD_LIBRARIES)
1136 # Standard include directories
1137 file.write('# Add the standard include files to the build\n')
1138 file.write(f'target_include_directories({projectName} PRIVATE\n')
1139 file.write(" ${CMAKE_CURRENT_LIST_DIR}\n")
1140 file.write(" ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required\n")
1143 # Selected libraries/features
1144 if (params['features']):
1145 file.write('# Add any user requested libraries\n')
1146 file.write(f'target_link_libraries({projectName} \n')
1147 for feat in params['features']:
1148 if (feat in features_list):
1149 file.write(" " + features_list[feat][LIB_NAME] + '\n')
1150 if (feat in picow_options_list):
1151 file.write(" " + picow_options_list[feat][LIB_NAME] + '\n')
1152 file.write(' )\n\n')
1154 file.write(f'pico_add_extra_outputs({projectName})\n\n')
1159 # Generates the requested project files, if any
1160 def generateProjectFiles(projectPath, projectName, sdkPath, projects, debugger, sdkVersion, toolchainVersion):
1162 oldCWD = os.getcwd()
1164 os.chdir(projectPath)
1166 debugger = debugger_config_list[debugger]
1167 gdbPath = Path(codeToolchainPath(toolchainVersion)+"/bin/arm-none-eabi-gdb") if isWindows else "gdb-multiarch"
1168 # Need to escape windows files paths backslashes
1169 # TODO: env in currently not supported in compilerPath var
1170 #cPath = f"${{env:PICO_TOOLCHAIN_PATH_{envSuffix}}}" + os.path.sep + os.path.basename(str(compilerPath).replace('\\', '\\\\' ))
1171 cPath = str(compilerPath).replace('\\', '\\\\' )
1179 "name": "Pico Debug (Cortex-Debug)",
1180 "cwd": "${{workspaceRoot}}",
1181 "executable": "${{command:raspberry-pi-pico.launchTargetPath}}",
1182 "request": "launch",
1183 "type": "cortex-debug",
1184 "servertype": "openocd",
1185 "gdbPath": "{gdbPath}",
1188 "interface/{debugger}",
1191 "svdFile": "{codeSdkPath(sdkVersion)}/src/rp2040/hardware_regs/rp2040.svd",
1192 "runToEntryPoint": "main",
1193 // Give restart the same functionality as runToEntryPoint - main
1194 "postRestartCommands": [
1198 "openOCDLaunchCommands": [
1199 "adapter speed 1000"
1201 "preLaunchTask": "Compile Project"
1204 "name": "Pico Debug (Cortex-Debug with external OpenOCD)",
1205 "cwd": "${{workspaceRoot}}",
1206 "executable": "${{command:raspberry-pi-pico.launchTargetPath}}",
1207 "request": "launch",
1208 "type": "cortex-debug",
1209 "servertype": "external",
1210 "gdbTarget": "localhost:3333",
1211 "gdbPath": "{gdbPath}",
1213 "svdFile": "{codeSdkPath(sdkVersion)}/src/rp2040/hardware_regs/rp2040.svd",
1214 "runToEntryPoint": "main",
1215 // Give restart the same functionality as runToEntryPoint - main
1216 "postRestartCommands": [
1220 "preLaunchTask": "Compile Project"
1223 "name": "Pico Debug (C++ Debugger)",
1225 "request": "launch",
1226 "cwd": "${{workspaceRoot}}",
1227 "program": "${{command:raspberry-pi-pico.launchTargetPath}}",
1229 "miDebuggerPath": "{gdbPath}",
1230 "miDebuggerServerAddress": "localhost:3333",
1231 "debugServerPath": "openocd",
1232 "debugServerArgs": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \\"adapter speed 1000\\"",
1233 "serverStarted": "Listening on port .* for gdb connections",
1234 "filterStderr": true,
1235 "stopAtEntry": true,
1236 "hardwareBreakpoints": {{
1240 "preLaunchTask": "Flash",
1241 "svdPath": "{codeSdkPath(sdkVersion)}/src/rp2040/hardware_regs/rp2040.svd"
1252 "${{workspaceFolder}}/**",
1253 "{codeSdkPath(sdkVersion)}/**"
1256 "compilerPath": "{cPath}",
1258 "cppStandard": "c++14",
1259 "intelliSenseMode": "linux-gcc-arm"
1269 "cmake.statusbar.visibility": "hidden",
1270 "cmake.configureOnEdit": false,
1271 "cmake.automaticReconfigure": false,
1272 "cmake.configureOnOpen": false,
1273 "cmake.generator": "Ninja",
1274 "raspberry-pi-pico.cmakeAutoConfigure": true,
1280 "recommendations": [
1281 "marus25.cortex-debug",
1282 "ms-vscode.cpptools",
1283 "ms-vscode.cpptools-extension-pack",
1284 "ms-vscode.vscode-serial-monitor",
1285 "paulober.raspberry-pi-pico",
1293 "label": "Compile Project",
1296 "args": ["-C", "${{workspaceFolder}}/build"],
1300 "panel": "dedicated"
1302 "problemMatcher": "$gcc"
1308 # Create a build folder, and run our cmake project build from it
1309 if not os.path.exists(VSCODE_FOLDER):
1310 os.mkdir(VSCODE_FOLDER)
1312 os.chdir(VSCODE_FOLDER)
1314 file = open(VSCODE_TASKS_FILENAME, 'w')
1318 filename = VSCODE_LAUNCH_FILENAME
1319 file = open(filename, 'w')
1323 file = open(VSCODE_C_PROPERTIES_FILENAME, 'w')
1324 file.write(properties)
1327 file = open(VSCODE_SETTINGS_FILENAME, 'w')
1328 file.write(settings)
1331 file = open(VSCODE_EXTENSIONS_FILENAME, 'w')
1332 file.write(extensions)
1336 print('Unknown project type requested')
1341 def LoadConfigurations():
1343 with open(args.tsv) as tsvfile:
1344 reader = csv.DictReader(tsvfile, dialect='excel-tab')
1346 configuration_dictionary.append(row)
1348 print("No Pico configurations file found. Continuing without")
1350 def LoadBoardTypes(sdkPath):
1351 # Scan the boards folder for all header files, extract filenames, and make a list of the results
1352 # default folder is <PICO_SDK_PATH>/src/boards/include/boards/*
1353 # If the PICO_BOARD_HEADER_DIRS environment variable is set, use that as well
1355 loc = sdkPath / "src/boards/include/boards"
1357 for x in Path(loc).iterdir():
1358 if x.suffix == '.h':
1359 boards.append(x.stem)
1361 loc = os.getenv('PICO_BOARD_HEADER_DIRS')
1364 for x in Path(loc).iterdir():
1365 if x.suffix == '.h':
1366 boards.append(x.stem)
1370 def DoEverything(parent, params):
1372 if not os.path.exists(params['projectRoot']):
1373 if params['wantGUI'] and ENABLE_TK_GUI:
1374 mb.showerror('Raspberry Pi Pico Project Generator', 'Invalid project path. Select a valid path and try again')
1377 print('Invalid project path')
1380 oldCWD = os.getcwd()
1381 os.chdir(params['projectRoot'])
1383 # Create our project folder as subfolder
1384 os.makedirs(params['projectName'], exist_ok=True)
1386 os.chdir(params['projectName'])
1388 projectPath = params['projectRoot'] / params['projectName']
1390 # First check if there is already a project in the folder
1391 # If there is we abort unless the overwrite flag it set
1392 if os.path.exists(CMAKELIST_FILENAME):
1393 if not params['wantOverwrite'] :
1394 if params['wantGUI'] and ENABLE_TK_GUI:
1395 # We can ask the user if they want to overwrite
1396 y = mb.askquestion('Raspberry Pi Pico Project Generator', 'There already appears to be a project in this folder. \nPress Yes to overwrite project files, or Cancel to chose another folder')
1400 print('There already appears to be a project in this folder. Use the --overwrite option to overwrite the existing project')
1403 # We should really confirm the user wants to overwrite
1404 #print('Are you sure you want to overwrite the existing project files? (y/N)')
1405 #c = input().split(" ")[0]
1406 #if c != 'y' and c != 'Y' :
1409 # Copy the SDK finder cmake file to our project folder
1410 # Can be found here <PICO_SDK_PATH>/external/pico_sdk_import.cmake
1411 shutil.copyfile(params['sdkPath'] / 'external' / 'pico_sdk_import.cmake', projectPath / 'pico_sdk_import.cmake' )
1413 if params['features']:
1414 features_and_examples = params['features'][:]
1416 features_and_examples= []
1418 if params['wantExamples']:
1419 features_and_examples = list(stdlib_examples_list.keys()) + features_and_examples
1421 GenerateMain('.', params['projectName'], features_and_examples, params['wantCPP'])
1423 GenerateCMake('.', params)
1425 # If we have any ancilliary files, copy them to our project folder
1426 # Currently only the picow with lwIP support needs an extra file, so just check that list
1427 for feat in features_and_examples:
1428 if feat in picow_options_list:
1429 if picow_options_list[feat][ANCILLARY_FILE] != "":
1430 shutil.copy(sourcefolder + "/" + picow_options_list[feat][ANCILLARY_FILE], projectPath / picow_options_list[feat][ANCILLARY_FILE])
1432 # Create a build folder, and run our cmake project build from it
1433 if not os.path.exists('build'):
1438 # If we are overwriting a previous project, we should probably clear the folder, but that might delete something the users thinks is important, so
1439 # for the moment, just delete the CMakeCache.txt file as certain changes may need that to be recreated.
1441 if os.path.exists(CMAKECACHE_FILENAME):
1442 os.remove(CMAKECACHE_FILENAME)
1444 cpus = os.cpu_count()
1449 # Had a special case report, when using MinGW, need to check if using nmake or mingw32-make.
1450 if shutil.which("mingw32-make"):
1451 # Assume MinGW environment
1452 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G "MinGW Makefiles" ..'
1453 makeCmd = 'mingw32-make '
1454 elif shutil.which("ninja"):
1455 # When installing SDK version 1.5.0 on windows with installer pico-setup-windows-x64-standalone.exe, ninja is used
1456 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja ..'
1459 # Everything else assume nmake
1460 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G "NMake Makefiles" ..'
1463 # Ninja now works OK under Linux, so if installed use it by default. It's faster.
1464 if shutil.which("ninja"):
1465 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja ..'
1468 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug ..'
1469 makeCmd = 'make -j' + str(cpus)
1471 if params['wantGUI'] and ENABLE_TK_GUI:
1472 RunCommandInWindow(parent, cmakeCmd)
1476 if params['projects']:
1477 generateProjectFiles(projectPath, params['projectName'], params['sdkPath'], params['projects'], params['debugger'], params["sdkVersion"], params["toolchainVersion"])
1479 if params['wantBuild']:
1480 if params['wantGUI'] and ENABLE_TK_GUI:
1481 RunCommandInWindow(parent, makeCmd)
1484 print('\nIf the application has built correctly, you can now transfer it to the Raspberry Pi Pico board')
1489 ###################################################################################
1490 # main execution starteth here
1492 sourcefolder = os.path.dirname(os.path.abspath(__file__))
1494 args = ParseCommandLine()
1499 if args.debugger > len(debugger_list) - 1:
1502 # Check we have everything we need to compile etc
1503 c = CheckPrerequisites()
1505 ## TODO Do both warnings in the same error message so user does have to keep coming back to find still more to do
1508 m = f'Unable to find the `{COMPILER_NAME}` compiler\n'
1509 m +='You will need to install an appropriate compiler to build a Raspberry Pi Pico project\n'
1510 m += 'See the Raspberry Pi Pico documentation for how to do this on your particular platform\n'
1512 if args.gui and ENABLE_TK_GUI:
1518 if args.name == None and not args.gui and not args.list and not args.configs and not args.boardlist:
1519 print("No project name specfied\n")
1522 # Check if we were provided a compiler path, and override the default if so
1524 compilerPath = Path(args.cpath)
1525 elif args.toolchainVersion:
1526 compilerPath = Path(codeToolchainPath(args.toolchainVersion)+"/bin/"+COMPILER_NAME)
1528 compilerPath = Path(c)
1530 # load/parse any configuration dictionary we may have
1531 LoadConfigurations()
1533 p = CheckSDKPath(args.gui)
1540 boardtype_list = LoadBoardTypes(sdkPath)
1541 boardtype_list.sort()
1543 if args.gui and ENABLE_TK_GUI:
1544 RunGUI(sdkPath, args) # does not return, only exits
1546 projectRoot = Path(os.getcwd()) if not args.projectRoot else Path(args.projectRoot)
1548 if args.list or args.configs or args.boardlist:
1550 print("Available project features:\n")
1551 for feat in features_list:
1552 print(feat.ljust(6), '\t', features_list[feat][GUI_TEXT])
1556 print("Available project configuration items:\n")
1557 for conf in configuration_dictionary:
1558 print(conf['name'].ljust(40), '\t', conf['description'])
1562 print("Available board types:\n")
1563 for board in boardtype_list:
1570 'sdkPath' : sdkPath,
1571 'projectRoot' : projectRoot,
1572 'projectName' : args.name,
1574 'wantOverwrite' : args.overwrite,
1575 'boardtype' : args.boardtype,
1576 'wantBuild' : args.build,
1577 'features' : args.feature,
1578 'projects' : args.project,
1580 'wantRunFromRAM': args.runFromRAM,
1581 'wantExamples' : args.examples,
1582 'wantUART' : args.uart,
1583 'wantUSB' : args.usb,
1584 'wantCPP' : args.cpp,
1585 'debugger' : args.debugger,
1586 'exceptions' : args.cppexceptions,
1587 'rtti' : args.cpprtti,
1590 'sdkVersion' : args.sdkVersion,
1591 'toolchainVersion': args.toolchainVersion,
1594 DoEverything(None, params)