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 relativeToolsPath(sdkVersion):
234 return f"/.pico-sdk/tools/{sdkVersion}"
236 def cmakeSdkPath(sdkVersion):
237 return f"${{USERHOME}}{relativeSDKPath(sdkVersion)}"
239 def cmakeToolchainPath(toolchainVersion):
240 return f"${{USERHOME}}{relativeToolchainPath(toolchainVersion)}"
242 def cmakeToolsPath(sdkVersion):
243 return f"${{USERHOME}}{relativeToolsPath(sdkVersion)}"
245 def propertiesSdkPath(sdkVersion):
247 return f"${{env:USERPROFILE}}{relativeSDKPath(sdkVersion)}"
249 return f"${{env:HOME}}{relativeSDKPath(sdkVersion)}"
251 def codeSdkPath(sdkVersion):
252 return f"${{userHome}}{relativeSDKPath(sdkVersion)}"
254 def propertiesToolchainPath(toolchainVersion):
256 return f"${{env:USERPROFILE}}{relativeToolchainPath(toolchainVersion)}"
258 return f"${{env:HOME}}{relativeToolchainPath(toolchainVersion)}"
260 def codeToolchainPath(toolchainVersion):
261 return f"${{userHome}}{relativeToolchainPath(toolchainVersion)}"
266 def GetButtonBackground():
272 def GetButtonTextColour():
276 def RunGUI(sdkpath, args):
278 style = ttk.Style(root)
279 style.theme_use('default')
281 style.configure("TButton", padding=6, relief="groove", border=2, foreground=GetButtonTextColour(), background=GetButtonBackground())
282 style.configure("TLabel", foreground=GetTextColour(), background=GetBackground() )
283 style.configure("TCheckbutton", foreground=GetTextColour(), background=GetBackground())
284 style.configure("TRadiobutton", foreground=GetTextColour(), background=GetBackground() )
285 style.configure("TLabelframe", foreground=GetTextColour(), background=GetBackground() )
286 style.configure("TLabelframe.Label", foreground=GetTextColour(), background=GetBackground() )
287 style.configure("TCombobox", foreground=GetTextColour(), background=GetBackground() )
288 style.configure("TListbox", foreground=GetTextColour(), background=GetBackground() )
290 style.map("TCheckbutton", background = [('disabled', GetBackground())])
291 style.map("TRadiobutton", background = [('disabled', GetBackground())])
292 style.map("TButton", background = [('disabled', GetBackground())])
293 style.map("TLabel", background = [('background', GetBackground())])
294 style.map("TComboBox", background = [('readonly', GetBackground())])
296 app = ProjectWindow(root, sdkpath, args)
298 app.configure(background=GetBackground())
304 def RunWarning(message):
305 mb.showwarning('Raspberry Pi Pico Project Generator', message)
311 def thread_function(text, command, ok):
312 l = shlex.split(command)
313 proc = subprocess.Popen(l, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
314 for line in iter(proc.stdout.readline,''):
317 ok["state"] = tk.NORMAL
319 text.insert(tk.END, line)
322 # Function to run an OS command and display the output in a new modal window
324 class DisplayWindow(tk.Toplevel):
325 def __init__(self, parent, title):
326 tk.Toplevel.__init__(self, parent)
328 self.init_window(title)
330 def init_window(self, title):
333 frame = tk.Frame(self, borderwidth=5, relief=tk.RIDGE)
334 frame.pack(fill=tk.X, expand=True, side=tk.TOP)
336 scrollbar = tk.Scrollbar(frame)
337 self.text = tk.Text(frame, bg='gray14', fg='gray99')
338 scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
339 self.text.pack(side=tk.LEFT, fill=tk.Y)
340 scrollbar.config(command=self.text.yview)
341 self.text.config(yscrollcommand=scrollbar.set)
343 frame1 = tk.Frame(self, borderwidth=1)
344 frame1.pack(fill=tk.X, expand=True, side=tk.BOTTOM)
345 self.OKButton = ttk.Button(frame1, text="OK", command=self.OK)
346 self.OKButton["state"] = tk.DISABLED
350 self.transient(self.parent)
357 def RunCommandInWindow(parent, command):
358 w = DisplayWindow(parent, command)
359 x = threading.Thread(target=thread_function, args=(w.text, command, w.OKButton))
361 parent.wait_window(w)
363 class EditBoolWindow(sd.Dialog):
365 def __init__(self, parent, configitem, current):
367 self.config_item = configitem
368 self.current = current
369 sd.Dialog.__init__(self, parent, "Edit boolean configuration")
372 def body(self, master):
373 self.configure(background=GetBackground())
374 ttk.Label(self, text=self.config_item['name']).pack()
375 self.result = tk.StringVar()
376 self.result.set(self.current)
377 ttk.Radiobutton(master, text="True", variable=self.result, value="True").pack(anchor=tk.W)
378 ttk.Radiobutton(master, text="False", variable=self.result, value="False").pack(anchor=tk.W)
379 ttk.Radiobutton(master, text=CONFIG_UNSET, variable=self.result, value=CONFIG_UNSET).pack(anchor=tk.W)
382 return self.result.get()
384 class EditIntWindow(sd.Dialog):
386 def __init__(self, parent, configitem, current):
388 self.config_item = configitem
389 self.current = current
390 sd.Dialog.__init__(self, parent, "Edit integer configuration")
392 def body(self, master):
393 self.configure(background=GetBackground())
394 str = self.config_item['name'] + " Max = " + self.config_item['max'] + " Min = " + self.config_item['min']
395 ttk.Label(self, text=str).pack()
396 self.input = tk.Entry(self)
397 self.input.pack(pady=4)
398 self.input.insert(0, self.current)
399 ttk.Button(self, text=CONFIG_UNSET, command=self.unset).pack(pady=5)
402 self.result = self.input.get()
403 # Check for numeric entry
407 self.result = CONFIG_UNSET
413 class EditEnumWindow(sd.Dialog):
414 def __init__(self, parent, configitem, current):
416 self.config_item = configitem
417 self.current = current
418 sd.Dialog.__init__(self, parent, "Edit Enumeration configuration")
420 def body(self, master):
421 #self.configure(background=GetBackground())
422 values = self.config_item['enumvalues'].split('|')
423 values.insert(0,'Not set')
424 self.input = ttk.Combobox(self, values=values, state='readonly')
425 self.input.set(self.current)
426 self.input.pack(pady=12)
429 self.result = self.input.get()
436 class ConfigurationWindow(tk.Toplevel):
438 def __init__(self, parent, initial_config):
439 tk.Toplevel.__init__(self, parent)
441 self.results = initial_config
442 self.init_window(self)
444 def init_window(self, args):
445 self.configure(background=GetBackground())
446 self.title("Advanced Configuration")
447 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)
448 ttk.Label(self, text="Name").grid(row=1, column=0, sticky=tk.W)
449 ttk.Label(self, text="Type").grid(row=1, column=1, sticky=tk.W)
450 ttk.Label(self, text="Min").grid(row=1, column=2, sticky=tk.W)
451 ttk.Label(self, text="Max").grid(row=1, column=3, sticky=tk.W)
452 ttk.Label(self, text="Default").grid(row=1, column=4, sticky=tk.W)
453 ttk.Label(self, text="User").grid(row=1, column=5, sticky=tk.W)
455 okButton = ttk.Button(self, text="OK", command=self.ok)
456 cancelButton = ttk.Button(self, text="Cancel", command=self.cancel)
458 self.namelist = tk.Listbox(self, selectmode=tk.SINGLE)
459 self.typelist = tk.Listbox(self, selectmode=tk.SINGLE)
460 self.minlist = tk.Listbox(self, selectmode=tk.SINGLE)
461 self.maxlist = tk.Listbox(self, selectmode=tk.SINGLE)
462 self.defaultlist = tk.Listbox(self, selectmode=tk.SINGLE)
463 self.valuelist = tk.Listbox(self, selectmode=tk.SINGLE)
465 self.descriptionText = tk.Text(self, state=tk.DISABLED, height=2)
467 ## Make a list of our list boxes to make it all easier to handle
468 self.listlist = [self.namelist, self.typelist, self.minlist, self.maxlist, self.defaultlist, self.valuelist]
470 scroll = tk.Scrollbar(self, orient=tk.VERTICAL, command=self.yview)
472 for box in self.listlist:
474 box.config(yscrollcommand=scroll.set)
475 box.bind("<MouseWheel>", self.mousewheel)
476 box.bind("<Button-4>", self.mousewheel)
477 box.bind("<Button-5>", self.mousewheel)
478 box.bind("<<ListboxSelect>>", self.changeSelection)
479 box.bind("<Double-Button>", self.doubleClick)
480 box.config(exportselection=False)
481 box.bind("<Down>", self.OnEntryUpDown)
482 box.bind("<Up>", self.OnEntryUpDown)
484 scroll.grid(column=7, sticky=tk.N + tk.S)
487 for box in self.listlist:
488 box.grid(row=2, column=i, padx=0, sticky=tk.W + tk.E)
491 self.descriptionText.grid(row = 3, column=0, columnspan=4, sticky=tk.W + tk.E)
492 cancelButton.grid(column=5, row = 3, padx=5)
493 okButton.grid(column=4, row = 3, sticky=tk.E, padx=5)
495 # populate the list box with our config options
496 for conf in configuration_dictionary:
497 self.namelist.insert(tk.END, conf['name'])
501 self.typelist.insert(tk.END, s)
502 self.maxlist.insert(tk.END, conf['max'])
503 self.minlist.insert(tk.END, conf['min'])
504 self.defaultlist.insert(tk.END, conf['default'])
506 # see if this config has a setting, our results member has this predefined from init
507 val = self.results.get(conf['name'], CONFIG_UNSET)
508 self.valuelist.insert(tk.END, val)
509 if val != CONFIG_UNSET:
510 self.valuelist.itemconfig(self.valuelist.size() - 1, {'bg':'green'})
512 def yview(self, *args):
513 for box in self.listlist:
516 def mousewheel(self, event):
517 if (event.num == 4): # Linux encodes wheel as 'buttons' 4 and 5
519 elif (event.num == 5):
521 else: # Windows & OSX
524 for box in self.listlist:
525 box.yview("scroll", delta, "units")
528 def changeSelection(self, evt):
530 sellist = box.curselection()
533 index = int(sellist[0])
534 config = self.namelist.get(index)
535 # Now find the description for that config in the dictionary
536 for conf in configuration_dictionary:
537 if conf['name'] == config:
538 self.descriptionText.config(state=tk.NORMAL)
539 self.descriptionText.delete(1.0,tk.END)
540 str = config + "\n" + conf['description']
541 self.descriptionText.insert(1.0, str)
542 self.descriptionText.config(state=tk.DISABLED)
544 # Set all the other list boxes to the same index
545 for b in self.listlist:
547 b.selection_clear(0, tk.END)
548 b.selection_set(index)
550 def OnEntryUpDown(self, event):
552 selection = box.curselection()
555 index = int(selection[0])
556 if event.keysym == 'Up':
558 elif event.keysym == 'Down':
561 if 0 <= index < box.size():
562 for b in self.listlist:
563 b.selection_clear(0, tk.END)
564 b.selection_set(index)
567 def doubleClick(self, evt):
569 index = int(box.curselection()[0])
570 config = self.namelist.get(index)
571 # Get the associated dict entry from our list of configs
572 for conf in configuration_dictionary:
573 if conf['name'] == config:
574 if (conf['type'] == 'bool'):
575 result = EditBoolWindow(self, conf, self.valuelist.get(index)).get()
576 elif (conf['type'] == 'int' or conf['type'] == ""): # "" defaults to int
577 result = EditIntWindow(self, conf, self.valuelist.get(index)).get()
578 elif conf['type'] == 'enum':
579 result = EditEnumWindow(self, conf, self.valuelist.get(index)).get()
581 # Update the valuelist with our new item
582 self.valuelist.delete(index)
583 self.valuelist.insert(index, result)
584 if result != CONFIG_UNSET:
585 self.valuelist.itemconfig(index, {'bg':'green'})
589 # Get the selections, and create a list of them
590 for i, val in enumerate(self.valuelist.get(0, tk.END)):
591 if val != CONFIG_UNSET:
592 self.results[self.namelist.get(i)] = val
594 self.results.pop(self.namelist.get(i), None)
604 class WirelessSettingsWindow(sd.Dialog):
606 def __init__(self, parent):
607 sd.Dialog.__init__(self, parent, "Wireless settings")
610 def body(self, master):
611 self.configure(background=GetBackground())
612 master.configure(background=GetBackground())
613 self.ssid = tk.StringVar()
614 self.password = tk.StringVar()
616 a = ttk.Label(master, text='SSID :', background=GetBackground())
617 a.grid(row=0, column=0, sticky=tk.E)
618 a.configure(background=GetBackground())
619 ttk.Entry(master, textvariable=self.ssid).grid(row=0, column=1, sticky=tk.W+tk.E, padx=5)
621 ttk.Label(master, text='Password :').grid(row=1, column=0, sticky=tk.E)
622 ttk.Entry(master, textvariable=self.password).grid(row=1, column=1, sticky=tk.W+tk.E, padx=5)
624 self.transient(self.parent)
635 return (self.ssid.get(), self.password.get())
638 class ProjectWindow(tk.Frame):
640 def __init__(self, parent, sdkpath, args):
641 tk.Frame.__init__(self, parent)
643 self.sdkpath = sdkpath
644 self.init_window(args)
645 self.configs = dict()
647 self.password = str()
649 def setState(self, thing, state):
650 for child in thing.winfo_children():
651 child.configure(state=state)
653 def boardtype_change_callback(self, event):
654 boardtype = self.boardtype.get()
655 if boardtype == "pico_w":
656 self.setState(self.picowSubframe, "enabled")
658 self.setState(self.picowSubframe, "disabled")
660 def wirelessSettings(self):
661 result = WirelessSettingsWindow(self)
662 self.ssid, self.password = result.get()
664 def init_window(self, args):
665 self.master.title("Raspberry Pi Pico Project Generator")
666 self.master.configure(bg=GetBackground())
670 mainFrame = tk.Frame(self, bg=GetBackground()).grid(row=optionsRow, column=0, columnspan=6, rowspan=12)
672 # Need to keep a reference to the image or it will not appear.
673 self.logo = tk.PhotoImage(file=GetFilePath("logo_alpha.gif"))
674 logowidget = ttk.Label(mainFrame, image=self.logo, borderwidth=0, relief="solid").grid(row=0,column=0, columnspan=5, pady=10)
678 namelbl = ttk.Label(mainFrame, text='Project Name :').grid(row=optionsRow, column=0, sticky=tk.E)
679 self.projectName = tk.StringVar()
681 if args.name != None:
682 self.projectName.set(args.name)
684 self.projectName.set('ProjectName')
686 nameEntry = ttk.Entry(mainFrame, textvariable=self.projectName).grid(row=optionsRow, column=1, sticky=tk.W+tk.E, padx=5)
690 locationlbl = ttk.Label(mainFrame, text='Location :').grid(row=optionsRow, column=0, sticky=tk.E)
691 self.locationName = tk.StringVar()
692 self.locationName.set(os.getcwd())
693 locationEntry = ttk.Entry(mainFrame, textvariable=self.locationName).grid(row=optionsRow, column=1, columnspan=3, sticky=tk.W+tk.E, padx=5)
694 locationBrowse = ttk.Button(mainFrame, text='Browse', command=self.browse).grid(row=3, column=4)
698 ttk.Label(mainFrame, text = "Board Type :").grid(row=optionsRow, column=0, padx=4, sticky=tk.E)
699 self.boardtype = ttk.Combobox(mainFrame, values=boardtype_list, )
700 self.boardtype.grid(row=4, column=1, padx=4, sticky=tk.W+tk.E)
701 self.boardtype.set('pico')
702 self.boardtype.bind('<<ComboboxSelected>>',self.boardtype_change_callback)
706 featuresframe = ttk.LabelFrame(mainFrame, text="Library Options", relief=tk.RIDGE, borderwidth=2)
707 featuresframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=5, ipadx=5, padx=5, pady=5, sticky=tk.E+tk.W)
709 s = (len(features_list)/3)
711 self.feature_checkbox_vars = []
714 for i in features_list:
715 var = tk.StringVar(value='') # Off by default for the moment
716 c = features_list[i][GUI_TEXT]
717 cb = ttk.Checkbutton(featuresframe, text = c, var=var, onvalue=i, offvalue='')
718 cb.grid(row=row, column=col, padx=15, pady=2, ipadx=1, ipady=1, sticky=tk.E+tk.W)
719 self.feature_checkbox_vars.append(var)
727 # PicoW options section
728 self.picowSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Pico Wireless Options")
729 self.picowSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
730 self.pico_wireless = tk.StringVar()
734 for i in picow_options_list:
735 rb = ttk.Radiobutton(self.picowSubframe, text=picow_options_list[i][GUI_TEXT], variable=self.pico_wireless, val=i)
736 rb.grid(row=row, column=col, padx=15, pady=1, sticky=tk.E+tk.W)
742 # DOnt actually need any settings at the moment.
743 # ttk.Button(self.picowSubframe, text='Settings', command=self.wirelessSettings).grid(row=0, column=4, padx=5, pady=2, sticky=tk.E)
745 self.setState(self.picowSubframe, "disabled")
749 # output options section
750 ooptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Console Options")
751 ooptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
753 self.wantUART = tk.IntVar()
754 self.wantUART.set(args.uart)
755 ttk.Checkbutton(ooptionsSubframe, text="Console over UART", variable=self.wantUART).grid(row=0, column=0, padx=4, sticky=tk.W)
757 self.wantUSB = tk.IntVar()
758 self.wantUSB.set(args.usb)
759 ttk.Checkbutton(ooptionsSubframe, text="Console over USB (Disables other USB use)", variable=self.wantUSB).grid(row=0, column=1, padx=4, sticky=tk.W)
763 # Code options section
764 coptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Code Options")
765 coptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=3, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
767 self.wantExamples = tk.IntVar()
768 self.wantExamples.set(args.examples)
769 ttk.Checkbutton(coptionsSubframe, text="Add examples for Pico library", variable=self.wantExamples).grid(row=0, column=0, padx=4, sticky=tk.W)
771 self.wantRunFromRAM = tk.IntVar()
772 self.wantRunFromRAM.set(args.runFromRAM)
773 ttk.Checkbutton(coptionsSubframe, text="Run from RAM", variable=self.wantRunFromRAM).grid(row=0, column=1, padx=4, sticky=tk.W)
775 self.wantCPP = tk.IntVar()
776 self.wantCPP.set(args.cpp)
777 ttk.Checkbutton(coptionsSubframe, text="Generate C++", variable=self.wantCPP).grid(row=0, column=3, padx=4, sticky=tk.W)
779 ttk.Button(coptionsSubframe, text="Advanced...", command=self.config).grid(row=0, column=4, sticky=tk.E)
781 self.wantCPPExceptions = tk.IntVar()
782 self.wantCPPExceptions.set(args.cppexceptions)
783 ttk.Checkbutton(coptionsSubframe, text="Enable C++ exceptions", variable=self.wantCPPExceptions).grid(row=1, column=0, padx=4, sticky=tk.W)
785 self.wantCPPRTTI = tk.IntVar()
786 self.wantCPPRTTI.set(args.cpprtti)
787 ttk.Checkbutton(coptionsSubframe, text="Enable C++ RTTI", variable=self.wantCPPRTTI).grid(row=1, column=1, padx=4, sticky=tk.W)
791 # Build Options section
793 boptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Build Options")
794 boptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
796 self.wantBuild = tk.IntVar()
797 self.wantBuild.set(args.build)
798 ttk.Checkbutton(boptionsSubframe, text="Run build after generation", variable=self.wantBuild).grid(row=0, column=0, padx=4, sticky=tk.W)
800 self.wantOverwrite = tk.IntVar()
801 self.wantOverwrite.set(args.overwrite)
802 ttk.Checkbutton(boptionsSubframe, text="Overwrite existing projects", variable=self.wantOverwrite).grid(row=0, column=1, padx=4, sticky=tk.W)
806 # IDE Options section
808 vscodeoptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="IDE Options")
809 vscodeoptionsSubframe.grid_columnconfigure(2, weight=1)
810 vscodeoptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
812 self.wantVSCode = tk.IntVar()
813 if args.project is None:
814 self.wantVSCode.set(False)
816 self.wantVSCode.set('vscode' in args.project)
817 ttk.Checkbutton(vscodeoptionsSubframe, text="Create VSCode project", variable=self.wantVSCode).grid(row=0, column=0, padx=4, sticky=tk.W)
819 ttk.Label(vscodeoptionsSubframe, text = " Debugger:").grid(row=0, column=1, padx=4, sticky=tk.W)
821 self.debugger = ttk.Combobox(vscodeoptionsSubframe, values=debugger_list, state="readonly")
822 self.debugger.grid(row=0, column=2, padx=4, sticky=tk.EW)
823 self.debugger.current(args.debugger)
827 # OK, Cancel, Help section
829 QuitButton = ttk.Button(mainFrame, text="Quit", command=self.quit).grid(row=optionsRow, column=4, stick=tk.E, padx=10, pady=5)
830 OKButton = ttk.Button(mainFrame, text="OK", command=self.OK).grid(row=optionsRow, column=3, padx=4, pady=5, sticky=tk.E)
832 # TODO help not implemented yet
833 # HelpButton = ttk.Button(mainFrame, text="Help", command=self.help).grid(row=optionsRow, column=0, pady=5)
835 # You can set a default path here, replace the string with whereever you want.
836 # self.locationName.set('/home/pi/pico_projects')
838 def GetFeatures(self):
842 for cb in self.feature_checkbox_vars:
847 picow_extra = self.pico_wireless.get()
849 if picow_extra != 'picow_none':
850 features.append(picow_extra)
855 # TODO Check if we want to exit here
859 # OK, grab all the settings from the page, then call the generators
860 projectPath = self.locationName.get()
861 features = self.GetFeatures()
863 if (self.wantVSCode.get()):
864 projects.append("vscode")
867 'sdkPath' : self.sdkpath,
868 'projectRoot' : Path(projectPath),
869 'projectName' : self.projectName.get(),
871 'wantOverwrite' : self.wantOverwrite.get(),
872 'wantBuild' : self.wantBuild.get(),
873 'boardtype' : self.boardtype.get(),
874 'features' : features,
875 'projects' : projects,
876 'configs' : self.configs,
877 'wantRunFromRAM': self.wantRunFromRAM.get(),
878 'wantExamples' : self.wantExamples.get(),
879 'wantUART' : self.wantUART.get(),
880 'wantUSB' : self.wantUSB.get(),
881 'wantCPP' : self.wantCPP.get(),
882 'debugger' : self.debugger.current(),
883 'exceptions' : self.wantCPPExceptions.get(),
884 'rtti' : self.wantCPPRTTI.get(),
886 'password' : self.password,
889 DoEverything(self, params)
892 name = fd.askdirectory()
893 self.locationName.set(name)
899 # Run the configuration window
900 self.configs = ConfigurationWindow(self, self.configs).get()
902 def CheckPrerequisites():
903 global isMac, isWindows
904 isMac = (platform.system() == 'Darwin')
905 isWindows = (platform.system() == 'Windows')
907 # Do we have a compiler?
908 return shutil.which(COMPILER_NAME, 1, os.environ["Path" if isWindows else "PATH"])
911 def CheckSDKPath(gui):
912 sdkPath = os.getenv('PICO_SDK_PATH')
915 m = 'Unable to locate the Raspberry Pi Pico SDK, PICO_SDK_PATH is not set'
916 if gui and ENABLE_TK_GUI:
920 elif not os.path.isdir(sdkPath):
921 m = 'Unable to locate the Raspberry Pi Pico SDK, PICO_SDK_PATH does not point to a directory'
922 if gui and ENABLE_TK_GUI:
930 def GetFilePath(filename):
931 if os.path.islink(__file__):
932 script_file = os.readlink(__file__)
934 script_file = __file__
935 return os.path.join(os.path.dirname(script_file), filename)
937 def ParseCommandLine():
938 debugger_flags = ', '.join('{} = {}'.format(i, v) for i, v in enumerate(debugger_list))
939 parser = argparse.ArgumentParser(description='Pico Project generator')
940 parser.add_argument("name", nargs="?", help="Name of the project")
941 parser.add_argument("-t", "--tsv", help="Select an alternative pico_configs.tsv file", default=GetFilePath("pico_configs.tsv"))
942 parser.add_argument("-o", "--output", help="Set an alternative CMakeList.txt filename", default="CMakeLists.txt")
943 parser.add_argument("-x", "--examples", action='store_true', help="Add example code for the Pico standard library")
944 parser.add_argument("-l", "--list", action='store_true', help="List available features")
945 parser.add_argument("-c", "--configs", action='store_true', help="List available project configuration items")
946 parser.add_argument("-f", "--feature", action='append', help="Add feature to generated project")
947 parser.add_argument("-over", "--overwrite", action='store_true', help="Overwrite any existing project AND files")
948 parser.add_argument("-b", "--build", action='store_true', help="Build after project created")
949 parser.add_argument("-g", "--gui", action='store_true', help="Run a GUI version of the project generator")
950 parser.add_argument("-p", "--project", action='append', help="Generate projects files for IDE. Options are: vscode")
951 parser.add_argument("-r", "--runFromRAM", action='store_true', help="Run the program from RAM rather than flash")
952 parser.add_argument("-uart", "--uart", action='store_true', default=1, help="Console output to UART (default)")
953 parser.add_argument("-nouart", "--nouart", action='store_true', default=0, help="Disable console output to UART")
954 parser.add_argument("-usb", "--usb", action='store_true', help="Console output to USB (disables other USB functionality")
955 parser.add_argument("-cpp", "--cpp", action='store_true', default=0, help="Generate C++ code")
956 parser.add_argument("-cpprtti", "--cpprtti", action='store_true', default=0, help="Enable C++ RTTI (Uses more memory)")
957 parser.add_argument("-cppex", "--cppexceptions", action='store_true', default=0, help="Enable C++ exceptions (Uses more memory)")
958 parser.add_argument("-d", "--debugger", type=int, help="Select debugger ({})".format(debugger_flags), default=0)
959 parser.add_argument("-board", "--boardtype", action="store", default='pico', help="Select board type (see --boardlist for available boards)")
960 parser.add_argument("-bl", "--boardlist", action="store_true", help="List available board types")
961 parser.add_argument("-cp", "--cpath", help="Override default VSCode compiler path")
962 parser.add_argument("-root", "--projectRoot", help="Override default project root where the new project will be created")
963 parser.add_argument("-sdkVersion", "--sdkVersion", help="Pico SDK version to use (required)")
964 parser.add_argument("-tcVersion", "--toolchainVersion", help="ARM Embeded Toolchain version to use (required)")
965 parser.add_argument("-np", "--ninjaPath", help="Ninja path")
966 parser.add_argument("-cmp", "--cmakePath", help="CMake path")
967 parser.add_argument("-cupy", "--customPython", action='store_true', help="Custom python path used to execute the script.")
969 return parser.parse_args()
972 def GenerateMain(folder, projectName, features, cpp):
975 filename = Path(folder) / (projectName + '.cpp')
977 filename = Path(folder) / (projectName + '.c')
979 file = open(filename, 'w')
981 main = ('#include <stdio.h>\n'
982 '#include "pico/stdlib.h"\n'
989 for feat in features:
990 if (feat in features_list):
991 o = f'#include "{features_list[feat][H_FILE]}"\n'
993 if (feat in stdlib_examples_list):
994 o = f'#include "{stdlib_examples_list[feat][H_FILE]}"\n'
996 if (feat in picow_options_list):
997 o = f'#include "{picow_options_list[feat][H_FILE]}"\n'
1003 for feat in features:
1004 if (feat in code_fragments_per_feature):
1005 for s in code_fragments_per_feature[feat][DEFINES]:
1013 ' stdio_init_all();\n\n'
1017 # Add any initialisers
1019 for feat in features:
1020 if (feat in code_fragments_per_feature):
1021 for s in code_fragments_per_feature[feat][INITIALISERS]:
1022 main += (" " * indent)
1027 main += (' puts("Hello, world!");\n\n'
1037 def GenerateCMake(folder, params):
1039 filename = Path(folder) / CMAKELIST_FILENAME
1040 projectName = params['projectName']
1041 board_type = params['boardtype']
1043 # OK, for the path, CMake will accept forward slashes on Windows, and thats
1044 # seemingly a bit easier to handle than the backslashes
1045 p = str(params['sdkPath']).replace('\\','/')
1047 cmake_header1 = (f"# Generated Cmake Pico project file\n\n"
1048 "cmake_minimum_required(VERSION 3.13)\n\n"
1049 "set(CMAKE_C_STANDARD 11)\n"
1050 "set(CMAKE_CXX_STANDARD 17)\n\n"
1051 "# Initialise pico_sdk from installed location\n"
1052 "# (note this can come from environment, CMake cache etc)\n\n"
1053 "# == DO NEVER EDIT THE NEXT LINES for RaspberryPiPico VS Code Extension to work == \n"
1055 " set(USERHOME $ENV{USERPROFILE})\n"
1057 " set(USERHOME $ENV{HOME})\n"
1059 f"set(PICO_SDK_PATH {cmakeSdkPath(params['sdkVersion'])})\n"
1060 f"set(PICO_TOOLCHAIN_PATH {cmakeToolchainPath(params['toolchainVersion'])})\n"
1062 f" set(pico-sdk-tools_DIR {cmakeToolsPath(params['sdkVersion'])})\n"
1063 " include(${pico-sdk-tools_DIR}/pico-sdk-tools-config.cmake)\n"
1064 " include(${pico-sdk-tools_DIR}/pico-sdk-tools-config-version.cmake)\n"
1066 "# ====================================================================================\n"
1067 f"set(PICO_BOARD {board_type} CACHE STRING \"Board type\")\n\n"
1068 "# Pull in Raspberry Pi Pico SDK (must be before project)\n"
1069 "include(pico_sdk_import.cmake)\n\n"
1070 "if (PICO_SDK_VERSION_STRING VERSION_LESS \"1.4.0\")\n"
1071 " message(FATAL_ERROR \"Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}\")\n"
1073 f"project({projectName} C CXX ASM)\n"
1077 "\n# Initialise the Raspberry Pi Pico SDK\n"
1078 "pico_sdk_init()\n\n"
1079 "# Add executable. Default name is the project name, version 0.1\n\n"
1083 file = open(filename, 'w')
1085 file.write(cmake_header1)
1087 if params['exceptions']:
1088 file.write("\nset(PICO_CXX_ENABLE_EXCEPTIONS 1)\n")
1091 file.write("\nset(PICO_CXX_ENABLE_RTTI 1)\n")
1093 file.write(cmake_header3)
1095 # add the preprocessor defines for overall configuration
1096 if params['configs']:
1097 file.write('# Add any PICO_CONFIG entries specified in the Advanced settings\n')
1098 for c, v in params['configs'].items():
1103 file.write(f'add_compile_definitions({c} = {v})\n')
1106 # No GUI/command line to set a different executable name at this stage
1107 executableName = projectName
1109 if params['wantCPP']:
1110 file.write(f'add_executable({projectName} {projectName}.cpp )\n\n')
1112 file.write(f'add_executable({projectName} {projectName}.c )\n\n')
1114 file.write(f'pico_set_program_name({projectName} "{executableName}")\n')
1115 file.write(f'pico_set_program_version({projectName} "0.1")\n\n')
1117 if params['wantRunFromRAM']:
1118 file.write(f'# no_flash means the target is to run from RAM\n')
1119 file.write(f'pico_set_binary_type({projectName} no_flash)\n\n')
1121 # Console output destinations
1122 if params['wantUART']:
1123 file.write(f'pico_enable_stdio_uart({projectName} 1)\n')
1125 file.write(f'pico_enable_stdio_uart({projectName} 0)\n')
1127 if params['wantUSB']:
1128 file.write(f'pico_enable_stdio_usb({projectName} 1)\n\n')
1130 file.write(f'pico_enable_stdio_usb({projectName} 0)\n\n')
1132 # If we need wireless, check for SSID and password
1133 # removed for the moment as these settings are currently only needed for the pico-examples
1134 # but may be required in here at a later date.
1136 if 'ssid' in params or 'password' in params:
1137 file.write('# Add any wireless access point information\n')
1138 file.write(f'target_compile_definitions({projectName} PRIVATE\n')
1139 if 'ssid' in params:
1140 file.write(f'WIFI_SSID=\" {params["ssid"]} \"\n')
1142 file.write(f'WIFI_SSID=\"${WIFI_SSID}\"')
1144 if 'password' in params:
1145 file.write(f'WIFI_PASSWORD=\"{params["password"]}\"\n')
1147 file.write(f'WIFI_PASSWORD=\"${WIFI_PASSWORD}\"')
1150 # Standard libraries
1151 file.write('# Add the standard library to the build\n')
1152 file.write(f'target_link_libraries({projectName}\n')
1153 file.write(" " + STANDARD_LIBRARIES)
1156 # Standard include directories
1157 file.write('# Add the standard include files to the build\n')
1158 file.write(f'target_include_directories({projectName} PRIVATE\n')
1159 file.write(" ${CMAKE_CURRENT_LIST_DIR}\n")
1160 file.write(" ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required\n")
1163 # Selected libraries/features
1164 if (params['features']):
1165 file.write('# Add any user requested libraries\n')
1166 file.write(f'target_link_libraries({projectName} \n')
1167 for feat in params['features']:
1168 if (feat in features_list):
1169 file.write(" " + features_list[feat][LIB_NAME] + '\n')
1170 if (feat in picow_options_list):
1171 file.write(" " + picow_options_list[feat][LIB_NAME] + '\n')
1172 file.write(' )\n\n')
1174 file.write(f'pico_add_extra_outputs({projectName})\n\n')
1179 # Generates the requested project files, if any
1180 def generateProjectFiles(projectPath, projectName, sdkPath, projects, debugger, sdkVersion, toolchainVersion, ninjaPath, cmakePath, customPython):
1182 oldCWD = os.getcwd()
1184 os.chdir(projectPath)
1186 debugger = debugger_config_list[debugger]
1187 gdbPath = Path(codeToolchainPath(toolchainVersion)+"/bin/arm-none-eabi-gdb").as_posix() if isWindows else "gdb-multiarch"
1188 # Need to escape windows files paths backslashes
1189 # TODO: env in currently not supported in compilerPath var
1190 #cPath = f"${{env:PICO_TOOLCHAIN_PATH_{envSuffix}}}" + os.path.sep + os.path.basename(str(compilerPath).replace('\\', '\\\\' ))
1191 cPath = compilerPath.as_posix()
1193 # if this is a path in the .pico-sdk homedir tell the settings to use the homevar
1194 user_home = os.path.expanduser("~").replace("\\", "/")
1195 use_home_var = f"{user_home}/.pico-sdk" in ninjaPath
1203 "name": "Pico Debug (Cortex-Debug)",
1204 "cwd": "${{workspaceRoot}}",
1205 "executable": "${{command:raspberry-pi-pico.launchTargetPath}}",
1206 "request": "launch",
1207 "type": "cortex-debug",
1208 "servertype": "openocd",
1209 "gdbPath": "{gdbPath}",
1212 "interface/{debugger}",
1215 "svdFile": "{codeSdkPath(sdkVersion)}/src/rp2040/hardware_regs/rp2040.svd",
1216 "runToEntryPoint": "main",
1217 // Give restart the same functionality as runToEntryPoint - main
1218 "postRestartCommands": [
1222 "openOCDLaunchCommands": [
1223 "adapter speed 1000"
1225 "preLaunchTask": "Compile Project"
1228 "name": "Pico Debug (Cortex-Debug with external OpenOCD)",
1229 "cwd": "${{workspaceRoot}}",
1230 "executable": "${{command:raspberry-pi-pico.launchTargetPath}}",
1231 "request": "launch",
1232 "type": "cortex-debug",
1233 "servertype": "external",
1234 "gdbTarget": "localhost:3333",
1235 "gdbPath": "{gdbPath}",
1237 "svdFile": "{codeSdkPath(sdkVersion)}/src/rp2040/hardware_regs/rp2040.svd",
1238 "runToEntryPoint": "main",
1239 // Give restart the same functionality as runToEntryPoint - main
1240 "postRestartCommands": [
1244 "preLaunchTask": "Compile Project"
1247 "name": "Pico Debug (C++ Debugger)",
1249 "request": "launch",
1250 "cwd": "${{workspaceRoot}}",
1251 "program": "${{command:raspberry-pi-pico.launchTargetPath}}",
1253 "miDebuggerPath": "{gdbPath}",
1254 "miDebuggerServerAddress": "localhost:3333",
1255 "debugServerPath": "openocd",
1256 "debugServerArgs": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \\"adapter speed 1000\\"",
1257 "serverStarted": "Listening on port .* for gdb connections",
1258 "filterStderr": true,
1259 "stopAtEntry": true,
1260 "hardwareBreakpoints": {{
1264 "preLaunchTask": "Flash",
1265 "svdPath": "{codeSdkPath(sdkVersion)}/src/rp2040/hardware_regs/rp2040.svd"
1276 "${{workspaceFolder}}/**",
1277 "{propertiesSdkPath(sdkVersion)}/**"
1280 "compilerPath": "{cPath}",
1282 "cppStandard": "c++14",
1283 "intelliSenseMode": "linux-gcc-arm"
1290 pythonExe = sys.executable.replace("\\", "/").replace(user_home, "${HOME}") if use_home_var else sys.executable
1294 "cmake.statusbar.visibility": "hidden",
1295 "cmake.configureOnEdit": false,
1296 "cmake.automaticReconfigure": false,
1297 "cmake.configureOnOpen": false,
1298 "cmake.generator": "Ninja",
1299 "cmake.cmakePath": "{cmakePath}",
1300 "raspberry-pi-pico.cmakeAutoConfigure": true,
1301 "raspberry-pi-pico.cmakePath": "{cmakePath.replace(user_home, "${HOME}") if use_home_var else cmakePath}",
1302 "raspberry-pi-pico.ninjaPath": "{ninjaPath.replace(user_home, "${HOME}") if use_home_var else ninjaPath}"'''
1306 "raspberry-pi-pico.python3Path": "{pythonExe}"'''
1312 "recommendations": [
1313 "marus25.cortex-debug",
1314 "ms-vscode.cpptools",
1315 "ms-vscode.cpptools-extension-pack",
1316 "ms-vscode.vscode-serial-monitor",
1317 "paulober.raspberry-pi-pico",
1325 "label": "Compile Project",
1327 "command": "{ninjaPath.replace(user_home, "${userHome}") if use_home_var else ninjaPath}",
1328 "args": ["-C", "${{workspaceFolder}}/build"],
1332 "panel": "dedicated"
1334 "problemMatcher": "$gcc"
1340 # Create a build folder, and run our cmake project build from it
1341 if not os.path.exists(VSCODE_FOLDER):
1342 os.mkdir(VSCODE_FOLDER)
1344 os.chdir(VSCODE_FOLDER)
1346 file = open(VSCODE_TASKS_FILENAME, 'w')
1350 filename = VSCODE_LAUNCH_FILENAME
1351 file = open(filename, 'w')
1355 file = open(VSCODE_C_PROPERTIES_FILENAME, 'w')
1356 file.write(properties)
1359 file = open(VSCODE_SETTINGS_FILENAME, 'w')
1360 file.write(settings)
1363 file = open(VSCODE_EXTENSIONS_FILENAME, 'w')
1364 file.write(extensions)
1368 print('Unknown project type requested')
1373 def LoadConfigurations():
1375 with open(args.tsv) as tsvfile:
1376 reader = csv.DictReader(tsvfile, dialect='excel-tab')
1378 configuration_dictionary.append(row)
1380 print("No Pico configurations file found. Continuing without")
1382 def LoadBoardTypes(sdkPath):
1383 # Scan the boards folder for all header files, extract filenames, and make a list of the results
1384 # default folder is <PICO_SDK_PATH>/src/boards/include/boards/*
1385 # If the PICO_BOARD_HEADER_DIRS environment variable is set, use that as well
1387 loc = sdkPath / "src/boards/include/boards"
1389 for x in Path(loc).iterdir():
1390 if x.suffix == '.h':
1391 boards.append(x.stem)
1393 loc = os.getenv('PICO_BOARD_HEADER_DIRS')
1396 for x in Path(loc).iterdir():
1397 if x.suffix == '.h':
1398 boards.append(x.stem)
1402 def DoEverything(parent, params):
1404 if not os.path.exists(params['projectRoot']):
1405 if params['wantGUI'] and ENABLE_TK_GUI:
1406 mb.showerror('Raspberry Pi Pico Project Generator', 'Invalid project path. Select a valid path and try again')
1409 print('Invalid project path')
1412 oldCWD = os.getcwd()
1413 os.chdir(params['projectRoot'])
1415 # Create our project folder as subfolder
1416 os.makedirs(params['projectName'], exist_ok=True)
1418 os.chdir(params['projectName'])
1420 projectPath = params['projectRoot'] / params['projectName']
1422 # First check if there is already a project in the folder
1423 # If there is we abort unless the overwrite flag it set
1424 if os.path.exists(CMAKELIST_FILENAME):
1425 if not params['wantOverwrite'] :
1426 if params['wantGUI'] and ENABLE_TK_GUI:
1427 # We can ask the user if they want to overwrite
1428 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')
1432 print('There already appears to be a project in this folder. Use the --overwrite option to overwrite the existing project')
1435 # We should really confirm the user wants to overwrite
1436 #print('Are you sure you want to overwrite the existing project files? (y/N)')
1437 #c = input().split(" ")[0]
1438 #if c != 'y' and c != 'Y' :
1441 # Copy the SDK finder cmake file to our project folder
1442 # Can be found here <PICO_SDK_PATH>/external/pico_sdk_import.cmake
1443 shutil.copyfile(params['sdkPath'] / 'external' / 'pico_sdk_import.cmake', projectPath / 'pico_sdk_import.cmake' )
1445 if params['features']:
1446 features_and_examples = params['features'][:]
1448 features_and_examples= []
1450 if params['wantExamples']:
1451 features_and_examples = list(stdlib_examples_list.keys()) + features_and_examples
1453 GenerateMain('.', params['projectName'], features_and_examples, params['wantCPP'])
1455 GenerateCMake('.', params)
1457 # If we have any ancilliary files, copy them to our project folder
1458 # Currently only the picow with lwIP support needs an extra file, so just check that list
1459 for feat in features_and_examples:
1460 if feat in picow_options_list:
1461 if picow_options_list[feat][ANCILLARY_FILE] != "":
1462 shutil.copy(sourcefolder + "/" + picow_options_list[feat][ANCILLARY_FILE], projectPath / picow_options_list[feat][ANCILLARY_FILE])
1464 # Create a build folder, and run our cmake project build from it
1465 if not os.path.exists('build'):
1470 # If we are overwriting a previous project, we should probably clear the folder, but that might delete something the users thinks is important, so
1471 # for the moment, just delete the CMakeCache.txt file as certain changes may need that to be recreated.
1473 if os.path.exists(CMAKECACHE_FILENAME):
1474 os.remove(CMAKECACHE_FILENAME)
1476 cpus = os.cpu_count()
1481 if shutil.which("ninja") or (params["ninjaPath"] != None and params["ninjaPath"] != ""):
1482 # When installing SDK version 1.5.0 on windows with installer pico-setup-windows-x64-standalone.exe, ninja is used
1483 cmakeCmd = params['cmakePath'] + ' -DCMAKE_BUILD_TYPE=Debug -G Ninja ..'
1484 makeCmd = params['ninjaPath'] + ' '
1486 # Everything else assume nmake
1487 cmakeCmd = params['cmakePath'] + ' -DCMAKE_BUILD_TYPE=Debug -G "NMake Makefiles" ..'
1490 # Ninja now works OK under Linux, so if installed use it by default. It's faster.
1491 if shutil.which("ninja") or (params["ninjaPath"] != None and params["ninjaPath"] != ""):
1492 cmakeCmd = params['cmakePath'] + ' -DCMAKE_BUILD_TYPE=Debug -G Ninja ..'
1493 makeCmd = params['ninjaPath'] + ' '
1495 cmakeCmd = params['cmakePath'] + ' -DCMAKE_BUILD_TYPE=Debug ..'
1496 makeCmd = 'make -j' + str(cpus)
1498 if params['wantGUI'] and ENABLE_TK_GUI:
1499 RunCommandInWindow(parent, cmakeCmd)
1503 if params['projects']:
1504 generateProjectFiles(
1506 params['projectName'],
1510 params["sdkVersion"],
1511 params["toolchainVersion"],
1512 params["ninjaPath"],
1513 params["cmakePath"],
1514 params["customPython"])
1516 if params['wantBuild']:
1517 if params['wantGUI'] and ENABLE_TK_GUI:
1518 RunCommandInWindow(parent, makeCmd)
1521 print('\nIf the application has built correctly, you can now transfer it to the Raspberry Pi Pico board')
1526 ###################################################################################
1527 # main execution starteth here
1529 sourcefolder = os.path.dirname(os.path.abspath(__file__))
1531 args = ParseCommandLine()
1536 if args.debugger > len(debugger_list) - 1:
1539 # Check we have everything we need to compile etc
1540 c = CheckPrerequisites()
1542 ## TODO Do both warnings in the same error message so user does have to keep coming back to find still more to do
1545 m = f'Unable to find the `{COMPILER_NAME}` compiler\n'
1546 m +='You will need to install an appropriate compiler to build a Raspberry Pi Pico project\n'
1547 m += 'See the Raspberry Pi Pico documentation for how to do this on your particular platform\n'
1549 if args.gui and ENABLE_TK_GUI:
1555 if args.name == None and not args.gui and not args.list and not args.configs and not args.boardlist:
1556 print("No project name specfied\n")
1559 # Check if we were provided a compiler path, and override the default if so
1561 compilerPath = Path(args.cpath)
1562 elif args.toolchainVersion:
1563 compilerPath = Path(propertiesToolchainPath(args.toolchainVersion)+"/bin/"+COMPILER_NAME)
1565 compilerPath = Path(c)
1567 # load/parse any configuration dictionary we may have
1568 LoadConfigurations()
1570 p = CheckSDKPath(args.gui)
1577 boardtype_list = LoadBoardTypes(sdkPath)
1578 boardtype_list.sort()
1580 if args.gui and ENABLE_TK_GUI:
1581 RunGUI(sdkPath, args) # does not return, only exits
1583 projectRoot = Path(os.getcwd()) if not args.projectRoot else Path(args.projectRoot)
1585 if args.list or args.configs or args.boardlist:
1587 print("Available project features:\n")
1588 for feat in features_list:
1589 print(feat.ljust(6), '\t', features_list[feat][GUI_TEXT])
1593 print("Available project configuration items:\n")
1594 for conf in configuration_dictionary:
1595 print(conf['name'].ljust(40), '\t', conf['description'])
1599 print("Available board types:\n")
1600 for board in boardtype_list:
1607 'sdkPath' : sdkPath,
1608 'projectRoot' : projectRoot,
1609 'projectName' : args.name,
1611 'wantOverwrite' : args.overwrite,
1612 'boardtype' : args.boardtype,
1613 'wantBuild' : args.build,
1614 'features' : args.feature,
1615 'projects' : args.project,
1617 'wantRunFromRAM': args.runFromRAM,
1618 'wantExamples' : args.examples,
1619 'wantUART' : args.uart,
1620 'wantUSB' : args.usb,
1621 'wantCPP' : args.cpp,
1622 'debugger' : args.debugger,
1623 'exceptions' : args.cppexceptions,
1624 'rtti' : args.cpprtti,
1627 'sdkVersion' : args.sdkVersion,
1628 'toolchainVersion': args.toolchainVersion,
1629 'ninjaPath' : args.ninjaPath,
1630 'cmakePath' : args.cmakePath,
1631 'customPython' : args.customPython
1634 DoEverything(None, params)