Notes on Adafruit “Welcome to CircuitPython”

After getting comfortable with the idea of learning CircuitPython, I began reading Adafruit’s Welcome to CircuitPython starting guide. I found this to be a great introduction and met my expectation for a document helping beginners get started with CircuitPython. That said, my opinion isn’t as good as one coming from an actual beginner! They would be the true test whether Adafruit succeeded.

[UPDATE: I just noticed that there is a lot of overlap between the general welcome guide and information posted under the KB2040 umbrella. However, they’re not identical. The KB2040-specific version had no reason to cover CircuitPython Hardware, and the general version lacked some sections like CircuitPython Pins and Modules.]

The most immediate interface point of CircuitPython is that it shows up as a USB file storage device. We just have to put our code in a file named code.py on the device, and every time we save our changes they will immediately run. This architecture means any device that can edit files on a USB flash drive can be a CircuitPython development tool, and feedback is immediate for instant gratification. This is great for beginner-friendliness! But I’m a grumpy old man set in my ways. I like keeping my source code in a git repository and that means I have to copy my file from my repository to the USB drive. This is probably not a deal breaker and I expect to find a workflow that will work for me, but it did start me on the wrong foot.

While any text editor will work, Adafruit does name Mu as their recommended Python code editor. Advertising itself as a simple Python editor for beginner programmers I thought I would use it at least for the duration of my learning on-ramp. I found it to be a simple straightforward Python editor, supporting not just CircuitPython but also MicroPython and just regular ol’ Python on computers. It felt a bit like what Visual Studio Code used to be, before it got all bloated with features. Notably, Mu is not so overly simple that it became infuriating for me to use, which makes it better than Arduino IDE before version 2.0.

Speaking of Arduino IDE idiosyncrasies, I like the fact CircuitPython handles libraries for a project by putting them in a \lib\ subdirectory. I felt this was more straightforward than Arduino’s package manager, which always felt more complicated than it needed to be and weighed down by a massive catalog of community contributions.

In comparison, CircuitPython doesn’t have as long of a history to build up such a huge library, but Adafruit & contributors have been making great effort covering common needs. Every time I thought of something I might need from a library, a search quickly found one. (The latest: rotaryio to handle quadrature encoders.) So far, so good! What I need now is a learning project putting CircuitPython to work for me.

MicroPython And CircuitPython Are More Alike Than Different

I want to learn how to work with RP2040-based microcontrollers, and I like the idea of working in a high-level language like Python. Or at least, I would like to give it a shot. Reading about Python for microcontrollers quickly run into two names: MicroPython and CircuitPython. Looking around for information on why I might choose one or the other, I found a wonderful comparison guide published by Adafruit. Based on this information, I now feel more comfortable starting with CircuitPython.

For me, the most important piece of background is that CircuitPython is built as a fork of MicroPython, with enhancements focused on beginner friendliness (an Adafruit signature) versus full support for all features. For someone learning to blink a LED, CircuitPython aims to be the best solution. Beyond the basics, intermediate level projects (where I feel I fit) are likely to be served equally well by either platform. Advanced features like threading and PIO are where CircuitPython concedes to MicroPython, but speaking for myself I won’t have to worry about that for a while if ever. And even if I find myself in a situation where I need to switch, I’m comforted by the claim 95% of knowledge will transfer directly between the two platforms.

What will likely swing the decision for most people are software modules they want to pull into their project. And here I think CircuitPython will take the lead for my scenarios. I buy a lot of stuff from Adafruit, and they are likely to provide CircuitPython libraries for those components. So that covers a lot of hardware which also serves as example code to learn from if certain hardware is not explicitly supported. And following my earlier search for a MicroPython web server with WebSocket support, I found that CircuitPython offers a counterpart httpserver which should be sufficient for my rover project purposes. Looks good enough for me to dive on in.

Search For Pico W Web Server Led To MicroWebSrv2

One of the bare minimalist examples I saw while readingConnecting to the Internet with Raspberry Pi Pico W” was a simple HTTP web server. It only serves a single page which is represented by a string embedded in the source code. This is obviously not going to work for anything serious.

Fortunately, the popularity of ESP8266 and ESP32 pioneered an ecosystem of web servers suitable for running on microcontrollers. Many of which have already been adapted to run on the Pico W, as the new hotness on the block. They all meet the primary requirement: Serving static files on board microcontroller flash storage, rather than strings embedded in source code. This is important because I want to update them without having to touch my web server files, and second because I don’t want to hassle with string escape and other inconveniences of embedding HTML/CSS/JS syntax inside valid C or Python syntax.

Most of these web server options were written in Arduino or another C-based framework like ESP-IDF, but a few of them were written in MicroPython so I thought I would look at those first. Beyond serving static files, many of them have routing and associated mechanisms for dynamic server-side responses. Several such frameworks said they’re modeled after Flask, which I’ve played with before, and makes sense in a Python context.

