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")
230 def GetButtonBackground():
236 def GetButtonTextColour():
240 def RunGUI(sdkpath, args):
242 style = ttk.Style(root)
243 style.theme_use('default')
245 style.configure("TButton", padding=6, relief="groove", border=2, foreground=GetButtonTextColour(), background=GetButtonBackground())
246 style.configure("TLabel", foreground=GetTextColour(), background=GetBackground() )
247 style.configure("TCheckbutton", foreground=GetTextColour(), background=GetBackground())
248 style.configure("TRadiobutton", foreground=GetTextColour(), background=GetBackground() )
249 style.configure("TLabelframe", foreground=GetTextColour(), background=GetBackground() )
250 style.configure("TLabelframe.Label", foreground=GetTextColour(), background=GetBackground() )
251 style.configure("TCombobox", foreground=GetTextColour(), background=GetBackground() )
252 style.configure("TListbox", foreground=GetTextColour(), background=GetBackground() )
254 style.map("TCheckbutton", background = [('disabled', GetBackground())])
255 style.map("TRadiobutton", background = [('disabled', GetBackground())])
256 style.map("TButton", background = [('disabled', GetBackground())])
257 style.map("TLabel", background = [('background', GetBackground())])
258 style.map("TComboBox", background = [('readonly', GetBackground())])
260 app = ProjectWindow(root, sdkpath, args)
262 app.configure(background=GetBackground())
268 def RunWarning(message):
269 mb.showwarning('Raspberry Pi Pico Project Generator', message)
275 def thread_function(text, command, ok):
276 l = shlex.split(command)
277 proc = subprocess.Popen(l, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
278 for line in iter(proc.stdout.readline,''):
281 ok["state"] = tk.NORMAL
283 text.insert(tk.END, line)
286 # Function to run an OS command and display the output in a new modal window
288 class DisplayWindow(tk.Toplevel):
289 def __init__(self, parent, title):
290 tk.Toplevel.__init__(self, parent)
292 self.init_window(title)
294 def init_window(self, title):
297 frame = tk.Frame(self, borderwidth=5, relief=tk.RIDGE)
298 frame.pack(fill=tk.X, expand=True, side=tk.TOP)
300 scrollbar = tk.Scrollbar(frame)
301 self.text = tk.Text(frame, bg='gray14', fg='gray99')
302 scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
303 self.text.pack(side=tk.LEFT, fill=tk.Y)
304 scrollbar.config(command=self.text.yview)
305 self.text.config(yscrollcommand=scrollbar.set)
307 frame1 = tk.Frame(self, borderwidth=1)
308 frame1.pack(fill=tk.X, expand=True, side=tk.BOTTOM)
309 self.OKButton = ttk.Button(frame1, text="OK", command=self.OK)
310 self.OKButton["state"] = tk.DISABLED
314 self.transient(self.parent)
321 def RunCommandInWindow(parent, command):
322 w = DisplayWindow(parent, command)
323 x = threading.Thread(target=thread_function, args=(w.text, command, w.OKButton))
325 parent.wait_window(w)
327 class EditBoolWindow(sd.Dialog):
329 def __init__(self, parent, configitem, current):
331 self.config_item = configitem
332 self.current = current
333 sd.Dialog.__init__(self, parent, "Edit boolean configuration")
336 def body(self, master):
337 self.configure(background=GetBackground())
338 ttk.Label(self, text=self.config_item['name']).pack()
339 self.result = tk.StringVar()
340 self.result.set(self.current)
341 ttk.Radiobutton(master, text="True", variable=self.result, value="True").pack(anchor=tk.W)
342 ttk.Radiobutton(master, text="False", variable=self.result, value="False").pack(anchor=tk.W)
343 ttk.Radiobutton(master, text=CONFIG_UNSET, variable=self.result, value=CONFIG_UNSET).pack(anchor=tk.W)
346 return self.result.get()
348 class EditIntWindow(sd.Dialog):
350 def __init__(self, parent, configitem, current):
352 self.config_item = configitem
353 self.current = current
354 sd.Dialog.__init__(self, parent, "Edit integer configuration")
356 def body(self, master):
357 self.configure(background=GetBackground())
358 str = self.config_item['name'] + " Max = " + self.config_item['max'] + " Min = " + self.config_item['min']
359 ttk.Label(self, text=str).pack()
360 self.input = tk.Entry(self)
361 self.input.pack(pady=4)
362 self.input.insert(0, self.current)
363 ttk.Button(self, text=CONFIG_UNSET, command=self.unset).pack(pady=5)
366 self.result = self.input.get()
367 # Check for numeric entry
371 self.result = CONFIG_UNSET
377 class EditEnumWindow(sd.Dialog):
378 def __init__(self, parent, configitem, current):
380 self.config_item = configitem
381 self.current = current
382 sd.Dialog.__init__(self, parent, "Edit Enumeration configuration")
384 def body(self, master):
385 #self.configure(background=GetBackground())
386 values = self.config_item['enumvalues'].split('|')
387 values.insert(0,'Not set')
388 self.input = ttk.Combobox(self, values=values, state='readonly')
389 self.input.set(self.current)
390 self.input.pack(pady=12)
393 self.result = self.input.get()
400 class ConfigurationWindow(tk.Toplevel):
402 def __init__(self, parent, initial_config):
403 tk.Toplevel.__init__(self, parent)
405 self.results = initial_config
406 self.init_window(self)
408 def init_window(self, args):
409 self.configure(background=GetBackground())
410 self.title("Advanced Configuration")
411 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)
412 ttk.Label(self, text="Name").grid(row=1, column=0, sticky=tk.W)
413 ttk.Label(self, text="Type").grid(row=1, column=1, sticky=tk.W)
414 ttk.Label(self, text="Min").grid(row=1, column=2, sticky=tk.W)
415 ttk.Label(self, text="Max").grid(row=1, column=3, sticky=tk.W)
416 ttk.Label(self, text="Default").grid(row=1, column=4, sticky=tk.W)
417 ttk.Label(self, text="User").grid(row=1, column=5, sticky=tk.W)
419 okButton = ttk.Button(self, text="OK", command=self.ok)
420 cancelButton = ttk.Button(self, text="Cancel", command=self.cancel)
422 self.namelist = tk.Listbox(self, selectmode=tk.SINGLE)
423 self.typelist = tk.Listbox(self, selectmode=tk.SINGLE)
424 self.minlist = tk.Listbox(self, selectmode=tk.SINGLE)
425 self.maxlist = tk.Listbox(self, selectmode=tk.SINGLE)
426 self.defaultlist = tk.Listbox(self, selectmode=tk.SINGLE)
427 self.valuelist = tk.Listbox(self, selectmode=tk.SINGLE)
429 self.descriptionText = tk.Text(self, state=tk.DISABLED, height=2)
431 ## Make a list of our list boxes to make it all easier to handle
432 self.listlist = [self.namelist, self.typelist, self.minlist, self.maxlist, self.defaultlist, self.valuelist]
434 scroll = tk.Scrollbar(self, orient=tk.VERTICAL, command=self.yview)
436 for box in self.listlist:
438 box.config(yscrollcommand=scroll.set)
439 box.bind("<MouseWheel>", self.mousewheel)
440 box.bind("<Button-4>", self.mousewheel)
441 box.bind("<Button-5>", self.mousewheel)
442 box.bind("<<ListboxSelect>>", self.changeSelection)
443 box.bind("<Double-Button>", self.doubleClick)
444 box.config(exportselection=False)
445 box.bind("<Down>", self.OnEntryUpDown)
446 box.bind("<Up>", self.OnEntryUpDown)
448 scroll.grid(column=7, sticky=tk.N + tk.S)
451 for box in self.listlist:
452 box.grid(row=2, column=i, padx=0, sticky=tk.W + tk.E)
455 self.descriptionText.grid(row = 3, column=0, columnspan=4, sticky=tk.W + tk.E)
456 cancelButton.grid(column=5, row = 3, padx=5)
457 okButton.grid(column=4, row = 3, sticky=tk.E, padx=5)
459 # populate the list box with our config options
460 for conf in configuration_dictionary:
461 self.namelist.insert(tk.END, conf['name'])
465 self.typelist.insert(tk.END, s)
466 self.maxlist.insert(tk.END, conf['max'])
467 self.minlist.insert(tk.END, conf['min'])
468 self.defaultlist.insert(tk.END, conf['default'])
470 # see if this config has a setting, our results member has this predefined from init
471 val = self.results.get(conf['name'], CONFIG_UNSET)
472 self.valuelist.insert(tk.END, val)
473 if val != CONFIG_UNSET:
474 self.valuelist.itemconfig(self.valuelist.size() - 1, {'bg':'green'})
476 def yview(self, *args):
477 for box in self.listlist:
480 def mousewheel(self, event):
481 if (event.num == 4): # Linux encodes wheel as 'buttons' 4 and 5
483 elif (event.num == 5):
485 else: # Windows & OSX
488 for box in self.listlist:
489 box.yview("scroll", delta, "units")
492 def changeSelection(self, evt):
494 sellist = box.curselection()
497 index = int(sellist[0])
498 config = self.namelist.get(index)
499 # Now find the description for that config in the dictionary
500 for conf in configuration_dictionary:
501 if conf['name'] == config:
502 self.descriptionText.config(state=tk.NORMAL)
503 self.descriptionText.delete(1.0,tk.END)
504 str = config + "\n" + conf['description']
505 self.descriptionText.insert(1.0, str)
506 self.descriptionText.config(state=tk.DISABLED)
508 # Set all the other list boxes to the same index
509 for b in self.listlist:
511 b.selection_clear(0, tk.END)
512 b.selection_set(index)
514 def OnEntryUpDown(self, event):
516 selection = box.curselection()
519 index = int(selection[0])
520 if event.keysym == 'Up':
522 elif event.keysym == 'Down':
525 if 0 <= index < box.size():
526 for b in self.listlist:
527 b.selection_clear(0, tk.END)
528 b.selection_set(index)
531 def doubleClick(self, evt):
533 index = int(box.curselection()[0])
534 config = self.namelist.get(index)
535 # Get the associated dict entry from our list of configs
536 for conf in configuration_dictionary:
537 if conf['name'] == config:
538 if (conf['type'] == 'bool'):
539 result = EditBoolWindow(self, conf, self.valuelist.get(index)).get()
540 elif (conf['type'] == 'int' or conf['type'] == ""): # "" defaults to int
541 result = EditIntWindow(self, conf, self.valuelist.get(index)).get()
542 elif conf['type'] == 'enum':
543 result = EditEnumWindow(self, conf, self.valuelist.get(index)).get()
545 # Update the valuelist with our new item
546 self.valuelist.delete(index)
547 self.valuelist.insert(index, result)
548 if result != CONFIG_UNSET:
549 self.valuelist.itemconfig(index, {'bg':'green'})
553 # Get the selections, and create a list of them
554 for i, val in enumerate(self.valuelist.get(0, tk.END)):
555 if val != CONFIG_UNSET:
556 self.results[self.namelist.get(i)] = val
558 self.results.pop(self.namelist.get(i), None)
568 class WirelessSettingsWindow(sd.Dialog):
570 def __init__(self, parent):
571 sd.Dialog.__init__(self, parent, "Wireless settings")
574 def body(self, master):
575 self.configure(background=GetBackground())
576 master.configure(background=GetBackground())
577 self.ssid = tk.StringVar()
578 self.password = tk.StringVar()
580 a = ttk.Label(master, text='SSID :', background=GetBackground())
581 a.grid(row=0, column=0, sticky=tk.E)
582 a.configure(background=GetBackground())
583 ttk.Entry(master, textvariable=self.ssid).grid(row=0, column=1, sticky=tk.W+tk.E, padx=5)
585 ttk.Label(master, text='Password :').grid(row=1, column=0, sticky=tk.E)
586 ttk.Entry(master, textvariable=self.password).grid(row=1, column=1, sticky=tk.W+tk.E, padx=5)
588 self.transient(self.parent)
599 return (self.ssid.get(), self.password.get())
602 class ProjectWindow(tk.Frame):
604 def __init__(self, parent, sdkpath, args):
605 tk.Frame.__init__(self, parent)
607 self.sdkpath = sdkpath
608 self.init_window(args)
609 self.configs = dict()
611 self.password = str()
613 def setState(self, thing, state):
614 for child in thing.winfo_children():
615 child.configure(state=state)
617 def boardtype_change_callback(self, event):
618 boardtype = self.boardtype.get()
619 if boardtype == "pico_w":
620 self.setState(self.picowSubframe, "enabled")
622 self.setState(self.picowSubframe, "disabled")
624 def wirelessSettings(self):
625 result = WirelessSettingsWindow(self)
626 self.ssid, self.password = result.get()
628 def init_window(self, args):
629 self.master.title("Raspberry Pi Pico Project Generator")
630 self.master.configure(bg=GetBackground())
634 mainFrame = tk.Frame(self, bg=GetBackground()).grid(row=optionsRow, column=0, columnspan=6, rowspan=12)
636 # Need to keep a reference to the image or it will not appear.
637 self.logo = tk.PhotoImage(file=GetFilePath("logo_alpha.gif"))
638 logowidget = ttk.Label(mainFrame, image=self.logo, borderwidth=0, relief="solid").grid(row=0,column=0, columnspan=5, pady=10)
642 namelbl = ttk.Label(mainFrame, text='Project Name :').grid(row=optionsRow, column=0, sticky=tk.E)
643 self.projectName = tk.StringVar()
645 if args.name != None:
646 self.projectName.set(args.name)
648 self.projectName.set('ProjectName')
650 nameEntry = ttk.Entry(mainFrame, textvariable=self.projectName).grid(row=optionsRow, column=1, sticky=tk.W+tk.E, padx=5)
654 locationlbl = ttk.Label(mainFrame, text='Location :').grid(row=optionsRow, column=0, sticky=tk.E)
655 self.locationName = tk.StringVar()
656 self.locationName.set(os.getcwd())
657 locationEntry = ttk.Entry(mainFrame, textvariable=self.locationName).grid(row=optionsRow, column=1, columnspan=3, sticky=tk.W+tk.E, padx=5)
658 locationBrowse = ttk.Button(mainFrame, text='Browse', command=self.browse).grid(row=3, column=4)
662 ttk.Label(mainFrame, text = "Board Type :").grid(row=optionsRow, column=0, padx=4, sticky=tk.E)
663 self.boardtype = ttk.Combobox(mainFrame, values=boardtype_list, )
664 self.boardtype.grid(row=4, column=1, padx=4, sticky=tk.W+tk.E)
665 self.boardtype.set('pico')
666 self.boardtype.bind('<<ComboboxSelected>>',self.boardtype_change_callback)
670 featuresframe = ttk.LabelFrame(mainFrame, text="Library Options", relief=tk.RIDGE, borderwidth=2)
671 featuresframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=5, ipadx=5, padx=5, pady=5, sticky=tk.E+tk.W)
673 s = (len(features_list)/3)
675 self.feature_checkbox_vars = []
678 for i in features_list:
679 var = tk.StringVar(value='') # Off by default for the moment
680 c = features_list[i][GUI_TEXT]
681 cb = ttk.Checkbutton(featuresframe, text = c, var=var, onvalue=i, offvalue='')
682 cb.grid(row=row, column=col, padx=15, pady=2, ipadx=1, ipady=1, sticky=tk.E+tk.W)
683 self.feature_checkbox_vars.append(var)
691 # PicoW options section
692 self.picowSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Pico Wireless Options")
693 self.picowSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
694 self.pico_wireless = tk.StringVar()
698 for i in picow_options_list:
699 rb = ttk.Radiobutton(self.picowSubframe, text=picow_options_list[i][GUI_TEXT], variable=self.pico_wireless, val=i)
700 rb.grid(row=row, column=col, padx=15, pady=1, sticky=tk.E+tk.W)
706 # DOnt actually need any settings at the moment.
707 # ttk.Button(self.picowSubframe, text='Settings', command=self.wirelessSettings).grid(row=0, column=4, padx=5, pady=2, sticky=tk.E)
709 self.setState(self.picowSubframe, "disabled")
713 # output options section
714 ooptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Console Options")
715 ooptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
717 self.wantUART = tk.IntVar()
718 self.wantUART.set(args.uart)
719 ttk.Checkbutton(ooptionsSubframe, text="Console over UART", variable=self.wantUART).grid(row=0, column=0, padx=4, sticky=tk.W)
721 self.wantUSB = tk.IntVar()
722 self.wantUSB.set(args.usb)
723 ttk.Checkbutton(ooptionsSubframe, text="Console over USB (Disables other USB use)", variable=self.wantUSB).grid(row=0, column=1, padx=4, sticky=tk.W)
727 # Code options section
728 coptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Code Options")
729 coptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=3, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
731 self.wantExamples = tk.IntVar()
732 self.wantExamples.set(args.examples)
733 ttk.Checkbutton(coptionsSubframe, text="Add examples for Pico library", variable=self.wantExamples).grid(row=0, column=0, padx=4, sticky=tk.W)
735 self.wantRunFromRAM = tk.IntVar()
736 self.wantRunFromRAM.set(args.runFromRAM)
737 ttk.Checkbutton(coptionsSubframe, text="Run from RAM", variable=self.wantRunFromRAM).grid(row=0, column=1, padx=4, sticky=tk.W)
739 self.wantCPP = tk.IntVar()
740 self.wantCPP.set(args.cpp)
741 ttk.Checkbutton(coptionsSubframe, text="Generate C++", variable=self.wantCPP).grid(row=0, column=3, padx=4, sticky=tk.W)
743 ttk.Button(coptionsSubframe, text="Advanced...", command=self.config).grid(row=0, column=4, sticky=tk.E)
745 self.wantCPPExceptions = tk.IntVar()
746 self.wantCPPExceptions.set(args.cppexceptions)
747 ttk.Checkbutton(coptionsSubframe, text="Enable C++ exceptions", variable=self.wantCPPExceptions).grid(row=1, column=0, padx=4, sticky=tk.W)
749 self.wantCPPRTTI = tk.IntVar()
750 self.wantCPPRTTI.set(args.cpprtti)
751 ttk.Checkbutton(coptionsSubframe, text="Enable C++ RTTI", variable=self.wantCPPRTTI).grid(row=1, column=1, padx=4, sticky=tk.W)
755 # Build Options section
757 boptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Build Options")
758 boptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
760 self.wantBuild = tk.IntVar()
761 self.wantBuild.set(args.build)
762 ttk.Checkbutton(boptionsSubframe, text="Run build after generation", variable=self.wantBuild).grid(row=0, column=0, padx=4, sticky=tk.W)
764 self.wantOverwrite = tk.IntVar()
765 self.wantOverwrite.set(args.overwrite)
766 ttk.Checkbutton(boptionsSubframe, text="Overwrite existing projects", variable=self.wantOverwrite).grid(row=0, column=1, padx=4, sticky=tk.W)
770 # IDE Options section
772 vscodeoptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="IDE Options")
773 vscodeoptionsSubframe.grid_columnconfigure(2, weight=1)
774 vscodeoptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
776 self.wantVSCode = tk.IntVar()
777 if args.project is None:
778 self.wantVSCode.set(False)
780 self.wantVSCode.set('vscode' in args.project)
781 ttk.Checkbutton(vscodeoptionsSubframe, text="Create VSCode project", variable=self.wantVSCode).grid(row=0, column=0, padx=4, sticky=tk.W)
783 ttk.Label(vscodeoptionsSubframe, text = " Debugger:").grid(row=0, column=1, padx=4, sticky=tk.W)
785 self.debugger = ttk.Combobox(vscodeoptionsSubframe, values=debugger_list, state="readonly")
786 self.debugger.grid(row=0, column=2, padx=4, sticky=tk.EW)
787 self.debugger.current(args.debugger)
791 # OK, Cancel, Help section
793 QuitButton = ttk.Button(mainFrame, text="Quit", command=self.quit).grid(row=optionsRow, column=4, stick=tk.E, padx=10, pady=5)
794 OKButton = ttk.Button(mainFrame, text="OK", command=self.OK).grid(row=optionsRow, column=3, padx=4, pady=5, sticky=tk.E)
796 # TODO help not implemented yet
797 # HelpButton = ttk.Button(mainFrame, text="Help", command=self.help).grid(row=optionsRow, column=0, pady=5)
799 # You can set a default path here, replace the string with whereever you want.
800 # self.locationName.set('/home/pi/pico_projects')
802 def GetFeatures(self):
806 for cb in self.feature_checkbox_vars:
811 picow_extra = self.pico_wireless.get()
813 if picow_extra != 'picow_none':
814 features.append(picow_extra)
819 # TODO Check if we want to exit here
823 # OK, grab all the settings from the page, then call the generators
824 projectPath = self.locationName.get()
825 features = self.GetFeatures()
827 if (self.wantVSCode.get()):
828 projects.append("vscode")
831 'sdkPath' : self.sdkpath,
832 'projectRoot' : Path(projectPath),
833 'projectName' : self.projectName.get(),
835 'wantOverwrite' : self.wantOverwrite.get(),
836 'wantBuild' : self.wantBuild.get(),
837 'boardtype' : self.boardtype.get(),
838 'features' : features,
839 'projects' : projects,
840 'configs' : self.configs,
841 'wantRunFromRAM': self.wantRunFromRAM.get(),
842 'wantExamples' : self.wantExamples.get(),
843 'wantUART' : self.wantUART.get(),
844 'wantUSB' : self.wantUSB.get(),
845 'wantCPP' : self.wantCPP.get(),
846 'debugger' : self.debugger.current(),
847 'exceptions' : self.wantCPPExceptions.get(),
848 'rtti' : self.wantCPPRTTI.get(),
850 'password' : self.password,
851 'envSuffix' : self.envSuffix,
854 DoEverything(self, params)
857 name = fd.askdirectory()
858 self.locationName.set(name)
864 # Run the configuration window
865 self.configs = ConfigurationWindow(self, self.configs).get()
867 def CheckPrerequisites():
868 global isMac, isWindows
869 isMac = (platform.system() == 'Darwin')
870 isWindows = (platform.system() == 'Windows')
872 # Do we have a compiler?
873 return shutil.which(COMPILER_NAME, 1, os.environ["Path" if isWindows else "PATH"])
876 def CheckSDKPath(gui):
877 sdkPath = os.getenv('PICO_SDK_PATH')
880 m = 'Unable to locate the Raspberry Pi Pico SDK, PICO_SDK_PATH is not set'
881 if gui and ENABLE_TK_GUI:
885 elif not os.path.isdir(sdkPath):
886 m = 'Unable to locate the Raspberry Pi Pico SDK, PICO_SDK_PATH does not point to a directory'
887 if gui and ENABLE_TK_GUI:
895 def GetFilePath(filename):
896 if os.path.islink(__file__):
897 script_file = os.readlink(__file__)
899 script_file = __file__
900 return os.path.join(os.path.dirname(script_file), filename)
902 def ParseCommandLine():
903 debugger_flags = ', '.join('{} = {}'.format(i, v) for i, v in enumerate(debugger_list))
904 parser = argparse.ArgumentParser(description='Pico Project generator')
905 parser.add_argument("name", nargs="?", help="Name of the project")
906 parser.add_argument("-t", "--tsv", help="Select an alternative pico_configs.tsv file", default=GetFilePath("pico_configs.tsv"))
907 parser.add_argument("-o", "--output", help="Set an alternative CMakeList.txt filename", default="CMakeLists.txt")
908 parser.add_argument("-x", "--examples", action='store_true', help="Add example code for the Pico standard library")
909 parser.add_argument("-l", "--list", action='store_true', help="List available features")
910 parser.add_argument("-c", "--configs", action='store_true', help="List available project configuration items")
911 parser.add_argument("-f", "--feature", action='append', help="Add feature to generated project")
912 parser.add_argument("-over", "--overwrite", action='store_true', help="Overwrite any existing project AND files")
913 parser.add_argument("-b", "--build", action='store_true', help="Build after project created")
914 parser.add_argument("-g", "--gui", action='store_true', help="Run a GUI version of the project generator")
915 parser.add_argument("-p", "--project", action='append', help="Generate projects files for IDE. Options are: vscode")
916 parser.add_argument("-r", "--runFromRAM", action='store_true', help="Run the program from RAM rather than flash")
917 parser.add_argument("-uart", "--uart", action='store_true', default=1, help="Console output to UART (default)")
918 parser.add_argument("-nouart", "--nouart", action='store_true', default=0, help="Disable console output to UART")
919 parser.add_argument("-usb", "--usb", action='store_true', help="Console output to USB (disables other USB functionality")
920 parser.add_argument("-cpp", "--cpp", action='store_true', default=0, help="Generate C++ code")
921 parser.add_argument("-cpprtti", "--cpprtti", action='store_true', default=0, help="Enable C++ RTTI (Uses more memory)")
922 parser.add_argument("-cppex", "--cppexceptions", action='store_true', default=0, help="Enable C++ exceptions (Uses more memory)")
923 parser.add_argument("-d", "--debugger", type=int, help="Select debugger ({})".format(debugger_flags), default=0)
924 parser.add_argument("-board", "--boardtype", action="store", default='pico', help="Select board type (see --boardlist for available boards)")
925 parser.add_argument("-bl", "--boardlist", action="store_true", help="List available board types")
926 parser.add_argument("-cp", "--cpath", help="Override default VSCode compiler path")
927 parser.add_argument("-root", "--projectRoot", help="Override default project root where the new project will be created")
928 parser.add_argument("-envSuffix", "--envSuffix", help="[A suffix ot the PICO_SDK_PATH environment variable]")
929 parser.add_argument("-sdkVersion", "--sdkVersion", help="[A sdkVersion to set in settings]")
931 return parser.parse_args()
934 def GenerateMain(folder, projectName, features, cpp):
937 filename = Path(folder) / (projectName + '.cpp')
939 filename = Path(folder) / (projectName + '.c')
941 file = open(filename, 'w')
943 main = ('#include <stdio.h>\n'
944 '#include "pico/stdlib.h"\n'
951 for feat in features:
952 if (feat in features_list):
953 o = f'#include "{features_list[feat][H_FILE]}"\n'
955 if (feat in stdlib_examples_list):
956 o = f'#include "{stdlib_examples_list[feat][H_FILE]}"\n'
958 if (feat in picow_options_list):
959 o = f'#include "{picow_options_list[feat][H_FILE]}"\n'
965 for feat in features:
966 if (feat in code_fragments_per_feature):
967 for s in code_fragments_per_feature[feat][DEFINES]:
975 ' stdio_init_all();\n\n'
979 # Add any initialisers
981 for feat in features:
982 if (feat in code_fragments_per_feature):
983 for s in code_fragments_per_feature[feat][INITIALISERS]:
984 main += (" " * indent)
989 main += (' puts("Hello, world!");\n\n'
999 def GenerateCMake(folder, params):
1001 filename = Path(folder) / CMAKELIST_FILENAME
1002 projectName = params['projectName']
1003 board_type = params['boardtype']
1005 # OK, for the path, CMake will accept forward slashes on Windows, and thats
1006 # seemingly a bit easier to handle than the backslashes
1007 p = str(params['sdkPath']).replace('\\','/')
1009 cmake_header1 = (f"# Generated Cmake Pico project file\n\n"
1010 "cmake_minimum_required(VERSION 3.13)\n\n"
1011 "set(CMAKE_C_STANDARD 11)\n"
1012 "set(CMAKE_CXX_STANDARD 17)\n\n"
1013 "# Initialise pico_sdk from installed location\n"
1014 "# (note this can come from environment, CMake cache etc)\n"
1015 "# DO NOT EDIT THE NEXT TWO LINES for RaspberryPiPico VS Code Extension to work \n"
1016 f"set(PICO_SDK_PATH $ENV{{PICO_SDK_PATH_{params['envSuffix']}}})\n"
1017 f"set(PICO_TOOLCHAIN_PATH $ENV{{PICO_TOOLCHAIN_PATH_{params['envSuffix']}}})\n\n"
1018 f"set(PICO_BOARD {board_type} CACHE STRING \"Board type\")\n\n"
1019 "# Pull in Raspberry Pi Pico SDK (must be before project)\n"
1020 "include(pico_sdk_import.cmake)\n\n"
1021 "if (PICO_SDK_VERSION_STRING VERSION_LESS \"1.4.0\")\n"
1022 " message(FATAL_ERROR \"Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}\")\n"
1024 f"project({projectName} C CXX ASM)\n"
1028 "\n# Initialise the Raspberry Pi Pico SDK\n"
1029 "pico_sdk_init()\n\n"
1030 "# Add executable. Default name is the project name, version 0.1\n\n"
1034 file = open(filename, 'w')
1036 file.write(cmake_header1)
1038 if params['exceptions']:
1039 file.write("\nset(PICO_CXX_ENABLE_EXCEPTIONS 1)\n")
1042 file.write("\nset(PICO_CXX_ENABLE_RTTI 1)\n")
1044 file.write(cmake_header3)
1046 # add the preprocessor defines for overall configuration
1047 if params['configs']:
1048 file.write('# Add any PICO_CONFIG entries specified in the Advanced settings\n')
1049 for c, v in params['configs'].items():
1054 file.write(f'add_compile_definitions({c} = {v})\n')
1057 # No GUI/command line to set a different executable name at this stage
1058 executableName = projectName
1060 if params['wantCPP']:
1061 file.write(f'add_executable({projectName} {projectName}.cpp )\n\n')
1063 file.write(f'add_executable({projectName} {projectName}.c )\n\n')
1065 file.write(f'pico_set_program_name({projectName} "{executableName}")\n')
1066 file.write(f'pico_set_program_version({projectName} "0.1")\n\n')
1068 if params['wantRunFromRAM']:
1069 file.write(f'# no_flash means the target is to run from RAM\n')
1070 file.write(f'pico_set_binary_type({projectName} no_flash)\n\n')
1072 # Console output destinations
1073 if params['wantUART']:
1074 file.write(f'pico_enable_stdio_uart({projectName} 1)\n')
1076 file.write(f'pico_enable_stdio_uart({projectName} 0)\n')
1078 if params['wantUSB']:
1079 file.write(f'pico_enable_stdio_usb({projectName} 1)\n\n')
1081 file.write(f'pico_enable_stdio_usb({projectName} 0)\n\n')
1083 # If we need wireless, check for SSID and password
1084 # removed for the moment as these settings are currently only needed for the pico-examples
1085 # but may be required in here at a later date.
1087 if 'ssid' in params or 'password' in params:
1088 file.write('# Add any wireless access point information\n')
1089 file.write(f'target_compile_definitions({projectName} PRIVATE\n')
1090 if 'ssid' in params:
1091 file.write(f'WIFI_SSID=\" {params["ssid"]} \"\n')
1093 file.write(f'WIFI_SSID=\"${WIFI_SSID}\"')
1095 if 'password' in params:
1096 file.write(f'WIFI_PASSWORD=\"{params["password"]}\"\n')
1098 file.write(f'WIFI_PASSWORD=\"${WIFI_PASSWORD}\"')
1101 # Standard libraries
1102 file.write('# Add the standard library to the build\n')
1103 file.write(f'target_link_libraries({projectName}\n')
1104 file.write(" " + STANDARD_LIBRARIES)
1107 # Standard include directories
1108 file.write('# Add the standard include files to the build\n')
1109 file.write(f'target_include_directories({projectName} PRIVATE\n')
1110 file.write(" ${CMAKE_CURRENT_LIST_DIR}\n")
1111 file.write(" ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required\n")
1114 # Selected libraries/features
1115 if (params['features']):
1116 file.write('# Add any user requested libraries\n')
1117 file.write(f'target_link_libraries({projectName} \n')
1118 for feat in params['features']:
1119 if (feat in features_list):
1120 file.write(" " + features_list[feat][LIB_NAME] + '\n')
1121 if (feat in picow_options_list):
1122 file.write(" " + picow_options_list[feat][LIB_NAME] + '\n')
1123 file.write(' )\n\n')
1125 file.write(f'pico_add_extra_outputs({projectName})\n\n')
1130 # Generates the requested project files, if any
1131 def generateProjectFiles(projectPath, projectName, sdkPath, projects, debugger, envSuffix, sdkVersion):
1133 oldCWD = os.getcwd()
1135 os.chdir(projectPath)
1137 debugger = debugger_config_list[debugger]
1138 gdbPath = "arm-none-eabi-gdb" if isWindows else "gdb-multiarch"
1139 # Need to escape windows files paths backslashes
1140 # TODO: env in currently not supported in compilerPath var
1141 #cPath = f"${{env:PICO_TOOLCHAIN_PATH_{envSuffix}}}" + os.path.sep + os.path.basename(str(compilerPath).replace('\\', '\\\\' ))
1142 cPath = str(compilerPath).replace('\\', '\\\\' )
1150 "name": "Pico Debug (Cortex-Debug)",
1151 "cwd": "${{workspaceRoot}}",
1152 "executable": "${{command:raspberry-pi-pico.launchTargetPath}}",
1153 "request": "launch",
1154 "type": "cortex-debug",
1155 "servertype": "openocd",
1156 "gdbPath": "{gdbPath}",
1159 "interface/{debugger}",
1162 "svdFile": "${{command:raspberry-pi-pico.getSDKPath}}/src/rp2040/hardware_regs/rp2040.svd",
1163 "runToEntryPoint": "main",
1164 // Give restart the same functionality as runToEntryPoint - main
1165 "postRestartCommands": [
1169 "openOCDLaunchCommands": [
1170 "adapter speed 1000"
1172 "preLaunchTask": "Compile Project"
1175 "name": "Pico Debug (Cortex-Debug with external OpenOCD)",
1176 "cwd": "${{workspaceRoot}}",
1177 "executable": "${{command:raspberry-pi-pico.launchTargetPath}}",
1178 "request": "launch",
1179 "type": "cortex-debug",
1180 "servertype": "external",
1181 "gdbTarget": "localhost:3333",
1182 "gdbPath": "{gdbPath}",
1184 "svdFile": "${{command:raspberry-pi-pico.getSDKPath}}/src/rp2040/hardware_regs/rp2040.svd",
1185 "runToEntryPoint": "main",
1186 // Give restart the same functionality as runToEntryPoint - main
1187 "postRestartCommands": [
1191 "preLaunchTask": "Compile Project"
1194 "name": "Pico Debug (C++ Debugger)",
1196 "request": "launch",
1197 "cwd": "${{workspaceRoot}}",
1198 "program": "${{command:raspberry-pi-pico.launchTargetPath}}",
1200 "miDebuggerPath": "{gdbPath}",
1201 "miDebuggerServerAddress": "localhost:3333",
1202 "debugServerPath": "openocd",
1203 "debugServerArgs": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \\"adapter speed 1000\\"",
1204 "serverStarted": "Listening on port .* for gdb connections",
1205 "filterStderr": true,
1206 "stopAtEntry": true,
1207 "hardwareBreakpoints": {{
1211 "preLaunchTask": "Flash",
1212 "svdPath": "${{command:raspberry-pi-pico.getSDKPath}}/src/rp2040/hardware_regs/rp2040.svd"
1223 "${{workspaceFolder}}/**",
1224 "${{env:PICO_SDK_PATH_{envSuffix}}}/**"
1227 "compilerPath": "{cPath}",
1229 "cppStandard": "c++14",
1230 "intelliSenseMode": "linux-gcc-arm"
1240 "cmake.statusbar.visibility": "hidden",
1241 "cmake.configureOnEdit": false,
1242 "cmake.automaticReconfigure": false,
1243 "cmake.configureOnOpen": false,
1244 "cmake.generator": "Ninja",
1245 "raspberry-pi-pico.sdk": "{sdkVersion}",
1246 "raspberry-pi-pico.envSuffix": "{envSuffix}",
1252 "recommendations": [
1253 "marus25.cortex-debug",
1254 "ms-vscode.cpptools",
1255 "ms-vscode.cpptools-extension-pack",
1256 "ms-vscode.vscode-serial-monitor",
1257 "paulober.raspberry-pi-pico",
1265 "label": "Compile Project",
1268 "args": ["-C", "${{workspaceFolder}}/build"],
1272 "panel": "dedicated"
1274 "problemMatcher": "$gcc"
1280 # Create a build folder, and run our cmake project build from it
1281 if not os.path.exists(VSCODE_FOLDER):
1282 os.mkdir(VSCODE_FOLDER)
1284 os.chdir(VSCODE_FOLDER)
1286 file = open(VSCODE_TASKS_FILENAME, 'w')
1290 filename = VSCODE_LAUNCH_FILENAME
1291 file = open(filename, 'w')
1295 file = open(VSCODE_C_PROPERTIES_FILENAME, 'w')
1296 file.write(properties)
1299 file = open(VSCODE_SETTINGS_FILENAME, 'w')
1300 file.write(settings)
1303 file = open(VSCODE_EXTENSIONS_FILENAME, 'w')
1304 file.write(extensions)
1308 print('Unknown project type requested')
1313 def LoadConfigurations():
1315 with open(args.tsv) as tsvfile:
1316 reader = csv.DictReader(tsvfile, dialect='excel-tab')
1318 configuration_dictionary.append(row)
1320 print("No Pico configurations file found. Continuing without")
1322 def LoadBoardTypes(sdkPath):
1323 # Scan the boards folder for all header files, extract filenames, and make a list of the results
1324 # default folder is <PICO_SDK_PATH>/src/boards/include/boards/*
1325 # If the PICO_BOARD_HEADER_DIRS environment variable is set, use that as well
1327 loc = sdkPath / "src/boards/include/boards"
1329 for x in Path(loc).iterdir():
1330 if x.suffix == '.h':
1331 boards.append(x.stem)
1333 loc = os.getenv('PICO_BOARD_HEADER_DIRS')
1336 for x in Path(loc).iterdir():
1337 if x.suffix == '.h':
1338 boards.append(x.stem)
1342 def DoEverything(parent, params):
1344 if not os.path.exists(params['projectRoot']):
1345 if params['wantGUI'] and ENABLE_TK_GUI:
1346 mb.showerror('Raspberry Pi Pico Project Generator', 'Invalid project path. Select a valid path and try again')
1349 print('Invalid project path')
1352 oldCWD = os.getcwd()
1353 os.chdir(params['projectRoot'])
1355 # Create our project folder as subfolder
1356 os.makedirs(params['projectName'], exist_ok=True)
1358 os.chdir(params['projectName'])
1360 projectPath = params['projectRoot'] / params['projectName']
1362 # First check if there is already a project in the folder
1363 # If there is we abort unless the overwrite flag it set
1364 if os.path.exists(CMAKELIST_FILENAME):
1365 if not params['wantOverwrite'] :
1366 if params['wantGUI'] and ENABLE_TK_GUI:
1367 # We can ask the user if they want to overwrite
1368 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')
1372 print('There already appears to be a project in this folder. Use the --overwrite option to overwrite the existing project')
1375 # We should really confirm the user wants to overwrite
1376 #print('Are you sure you want to overwrite the existing project files? (y/N)')
1377 #c = input().split(" ")[0]
1378 #if c != 'y' and c != 'Y' :
1381 # Copy the SDK finder cmake file to our project folder
1382 # Can be found here <PICO_SDK_PATH>/external/pico_sdk_import.cmake
1383 shutil.copyfile(params['sdkPath'] / 'external' / 'pico_sdk_import.cmake', projectPath / 'pico_sdk_import.cmake' )
1385 if params['features']:
1386 features_and_examples = params['features'][:]
1388 features_and_examples= []
1390 if params['wantExamples']:
1391 features_and_examples = list(stdlib_examples_list.keys()) + features_and_examples
1393 GenerateMain('.', params['projectName'], features_and_examples, params['wantCPP'])
1395 GenerateCMake('.', params)
1397 # If we have any ancilliary files, copy them to our project folder
1398 # Currently only the picow with lwIP support needs an extra file, so just check that list
1399 for feat in features_and_examples:
1400 if feat in picow_options_list:
1401 if picow_options_list[feat][ANCILLARY_FILE] != "":
1402 shutil.copy(sourcefolder + "/" + picow_options_list[feat][ANCILLARY_FILE], projectPath / picow_options_list[feat][ANCILLARY_FILE])
1404 # Create a build folder, and run our cmake project build from it
1405 if not os.path.exists('build'):
1410 # If we are overwriting a previous project, we should probably clear the folder, but that might delete something the users thinks is important, so
1411 # for the moment, just delete the CMakeCache.txt file as certain changes may need that to be recreated.
1413 if os.path.exists(CMAKECACHE_FILENAME):
1414 os.remove(CMAKECACHE_FILENAME)
1416 cpus = os.cpu_count()
1421 # Had a special case report, when using MinGW, need to check if using nmake or mingw32-make.
1422 if shutil.which("mingw32-make"):
1423 # Assume MinGW environment
1424 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G "MinGW Makefiles" ..'
1425 makeCmd = 'mingw32-make '
1426 elif shutil.which("ninja"):
1427 # When installing SDK version 1.5.0 on windows with installer pico-setup-windows-x64-standalone.exe, ninja is used
1428 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja ..'
1431 # Everything else assume nmake
1432 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G "NMake Makefiles" ..'
1435 # Ninja now works OK under Linux, so if installed use it by default. It's faster.
1436 if shutil.which("ninja"):
1437 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja ..'
1440 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug ..'
1441 makeCmd = 'make -j' + str(cpus)
1443 if params['wantGUI'] and ENABLE_TK_GUI:
1444 RunCommandInWindow(parent, cmakeCmd)
1448 if params['projects']:
1449 generateProjectFiles(projectPath, params['projectName'], params['sdkPath'], params['projects'], params['debugger'], params["envSuffix"], params["sdkVersion"])
1451 if params['wantBuild']:
1452 if params['wantGUI'] and ENABLE_TK_GUI:
1453 RunCommandInWindow(parent, makeCmd)
1456 print('\nIf the application has built correctly, you can now transfer it to the Raspberry Pi Pico board')
1461 ###################################################################################
1462 # main execution starteth here
1464 sourcefolder = os.path.dirname(os.path.abspath(__file__))
1466 args = ParseCommandLine()
1471 if args.debugger > len(debugger_list) - 1:
1474 # Check we have everything we need to compile etc
1475 c = CheckPrerequisites()
1477 ## TODO Do both warnings in the same error message so user does have to keep coming back to find still more to do
1480 m = f'Unable to find the `{COMPILER_NAME}` compiler\n'
1481 m +='You will need to install an appropriate compiler to build a Raspberry Pi Pico project\n'
1482 m += 'See the Raspberry Pi Pico documentation for how to do this on your particular platform\n'
1484 if args.gui and ENABLE_TK_GUI:
1490 if args.name == None and not args.gui and not args.list and not args.configs and not args.boardlist:
1491 print("No project name specfied\n")
1494 # Check if we were provided a compiler path, and override the default if so
1496 compilerPath = Path(args.cpath)
1498 compilerPath = Path(c)
1500 # load/parse any configuration dictionary we may have
1501 LoadConfigurations()
1503 p = CheckSDKPath(args.gui)
1510 boardtype_list = LoadBoardTypes(sdkPath)
1511 boardtype_list.sort()
1513 if args.gui and ENABLE_TK_GUI:
1514 RunGUI(sdkPath, args) # does not return, only exits
1516 projectRoot = Path(os.getcwd()) if not args.projectRoot else Path(args.projectRoot)
1518 if args.list or args.configs or args.boardlist:
1520 print("Available project features:\n")
1521 for feat in features_list:
1522 print(feat.ljust(6), '\t', features_list[feat][GUI_TEXT])
1526 print("Available project configuration items:\n")
1527 for conf in configuration_dictionary:
1528 print(conf['name'].ljust(40), '\t', conf['description'])
1532 print("Available board types:\n")
1533 for board in boardtype_list:
1540 'sdkPath' : sdkPath,
1541 'projectRoot' : projectRoot,
1542 'projectName' : args.name,
1544 'wantOverwrite' : args.overwrite,
1545 'boardtype' : args.boardtype,
1546 'wantBuild' : args.build,
1547 'features' : args.feature,
1548 'projects' : args.project,
1550 'wantRunFromRAM': args.runFromRAM,
1551 'wantExamples' : args.examples,
1552 'wantUART' : args.uart,
1553 'wantUSB' : args.usb,
1554 'wantCPP' : args.cpp,
1555 'debugger' : args.debugger,
1556 'exceptions' : args.cppexceptions,
1557 'rtti' : args.cpprtti,
1560 'envSuffix' : args.envSuffix,
1561 'sdkVersion' : args.sdkVersion,
1564 DoEverything(None, params)