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"$ENV{{HOME}}{relativeSDKPath(sdkVersion)}"
236 def cmakeToolchainPath(toolchainVersion):
237 return f"$ENV{{HOME}}{relativeToolchainPath(toolchainVersion)}"
239 def codeSdkPath(sdkVersion):
240 return f"${{env:HOME}}{relativeSDKPath(sdkVersion)}"
242 def codeToolchainPath(toolchainVersion):
243 return f"${{env:HOME}}{relativeToolchainPath(toolchainVersion)}"
248 def GetButtonBackground():
254 def GetButtonTextColour():
258 def RunGUI(sdkpath, args):
260 style = ttk.Style(root)
261 style.theme_use('default')
263 style.configure("TButton", padding=6, relief="groove", border=2, foreground=GetButtonTextColour(), background=GetButtonBackground())
264 style.configure("TLabel", foreground=GetTextColour(), background=GetBackground() )
265 style.configure("TCheckbutton", foreground=GetTextColour(), background=GetBackground())
266 style.configure("TRadiobutton", foreground=GetTextColour(), background=GetBackground() )
267 style.configure("TLabelframe", foreground=GetTextColour(), background=GetBackground() )
268 style.configure("TLabelframe.Label", foreground=GetTextColour(), background=GetBackground() )
269 style.configure("TCombobox", foreground=GetTextColour(), background=GetBackground() )
270 style.configure("TListbox", foreground=GetTextColour(), background=GetBackground() )
272 style.map("TCheckbutton", background = [('disabled', GetBackground())])
273 style.map("TRadiobutton", background = [('disabled', GetBackground())])
274 style.map("TButton", background = [('disabled', GetBackground())])
275 style.map("TLabel", background = [('background', GetBackground())])
276 style.map("TComboBox", background = [('readonly', GetBackground())])
278 app = ProjectWindow(root, sdkpath, args)
280 app.configure(background=GetBackground())
286 def RunWarning(message):
287 mb.showwarning('Raspberry Pi Pico Project Generator', message)
293 def thread_function(text, command, ok):
294 l = shlex.split(command)
295 proc = subprocess.Popen(l, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
296 for line in iter(proc.stdout.readline,''):
299 ok["state"] = tk.NORMAL
301 text.insert(tk.END, line)
304 # Function to run an OS command and display the output in a new modal window
306 class DisplayWindow(tk.Toplevel):
307 def __init__(self, parent, title):
308 tk.Toplevel.__init__(self, parent)
310 self.init_window(title)
312 def init_window(self, title):
315 frame = tk.Frame(self, borderwidth=5, relief=tk.RIDGE)
316 frame.pack(fill=tk.X, expand=True, side=tk.TOP)
318 scrollbar = tk.Scrollbar(frame)
319 self.text = tk.Text(frame, bg='gray14', fg='gray99')
320 scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
321 self.text.pack(side=tk.LEFT, fill=tk.Y)
322 scrollbar.config(command=self.text.yview)
323 self.text.config(yscrollcommand=scrollbar.set)
325 frame1 = tk.Frame(self, borderwidth=1)
326 frame1.pack(fill=tk.X, expand=True, side=tk.BOTTOM)
327 self.OKButton = ttk.Button(frame1, text="OK", command=self.OK)
328 self.OKButton["state"] = tk.DISABLED
332 self.transient(self.parent)
339 def RunCommandInWindow(parent, command):
340 w = DisplayWindow(parent, command)
341 x = threading.Thread(target=thread_function, args=(w.text, command, w.OKButton))
343 parent.wait_window(w)
345 class EditBoolWindow(sd.Dialog):
347 def __init__(self, parent, configitem, current):
349 self.config_item = configitem
350 self.current = current
351 sd.Dialog.__init__(self, parent, "Edit boolean configuration")
354 def body(self, master):
355 self.configure(background=GetBackground())
356 ttk.Label(self, text=self.config_item['name']).pack()
357 self.result = tk.StringVar()
358 self.result.set(self.current)
359 ttk.Radiobutton(master, text="True", variable=self.result, value="True").pack(anchor=tk.W)
360 ttk.Radiobutton(master, text="False", variable=self.result, value="False").pack(anchor=tk.W)
361 ttk.Radiobutton(master, text=CONFIG_UNSET, variable=self.result, value=CONFIG_UNSET).pack(anchor=tk.W)
364 return self.result.get()
366 class EditIntWindow(sd.Dialog):
368 def __init__(self, parent, configitem, current):
370 self.config_item = configitem
371 self.current = current
372 sd.Dialog.__init__(self, parent, "Edit integer configuration")
374 def body(self, master):
375 self.configure(background=GetBackground())
376 str = self.config_item['name'] + " Max = " + self.config_item['max'] + " Min = " + self.config_item['min']
377 ttk.Label(self, text=str).pack()
378 self.input = tk.Entry(self)
379 self.input.pack(pady=4)
380 self.input.insert(0, self.current)
381 ttk.Button(self, text=CONFIG_UNSET, command=self.unset).pack(pady=5)
384 self.result = self.input.get()
385 # Check for numeric entry
389 self.result = CONFIG_UNSET
395 class EditEnumWindow(sd.Dialog):
396 def __init__(self, parent, configitem, current):
398 self.config_item = configitem
399 self.current = current
400 sd.Dialog.__init__(self, parent, "Edit Enumeration configuration")
402 def body(self, master):
403 #self.configure(background=GetBackground())
404 values = self.config_item['enumvalues'].split('|')
405 values.insert(0,'Not set')
406 self.input = ttk.Combobox(self, values=values, state='readonly')
407 self.input.set(self.current)
408 self.input.pack(pady=12)
411 self.result = self.input.get()
418 class ConfigurationWindow(tk.Toplevel):
420 def __init__(self, parent, initial_config):
421 tk.Toplevel.__init__(self, parent)
423 self.results = initial_config
424 self.init_window(self)
426 def init_window(self, args):
427 self.configure(background=GetBackground())
428 self.title("Advanced Configuration")
429 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)
430 ttk.Label(self, text="Name").grid(row=1, column=0, sticky=tk.W)
431 ttk.Label(self, text="Type").grid(row=1, column=1, sticky=tk.W)
432 ttk.Label(self, text="Min").grid(row=1, column=2, sticky=tk.W)
433 ttk.Label(self, text="Max").grid(row=1, column=3, sticky=tk.W)
434 ttk.Label(self, text="Default").grid(row=1, column=4, sticky=tk.W)
435 ttk.Label(self, text="User").grid(row=1, column=5, sticky=tk.W)
437 okButton = ttk.Button(self, text="OK", command=self.ok)
438 cancelButton = ttk.Button(self, text="Cancel", command=self.cancel)
440 self.namelist = tk.Listbox(self, selectmode=tk.SINGLE)
441 self.typelist = tk.Listbox(self, selectmode=tk.SINGLE)
442 self.minlist = tk.Listbox(self, selectmode=tk.SINGLE)
443 self.maxlist = tk.Listbox(self, selectmode=tk.SINGLE)
444 self.defaultlist = tk.Listbox(self, selectmode=tk.SINGLE)
445 self.valuelist = tk.Listbox(self, selectmode=tk.SINGLE)
447 self.descriptionText = tk.Text(self, state=tk.DISABLED, height=2)
449 ## Make a list of our list boxes to make it all easier to handle
450 self.listlist = [self.namelist, self.typelist, self.minlist, self.maxlist, self.defaultlist, self.valuelist]
452 scroll = tk.Scrollbar(self, orient=tk.VERTICAL, command=self.yview)
454 for box in self.listlist:
456 box.config(yscrollcommand=scroll.set)
457 box.bind("<MouseWheel>", self.mousewheel)
458 box.bind("<Button-4>", self.mousewheel)
459 box.bind("<Button-5>", self.mousewheel)
460 box.bind("<<ListboxSelect>>", self.changeSelection)
461 box.bind("<Double-Button>", self.doubleClick)
462 box.config(exportselection=False)
463 box.bind("<Down>", self.OnEntryUpDown)
464 box.bind("<Up>", self.OnEntryUpDown)
466 scroll.grid(column=7, sticky=tk.N + tk.S)
469 for box in self.listlist:
470 box.grid(row=2, column=i, padx=0, sticky=tk.W + tk.E)
473 self.descriptionText.grid(row = 3, column=0, columnspan=4, sticky=tk.W + tk.E)
474 cancelButton.grid(column=5, row = 3, padx=5)
475 okButton.grid(column=4, row = 3, sticky=tk.E, padx=5)
477 # populate the list box with our config options
478 for conf in configuration_dictionary:
479 self.namelist.insert(tk.END, conf['name'])
483 self.typelist.insert(tk.END, s)
484 self.maxlist.insert(tk.END, conf['max'])
485 self.minlist.insert(tk.END, conf['min'])
486 self.defaultlist.insert(tk.END, conf['default'])
488 # see if this config has a setting, our results member has this predefined from init
489 val = self.results.get(conf['name'], CONFIG_UNSET)
490 self.valuelist.insert(tk.END, val)
491 if val != CONFIG_UNSET:
492 self.valuelist.itemconfig(self.valuelist.size() - 1, {'bg':'green'})
494 def yview(self, *args):
495 for box in self.listlist:
498 def mousewheel(self, event):
499 if (event.num == 4): # Linux encodes wheel as 'buttons' 4 and 5
501 elif (event.num == 5):
503 else: # Windows & OSX
506 for box in self.listlist:
507 box.yview("scroll", delta, "units")
510 def changeSelection(self, evt):
512 sellist = box.curselection()
515 index = int(sellist[0])
516 config = self.namelist.get(index)
517 # Now find the description for that config in the dictionary
518 for conf in configuration_dictionary:
519 if conf['name'] == config:
520 self.descriptionText.config(state=tk.NORMAL)
521 self.descriptionText.delete(1.0,tk.END)
522 str = config + "\n" + conf['description']
523 self.descriptionText.insert(1.0, str)
524 self.descriptionText.config(state=tk.DISABLED)
526 # Set all the other list boxes to the same index
527 for b in self.listlist:
529 b.selection_clear(0, tk.END)
530 b.selection_set(index)
532 def OnEntryUpDown(self, event):
534 selection = box.curselection()
537 index = int(selection[0])
538 if event.keysym == 'Up':
540 elif event.keysym == 'Down':
543 if 0 <= index < box.size():
544 for b in self.listlist:
545 b.selection_clear(0, tk.END)
546 b.selection_set(index)
549 def doubleClick(self, evt):
551 index = int(box.curselection()[0])
552 config = self.namelist.get(index)
553 # Get the associated dict entry from our list of configs
554 for conf in configuration_dictionary:
555 if conf['name'] == config:
556 if (conf['type'] == 'bool'):
557 result = EditBoolWindow(self, conf, self.valuelist.get(index)).get()
558 elif (conf['type'] == 'int' or conf['type'] == ""): # "" defaults to int
559 result = EditIntWindow(self, conf, self.valuelist.get(index)).get()
560 elif conf['type'] == 'enum':
561 result = EditEnumWindow(self, conf, self.valuelist.get(index)).get()
563 # Update the valuelist with our new item
564 self.valuelist.delete(index)
565 self.valuelist.insert(index, result)
566 if result != CONFIG_UNSET:
567 self.valuelist.itemconfig(index, {'bg':'green'})
571 # Get the selections, and create a list of them
572 for i, val in enumerate(self.valuelist.get(0, tk.END)):
573 if val != CONFIG_UNSET:
574 self.results[self.namelist.get(i)] = val
576 self.results.pop(self.namelist.get(i), None)
586 class WirelessSettingsWindow(sd.Dialog):
588 def __init__(self, parent):
589 sd.Dialog.__init__(self, parent, "Wireless settings")
592 def body(self, master):
593 self.configure(background=GetBackground())
594 master.configure(background=GetBackground())
595 self.ssid = tk.StringVar()
596 self.password = tk.StringVar()
598 a = ttk.Label(master, text='SSID :', background=GetBackground())
599 a.grid(row=0, column=0, sticky=tk.E)
600 a.configure(background=GetBackground())
601 ttk.Entry(master, textvariable=self.ssid).grid(row=0, column=1, sticky=tk.W+tk.E, padx=5)
603 ttk.Label(master, text='Password :').grid(row=1, column=0, sticky=tk.E)
604 ttk.Entry(master, textvariable=self.password).grid(row=1, column=1, sticky=tk.W+tk.E, padx=5)
606 self.transient(self.parent)
617 return (self.ssid.get(), self.password.get())
620 class ProjectWindow(tk.Frame):
622 def __init__(self, parent, sdkpath, args):
623 tk.Frame.__init__(self, parent)
625 self.sdkpath = sdkpath
626 self.init_window(args)
627 self.configs = dict()
629 self.password = str()
631 def setState(self, thing, state):
632 for child in thing.winfo_children():
633 child.configure(state=state)
635 def boardtype_change_callback(self, event):
636 boardtype = self.boardtype.get()
637 if boardtype == "pico_w":
638 self.setState(self.picowSubframe, "enabled")
640 self.setState(self.picowSubframe, "disabled")
642 def wirelessSettings(self):
643 result = WirelessSettingsWindow(self)
644 self.ssid, self.password = result.get()
646 def init_window(self, args):
647 self.master.title("Raspberry Pi Pico Project Generator")
648 self.master.configure(bg=GetBackground())
652 mainFrame = tk.Frame(self, bg=GetBackground()).grid(row=optionsRow, column=0, columnspan=6, rowspan=12)
654 # Need to keep a reference to the image or it will not appear.
655 self.logo = tk.PhotoImage(file=GetFilePath("logo_alpha.gif"))
656 logowidget = ttk.Label(mainFrame, image=self.logo, borderwidth=0, relief="solid").grid(row=0,column=0, columnspan=5, pady=10)
660 namelbl = ttk.Label(mainFrame, text='Project Name :').grid(row=optionsRow, column=0, sticky=tk.E)
661 self.projectName = tk.StringVar()
663 if args.name != None:
664 self.projectName.set(args.name)
666 self.projectName.set('ProjectName')
668 nameEntry = ttk.Entry(mainFrame, textvariable=self.projectName).grid(row=optionsRow, column=1, sticky=tk.W+tk.E, padx=5)
672 locationlbl = ttk.Label(mainFrame, text='Location :').grid(row=optionsRow, column=0, sticky=tk.E)
673 self.locationName = tk.StringVar()
674 self.locationName.set(os.getcwd())
675 locationEntry = ttk.Entry(mainFrame, textvariable=self.locationName).grid(row=optionsRow, column=1, columnspan=3, sticky=tk.W+tk.E, padx=5)
676 locationBrowse = ttk.Button(mainFrame, text='Browse', command=self.browse).grid(row=3, column=4)
680 ttk.Label(mainFrame, text = "Board Type :").grid(row=optionsRow, column=0, padx=4, sticky=tk.E)
681 self.boardtype = ttk.Combobox(mainFrame, values=boardtype_list, )
682 self.boardtype.grid(row=4, column=1, padx=4, sticky=tk.W+tk.E)
683 self.boardtype.set('pico')
684 self.boardtype.bind('<<ComboboxSelected>>',self.boardtype_change_callback)
688 featuresframe = ttk.LabelFrame(mainFrame, text="Library Options", relief=tk.RIDGE, borderwidth=2)
689 featuresframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=5, ipadx=5, padx=5, pady=5, sticky=tk.E+tk.W)
691 s = (len(features_list)/3)
693 self.feature_checkbox_vars = []
696 for i in features_list:
697 var = tk.StringVar(value='') # Off by default for the moment
698 c = features_list[i][GUI_TEXT]
699 cb = ttk.Checkbutton(featuresframe, text = c, var=var, onvalue=i, offvalue='')
700 cb.grid(row=row, column=col, padx=15, pady=2, ipadx=1, ipady=1, sticky=tk.E+tk.W)
701 self.feature_checkbox_vars.append(var)
709 # PicoW options section
710 self.picowSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Pico Wireless Options")
711 self.picowSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
712 self.pico_wireless = tk.StringVar()
716 for i in picow_options_list:
717 rb = ttk.Radiobutton(self.picowSubframe, text=picow_options_list[i][GUI_TEXT], variable=self.pico_wireless, val=i)
718 rb.grid(row=row, column=col, padx=15, pady=1, sticky=tk.E+tk.W)
724 # DOnt actually need any settings at the moment.
725 # ttk.Button(self.picowSubframe, text='Settings', command=self.wirelessSettings).grid(row=0, column=4, padx=5, pady=2, sticky=tk.E)
727 self.setState(self.picowSubframe, "disabled")
731 # output options section
732 ooptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Console Options")
733 ooptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
735 self.wantUART = tk.IntVar()
736 self.wantUART.set(args.uart)
737 ttk.Checkbutton(ooptionsSubframe, text="Console over UART", variable=self.wantUART).grid(row=0, column=0, padx=4, sticky=tk.W)
739 self.wantUSB = tk.IntVar()
740 self.wantUSB.set(args.usb)
741 ttk.Checkbutton(ooptionsSubframe, text="Console over USB (Disables other USB use)", variable=self.wantUSB).grid(row=0, column=1, padx=4, sticky=tk.W)
745 # Code options section
746 coptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Code Options")
747 coptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=3, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
749 self.wantExamples = tk.IntVar()
750 self.wantExamples.set(args.examples)
751 ttk.Checkbutton(coptionsSubframe, text="Add examples for Pico library", variable=self.wantExamples).grid(row=0, column=0, padx=4, sticky=tk.W)
753 self.wantRunFromRAM = tk.IntVar()
754 self.wantRunFromRAM.set(args.runFromRAM)
755 ttk.Checkbutton(coptionsSubframe, text="Run from RAM", variable=self.wantRunFromRAM).grid(row=0, column=1, padx=4, sticky=tk.W)
757 self.wantCPP = tk.IntVar()
758 self.wantCPP.set(args.cpp)
759 ttk.Checkbutton(coptionsSubframe, text="Generate C++", variable=self.wantCPP).grid(row=0, column=3, padx=4, sticky=tk.W)
761 ttk.Button(coptionsSubframe, text="Advanced...", command=self.config).grid(row=0, column=4, sticky=tk.E)
763 self.wantCPPExceptions = tk.IntVar()
764 self.wantCPPExceptions.set(args.cppexceptions)
765 ttk.Checkbutton(coptionsSubframe, text="Enable C++ exceptions", variable=self.wantCPPExceptions).grid(row=1, column=0, padx=4, sticky=tk.W)
767 self.wantCPPRTTI = tk.IntVar()
768 self.wantCPPRTTI.set(args.cpprtti)
769 ttk.Checkbutton(coptionsSubframe, text="Enable C++ RTTI", variable=self.wantCPPRTTI).grid(row=1, column=1, padx=4, sticky=tk.W)
773 # Build Options section
775 boptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Build Options")
776 boptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
778 self.wantBuild = tk.IntVar()
779 self.wantBuild.set(args.build)
780 ttk.Checkbutton(boptionsSubframe, text="Run build after generation", variable=self.wantBuild).grid(row=0, column=0, padx=4, sticky=tk.W)
782 self.wantOverwrite = tk.IntVar()
783 self.wantOverwrite.set(args.overwrite)
784 ttk.Checkbutton(boptionsSubframe, text="Overwrite existing projects", variable=self.wantOverwrite).grid(row=0, column=1, padx=4, sticky=tk.W)
788 # IDE Options section
790 vscodeoptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="IDE Options")
791 vscodeoptionsSubframe.grid_columnconfigure(2, weight=1)
792 vscodeoptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
794 self.wantVSCode = tk.IntVar()
795 if args.project is None:
796 self.wantVSCode.set(False)
798 self.wantVSCode.set('vscode' in args.project)
799 ttk.Checkbutton(vscodeoptionsSubframe, text="Create VSCode project", variable=self.wantVSCode).grid(row=0, column=0, padx=4, sticky=tk.W)
801 ttk.Label(vscodeoptionsSubframe, text = " Debugger:").grid(row=0, column=1, padx=4, sticky=tk.W)
803 self.debugger = ttk.Combobox(vscodeoptionsSubframe, values=debugger_list, state="readonly")
804 self.debugger.grid(row=0, column=2, padx=4, sticky=tk.EW)
805 self.debugger.current(args.debugger)
809 # OK, Cancel, Help section
811 QuitButton = ttk.Button(mainFrame, text="Quit", command=self.quit).grid(row=optionsRow, column=4, stick=tk.E, padx=10, pady=5)
812 OKButton = ttk.Button(mainFrame, text="OK", command=self.OK).grid(row=optionsRow, column=3, padx=4, pady=5, sticky=tk.E)
814 # TODO help not implemented yet
815 # HelpButton = ttk.Button(mainFrame, text="Help", command=self.help).grid(row=optionsRow, column=0, pady=5)
817 # You can set a default path here, replace the string with whereever you want.
818 # self.locationName.set('/home/pi/pico_projects')
820 def GetFeatures(self):
824 for cb in self.feature_checkbox_vars:
829 picow_extra = self.pico_wireless.get()
831 if picow_extra != 'picow_none':
832 features.append(picow_extra)
837 # TODO Check if we want to exit here
841 # OK, grab all the settings from the page, then call the generators
842 projectPath = self.locationName.get()
843 features = self.GetFeatures()
845 if (self.wantVSCode.get()):
846 projects.append("vscode")
849 'sdkPath' : self.sdkpath,
850 'projectRoot' : Path(projectPath),
851 'projectName' : self.projectName.get(),
853 'wantOverwrite' : self.wantOverwrite.get(),
854 'wantBuild' : self.wantBuild.get(),
855 'boardtype' : self.boardtype.get(),
856 'features' : features,
857 'projects' : projects,
858 'configs' : self.configs,
859 'wantRunFromRAM': self.wantRunFromRAM.get(),
860 'wantExamples' : self.wantExamples.get(),
861 'wantUART' : self.wantUART.get(),
862 'wantUSB' : self.wantUSB.get(),
863 'wantCPP' : self.wantCPP.get(),
864 'debugger' : self.debugger.current(),
865 'exceptions' : self.wantCPPExceptions.get(),
866 'rtti' : self.wantCPPRTTI.get(),
868 'password' : self.password,
871 DoEverything(self, params)
874 name = fd.askdirectory()
875 self.locationName.set(name)
881 # Run the configuration window
882 self.configs = ConfigurationWindow(self, self.configs).get()
884 def CheckPrerequisites():
885 global isMac, isWindows
886 isMac = (platform.system() == 'Darwin')
887 isWindows = (platform.system() == 'Windows')
889 # Do we have a compiler?
890 return shutil.which(COMPILER_NAME, 1, os.environ["Path" if isWindows else "PATH"])
893 def CheckSDKPath(gui):
894 sdkPath = os.getenv('PICO_SDK_PATH')
897 m = 'Unable to locate the Raspberry Pi Pico SDK, PICO_SDK_PATH is not set'
898 if gui and ENABLE_TK_GUI:
902 elif not os.path.isdir(sdkPath):
903 m = 'Unable to locate the Raspberry Pi Pico SDK, PICO_SDK_PATH does not point to a directory'
904 if gui and ENABLE_TK_GUI:
912 def GetFilePath(filename):
913 if os.path.islink(__file__):
914 script_file = os.readlink(__file__)
916 script_file = __file__
917 return os.path.join(os.path.dirname(script_file), filename)
919 def ParseCommandLine():
920 debugger_flags = ', '.join('{} = {}'.format(i, v) for i, v in enumerate(debugger_list))
921 parser = argparse.ArgumentParser(description='Pico Project generator')
922 parser.add_argument("name", nargs="?", help="Name of the project")
923 parser.add_argument("-t", "--tsv", help="Select an alternative pico_configs.tsv file", default=GetFilePath("pico_configs.tsv"))
924 parser.add_argument("-o", "--output", help="Set an alternative CMakeList.txt filename", default="CMakeLists.txt")
925 parser.add_argument("-x", "--examples", action='store_true', help="Add example code for the Pico standard library")
926 parser.add_argument("-l", "--list", action='store_true', help="List available features")
927 parser.add_argument("-c", "--configs", action='store_true', help="List available project configuration items")
928 parser.add_argument("-f", "--feature", action='append', help="Add feature to generated project")
929 parser.add_argument("-over", "--overwrite", action='store_true', help="Overwrite any existing project AND files")
930 parser.add_argument("-b", "--build", action='store_true', help="Build after project created")
931 parser.add_argument("-g", "--gui", action='store_true', help="Run a GUI version of the project generator")
932 parser.add_argument("-p", "--project", action='append', help="Generate projects files for IDE. Options are: vscode")
933 parser.add_argument("-r", "--runFromRAM", action='store_true', help="Run the program from RAM rather than flash")
934 parser.add_argument("-uart", "--uart", action='store_true', default=1, help="Console output to UART (default)")
935 parser.add_argument("-nouart", "--nouart", action='store_true', default=0, help="Disable console output to UART")
936 parser.add_argument("-usb", "--usb", action='store_true', help="Console output to USB (disables other USB functionality")
937 parser.add_argument("-cpp", "--cpp", action='store_true', default=0, help="Generate C++ code")
938 parser.add_argument("-cpprtti", "--cpprtti", action='store_true', default=0, help="Enable C++ RTTI (Uses more memory)")
939 parser.add_argument("-cppex", "--cppexceptions", action='store_true', default=0, help="Enable C++ exceptions (Uses more memory)")
940 parser.add_argument("-d", "--debugger", type=int, help="Select debugger ({})".format(debugger_flags), default=0)
941 parser.add_argument("-board", "--boardtype", action="store", default='pico', help="Select board type (see --boardlist for available boards)")
942 parser.add_argument("-bl", "--boardlist", action="store_true", help="List available board types")
943 parser.add_argument("-cp", "--cpath", help="Override default VSCode compiler path")
944 parser.add_argument("-root", "--projectRoot", help="Override default project root where the new project will be created")
945 parser.add_argument("-sdkVersion", "--sdkVersion", help="Pico SDK version to use (required)")
946 parser.add_argument("-tcVersion", "--toolchainVersion", help="ARM Embeded Toolchain version to use (required)")
948 return parser.parse_args()
951 def GenerateMain(folder, projectName, features, cpp):
954 filename = Path(folder) / (projectName + '.cpp')
956 filename = Path(folder) / (projectName + '.c')
958 file = open(filename, 'w')
960 main = ('#include <stdio.h>\n'
961 '#include "pico/stdlib.h"\n'
968 for feat in features:
969 if (feat in features_list):
970 o = f'#include "{features_list[feat][H_FILE]}"\n'
972 if (feat in stdlib_examples_list):
973 o = f'#include "{stdlib_examples_list[feat][H_FILE]}"\n'
975 if (feat in picow_options_list):
976 o = f'#include "{picow_options_list[feat][H_FILE]}"\n'
982 for feat in features:
983 if (feat in code_fragments_per_feature):
984 for s in code_fragments_per_feature[feat][DEFINES]:
992 ' stdio_init_all();\n\n'
996 # Add any initialisers
998 for feat in features:
999 if (feat in code_fragments_per_feature):
1000 for s in code_fragments_per_feature[feat][INITIALISERS]:
1001 main += (" " * indent)
1006 main += (' puts("Hello, world!");\n\n'
1016 def GenerateCMake(folder, params):
1018 filename = Path(folder) / CMAKELIST_FILENAME
1019 projectName = params['projectName']
1020 board_type = params['boardtype']
1022 # OK, for the path, CMake will accept forward slashes on Windows, and thats
1023 # seemingly a bit easier to handle than the backslashes
1024 p = str(params['sdkPath']).replace('\\','/')
1026 cmake_header1 = (f"# Generated Cmake Pico project file\n\n"
1027 "cmake_minimum_required(VERSION 3.13)\n\n"
1028 "set(CMAKE_C_STANDARD 11)\n"
1029 "set(CMAKE_CXX_STANDARD 17)\n\n"
1030 "# Initialise pico_sdk from installed location\n"
1031 "# (note this can come from environment, CMake cache etc)\n\n"
1032 "# == DO NEVER EDIT THE NEXT TWO LINES for RaspberryPiPico VS Code Extension to work == \n"
1033 f"set(PICO_SDK_PATH {cmakeSdkPath(params['sdkVersion'])})\n"
1034 f"set(PICO_TOOLCHAIN_PATH {cmakeToolchainPath(params['toolchainVersion'])})\n"
1035 "# ====================================================================================\n"
1036 f"set(PICO_BOARD {board_type} CACHE STRING \"Board type\")\n\n"
1037 "# Pull in Raspberry Pi Pico SDK (must be before project)\n"
1038 "include(pico_sdk_import.cmake)\n\n"
1039 "if (PICO_SDK_VERSION_STRING VERSION_LESS \"1.4.0\")\n"
1040 " message(FATAL_ERROR \"Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}\")\n"
1042 f"project({projectName} C CXX ASM)\n"
1046 "\n# Initialise the Raspberry Pi Pico SDK\n"
1047 "pico_sdk_init()\n\n"
1048 "# Add executable. Default name is the project name, version 0.1\n\n"
1052 file = open(filename, 'w')
1054 file.write(cmake_header1)
1056 if params['exceptions']:
1057 file.write("\nset(PICO_CXX_ENABLE_EXCEPTIONS 1)\n")
1060 file.write("\nset(PICO_CXX_ENABLE_RTTI 1)\n")
1062 file.write(cmake_header3)
1064 # add the preprocessor defines for overall configuration
1065 if params['configs']:
1066 file.write('# Add any PICO_CONFIG entries specified in the Advanced settings\n')
1067 for c, v in params['configs'].items():
1072 file.write(f'add_compile_definitions({c} = {v})\n')
1075 # No GUI/command line to set a different executable name at this stage
1076 executableName = projectName
1078 if params['wantCPP']:
1079 file.write(f'add_executable({projectName} {projectName}.cpp )\n\n')
1081 file.write(f'add_executable({projectName} {projectName}.c )\n\n')
1083 file.write(f'pico_set_program_name({projectName} "{executableName}")\n')
1084 file.write(f'pico_set_program_version({projectName} "0.1")\n\n')
1086 if params['wantRunFromRAM']:
1087 file.write(f'# no_flash means the target is to run from RAM\n')
1088 file.write(f'pico_set_binary_type({projectName} no_flash)\n\n')
1090 # Console output destinations
1091 if params['wantUART']:
1092 file.write(f'pico_enable_stdio_uart({projectName} 1)\n')
1094 file.write(f'pico_enable_stdio_uart({projectName} 0)\n')
1096 if params['wantUSB']:
1097 file.write(f'pico_enable_stdio_usb({projectName} 1)\n\n')
1099 file.write(f'pico_enable_stdio_usb({projectName} 0)\n\n')
1101 # If we need wireless, check for SSID and password
1102 # removed for the moment as these settings are currently only needed for the pico-examples
1103 # but may be required in here at a later date.
1105 if 'ssid' in params or 'password' in params:
1106 file.write('# Add any wireless access point information\n')
1107 file.write(f'target_compile_definitions({projectName} PRIVATE\n')
1108 if 'ssid' in params:
1109 file.write(f'WIFI_SSID=\" {params["ssid"]} \"\n')
1111 file.write(f'WIFI_SSID=\"${WIFI_SSID}\"')
1113 if 'password' in params:
1114 file.write(f'WIFI_PASSWORD=\"{params["password"]}\"\n')
1116 file.write(f'WIFI_PASSWORD=\"${WIFI_PASSWORD}\"')
1119 # Standard libraries
1120 file.write('# Add the standard library to the build\n')
1121 file.write(f'target_link_libraries({projectName}\n')
1122 file.write(" " + STANDARD_LIBRARIES)
1125 # Standard include directories
1126 file.write('# Add the standard include files to the build\n')
1127 file.write(f'target_include_directories({projectName} PRIVATE\n')
1128 file.write(" ${CMAKE_CURRENT_LIST_DIR}\n")
1129 file.write(" ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required\n")
1132 # Selected libraries/features
1133 if (params['features']):
1134 file.write('# Add any user requested libraries\n')
1135 file.write(f'target_link_libraries({projectName} \n')
1136 for feat in params['features']:
1137 if (feat in features_list):
1138 file.write(" " + features_list[feat][LIB_NAME] + '\n')
1139 if (feat in picow_options_list):
1140 file.write(" " + picow_options_list[feat][LIB_NAME] + '\n')
1141 file.write(' )\n\n')
1143 file.write(f'pico_add_extra_outputs({projectName})\n\n')
1148 # Generates the requested project files, if any
1149 def generateProjectFiles(projectPath, projectName, sdkPath, projects, debugger, sdkVersion):
1151 oldCWD = os.getcwd()
1153 os.chdir(projectPath)
1155 debugger = debugger_config_list[debugger]
1156 gdbPath = "arm-none-eabi-gdb" if isWindows else "gdb-multiarch"
1157 # Need to escape windows files paths backslashes
1158 # TODO: env in currently not supported in compilerPath var
1159 #cPath = f"${{env:PICO_TOOLCHAIN_PATH_{envSuffix}}}" + os.path.sep + os.path.basename(str(compilerPath).replace('\\', '\\\\' ))
1160 cPath = str(compilerPath).replace('\\', '\\\\' )
1168 "name": "Pico Debug (Cortex-Debug)",
1169 "cwd": "${{workspaceRoot}}",
1170 "executable": "${{command:raspberry-pi-pico.launchTargetPath}}",
1171 "request": "launch",
1172 "type": "cortex-debug",
1173 "servertype": "openocd",
1174 "gdbPath": "{gdbPath}",
1177 "interface/{debugger}",
1180 "svdFile": "{codeSdkPath(sdkVersion)}/src/rp2040/hardware_regs/rp2040.svd",
1181 "runToEntryPoint": "main",
1182 // Give restart the same functionality as runToEntryPoint - main
1183 "postRestartCommands": [
1187 "openOCDLaunchCommands": [
1188 "adapter speed 1000"
1190 "preLaunchTask": "Compile Project"
1193 "name": "Pico Debug (Cortex-Debug with external OpenOCD)",
1194 "cwd": "${{workspaceRoot}}",
1195 "executable": "${{command:raspberry-pi-pico.launchTargetPath}}",
1196 "request": "launch",
1197 "type": "cortex-debug",
1198 "servertype": "external",
1199 "gdbTarget": "localhost:3333",
1200 "gdbPath": "{gdbPath}",
1202 "svdFile": "{codeSdkPath(sdkVersion)}/src/rp2040/hardware_regs/rp2040.svd",
1203 "runToEntryPoint": "main",
1204 // Give restart the same functionality as runToEntryPoint - main
1205 "postRestartCommands": [
1209 "preLaunchTask": "Compile Project"
1212 "name": "Pico Debug (C++ Debugger)",
1214 "request": "launch",
1215 "cwd": "${{workspaceRoot}}",
1216 "program": "${{command:raspberry-pi-pico.launchTargetPath}}",
1218 "miDebuggerPath": "{gdbPath}",
1219 "miDebuggerServerAddress": "localhost:3333",
1220 "debugServerPath": "openocd",
1221 "debugServerArgs": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \\"adapter speed 1000\\"",
1222 "serverStarted": "Listening on port .* for gdb connections",
1223 "filterStderr": true,
1224 "stopAtEntry": true,
1225 "hardwareBreakpoints": {{
1229 "preLaunchTask": "Flash",
1230 "svdPath": "{codeSdkPath(sdkVersion)}/src/rp2040/hardware_regs/rp2040.svd"
1241 "${{workspaceFolder}}/**",
1242 "{codeSdkPath(sdkVersion)}/**"
1245 "compilerPath": "{cPath}",
1247 "cppStandard": "c++14",
1248 "intelliSenseMode": "linux-gcc-arm"
1258 "cmake.statusbar.visibility": "hidden",
1259 "cmake.configureOnEdit": false,
1260 "cmake.automaticReconfigure": false,
1261 "cmake.configureOnOpen": false,
1262 "cmake.generator": "Ninja",
1263 "raspberry-pi-pico.cmakeAutoConfigure": true,
1269 "recommendations": [
1270 "marus25.cortex-debug",
1271 "ms-vscode.cpptools",
1272 "ms-vscode.cpptools-extension-pack",
1273 "ms-vscode.vscode-serial-monitor",
1274 "paulober.raspberry-pi-pico",
1282 "label": "Compile Project",
1285 "args": ["-C", "${{workspaceFolder}}/build"],
1289 "panel": "dedicated"
1291 "problemMatcher": "$gcc"
1297 # Create a build folder, and run our cmake project build from it
1298 if not os.path.exists(VSCODE_FOLDER):
1299 os.mkdir(VSCODE_FOLDER)
1301 os.chdir(VSCODE_FOLDER)
1303 file = open(VSCODE_TASKS_FILENAME, 'w')
1307 filename = VSCODE_LAUNCH_FILENAME
1308 file = open(filename, 'w')
1312 file = open(VSCODE_C_PROPERTIES_FILENAME, 'w')
1313 file.write(properties)
1316 file = open(VSCODE_SETTINGS_FILENAME, 'w')
1317 file.write(settings)
1320 file = open(VSCODE_EXTENSIONS_FILENAME, 'w')
1321 file.write(extensions)
1325 print('Unknown project type requested')
1330 def LoadConfigurations():
1332 with open(args.tsv) as tsvfile:
1333 reader = csv.DictReader(tsvfile, dialect='excel-tab')
1335 configuration_dictionary.append(row)
1337 print("No Pico configurations file found. Continuing without")
1339 def LoadBoardTypes(sdkPath):
1340 # Scan the boards folder for all header files, extract filenames, and make a list of the results
1341 # default folder is <PICO_SDK_PATH>/src/boards/include/boards/*
1342 # If the PICO_BOARD_HEADER_DIRS environment variable is set, use that as well
1344 loc = sdkPath / "src/boards/include/boards"
1346 for x in Path(loc).iterdir():
1347 if x.suffix == '.h':
1348 boards.append(x.stem)
1350 loc = os.getenv('PICO_BOARD_HEADER_DIRS')
1353 for x in Path(loc).iterdir():
1354 if x.suffix == '.h':
1355 boards.append(x.stem)
1359 def DoEverything(parent, params):
1361 if not os.path.exists(params['projectRoot']):
1362 if params['wantGUI'] and ENABLE_TK_GUI:
1363 mb.showerror('Raspberry Pi Pico Project Generator', 'Invalid project path. Select a valid path and try again')
1366 print('Invalid project path')
1369 oldCWD = os.getcwd()
1370 os.chdir(params['projectRoot'])
1372 # Create our project folder as subfolder
1373 os.makedirs(params['projectName'], exist_ok=True)
1375 os.chdir(params['projectName'])
1377 projectPath = params['projectRoot'] / params['projectName']
1379 # First check if there is already a project in the folder
1380 # If there is we abort unless the overwrite flag it set
1381 if os.path.exists(CMAKELIST_FILENAME):
1382 if not params['wantOverwrite'] :
1383 if params['wantGUI'] and ENABLE_TK_GUI:
1384 # We can ask the user if they want to overwrite
1385 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')
1389 print('There already appears to be a project in this folder. Use the --overwrite option to overwrite the existing project')
1392 # We should really confirm the user wants to overwrite
1393 #print('Are you sure you want to overwrite the existing project files? (y/N)')
1394 #c = input().split(" ")[0]
1395 #if c != 'y' and c != 'Y' :
1398 # Copy the SDK finder cmake file to our project folder
1399 # Can be found here <PICO_SDK_PATH>/external/pico_sdk_import.cmake
1400 shutil.copyfile(params['sdkPath'] / 'external' / 'pico_sdk_import.cmake', projectPath / 'pico_sdk_import.cmake' )
1402 if params['features']:
1403 features_and_examples = params['features'][:]
1405 features_and_examples= []
1407 if params['wantExamples']:
1408 features_and_examples = list(stdlib_examples_list.keys()) + features_and_examples
1410 GenerateMain('.', params['projectName'], features_and_examples, params['wantCPP'])
1412 GenerateCMake('.', params)
1414 # If we have any ancilliary files, copy them to our project folder
1415 # Currently only the picow with lwIP support needs an extra file, so just check that list
1416 for feat in features_and_examples:
1417 if feat in picow_options_list:
1418 if picow_options_list[feat][ANCILLARY_FILE] != "":
1419 shutil.copy(sourcefolder + "/" + picow_options_list[feat][ANCILLARY_FILE], projectPath / picow_options_list[feat][ANCILLARY_FILE])
1421 # Create a build folder, and run our cmake project build from it
1422 if not os.path.exists('build'):
1427 # If we are overwriting a previous project, we should probably clear the folder, but that might delete something the users thinks is important, so
1428 # for the moment, just delete the CMakeCache.txt file as certain changes may need that to be recreated.
1430 if os.path.exists(CMAKECACHE_FILENAME):
1431 os.remove(CMAKECACHE_FILENAME)
1433 cpus = os.cpu_count()
1438 # Had a special case report, when using MinGW, need to check if using nmake or mingw32-make.
1439 if shutil.which("mingw32-make"):
1440 # Assume MinGW environment
1441 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G "MinGW Makefiles" ..'
1442 makeCmd = 'mingw32-make '
1443 elif shutil.which("ninja"):
1444 # When installing SDK version 1.5.0 on windows with installer pico-setup-windows-x64-standalone.exe, ninja is used
1445 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja ..'
1448 # Everything else assume nmake
1449 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G "NMake Makefiles" ..'
1452 # Ninja now works OK under Linux, so if installed use it by default. It's faster.
1453 if shutil.which("ninja"):
1454 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja ..'
1457 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug ..'
1458 makeCmd = 'make -j' + str(cpus)
1460 if params['wantGUI'] and ENABLE_TK_GUI:
1461 RunCommandInWindow(parent, cmakeCmd)
1465 if params['projects']:
1466 generateProjectFiles(projectPath, params['projectName'], params['sdkPath'], params['projects'], params['debugger'], params["sdkVersion"])
1468 if params['wantBuild']:
1469 if params['wantGUI'] and ENABLE_TK_GUI:
1470 RunCommandInWindow(parent, makeCmd)
1473 print('\nIf the application has built correctly, you can now transfer it to the Raspberry Pi Pico board')
1478 ###################################################################################
1479 # main execution starteth here
1481 sourcefolder = os.path.dirname(os.path.abspath(__file__))
1483 args = ParseCommandLine()
1488 if args.debugger > len(debugger_list) - 1:
1491 # Check we have everything we need to compile etc
1492 c = CheckPrerequisites()
1494 ## TODO Do both warnings in the same error message so user does have to keep coming back to find still more to do
1497 m = f'Unable to find the `{COMPILER_NAME}` compiler\n'
1498 m +='You will need to install an appropriate compiler to build a Raspberry Pi Pico project\n'
1499 m += 'See the Raspberry Pi Pico documentation for how to do this on your particular platform\n'
1501 if args.gui and ENABLE_TK_GUI:
1507 if args.name == None and not args.gui and not args.list and not args.configs and not args.boardlist:
1508 print("No project name specfied\n")
1511 # Check if we were provided a compiler path, and override the default if so
1513 compilerPath = Path(args.cpath)
1514 elif args.toolchainVersion:
1515 compilerPath = Path(codeToolchainPath(args.toolchainVersion)+"/bin/"+COMPILER_NAME)
1517 compilerPath = Path(c)
1519 # load/parse any configuration dictionary we may have
1520 LoadConfigurations()
1522 p = CheckSDKPath(args.gui)
1529 boardtype_list = LoadBoardTypes(sdkPath)
1530 boardtype_list.sort()
1532 if args.gui and ENABLE_TK_GUI:
1533 RunGUI(sdkPath, args) # does not return, only exits
1535 projectRoot = Path(os.getcwd()) if not args.projectRoot else Path(args.projectRoot)
1537 if args.list or args.configs or args.boardlist:
1539 print("Available project features:\n")
1540 for feat in features_list:
1541 print(feat.ljust(6), '\t', features_list[feat][GUI_TEXT])
1545 print("Available project configuration items:\n")
1546 for conf in configuration_dictionary:
1547 print(conf['name'].ljust(40), '\t', conf['description'])
1551 print("Available board types:\n")
1552 for board in boardtype_list:
1559 'sdkPath' : sdkPath,
1560 'projectRoot' : projectRoot,
1561 'projectName' : args.name,
1563 'wantOverwrite' : args.overwrite,
1564 'boardtype' : args.boardtype,
1565 'wantBuild' : args.build,
1566 'features' : args.feature,
1567 'projects' : args.project,
1569 'wantRunFromRAM': args.runFromRAM,
1570 'wantExamples' : args.examples,
1571 'wantUART' : args.uart,
1572 'wantUSB' : args.usb,
1573 'wantCPP' : args.cpp,
1574 'debugger' : args.debugger,
1575 'exceptions' : args.cppexceptions,
1576 'rtti' : args.cpprtti,
1579 'sdkVersion' : args.sdkVersion,
1580 'toolchainVersion': args.toolchainVersion,
1583 DoEverything(None, params)