But the one feature I would like is web socket support. I want to try porting my micro Sawppy interface to such a system and that uses web sockets to communicate between the cell phone browser acting as remote control and the microcontroller driving my mini rover. This surprisingly eliminated almost all of the candidates. I found only MicroWebSrv2 for MicroPython compatible web server framework with web socket support.

I am intrigued after a cursory glance over MicroWebSrv 2 README. Especially the claim that MicroWebSrv2 can run on desktop computer Python as well as microcontroller MicroPython. In my earlier development efforts for micro Sawppy interface, I wrote the server-side code twice. Once in NodeJS (JavaScript) as a stub to help me develop on the desktop, and again in ESP-IDF to run on an ESP32. It would be great if I can have a single chunk of Python code that handles both cases. I have to review my code to see what other support infrastructure I would need. The first one off the top of my head is a JSON parsing library, which is included in MicroPython, a good start.

Notes on “Connecting to the Internet with Raspberry Pi Pico W” eBook

After getting my bearings with the “Raspberry Pi Pico Python SDK” eBook, I checked out the Pico W-specific “Connecting to the Internet with Raspberry Pi Pico W“. This volume is even smaller at 49 pages, fitting for an quick introduction to potential of Pico W connectivity. It felt to me like the word “Connecting” in the title is quite literal: this title covers how to establish a connection and not much else on what to do after you connect. Perhaps the authors believed that networking APIs are common with existing platforms. So maybe once we get a Pico W set up enough to connect, everything else will be the same as existing general resources out on the internet? That seems unlikely but maybe a valid stance for an introductory title.

But while the word “Connecting” might be literal, the word “Internet” is not. This book actually goes beyond Pico W’s WiFi connectivity and also covers its Bluetooth LE capability. BLE is not typically used to connect to the internet. It appears the Pico W is also capable of Classic Bluetooth, but this book doesn’t spend much time on that beyond mentioning it was possible.

This book also covers each concept twice: once with the C/C++ SDK, then again with the MicroPython SDK. Seeing them side by side really brought home the fact MicroPython code is generally easier to read. But sadly, the language can only do so much. Bluetooth LE is full of esoteric overhead that needs to be configured no matter the language. An uninformative barrier against beginners trying to learn the technology. While reading one particular example’s MicroPython code in the PDF, I got tired trying to parse BLE minutiae and it occurred to me… I don’t remember reading anything equally dry in the C section. I scrolled back up and that’s when I realized the book authors gave up: there was no C section. Instead of cluttering the book with a bunch of code dealing with C overhead plus BLE overhead, they just said the example code is on their GitHub repository. With a URL for the C developers who care to go wading through it all.

This book tried to cover WiFi, Bluetooth Classic, and Bluetooth LE connectivity enabled by a Pico W over its non-W counterpart. And cover those topics via both C and MicroPython languages. That’s a lot of ground to cover in 49 pages, resulting in a book that feels more like a Getting Started pamphlet. Which is a valuable resource! It was just less than what I had hoped.

Notes on “Raspberry Pi Pico Python SDK” eBook

I’m playing with a borrowed Raspberry Pi Pico W, and thought I’d play with MicroPython on it. That software platform seems to have support direct from Raspberry Pi, which is always promising. I thought perhaps it might even get equal billing with the traditional C-based platform! Well, as soon as I opened up Raspberry Pi Pico Python SDK ebook I knew they were not equal. Despite the usual warning not to judge a book by its cover or length, the fact is there are 54 pages in the Python SDK ebook which is barely 10% of its counterpart C SDK ebook‘s 523 pages.

But I don’t necessarily see that as a bad thing. The advantage of a smaller book is that it’s easier for people to read through and absorb, which aligns with the goal of having Python on a microcontroller to begin with. The book frequently refers the reader to the C book for details not specific to the language, such as details on working with the programmable input/output (PIO) blocks. Writing PIO code is little different from writing in assembly language, and a challenge I intend to tackle sometime in the future.

I was happy to see the prebuilt MicroPython distribution binary for Raspberry Pi Pico W was generated by MicroPython organization, instead of a separate fork maintained by Raspberry Pi. This should mean features are introduced in step with the rest of the MicroPython ecosystem and not lagging behind waiting for someone else to integrate upstream changes.

Another item I was happy to see was a Visual Studio Code extension to ease working with MicroPython. MicroPico, formerly Pico-W-Go, promises a lot of convenience over using a serial terminal like PuTTY, which was what I did before. I’ve become familiar with VSCode so I’ll use the extension first, but people without such affinity have other options like the popular Python IDE “Thonny” and a command-line based “rshell” tool that should be useful for scripting.

Trying MicroPython Again This Time On Raspberry Pi Pico W

I’ve borrowed a Raspberry Pi Pico W for experimentation. I’ve verified it could easily integrate into Home Assistant via ESPHome. (Which is now somewhat misnamed as it works for more than just Espressif ESP chips…) For my next set of Pico W experiments I decided to give MicroPython another try. I tried MicroPython earlier on an ESP8266 and it was not all smooth sailing. Looking for an existing solution to my problem was actually the reason I stopped my MicroPython experiment and found Home Assistant and ESPHome! So this is sort of coming full circle.

The main reason I wanted to give MicroPython another try was the fact Raspberry Pi foundation listed C and MicroPython SDKs side by side as software development platforms for Pico and Pico W boards. This implies MicroPython is treated as a first-class citizen and supported directly from the source. Will actual reality live up to this implication? I don’t know yet but I’m willing to find out. This is also why I’m trying MicroPython before investigating Adafruit’s CircuitPython for the board. As much as I love Adafruit, direct manufacturer support is very compelling.

The obvious criticism of MicroPython is runtime performance. I will have to see how much of a problem it would actually cause for my projects. I’m cautiously optimistic runtime performance will not be a hindrance for two reasons. One, hardware peripherals can handle most performance-critical tasks asked of a microcontroller, and that will largely be the same whether overarching high level application code is in C or in MicroPython. What if there isn’t an applicable hardware peripheral on the RP2040 chip? That leads to the second reason: RP2040’s programmable input-output (PIO) capability. Proven to be adept handling tasks that would otherwise need to be written in low-level interrupt handler code, if it can be implemented that way at all. Sounds like a potentially fun playground to see if reality lives up to theory. I’ll start with the PDF published by Raspberry Pi to guide curious Python developers to Pico W.

CadQuery Definitely Under Active Development

CadQuery is definitely under active development, with a long to-do list they still want to tackle. For a beginner, it can be occasionally disorienting learning a moving target. Generally speaking it’s better to have an active and evolving project rather than relying on something that has been abandoned, but it’s definitely a double-edged sword.

On the upside, new features are being added. Reading through documentation I noticed a new section “Free function API” just popped up. That page doesn’t explain exactly why someone would or would not use this new API. Or at least, if it did the explanation didn’t make sense to this beginner.

And on the downside, sometimes things break. I am still learning the basics of CadQuery assemblies, so I noticed the assembly constraint solver demonstrations have gone out of whack. What used to be a nicely laid out door with doorway built of aluminum enclosures is now a jumbled arrangement of parts.

I copy/pasted the example code into my copy of cq-editor and it looks OK. I wonder if a newer build of cq-editor would mirror this regression. Whatever generated this documentation page certainly had some sort of problem I don’t understand.

There’s one upside to this bug: it told me these aren’t static screenshot images. And I thought “I wonder if these are actual 3D viewer controls” and they are! I could click inside the area and change my viewpoint. There were no visible controls or anything giving me a hint this might have been the case, until I noticed the error.

I looked on CadQuery issues database and didn’t see anything that directly mentions this problem, so I opened issue #1585. We’ll see where this goes. [UPDATE: the developers found an angle unit degree/radian mixup in conversion to JSON, a code path used by sphinx-doc interface to generate documentation. The bug was inadvertently introduced alongside free function API, and the bug has been fixed.]

CadQuery Learning Curve Climbing Plan

Reading through CadQuery documentation, I’m getting into topics like software integration. While fascinating and full of possibilities, this is putting the cart before the horse. I don’t know if CadQuery will be useful for my own projects. If I end up hating CadQuery, none of the rest would matter. Here are some foreseeable markers to help measure progress ramping up my CadQuery skills, and I’ll find out how far I get.

North Star

The long term goal is for CadQuery (or whatever I decide to adopt) is to fulfill my wish list for open-source collaborative CAD which includes not just the CAD aspect but also integration into project reference documentation. I want to pick up my neglected micro Sawppy project with CadQuery plus associated tools, then roll what I’ve learned (both in the rover design itself, as well as CAD and documentation systems) to revamp standard size Sawppy into a significantly enhanced version 2.0. Even if everything goes perfectly (which it won’t) I expect to take at least a year for this to unfold.

Starting Small

Since it would be foolhardy to jump into such a big project with a new platform, I will start small and tackle small 3D printing projects. Solve problems that can be solved with only a few pieces or just a single piece, before I increase complexity with multi-piece assemblies.

External Dimensions

My 3D printing objects are rarely standalone, they almost always fit onto something that already exists or tie multiple existing components together. Which means dealing with measurements of those existing objects, and I don’t yet know how well I can handle that with a code CAD platform like CadQuery. This will be interesting to figure out on my own, as CadQuery documentation doesn’t cover this topic.

Documentation Updates

When looking at somebody’s GitHub repository, I appreciate having a picture to help tie the words and code and other pieces together. I do the same for my mature project repositories. I include a screen shot for a web app project, and the same for a reverse-engineered KiCad schematic. The downside is keeping the image in sync with latest changes to the project. Instead of having to remember to go back and take another screen shot, I should be able to script CadQuery to automate image update.

  • Crawl: A Python script I manually run on my own computer to dump out an image file.
  • Walk: Translate that to GitHub Actions to run automatically upon every new commit.
  • Run: Integrate with full-fledged documentation system (sphinx-doc or equivalent.)

I think it’ll take a few months just to cover those beginner goals. If I make it that far (which I failed to do with FreeCAD) then I’ll worry about picking some intermediate level goals.

CadQuery External Integration Potential

I was happy to learn CadQuery selecters weren’t limited to string descriptions, those strings were merely shorthand for a far more powerful (if verbose) Python syntax. That should be fun to experiment with later. Another CadQuery feature on the long term experimentation list is its potential to be integrated into other software workflows. The default GUI CQ-editor is merely one example of what the team hopes will become an ecosystem of embedded CadQuery. We will see if those hopes pan out but I like where their thoughts are going.

The other example listed in CadQuery documentation is cq-directive. This allows CadQuery to work as a component in Sphinx documentation generation engine. I need to examine this in more detail, because cq-directive or another tool like it can help me make progress towards a documentation system that solves the challenges I had on a documentation wish list. Come to think of it, this is actually an area of overlap between that and my wish list for collaborative open-source CAD solutions. In theory CadQuery meets almost everything on that latter document, but I won’t know more until I start working through some projects to get hands-on experience.

I expect that I will find a few features I like, but many other items in my wishlist will be missing. For a commercial package like Onshape that would simply be the end of the discussion. But with an open source CAD solution, technically speaking I could write my own implementation to patch these holes. Whether I find the motivation to learn enough to actually do so is a separate problem.

CadQuery Selector Full Capability Via Code

Since CadQuery designs are described via Python code syntax, it has several code-friendly advantages that I hope will prove useful. But I was wary of CadQuery selector examples like .edges("|Z") to select all edges parallel to the Z axis. This seems useful, but “|Z” means an opaque string as far as Python syntax checking is concerned. Special syntax for strings embedded in source code is nothing new, inline assembly and inline SQL as the top two examples coming to my mind. And as one language hiding inside another, I’ve always encountered problems with them.

So I was happy to learn that such strings are merely a shorthand and not selectors’ true form. Officially they are Python code just like the rest of CadQuery. That “|Z” shorthand translates into a call to cadquery.selectors.ParallelDirSelector() with a vector pointing along the Z axis. That is one of several listed in selector section of the API reference. In addition to selectors getting their own page in CadQuery documentation. The string shorthand is itself a Python selector method StringSyntaxSelector.

Selectors were one of the biggest features that motivated me to ramp up on CadQuery, so learning their implementation as actual methods gave me peace of mind. It means if I run into problems with the special text string, I can always fall back to code. The other advantage is that using long form code syntax selectors open up capabilities not (yet?) available via a shorthand string. Three selectors caught my attention:

  1. NearestPointSelector to select an entity near the given coordinate. Useful for finding that one face or edge at a specific known point.
  2. BoxSelector to select all entities within a box. This should allow me to do things like selecting all edges where two objects met up, so I can perform a chamfer operation to smooth edges transitioning from one object to another.
  3. BaseDirSelector may be part of a solution to build a CadQuery UI. GUI CAD users are accustomed to clicking on an object to select it. This involves performing a 3D ray cast to transform the mouse click coordinate backwards through the 3D camera transform to obtain a vector through 3D space. I think that vector can be passed into this selector to perform hit-testing, but I could be wrong. This is pretty far down the line (if I get to it at all) so there’s little immediate need to figure it out just yet. It’s one of many potential CadQuery extension project ideas.

CadQuery Natural Code CAD Advantages

I found some novel features like “extrude until” while reading through “Examples” section of CadQuery documentation. The final example on that page (as of this writing) was for a cycloidal gear and it reminded me of a big reason why I’m looking into code CAD to begin with: I can do things in code! Cycloidal gear profile can get a bit involved to draw with standard 2D sketching tools, and 2D sketches require work to make them adjustable to suit different parameters. But as a code CAD platform, CadQuery can consume gear profile defined with Python math functions. And since it’s a Python function, it takes very little extra work to have it recompute a different profile based on function parameters.

But while a cycloidal gear looks cool, it’s not what I would want to design and 3D print. Well, I tend to avoid printing gears entirely because 3D printers don’t have the precision necessary for good gears, but when it is unavoidable, I prefer to use standard mechanical engineering involute gears. As they are quite standard, I was confident someone has already written CadQuery code to generate involute gears and I found not one but two examples in the CadQuery contributions repository.

Easy publication of small functional snippets like this is another advantage of code CAD: it fits right into source code control and publishing infrastructure. A bit of Python file is a lot more easily maintained and ensure availability compared to FreeCAD’s approach of publishing an entire workbench called FCGear.

While I love seeing the concept of user-contributed code CAD repository, the actual execution may not be panning out. Code contribution frequency to cadquery-contrib isn’t as high as what I would interpret as a popular and vibrant user community. What’s going on? Maybe this specific repository fell out of favor for some reason with the user community, and they’re sharing via some other mechanism I haven’t discovered. Or maybe nobody is using CadQuery and the project is on a path of a slow decline into irrelevance. I hope it is the former and not the latter, but even then it wouldn’t be the first time I put effort into a platform that faded away. I shouldn’t let that fear discourage me as I climb CadQuery’s learning curve.

CadQuery “Extrude Until” Could Prove Powerful

As I’m slowly chewing through CadQuery examples (with a side of CadQuery error handling) I am mostly learning CadQuery syntax for familiar CAD concepts. But “Extruding until a given face” showed something novel I hadn’t seen before. Instead of extruding a 2D profile out to a specified distance, this option allows specifying an extrusion operation to go until it reaches an existing face. This is especially powerful when that destination face is irregularly shaped.

The example showed this to good effect: a circle is extruded out to a half-torus, then a rectangle is extruded until it reaches that doughnut shape. Without this tool, the rectangle would need to have been extruded then trimmed with a cylinder to fit doughnut curvature. This CadQuery “extrude until” option skips the intermediate cylinder shape and associated trim operation. This allows for simpler CadQuery command list and captures design intent, which I see as a good thing.

I hadn’t noticed any counterparts to this capability during my time with Fusion 360, Onshape, or FreeCAD. If it exists in any of those tools, it would have been hidden away in a menu option I had not explored. Perhaps it’s not a mainstream option because it has some “Here be Dragons” edge cases when the extrusion profile (rectangle in example above) could not be fully projected onto an existing face (half-doughnut.) There is a small Warning text box within CadQuery documentation explaining the result may be unpredictable. What does that mean? I increased the example rectangle size so it is larger than doughnut profile, and it extruded a flat shape out beyond the doughnut. From this experiment I think “unpredictable” only means “your shape will be wrong” and not “crash CadQuery and lose all of your recent unsaved changes”.

So it could cause problems in parametric designs if certain values go beyond others. Another downside of using a novel capability absent from other CAD packages is added work if I want to translate it to another CAD package. I rarely try to perform a direct translation, though, given it has rarely worked out well. When Sawppy project moved from Fusion 360 to Onshape, my first export/import effort was a disaster so I redrew from scratch. Given this precedent I think I’ll be fine to use “extrude until” in my CadQuery projects. It’s just something to keep in mind as I explore CadQuery advantages as a code CAD platform.

Exploring CadQuery Errors Via Examples

CadQuery documentation page on assemblies seem to be the last of their conceptual overviews page. After that was a large list of examples, which illustrated many concepts not explicitly covered by earlier overview pages. I expect this page will be a great reference if I continue using CadQuery. To make the most of it, though, I should run through it once now so I have concepts in the back of my head even if I don’t fully understand all the subtleties yet. It also turned out to be an opportunity to see how CadQuery behaves when things don’t go perfectly.

This opportunity came from my decision not to copy/paste each example into CQ-editor. I typed the commands in line by line, pressing F5 after each line to see results of the most recent action. This helps my memory retention and understanding of CadQuery commands. This also means I would occasionally make mistakes as I go.

I’m worried about the fact CadQuery documentation didn’t have a section on how CadQuery handles errors. I’m not talking about raising and catching exceptions kind of error-handling, I’m referring to how it reports errors in user input. FreeCAD set a low bar to clear, as it too often reports only a “Recompute failed!” and left the user up a creek. The good news is that CadQuery seems to offer more feedback, the bad news is the feedback can be quite opaque. Here are two examples:

TypeError: object of type ‘float‘ has no len()

I was mystified when this error came up because I didn’t call len() on anything, but I did type in a pair of coordinates as floating point numbers so I knew at least which line of code to look at. It didn’t take long before I found I had mistakenly typed a period instead of a comma on the second coordinate.

r = r.pushPoints([(0, 0.75),(0.-0.75)])

Great, but the error message was not terribly unhelpful. I’m worried I’ll pull my hair out at future equally unhelpful errors, we’ll just have to see. But I’m optimistic based on my experiment to deliberately make the same error on the first coordinate instead of the second:

r = r.pushPoints([(0. 0.75),(0,-0.75)])

For this line, CadQuery gave me this error:

SyntaxError: invalid syntax. Perhaps you forgot a comma?

This is wonderful! I don’t know why behavior differed between first and second coordinates. (Maybe the negative sign has something to do with it?) But I am very encouraged by the fact CadQuery at least tried to be helpful.

ValueError: No pending wires present

Thanks to CadQuery’s initial overview page, I know what “wires” are in this context. (2D entities with a length but no surface area or volume.) And since I typed in each command on its own separate line, I was able to use CQ-editor’s ability to step through line-by-line to understand this was an error from a call to extrude(). I looked over my commands before calling extrude(), which drew a 2D profile line by line. I eventually found a typo in one of the coordinates, which mean its endpoint didn’t line up with another and left a gap in my 2D profile.

The bad news is this is worse than FreeCAD error message, which at least told me the profile was not closed. The good news is that I can mitigate such errors by using different 2D sketch commands. For example lineTo() will always draw from the previous point to ensure there are no gaps, and close() will draw a line from the previous point to the first point and ensure shape is closed. I hope that will spare me future headaches.

In the meantime, exploring examples introduced me to some novel and potentially powerful tools like “extrude until”.

CadQuery Assemblies Overview

After learning basics of 2D sketching in CadQuery, I continued on to an overview of how CadQuery assembles multiple parts into assemblies. Many open-source CAD software focus only on making individual parts, providing no way to relate parts to each other. FreeCAD has the opposite problem: too many ways. Multiple competing FreeCAD assembly workbenches exist, leaving a beginner user confused. As of this writing, CadQuery has one and only one way to build assemblies, and I see that as an advantage over other alternatives.

The overview page starts with an example that was thin on explanation, fortunately it was there to set the stage for information that followed. Including a constraint solver that finds a way to mate parts together while satisfying all the specified constraints. It looks good on the surface but with three questions to be determined in the future:

  • I will need some first hand experience before I understand constraint specification syntax, at first glance they are quite opaque but I expected that.
  • During said first hand experience I’m sure I will run into errors. It remains to be seen how well I could understand those errors to find and fix their root cause. This is where FreeCAD failed me and I hope I have a better experience in CadQuery.
  • And finally, I don’t know computational performance of CadQuery constraint solver. The longer it takes between iterations, the more frustrating debugging will be.

We can assign different colors to each part in order to visually distinguish parts in an assembly. This is great, but I was surprised to see a complete color reference chart taking up a quarter of this document. It seems like a very large amount of space to convey relatively little information. I see my favorite HTML test color here: “aliceblue” and wonder if these are identical to HTML named colors. if so, there’s even less need to duplicate that full color chart in CadQuery documentation. But I guess it doesn’t hurt anybody by being here, so I shrug and move on.

CadQuery Sketch Overview

I’m getting oriented with CadQuery via the time-honored process of Reading The Fine Manual. After getting an overview of general concepts, I continued onward to an overview of CadQuery’s 2D sketch process. This concept is fairly limited in OpenSCAD to simpler geometry primitives. If I wanted to do something more complex, my takeaway was “do it somewhere else” then bring it into OpenSCAD via SVG or DXF imports. I’m glad to see CadQuery has more sophistication in this area, hopefully suffice for my future projects.

Similar to OpenSCAD, I can sketch in CadQuery by specifying simple 2D primitives. They can be combined using various modes. Fuse adds new shape to an existing one, and I assume it was indeed called “add” at some point by its mode specification character “a”. Similar cut mode probably started as “subtract” given it is “s”. At least intersection mode is still “i”. Finally there is mode “c” which is not cut but construction used to guide future operations.

Examples throughout this guide lets me walk through operations by typing them in step by step in CQ-editor. They illustrate the commands under discussion but they also used a few commands that went unexplained probably because they’re not core to 2D sketch concepts. I saw multiple calls to reset() and an explanation “Note that selectors are implemented, but selection has to be explicitly reset” but I don’t fully understand what that means yet. Nor do I yet understand the role of clean() which removes some visual clutter but I don’t fully understand what it means from a geometry standpoint. Are these operations strictly optional tidying up that I can choose to skip for performance? Or are there times when they are critically important? Or possibly, are there times when I explicit should not call them? Things to learn for the future.

The biggest attraction in this section was constraint-based sketches, a feature I failed to find in any other code CAD platforms I investigated. While I’m very hopeful this will prove useful capturing design intent in my CadQuery projects, I’m aware of the fact it is still an incomplete experimental feature with a small set of constraints. The final proof will be in the using but I think it’ll be fine. In the worst case I get frustrated and end up not using it, which leaves me no worse off than where I am now. I have a similar attitude going into CadQuery assemblies.

CadQuery Concepts Overview

After a few failed attempts, I got CadQuery’s default UI CQ-editor up and running which means it’s time to dive in! Here are some notes after reading CadQuery Concepts overview page.

Workplane vs. workplane

My biggest confusion came from two identically named things that I could only occasionally differentiate from context and capitalization. There is the Workplane class, whose name is always capitalized, and it is the Python object type serving as the main entry point for majority of CadQuery’s top level API. Then there is the general CAD workplane concept, usually defined by two parallel global axes (“XY”). If someone understands that there are two different things going on and understand their context, the next paragraph will make perfect sense. If they don’t, they’ll be hopelessly lost:

A sequence of CadQuery operations is represented by a chain/tree of Workplane objects, each with a reference to its parent except for the root Workplane. Each Workplane has a reference to a workplane and a workplane may be referenced by multiple Workplane objects.

Selectors

I like the theory of CadQuery selectors. It should be a more robust way to specify objects for various operations. Rather than specifying a target via, say, “Face #12” I can specify “Face with the greatest Z coordinate”. “Face #12” is closest to mechanisms within underlying Open Cascade Technology platform, but it is also the root of all FreeCAD “Topological Naming Problem” evil. From my brief look at Cascade Studio I didn’t see it offer a solution to the problem, but a brief look at Replicad found “Finders” which have some superficial similarity to CadQuery selectors. I’m looking forward to learning how well CadQuery selectors work in practice.

API Layers

CadQuery’s top level API, the one with all its user-friendly features like Selectors, is called the “Fluent API” in CadQuery documentation. It’s what I plan to learn, but I’m happy to see I won’t be restricted to the Fluent API. Architecturally underneath is the “Direct API”, which organizes underlying Open Cascade capabilities into Python classes. And underneath that is the “OCCT API” which are minimalist Python translations of the basic C++ API.

There are methods to translate data structures from one layer to another, and it is valid for CadQuery code to move between those layers. Such capabilities greatly reduce any concern of being limited by any one API.


With a quick general concepts overview in mind, I moved onward to learning about 2D sketching in CadQuery.

CQ-editor Standalone On VM Via RDP

I’m looking forward to learning CadQuery and I’m happy to learn constraint solver capability exists. It may be experimental, and it may yet prove to be as frustrating as its FreeCAD counterparts, but knowing it exists gives me a goal to learn enough CadQuery to get there. As part of that journey I thought I would take another attempt at getting its default GUI tool CQ-editor up and running.

On my first pass, I followed instructions to install CadQuery components in a Python virtual environment then installed CQ-editor as an add-on within that Python virtual environment. Sadly I only got into an useless state where I could launch CQ-editor but nothing ever showed on screen. Did I make a mistake somewhere? Or is this a bug? I had no diagnostic feedback. Very FreeCAD-like, and not a great start! At least I could get Jupyter working in that Python virtual environment, so I had something.

Re-reading CadQuery installation instructions again, I noticed there was a standalone installer for CQ-editor. So instead of installing CadQuery as a collection of Python libraries then installing CQ-editor as a consumer of those libraries, we have an installer more along the line of standard desktop software installer. Which in this case means installing CQ-editor and pulling in CadQuery as its dependency.

Wary of the possibility that this installer may go sour and collide with what I have running (it is just a “nightly” build and not an official release) I decided to try it on a virtual machine first. I spun up a new VM with Proxmox and booted it into a network installer for Debian. For most experiments that is enough, as I can remotely control the machine via a command line using SSH. But CQ-editor is a graphical interface tool and my main machine runs Windows 11 so I wanted to access its graphical interface via Microsoft’s Remote Desktop Protocol.

This is a job xrdp. Multiple tutorials exist on the internet for more details (example) but the succinct note for my future self is:

  • Install the component with sudo apt install xrdp
  • Give xrdp user permission to access SSL resources (to establish RDP connection) with sudo adduser xrdp ssl-cert
  • Restart xrdp so it can access its new SSL permissions with sudo systemctl restart xrdp
  • Verify it is back up and running with sudo systemctl status xrdp

This procedure allowed me to connect to this fresh virtual machine via “Remote Desktop Connection” application from my Windows machine, and I could follow instructions for CQ-editor standalone installer without any issue. And this time, when I launched CQ-editor, I saw its interface on screen come up and I could use it. Fantastic! I now have that option in addition to the Jupyter interface so I could play with both. I’m happy to have these tools at hand as I dive into CadQuery primer.

CadQuery Constraints: Friend or Foe?

Dreaming about cars I can’t buy was fun but now it’s back to study time. I’ve got CadQuery + Jupyter set up and ran through its quick start tutorial. I think there’s a lot of promise in CadQuery and plan to invest time to climb its learning curve. Before I roll up my sleeves and dive in, though, I skimmed ahead to check a few features, the biggest one being constraints.

Specifying constraints in CAD is a concept that is almost as old as CAD itself. It is an important tool for a part designer to embed intent into a design ensuring future changes will not deviate from original intent. The ideal target for a design is to be fully constrained. An under-constrained design is ambiguous because it means some attribute can change without violating specifications, and that is rarely desirable. An over-constrained design is an impossible contradiction that needs to be resolved, a task that can be frustrating if a CAD tool fails to provide sufficient feedback. Such frustration can drive people (me) away from certain tools (FreeCAD) to seek out others (CadQuery).

When I explored the world of code-based CAD earlier, I had no luck finding ways to specify constraints or provide tools to resolve constraints. I inferred this to mean that they’ve left constraints to be specified in our code. Want to constrain a line to be tangential to an arc? Calculate their intersection point in your own code and use that point. The upside is that we have full control, the downside is a lot of duplicate effort repeating common constraint-solving tasks.

The good news is that CadQuery doesn’t drop constraints in our lap as our responsibility. Or at least, it has ambition to handle constraints. I found a page discussing sketch constraints (addressing the arc+line tangent example above) but with a disclaimer the feature is still experimental in development. There are also ways to specify constraints in multi-part assemblies, which seem to be a counterpart to Onshape mates that I’ve used to specify relationship between Sawppy parts.

It looks good on paper! But then, FreeCAD looked good on paper as well. I won’t know if CadQuery constraints will be my friend or foe until I get deeper into learning it, but I wanted to know it at least existed before I got too deep into CadQuery. For now I’m still working to get basic tools up and running.

CadQuery Quick Start in Jupyter

After a few unproductive FreeCAD sessions where I spent most of my time trying to understand uninformative error messages, I decided I need to take a break from banging my head against that particular brick wall and look at something else. When I had performed a brief survey of open-source CAD solutions, I only scratched the surface of CadQuery because I had managed to catch it at an awkward stage. At the time most of Python ecosystem have moved to 3.11 but CadQuery had not done so yet. Most critically, I wanted the Jupyter interface and it would not be possible until the version support lines up between those two pieces of software.

In the time since my survey, this problem has been resolved and I could play with CadQuery in a Jupyter notebook. I tried to get the default GUI CQ-editor running just for the sake of looking it over, but I had no luck running it. I would start the executable, I could see an application process running, but no GUI ever showed up on screen. Shrug. I doubt I would have used it anyway because I like the concept of doing CadQuery in Jupyter.

A big advantage I see in code-based CAD is the ability to add code comments in between operations, describing the high-level intent behind the actual code statements. Commenting individual operations is not practical in GUI-based CAD tools like Onshape or FreeCAD. Furthermore, while I could enter comment text with Python comment syntax in CQ-editor, a Jupyter notebook offers significantly more documentation capabilities. As I understand it, it’s literally the difference between plain text and markdown.

While CadQuery’s quick start tutorial was written for a beginner to follow along in CQ-editor, my failure to get CQ-editor running meant I had to convert instructions to Jupyter on the fly. My biggest light bulb moment was realizing I could call display(result) at multiple points in my Jupyter notebook. This gives me a snapshot of the current incomplete state, every time I called display(result). So anyone reading the notebook (like me, 6 months from now) could visually see how the object evolved into its final form. I like where this is going.

Infuriating FreeCAD Error Messages

I haven’t 3D printed anything of my own design in many months, mostly due to a desire to switch from proprietary Onshape CAD to an open source CAD solution. I thought FreeCAD was the obvious thing to learn but after months of struggle I’m starting to think that decision may have been a mistake. While I have a long list of FreeCAD complaints, this page highlights the worst offender in my book: FreeCAD’s error feedback system.

Redundant constraint error has been the biggest enemy to my productivity. The most infuriating aspect is the message “Recompute failed! Please check report view.” It is a general catch-all message promising more details in the report view. The problem is that message is itself displayed in the report view and there are no more details to be found! I thought maybe I need to enable verbose mode or something, so I right-clicked in the report view and activated everything. While that did display more text in the report view, there was nothing I could understand as a helpful direction.

Graphically, redundant constraints are rendered in a different color. But it can be a time-consuming game of hide and seek to find that little icon of a different color within a complex shape. Sometimes I swear I’ve looked at every single constraint and couldn’t find one rendered in a different color.

The toolbar has a “select redundant constraints” icon. In theory I could click on that tool and hit delete to remove the redundant constraints. In practice I click on that tool, hit delete, and get an error message saying nothing is selected.

If the above steps fail, I could click “Undo” to remove the most recent action. Then I have to sit and think about what I did and how it might lead to redundant constraints. I test my hypothesis one by one and sometimes I find a solution that gets me out of my mess.

But then there are the times when I don’t have that option. Recently I drew a profile that I wanted to revolve into a solid. The operation failed with a complaint my profile (wire) is not closed, but it doesn’t tell me where the opening is and I couldn’t find it. The toolbar has a “close shape” icon but, if I click it, my profile is over constrained and I don’t know where to look because I don’t know what “close shape” did. Absent that knowledge I couldn’t form and test hypothesis to repair my shape.

In this specific instance I ended up redrawing the shape from scratch, and it was successful the second time. Experiences like this do not endear me to FreeCAD. Beginners like myself will make mistakes, and we want to learn from our mistakes, but FreeCAD error reporting system is of absolutely zero help. It’s hard for me to be an enthusiastic user when the system wastes my time and saps my motivation by repeatedly telling me to go find a needle in a haystack with no directions.